chore(deps): upgrade to Dart 3.7 (#2894)

This commit is contained in:
Felix Angelov
2025-02-14 13:04:26 -06:00
committed by GitHub
parent c89089d84c
commit c1bdc82104
279 changed files with 12460 additions and 14222 deletions
@@ -33,11 +33,9 @@ class ArtifactManifestClient {
);
final response = await _httpClient.get(url);
if (response.statusCode != HttpStatus.ok) {
throw Exception(
'''
throw Exception('''
Failed to fetch artifacts manifest for revision $revision.
${response.statusCode} ${response.reasonPhrase}''',
);
${response.statusCode} ${response.reasonPhrase}''');
}
return checkedYamlDecode(
@@ -114,7 +114,8 @@ String getFlutterArtifactLocation({
required String artifactPath,
String? engine,
}) {
final adjustedPath = engine != null
final adjustedPath =
engine != null
? artifactPath.replaceAll(r'$engine', engine)
: artifactPath;
@@ -17,21 +17,25 @@ ArtifactsManifest _$ArtifactsManifestFromJson(Map json) => $checkedCreate(
allowedKeys: const [
'flutter_engine_revision',
'storage_bucket',
'artifact_overrides'
'artifact_overrides',
],
);
final val = ArtifactsManifest(
flutterEngineRevision:
$checkedConvert('flutter_engine_revision', (v) => v as String),
flutterEngineRevision: $checkedConvert(
'flutter_engine_revision',
(v) => v as String,
),
storageBucket: $checkedConvert('storage_bucket', (v) => v as String),
artifactOverrides: $checkedConvert('artifact_overrides',
(v) => (v as List<dynamic>).map((e) => e as String).toSet()),
artifactOverrides: $checkedConvert(
'artifact_overrides',
(v) => (v as List<dynamic>).map((e) => e as String).toSet(),
),
);
return val;
},
fieldKeyMap: const {
'flutterEngineRevision': 'flutter_engine_revision',
'storageBucket': 'storage_bucket',
'artifactOverrides': 'artifact_overrides'
'artifactOverrides': 'artifact_overrides',
},
);
+1 -1
View File
@@ -599,4 +599,4 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.6.0 <4.0.0"
dart: ">=3.7.0 <4.0.0"
+1 -1
View File
@@ -5,7 +5,7 @@ repository: https://github.com/shorebirdtech/shorebird/
publish_to: "none"
environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.7.0 <4.0.0"
dependencies:
checked_yaml: ^2.0.2
@@ -63,22 +63,17 @@ void main() {
verify(() => client.getManifest(shorebirdEngineRevision)).called(1);
});
test(
'should proxy to Flutter artifacts '
test('should proxy to Flutter artifacts '
'when no engine revision is detected', () async {
const path =
'flutter_infra_release/flutter/fonts/3012db47f3130e62f7cc0beabff968a33cbec8d8/fonts.zip';
final request = buildRequest(path);
final response = await handler(request);
expect(
response,
isRedirectTo('https://storage.googleapis.com/$path'),
);
expect(response, isRedirectTo('https://storage.googleapis.com/$path'));
verifyNever(() => client.getManifest(any()));
});
test(
'should proxy to Shorebird artifacts '
test('should proxy to Shorebird artifacts '
'when an engine revision is detected with an override', () async {
const path =
'flutter_infra_release/flutter/$shorebirdEngineRevision/android-x64-release/artifacts.zip';
@@ -93,8 +88,7 @@ void main() {
verify(() => client.getManifest(shorebirdEngineRevision)).called(1);
});
test(
'should proxy to Flutter artifacts '
test('should proxy to Flutter artifacts '
'when an engine revision is detected with no override', () async {
const path =
'flutter_infra_release/flutter/$shorebirdEngineRevision/windows-x64/font-subset.zip';
@@ -109,8 +103,7 @@ void main() {
verify(() => client.getManifest(shorebirdEngineRevision)).called(1);
});
test(
'should return 404 '
test('should return 404 '
'when pattern is not recognized', () async {
const path =
'flutter_infra_release/flutter/$shorebirdEngineRevision/unknown/artifacts.zip';
@@ -31,7 +31,7 @@ Handler gcpAlertHandler({required String webhookUrl, http.Client? client}) {
{'name': 'Resource', 'value': resource, 'inline': true},
{'name': 'Condition', 'value': conditionName, 'inline': true},
],
}
},
],
'attachments': const <dynamic>[],
};
@@ -15,9 +15,8 @@ GCPAlert _$GCPAlertFromJson(Map<String, dynamic> json) => $checkedCreate(
final val = GCPAlert(
incident: $checkedConvert(
'incident',
(v) => v == null
? null
: Incident.fromJson(v as Map<String, dynamic>)),
(v) => v == null ? null : Incident.fromJson(v as Map<String, dynamic>),
),
);
return val;
},
@@ -40,6 +39,6 @@ Incident _$IncidentFromJson(Map<String, dynamic> json) => $checkedCreate(
fieldKeyMap: const {
'resourceName': 'resource_name',
'conditionName': 'condition_name',
'policyName': 'policy_name'
'policyName': 'policy_name',
},
);
+1 -1
View File
@@ -591,4 +591,4 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.6.0 <4.0.0"
dart: ">=3.7.0 <4.0.0"
+1 -1
View File
@@ -4,7 +4,7 @@ version: 1.0.0
publish_to: none
environment:
sdk: '>=3.0.0 <4.0.0'
sdk: ">=3.7.0 <4.0.0"
dependencies:
http: ^1.3.0
@@ -33,8 +33,7 @@ void main() {
Request(
'POST',
Uri.parse('http://localhost:8080/'),
body: json.encode(
{
body: json.encode({
'version': 'test',
'incident': {
'incident_id': '12345',
@@ -82,8 +81,7 @@ void main() {
'condition_name': 'Example condition',
'threshold_value': '0.5',
},
},
),
}),
),
);
@@ -109,9 +107,9 @@ void main() {
'name': 'Condition',
'value': 'Example condition',
'inline': true,
}
},
],
}
},
],
'attachments': <dynamic>[],
}),
+3 -13
View File
@@ -40,11 +40,7 @@ Future<PublicKeyStore?> _getPublicKeys(String url) async {
return null;
}
publicKeyStores.set(
url,
publicKeyStore,
ttl: Duration(seconds: maxAge),
);
publicKeyStores.set(url, publicKeyStore, ttl: Duration(seconds: maxAge));
return publicKeyStore;
}
@@ -105,10 +101,7 @@ Future<Jwt> verify(
return jwt;
}
Future<void> _verifyHeader(
JwtHeader header,
Iterable<String> keyIds,
) async {
Future<void> _verifyHeader(JwtHeader header, Iterable<String> keyIds) async {
if (header.typ != 'JWT') {
throw const JwtVerificationFailure('Invalid token type.');
}
@@ -171,10 +164,7 @@ bool _verifySignature(String jwt, String publicKey) {
final public = pair.public;
final signer = Signer('SHA-256/RSA');
final key = RSAPublicKey(
public!.modulus,
BigInt.from(public.publicExponent),
);
final key = RSAPublicKey(public!.modulus, BigInt.from(public.publicExponent));
final param = ParametersWithRandom(
PublicKeyParameter<RSAPublicKey>(key),
SecureRandom('AES/CTR/PRNG'),
+7 -8
View File
@@ -8,20 +8,19 @@ part of 'jwk.dart';
// JsonSerializableGenerator
// **************************************************************************
Jwk _$JwkFromJson(Map<String, dynamic> json) => $checkedCreate(
'Jwk',
json,
($checkedConvert) {
Jwk _$JwkFromJson(Map<String, dynamic> json) =>
$checkedCreate('Jwk', json, ($checkedConvert) {
final val = Jwk(
kty: $checkedConvert('kty', (v) => v as String),
use: $checkedConvert('use', (v) => v as String),
kid: $checkedConvert('kid', (v) => v as String),
x5c: $checkedConvert('x5c',
(v) => (v as List<dynamic>).map((e) => e as String).toList()),
x5c: $checkedConvert(
'x5c',
(v) => (v as List<dynamic>).map((e) => e as String).toList(),
),
x5t: $checkedConvert('x5t', (v) => v as String),
n: $checkedConvert('n', (v) => v as String),
e: $checkedConvert('e', (v) => v as String),
);
return val;
},
);
});
+1 -5
View File
@@ -8,11 +8,7 @@ part 'jwt_header.g.dart';
@JsonSerializable(createToJson: false)
class JwtHeader {
/// {@macro jwt_header}
const JwtHeader({
required this.alg,
required this.kid,
required this.typ,
});
const JwtHeader({required this.alg, required this.kid, required this.typ});
/// Decode a [JwtHeader] from a `Map<String, dynamic>`.
factory JwtHeader.fromJson(Map<String, dynamic> json) {
@@ -8,15 +8,12 @@ part of 'jwt_header.dart';
// JsonSerializableGenerator
// **************************************************************************
JwtHeader _$JwtHeaderFromJson(Map<String, dynamic> json) => $checkedCreate(
'JwtHeader',
json,
($checkedConvert) {
JwtHeader _$JwtHeaderFromJson(Map<String, dynamic> json) =>
$checkedCreate('JwtHeader', json, ($checkedConvert) {
final val = JwtHeader(
alg: $checkedConvert('alg', (v) => v as String),
kid: $checkedConvert('kid', (v) => v as String),
typ: $checkedConvert('typ', (v) => v as String),
);
return val;
},
);
});
@@ -8,10 +8,8 @@ part of 'jwt_payload.dart';
// JsonSerializableGenerator
// **************************************************************************
JwtPayload _$JwtPayloadFromJson(Map<String, dynamic> json) => $checkedCreate(
'JwtPayload',
json,
($checkedConvert) {
JwtPayload _$JwtPayloadFromJson(Map<String, dynamic> json) =>
$checkedCreate('JwtPayload', json, ($checkedConvert) {
final val = JwtPayload(
exp: $checkedConvert('exp', (v) => (v as num).toInt()),
iat: $checkedConvert('iat', (v) => (v as num).toInt()),
@@ -21,6 +19,4 @@ JwtPayload _$JwtPayloadFromJson(Map<String, dynamic> json) => $checkedCreate(
authTime: $checkedConvert('auth_time', (v) => (v as num?)?.toInt()),
);
return val;
},
fieldKeyMap: const {'authTime': 'auth_time'},
);
}, fieldKeyMap: const {'authTime': 'auth_time'});
@@ -8,17 +8,16 @@ part of 'jwk_key_store.dart';
// JsonSerializableGenerator
// **************************************************************************
JwkKeyStore _$JwkKeyStoreFromJson(Map<String, dynamic> json) => $checkedCreate(
'JwkKeyStore',
json,
($checkedConvert) {
JwkKeyStore _$JwkKeyStoreFromJson(Map<String, dynamic> json) =>
$checkedCreate('JwkKeyStore', json, ($checkedConvert) {
final val = JwkKeyStore(
keys: $checkedConvert(
'keys',
(v) => (v as List<dynamic>)
(v) =>
(v as List<dynamic>)
.map((e) => Jwk.fromJson(e as Map<String, dynamic>))
.toList()),
.toList(),
),
);
return val;
},
);
});
+1 -1
View File
@@ -4,7 +4,7 @@ version: 1.0.0+1
publish_to: none
environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.7.0 <4.0.0"
dependencies:
clock: ^1.1.0
+13 -10
View File
@@ -24,8 +24,9 @@ void main() {
final jwkKeyStoreJsonString =
File(p.join('test', 'fixtures', 'jwk_key_store.json')).readAsStringSync();
final keyValueKeyStoreString =
File(p.join('test', 'fixtures', 'key_value_key_store.json'))
.readAsStringSync();
File(
p.join('test', 'fixtures', 'key_value_key_store.json'),
).readAsStringSync();
final expiresAt = DateTime.fromMillisecondsSinceEpoch(1643687866 * 1000);
final validTime = expiresAt.subtract(const Duration(minutes: 15));
@@ -69,7 +70,8 @@ void main() {
);
});
test('throws a JwtVerificationFailure if string is not valid jwt',
test(
'throws a JwtVerificationFailure if string is not valid jwt',
() async {
await expectLater(
() => verify(
@@ -86,7 +88,8 @@ void main() {
),
),
);
});
},
);
test('throws a JwtVerificationFailure if payload is not valid', () async {
await expectLater(
@@ -106,7 +109,8 @@ void main() {
);
});
test('throws a JwtVerificationFailure if signature is not valid',
test(
'throws a JwtVerificationFailure if signature is not valid',
() async {
await withClock(Clock.fixed(validTime), () async {
await expectLater(
@@ -125,7 +129,8 @@ void main() {
),
);
});
});
},
);
test('throws exception if jwt has no matching public key id', () async {
await withClock(Clock.fixed(validTime), () async {
@@ -147,8 +152,7 @@ void main() {
});
});
test(
'throws exception if invalid keys are provided '
test('throws exception if invalid keys are provided '
'by the publicKeysUrl (KeyValueKeyStore)', () async {
getOverride = (Uri uri) async {
return Response(
@@ -177,8 +181,7 @@ void main() {
});
});
test(
'throws exception if invalid keys are provided '
test('throws exception if invalid keys are provided '
'by the publicKeysUrl (JwkKeyStore)', () async {
getOverride = (Uri uri) async {
return Response(
@@ -1 +1,6 @@
include: package:very_good_analysis/analysis_options.7.0.0.yaml
linter:
rules:
# Disabling in favor of the new Dart 3.7 format.
# https://github.com/VeryGoodOpenSource/very_good_analysis/issues/136
require_trailing_commas: false
@@ -160,10 +160,7 @@ class RedisClient {
/// Authenticate to the Redis server.
/// Equivalent to the `AUTH` command.
/// https://redis.io/commands/auth
Future<void> auth({
required String password,
String username = 'default',
}) {
Future<void> auth({required String password, String username = 'default'}) {
return execute(['AUTH', username, password]);
}
@@ -205,14 +202,11 @@ class RedisClient {
/// Send a command to the Redis server.
Future<dynamic> execute(List<Object?> command) async {
return _runWithRetry(
() async {
return _runWithRetry(() async {
final result = await RespCommandsTier0(_client!).execute(command);
if (result.isError) throw RedisException(result.toString());
return result.payload;
},
command: command.join(' '),
);
}, command: command.join(' '));
}
/// Establish a connection to the Redis server.
@@ -401,10 +395,7 @@ class RedisJson {
/// Returns null if the key does not exist.
/// Equivalent to the `JSON.GET` command.
/// https://redis.io/commands/json.get
Future<dynamic> get({
required String key,
String path = r'$',
}) async {
Future<dynamic> get({required String key, String path = r'$'}) async {
final result = await _client.execute(['JSON.GET', key, path]);
if (result is String) {
final parts = LineSplitter.split(result);
+1 -1
View File
@@ -6,7 +6,7 @@ repository: https://github.com/shorebirdtech/shorebird/tree/main/packages/redis_
topics: [redis, cache, shorebird]
environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.7.0 <4.0.0"
dependencies:
resp_client: ^1.2.0
@@ -27,13 +27,19 @@ void main() {
});
group('connect', () {
test('authenticates automatically when credentials are provided',
test(
'authenticates automatically when credentials are provided',
() async {
await expectLater(client.connect(), completes);
await expectLater(client.execute(['PING']), completion(equals('PONG')));
});
await expectLater(
client.execute(['PING']),
completion(equals('PONG')),
);
},
);
test('throws SocketException when connection times out w/retry',
test(
'throws SocketException when connection times out w/retry',
() async {
final client = RedisClient(
socket: const RedisSocketOptions(
@@ -52,7 +58,8 @@ void main() {
),
);
await client.close();
});
},
);
test('throws SocketException after max connection attempts', () async {
final client = RedisClient(
@@ -159,10 +166,7 @@ void main() {
});
test('succeeds when username/password are correct', () async {
await expectLater(
client.auth(password: 'password'),
completes,
);
await expectLater(client.auth(password: 'password'), completes);
});
});
@@ -205,8 +209,7 @@ void main() {
await expectLater(client.get(key: key), completion(isNull));
});
test(
'throws TimeoutException '
test('throws TimeoutException '
'when command timeout is exceeded', () async {
final client = RedisClient(
command: const RedisCommandOptions(timeout: Duration.zero),
@@ -243,9 +246,7 @@ void main() {
'nested': {
'1.0.0+1': {
'android': {
'arch64': {
'url': 'http://example.com',
},
'arch64': {'url': 'http://example.com'},
},
},
},
@@ -268,17 +269,13 @@ void main() {
await expectLater(
client.json.get(key: key, path: r'$.nested'),
completion(
equals(
{
equals({
'1.0.0+1': {
'android': {
'arch64': {
'url': 'http://example.com',
'arch64': {'url': 'http://example.com'},
},
},
},
},
),
}),
),
);
await expectLater(
@@ -1,3 +1,8 @@
include: package:very_good_analysis/analysis_options.7.0.0.yaml
linter:
rules:
# Disabling in favor of the new Dart 3.7 format.
# https://github.com/VeryGoodOpenSource/very_good_analysis/issues/136
require_trailing_commas: false
analyzer:
exclude: ["example/**"]
+4 -11
View File
@@ -10,9 +10,7 @@ class ScopedRef<T> {
/// {@macro scoped_ref}
ScopedRef(this._create) : _key = Object();
ScopedRef._(T Function() create, Object key)
: _create = create,
_key = key;
ScopedRef._(T Function() create, Object key) : _create = create, _key = key;
final T Function() _create;
final Object _key;
@@ -47,20 +45,15 @@ T read<T>(ScopedRef<T> ref, {T Function()? orElse}) {
final value = (Zone.current[ref._key] as ScopedRef<T>?)?._value;
if (value == null) {
if (orElse != null) return orElse();
throw StateError(
'''
throw StateError('''
read(ScopedRef<$T>) was called in a scope which does not contain a corresponding value for the provided ref.
Did you forget to call: runScoped(() {...}, values: {value})?''',
);
Did you forget to call: runScoped(() {...}, values: {value})?''');
}
return value;
}
/// Runs [body] within a scope which has access to the set of refs in [values].
R runScoped<R>(
R Function() body, {
Set<ScopedRef<dynamic>> values = const {},
}) {
R runScoped<R>(R Function() body, {Set<ScopedRef<dynamic>> values = const {}}) {
return runZoned(
body,
zoneValues: {for (final value in values) value._key: value},
+1 -1
View File
@@ -5,7 +5,7 @@ homepage: https://shorebird.dev
repository: https://github.com/shorebirdtech/shorebird/tree/main/packages/scoped_deps
environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.7.0 <4.0.0"
dependencies:
meta: ^1.0.0
@@ -25,10 +25,7 @@ void main() {
test('read accesses the value when ref is available', () {
final value = create(() => 42);
runScoped(
() => expect(read(value), equals(42)),
values: {value},
);
runScoped(() => expect(read(value), equals(42)), values: {value});
});
test('value is computed lazily and cached', () {
@@ -40,14 +37,11 @@ void main() {
expect(createCallCount, equals(0));
runScoped(
() {
runScoped(() {
expect(read(value), equals(42));
expect(read(value), equals(42));
expect(read(value), equals(42));
},
values: {value},
);
}, values: {value});
expect(createCallCount, equals(1));
});
@@ -55,17 +49,14 @@ void main() {
test('value can be overridden', () {
final value = create(() => 42);
runScoped(
() {
runScoped(() {
expect(read(value), equals(42));
runScoped(
() => expect(read(value), equals(0)),
values: {value.overrideWith(() => 0)},
);
},
values: {value},
);
}, values: {value});
});
test('overrides are considered equal', () {
@@ -1,4 +1,9 @@
include: package:very_good_analysis/analysis_options.7.0.0.yaml
linter:
rules:
# Disabling in favor of the new Dart 3.7 format.
# https://github.com/VeryGoodOpenSource/very_good_analysis/issues/136
require_trailing_commas: false
analyzer:
exclude:
- lib/**.g.dart
+6 -7
View File
@@ -37,13 +37,10 @@ Future<void> main(List<String> args) async {
);
// Write the current command to the top of the log file.
currentRunLogFile.writeAsStringSync(
'''
currentRunLogFile.writeAsStringSync('''
Command: shorebird ${args.join(' ')}
''',
mode: FileMode.append,
);
''', mode: FileMode.append);
await IOOverrides.runZoned(
() async => _flushThenExit(
@@ -107,6 +104,8 @@ Command: shorebird ${args.join(' ')}
/// exited already. This is useful to prevent Future chains from proceeding
/// after you've decided to exit.
Future<void> _flushThenExit(int status) {
return Future.wait<void>([stdout.close(), stderr.close()])
.then<void>((_) => exit(status));
return Future.wait<void>([
stdout.close(),
stderr.close(),
]).then<void>((_) => exit(status));
}
@@ -19,13 +19,7 @@ import 'package:uuid/uuid.dart';
R runWithOverrides<R>(R Function() body) {
return runScoped(
body,
values: {
authRef,
httpClientRef,
loggerRef,
platformRef,
shorebirdEnvRef,
},
values: {authRef, httpClientRef, loggerRef, platformRef, shorebirdEnvRef},
);
}
@@ -66,10 +60,7 @@ void main() {
test('--version', () {
final result = runCommand('shorebird --version', workingDirectory: '.');
expect(result.stderr, isEmpty);
expect(
result.stdout,
stringContainsInOrder(['Engine', 'revision']),
);
expect(result.stdout, stringContainsInOrder(['Engine', 'revision']));
expect(result.exitCode, equals(0));
});
@@ -90,8 +81,9 @@ void main() {
final uuid = const Uuid().v4().replaceAll('-', '_');
final testAppName = 'test_app_$uuid';
final tempDir = Directory.systemTemp.createTempSync();
final subDirWithSpace =
Directory(p.join(tempDir.path, 'flutter directory'))..createSync();
final subDirWithSpace = Directory(
p.join(tempDir.path, 'flutter directory'),
)..createSync();
var cwd = subDirWithSpace.path;
// Create the default flutter counter app
@@ -126,21 +118,13 @@ void main() {
completion(
contains(
isA<AppMetadata>()
.having(
(a) => a.appId,
'appId',
shorebirdYaml.appId,
)
.having((a) => a.appId, 'appId', shorebirdYaml.appId)
.having(
(a) => a.latestReleaseVersion,
'latestReleaseVersion',
null,
)
.having(
(a) => a.latestPatchNumber,
'latestPatchNumber',
null,
),
.having((a) => a.latestPatchNumber, 'latestPatchNumber', null),
),
),
);
@@ -175,21 +159,13 @@ void main() {
completion(
contains(
isA<AppMetadata>()
.having(
(a) => a.appId,
'appId',
shorebirdYaml.appId,
)
.having((a) => a.appId, 'appId', shorebirdYaml.appId)
.having(
(a) => a.latestReleaseVersion,
'latestReleaseVersion',
releaseVersion,
)
.having(
(a) => a.latestPatchNumber,
'latestPatchNumber',
null,
),
.having((a) => a.latestPatchNumber, 'latestPatchNumber', null),
),
),
);
@@ -221,21 +197,13 @@ void main() {
completion(
contains(
isA<AppMetadata>()
.having(
(a) => a.appId,
'appId',
shorebirdYaml.appId,
)
.having((a) => a.appId, 'appId', shorebirdYaml.appId)
.having(
(a) => a.latestReleaseVersion,
'latestReleaseVersion',
'1.0.0+1',
)
.having(
(a) => a.latestPatchNumber,
'latestPatchNumber',
1,
),
.having((a) => a.latestPatchNumber, 'latestPatchNumber', 1),
),
),
);
@@ -286,18 +254,16 @@ Future<bool> isPatchAvailable({
required String channel,
}) async {
final response = await http.post(
Uri.parse(Platform.environment['SHOREBIRD_HOSTED_URL']!).replace(
path: '/api/v1/patches/check',
),
body: jsonEncode(
{
Uri.parse(
Platform.environment['SHOREBIRD_HOSTED_URL']!,
).replace(path: '/api/v1/patches/check'),
body: jsonEncode({
'release_version': releaseVersion,
'platform': platform,
'arch': arch,
'app_id': appId,
'channel': channel,
},
),
}),
);
if (response.statusCode != HttpStatus.ok) {
throw Exception('Patch Check Failure: ${response.statusCode}');
@@ -69,7 +69,8 @@ class AndroidSdk {
/// Returns the default path to the Android SDK if a home directory is defined
/// and we're on a recognized platform, or null otherwise.
String? _defaultAndroidSdkPath() {
final home = platform.isWindows
final home =
platform.isWindows
? platform.environment['USERPROFILE']
: platform.environment['HOME'];
if (home == null) {
@@ -52,8 +52,9 @@ class AndroidStudio {
final directoryName = p.basename(directory.path);
// Because we've already performed this match above, we can safely
// assume that this will match.
final versionMatch =
androidStudioRegex.firstMatch(directoryName)!.group(1);
final versionMatch = androidStudioRegex
.firstMatch(directoryName)!
.group(1);
final version = tryParseVersion(versionMatch!, strict: false)!;
final homeFile = File(p.join(directory.path, '.home'));
@@ -43,9 +43,7 @@ class AppleArchiveDiffer extends ArchiveDiffer {
);
/// The regex pattern for identifying executable files within a macOS .app.
static final RegExp macosAppRegex = RegExp(
r'^Contents/MacOS/.+$',
);
static final RegExp macosAppRegex = RegExp(r'^Contents/MacOS/.+$');
/// Files that have been added, removed, or that have changed between the
/// archives at the two provided paths. This method will also unsign mach-o
@@ -101,8 +99,9 @@ class AppleArchiveDiffer extends ArchiveDiffer {
.where((file) => file.isFile)
.where(
(file) =>
_binaryFilePatterns
.any((pattern) => pattern.hasMatch(file.name)) ||
_binaryFilePatterns.any(
(pattern) => pattern.hasMatch(file.name),
) ||
xcFrameworkAppRegex.hasMatch(file.name),
)
.toList();
@@ -23,7 +23,8 @@ class FileSetDiff extends Equatable {
return FileSetDiff(
addedPaths: newPaths.difference(oldPaths),
removedPaths: oldPaths.difference(newPaths),
changedPaths: oldPaths
changedPaths:
oldPaths
.intersection(newPaths)
.where((name) => oldPathHashes[name] != newPathHashes[name])
.toSet(),
@@ -31,10 +32,7 @@ class FileSetDiff extends Equatable {
}
/// Creates an empty FileSetDiff.
FileSetDiff.empty()
: addedPaths = {},
removedPaths = {},
changedPaths = {};
FileSetDiff.empty() : addedPaths = {}, removedPaths = {}, changedPaths = {};
/// File paths that were added.
final Set<String> addedPaths;
@@ -8,9 +8,11 @@ import 'package:propertylistserialization/propertylistserialization.dart';
class Plist {
/// Creates a new [Plist] from the contents of the provided [file].
Plist({required File file}) {
properties = PropertyListSerialization.propertyListWithString(
properties =
PropertyListSerialization.propertyListWithString(
file.readAsStringSync(),
) as Map<String, Object>;
)
as Map<String, Object>;
}
/// This key is a user-visible string for the version of the bundle. The
@@ -17,9 +17,11 @@ class ArtifactBuildException implements Exception {
String? fixRecommendation,
}) : stdout = stdout ?? [],
stderr = stderr ?? [] {
flutterError =
_errorMessageFromOutput(this.stdout + this.stderr).join('\n');
this.fixRecommendation = fixRecommendation ??
flutterError = _errorMessageFromOutput(
this.stdout + this.stderr,
).join('\n');
this.fixRecommendation =
fixRecommendation ??
_recommendationFromOutput(this.stdout + this.stderr);
}
@@ -46,9 +46,7 @@ class IpaBuildResult {
/// {@endtemplate}
class IosFrameworkBuildResult {
/// {@macro ios_framework_build_result}
IosFrameworkBuildResult({
required this.kernelFile,
});
IosFrameworkBuildResult({required this.kernelFile});
/// The app.dill file produced by this invocation of `flutter build ipa`.
final File kernelFile;
@@ -136,7 +134,8 @@ class ArtifactBuilder {
}
});
final stderrLines = await buildProcess.stderr
final stderrLines =
await buildProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
@@ -257,11 +256,7 @@ class ArtifactBuilder {
...args,
];
final result = await process.run(
executable,
arguments,
runInShell: true,
);
final result = await process.run(executable, arguments, runInShell: true);
if (result.exitCode != ExitCode.success.code) {
throw ArtifactBuildException.fromProcessResult(
@@ -305,7 +300,8 @@ class ArtifactBuilder {
stdoutLines.add(line);
});
final stderrLines = await buildProcess.stderr
final stderrLines =
await buildProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
@@ -376,7 +372,8 @@ class ArtifactBuilder {
// }
});
final stderrLines = await buildProcess.stderr
final stderrLines =
await buildProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
@@ -450,7 +447,8 @@ class ArtifactBuilder {
}
});
final stderrLines = await buildProcess.stderr
final stderrLines =
await buildProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
@@ -510,11 +508,7 @@ class ArtifactBuilder {
...args,
];
final result = await process.run(
executable,
arguments,
runInShell: true,
);
final result = await process.run(executable, arguments, runInShell: true);
if (result.exitCode != ExitCode.success.code) {
throw ArtifactBuildException('Failed to build: ${result.stderr}');
@@ -569,13 +563,11 @@ class ArtifactBuilder {
);
if (result.exitCode != ExitCode.success.code) {
logger.warn(
'''
logger.warn('''
Build was successful, but `flutter pub get` failed to run after the build completed. You may see unexpected behavior in VS Code.
Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCodeUrl.toLink()}.
''',
);
''');
}
}
@@ -620,12 +612,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
}) async {
await _runShorebirdBuildCommand(() async {
const executable = 'flutter';
final arguments = [
'build',
'windows',
'--release',
...args,
];
final arguments = ['build', 'windows', '--release', ...args];
final buildProcess = await process.start(
executable,
@@ -644,7 +631,8 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
// TODO(bryanoltman): update build progress
});
final stderrLines = await buildProcess.stderr
final stderrLines =
await buildProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
@@ -668,14 +656,17 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
// xcodebuild -list is a command run early in `flutter build ipa` to read
// build settings and schemes. Most users aren't familiar with this command,
// so we translate it to "Collecting schemes" below.
final collectingSchemesRegex =
RegExp(r'\[.*\] executing:.*xcrun xcodebuild -list$');
final collectingSchemesRegex = RegExp(
r'\[.*\] executing:.*xcrun xcodebuild -list$',
);
final archivingRegex = RegExp(r'^\[.*\] (Archiving .+$)');
final runningXcodeBuildRegex = RegExp(r'^\[.*\] (Running Xcode build).*$');
final compilingLinkingSigningRegex =
RegExp(r'^\[.*\]\s+└─(Compiling, linking and signing).*$');
final buildingAppStoreIpaRegex =
RegExp(r'^\[.*\] (Building App Store IPA).*$');
final compilingLinkingSigningRegex = RegExp(
r'^\[.*\]\s+└─(Compiling, linking and signing).*$',
);
final buildingAppStoreIpaRegex = RegExp(
r'^\[.*\] (Building App Store IPA).*$',
);
final builtAppStoreIpaRegex = RegExp(r'^\[.*\] ✓ (Built IPA to \S+).*$');
final regexes = [
@@ -706,7 +697,9 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
/// command, finds the path to the app.dill file that was built.
@visibleForTesting
String? findAppDill({required String stdout}) {
final appDillLine = stdout.split('\n').firstWhereOrNull(
final appDillLine = stdout
.split('\n')
.firstWhereOrNull(
(l) => l.contains('gen_snapshot') && l.endsWith('app.dill'),
);
@@ -77,14 +77,8 @@ class ArtifactManager {
/// temporary directory if not.
///
/// Returns the downloaded [File].
Future<File> downloadFile(
Uri uri, {
String? outputPath,
}) async {
final download = await startFileDownload(
uri,
outputPath: outputPath,
);
Future<File> downloadFile(Uri uri, {String? outputPath}) async {
final download = await startFileDownload(uri, outputPath: outputPath);
return download.file;
}
@@ -94,10 +88,7 @@ class ArtifactManager {
/// Returns a [FileDownload] object containing the [Future<File>] and a
/// [Stream] of download progress updates.
@visibleForTesting
Future<FileDownload> startFileDownload(
Uri uri, {
String? outputPath,
}) async {
Future<FileDownload> startFileDownload(Uri uri, {String? outputPath}) async {
final request = http.Request('GET', uri);
final response = await httpClient.send(request);
@@ -235,30 +226,15 @@ class ArtifactManager {
//
// See https://github.com/shorebirdtech/shorebird/issues/1798
final strippedSymbolsDir = Directory(
p.join(
releasePath,
stripReleaseDebugSymbolsDirName,
),
p.join(releasePath, stripReleaseDebugSymbolsDirName),
);
final Directory archsDirectory;
if (strippedSymbolsDir.existsSync()) {
archsDirectory = Directory(
p.join(
strippedSymbolsDir.path,
'out',
'lib',
),
);
archsDirectory = Directory(p.join(strippedSymbolsDir.path, 'out', 'lib'));
} else {
// If the new path doesn't exist, fallback to the old path.
archsDirectory = Directory(
p.join(
releasePath,
'out',
'lib',
),
);
archsDirectory = Directory(p.join(releasePath, 'out', 'lib'));
}
return archsDirectory.existsSync() ? archsDirectory : null;
@@ -301,12 +277,7 @@ class ArtifactManager {
Directory? getXcarchiveDirectory() {
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
final archiveDirectory = Directory(
p.join(
projectRoot.path,
'build',
'ios',
'archive',
),
p.join(projectRoot.path, 'build', 'ios', 'archive'),
);
if (!archiveDirectory.existsSync()) return null;
@@ -330,11 +301,7 @@ class ArtifactManager {
/// traditionally named `Runner.app`, but can now be renamed.
Directory? getIosAppDirectory({required Directory xcarchiveDirectory}) {
final applicationsDirectory = Directory(
p.join(
xcarchiveDirectory.path,
'Products',
'Applications',
),
p.join(xcarchiveDirectory.path, 'Products', 'Applications'),
);
if (!applicationsDirectory.existsSync()) {
@@ -351,14 +318,7 @@ class ArtifactManager {
Directory get linuxBundleDirectory {
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
return Directory(
p.join(
projectRoot.path,
'build',
'linux',
'x64',
'release',
'bundle',
),
p.join(projectRoot.path, 'build', 'linux', 'x64', 'release', 'bundle'),
);
}
@@ -366,14 +326,7 @@ class ArtifactManager {
Directory getWindowsReleaseDirectory() {
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
return Directory(
p.join(
projectRoot.path,
'build',
'windows',
'x64',
'runner',
'Release',
),
p.join(projectRoot.path, 'build', 'windows', 'x64', 'runner', 'Release'),
);
}
@@ -386,12 +339,7 @@ class ArtifactManager {
File? getIpa() {
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
final ipaBuildDirectory = Directory(
p.join(
projectRoot.path,
'build',
'ios',
'ipa',
),
p.join(projectRoot.path, 'build', 'ios', 'ipa'),
);
if (!ipaBuildDirectory.existsSync()) {
@@ -475,13 +423,7 @@ class ArtifactManager {
Directory getAppXcframeworkDirectory() {
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
return Directory(
p.join(
projectRoot.path,
'build',
'ios',
'framework',
'Release',
),
p.join(projectRoot.path, 'build', 'ios', 'framework', 'Release'),
);
}
}
+15 -16
View File
@@ -41,7 +41,8 @@ const microsoftJwtIssuerPrefix = 'https://login.microsoftonline.com/';
const shorebirdTokenEnvVar = 'SHOREBIRD_TOKEN';
/// Callback for obtaining access credentials.
typedef ObtainAccessCredentials = Future<oauth2.AccessCredentials> Function(
typedef ObtainAccessCredentials =
Future<oauth2.AccessCredentials> Function(
oauth2.ClientId clientId,
List<String> scopes,
http.Client client,
@@ -50,7 +51,8 @@ typedef ObtainAccessCredentials = Future<oauth2.AccessCredentials> Function(
});
/// Callback for refreshing access credentials.
typedef RefreshCredentials = Future<oauth2.AccessCredentials> Function(
typedef RefreshCredentials =
Future<oauth2.AccessCredentials> Function(
oauth2.ClientId clientId,
oauth2.AccessCredentials credentials,
http.Client client, {
@@ -58,9 +60,8 @@ typedef RefreshCredentials = Future<oauth2.AccessCredentials> Function(
});
/// Callback when credentials are refreshed.
typedef OnRefreshCredentials = void Function(
oauth2.AccessCredentials credentials,
);
typedef OnRefreshCredentials =
void Function(oauth2.AccessCredentials credentials);
/// A client that automatically refreshes OAuth 2.0 credentials.
class AuthenticatedClient extends http.BaseClient {
@@ -116,7 +117,8 @@ class AuthenticatedClient extends http.BaseClient {
if (credentials == null) {
final token = _token!;
credentials = _credentials = await _tryRefreshCredentials(
credentials =
_credentials = await _tryRefreshCredentials(
token.authProvider.clientId,
oauth2.AccessCredentials(
// This isn't relevant for a refresh operation.
@@ -134,7 +136,8 @@ class AuthenticatedClient extends http.BaseClient {
final jwt = Jwt.parse(credentials.idToken!);
final authProvider = jwt.authProvider;
credentials = _credentials = await _tryRefreshCredentials(
credentials =
_credentials = await _tryRefreshCredentials(
authProvider.clientId,
credentials,
_baseClient,
@@ -186,7 +189,8 @@ class Auth {
}) : _httpClient = httpClient ?? _defaultHttpClient,
_credentialsDir =
credentialsDir ?? applicationConfigHome(executableName),
_obtainAccessCredentials = obtainAccessCredentials ??
_obtainAccessCredentials =
obtainAccessCredentials ??
oauth2.obtainAccessCredentialsViaUserConsent,
_buildCodePushClient = buildCodePushClient ?? CodePushClient.new {
_loadCredentials();
@@ -212,10 +216,7 @@ class Auth {
}
if (_token != null) {
return AuthenticatedClient.token(
token: _token!,
httpClient: _httpClient,
);
return AuthenticatedClient.token(token: _token!, httpClient: _httpClient);
}
return AuthenticatedClient.credentials(
@@ -321,12 +322,10 @@ class Auth {
_token = CiToken.fromBase64(envToken.trim());
} on FormatException catch (e) {
logger
..err(
'''
..err('''
Failed to parse CI token from environment. This likely means that your CI token is incorrectly formatted.
Please regenerate using `shorebird login:ci`, update the $shorebirdTokenEnvVar environment variable, and try again.''',
)
Please regenerate using `shorebird login:ci`, update the $shorebirdTokenEnvVar environment variable, and try again.''')
..detail(e.toString());
rethrow;
}
@@ -15,13 +15,15 @@ CiToken _$CiTokenFromJson(Map<String, dynamic> json) => $checkedCreate(
final val = CiToken(
refreshToken: $checkedConvert('refresh_token', (v) => v as String),
authProvider: $checkedConvert(
'auth_provider', (v) => $enumDecode(_$AuthProviderEnumMap, v)),
'auth_provider',
(v) => $enumDecode(_$AuthProviderEnumMap, v),
),
);
return val;
},
fieldKeyMap: const {
'refreshToken': 'refresh_token',
'authProvider': 'auth_provider'
'authProvider': 'auth_provider',
},
);
+5 -13
View File
@@ -98,23 +98,17 @@ class Cache {
/// The Shorebird cache directory.
static Directory get shorebirdCacheDirectory {
return Directory(
p.join(shorebirdEnv.shorebirdRoot.path, 'bin', 'cache'),
);
return Directory(p.join(shorebirdEnv.shorebirdRoot.path, 'bin', 'cache'));
}
/// The Shorebird cached previews directory.
static Directory get shorebirdPreviewsDirectory {
return Directory(
p.join(shorebirdCacheDirectory.path, 'previews'),
);
return Directory(p.join(shorebirdCacheDirectory.path, 'previews'));
}
/// The Shorebird cached artifacts directory.
static Directory get shorebirdArtifactsDirectory {
return Directory(
p.join(shorebirdCacheDirectory.path, 'artifacts'),
);
return Directory(p.join(shorebirdCacheDirectory.path, 'artifacts'));
}
final List<CachedArtifact> _artifacts = [];
@@ -208,12 +202,10 @@ abstract class CachedArtifact {
try {
response = await httpClient.send(request);
} catch (error) {
throw CacheUpdateFailure(
'''
throw CacheUpdateFailure('''
Failed to download $fileName: $error
If you're behind a firewall/proxy, please, make sure shorebird_cli is
allowed to access $storageUrl.''',
);
allowed to access $storageUrl.''');
}
if (response.statusCode != HttpStatus.ok) {
@@ -84,10 +84,7 @@ class CodePushClientWrapper {
final CodePushClient codePushClient;
Future<App> createApp({
required int organizationId,
String? appName,
}) async {
Future<App> createApp({required int organizationId, String? appName}) async {
late final String displayName;
if (appName == null) {
final defaultAppName = shorebirdEnv.getPubspecYaml()?.name;
@@ -132,11 +129,9 @@ class CodePushClientWrapper {
Future<AppMetadata> getApp({required String appId}) async {
final app = await maybeGetApp(appId: appId);
if (app == null) {
logger.err(
'''
logger.err('''
Could not find app with id: "$appId".
This app may not exist or you may not have permission to view it.''',
);
This app may not exist or you may not have permission to view it.''');
throw ProcessExit(ExitCode.software.code);
}
@@ -191,10 +186,7 @@ This app may not exist or you may not have permission to view it.''',
required ReleasePlatform platform,
}) {
if (release.platformStatuses[platform] == ReleaseStatus.active) {
final uri = ShorebirdWebConsole.appReleaseUri(
release.appId,
release.id,
);
final uri = ShorebirdWebConsole.appReleaseUri(release.appId, release.id);
logger.err(
'''
It looks like you have an existing ${platform.name} release for version ${lightCyan.wrap(release.version)}.
@@ -216,14 +208,12 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
);
if (release == null) {
logger.err(
'''
logger.err('''
Release not found: "$releaseVersion"
Patches can only be published for existing releases.
Please create a release using "shorebird release" and try again.
''',
);
''');
throw ProcessExit(ExitCode.software.code);
}
@@ -478,11 +468,9 @@ Looked in:
);
} on CodePushConflictException catch (_) {
// Newlines are due to how logger.info interacts with logger.progress.
logger.info(
'''
logger.info('''
${arch.arch} artifact already exists, continuing...''',
);
${arch.arch} artifact already exists, continuing...''');
} catch (error) {
_handleErrorAndExit(
error,
@@ -506,11 +494,9 @@ ${arch.arch} artifact already exists, continuing...''',
);
} on CodePushConflictException catch (_) {
// Newlines are due to how logger.info interacts with logger.progress.
logger.info(
'''
logger.info('''
aab artifact already exists, continuing...''',
);
aab artifact already exists, continuing...''');
} catch (error) {
_handleErrorAndExit(
error,
@@ -545,11 +531,9 @@ aab artifact already exists, continuing...''',
);
} on CodePushConflictException catch (_) {
// Newlines are due to how logger.info interacts with logger.progress.
logger.info(
'''
logger.info('''
Windows release (exe) artifact already exists, continuing...''',
);
Windows release (exe) artifact already exists, continuing...''');
} catch (error) {
_handleErrorAndExit(
error,
@@ -594,11 +578,9 @@ Windows release (exe) artifact already exists, continuing...''',
);
} on CodePushConflictException catch (_) {
// Newlines are due to how logger.info interacts with logger.progress.
logger.info(
'''
logger.info('''
${arch.arch} artifact already exists, continuing...''',
);
${arch.arch} artifact already exists, continuing...''');
} catch (error) {
_handleErrorAndExit(
error,
@@ -622,11 +604,9 @@ ${arch.arch} artifact already exists, continuing...''',
);
} on CodePushConflictException catch (_) {
// Newlines are due to how logger.info interacts with logger.progress.
logger.info(
'''
logger.info('''
aar artifact already exists, continuing...''',
);
aar artifact already exists, continuing...''');
} catch (error) {
_handleErrorAndExit(
error,
@@ -643,8 +623,9 @@ aar artifact already exists, continuing...''',
Future<Directory> _thinXcarchive({required String xcarchivePath}) async {
final xcarchiveDirectoryName = p.basename(xcarchivePath);
final tempDir = Directory.systemTemp.createTempSync();
final thinnedArchiveDirectory =
Directory(p.join(tempDir.path, xcarchiveDirectoryName));
final thinnedArchiveDirectory = Directory(
p.join(tempDir.path, xcarchiveDirectoryName),
);
await io.copyPath(xcarchivePath, thinnedArchiveDirectory.path);
thinnedArchiveDirectory
.listSync(recursive: true)
@@ -776,9 +757,9 @@ aar artifact already exists, continuing...''',
}
if (supplementPath != null) {
final zippedSupplement = await Directory(supplementPath).zipToTempFile(
name: 'ios_supplement',
);
final zippedSupplement = await Directory(
supplementPath,
).zipToTempFile(name: 'ios_supplement');
try {
await codePushClient.createReleaseArtifact(
appId: appId,
@@ -820,7 +801,8 @@ aar artifact already exists, continuing...''',
artifactPath: zippedAppFrameworkFile.path,
arch: 'xcframework',
platform: ReleasePlatform.ios,
hash: sha256
hash:
sha256
.convert(await zippedAppFrameworkFile.readAsBytes())
.toString(),
canSideload: false,
@@ -835,9 +817,9 @@ aar artifact already exists, continuing...''',
}
if (supplementPath != null) {
final zippedSupplement = await Directory(supplementPath).zipToTempFile(
name: 'ios_framework_supplement',
);
final zippedSupplement = await Directory(
supplementPath,
).zipToTempFile(name: 'ios_framework_supplement');
try {
await codePushClient.createReleaseArtifact(
appId: appId,
@@ -948,14 +930,9 @@ aar artifact already exists, continuing...''',
patchArtifactBundles: patchArtifactBundles,
);
final channel = await maybeGetChannel(
appId: appId,
name: track.channel,
) ??
await createChannel(
appId: appId,
name: track.channel,
);
final channel =
await maybeGetChannel(appId: appId, name: track.channel) ??
await createChannel(appId: appId, name: track.channel);
await promotePatch(appId: appId, patchId: patch.id, channel: channel);
@@ -45,7 +45,8 @@ class CodeSigner {
_pemBytes(pemFile: publicKeyPemFile, type: PemLabel.publicKey),
);
final publicKeySeq = ASN1Sequence()
final publicKeySeq =
ASN1Sequence()
..add(ASN1Integer(publicKey.modulus))
..add(ASN1Integer(publicKey.exponent))
..encode();
@@ -94,8 +95,9 @@ extension _RSAPublicKeyFromBytes on RSAPublicKey {
final asn1Parser = ASN1Parser(Uint8List.fromList(bytes));
final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
final publicKeyBitString = topLevelSeq.elements![1] as ASN1BitString;
final publicKeyAsn =
ASN1Parser(publicKeyBitString.stringValues as Uint8List?);
final publicKeyAsn = ASN1Parser(
publicKeyBitString.stringValues as Uint8List?,
);
final publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence;
final modulus = publicKeySeq.elements![0] as ASN1Integer;
final exponent = publicKeySeq.elements![1] as ASN1Integer;
@@ -31,9 +31,7 @@ class CleanCacheCommand extends ShorebirdCommand {
await cache.clear();
} on FileSystemException catch (error) {
final cachePath = Cache.shorebirdCacheDirectory.path;
progress.fail(
'''Failed to delete cache directory $cachePath: $error''',
);
progress.fail('''Failed to delete cache directory $cachePath: $error''');
if (!platform.isWindows) {
return ExitCode.software.code;
@@ -45,12 +43,10 @@ class CleanCacheCommand extends ShorebirdCommand {
),
);
logger.info(
'''
logger.info('''
This could be because a program is using a file in the cache directory. To find and stop such a program, see:
${lightCyan.wrap(superuserLink)}
''',
);
''');
return ExitCode.software.code;
}
@@ -52,12 +52,10 @@ class DoctorCommand extends ShorebirdCommand {
if (flutterVersion != null) {
shorebirdFlutterPrefix.write(' $flutterVersion');
}
output.writeln(
'''
output.writeln('''
Shorebird $packageVersion git@github.com:shorebirdtech/shorebird.git
$shorebirdFlutterPrefix revision ${shorebirdEnv.flutterRevision}
Engine revision ${shorebirdEnv.shorebirdEngineRevision}''',
);
Engine revision ${shorebirdEnv.shorebirdEngineRevision}''');
if (verbose) {
final notDetected = red.wrap('not detected');
@@ -127,9 +125,7 @@ Android Toolchain
'GCP download speed test failed: ${error.message}',
);
} on Exception catch (error) {
downloadProgress.fail(
'GCP download speed test failed: $error',
);
downloadProgress.fail('GCP download speed test failed: $error');
}
logger.info('');
}
@@ -30,10 +30,7 @@ class InitCommand extends ShorebirdCommand {
help: 'Initialize the app even if a "shorebird.yaml" already exists.',
negatable: false,
)
..addOption(
'display-name',
help: 'The display name of the app.',
);
..addOption('display-name', help: 'The display name of the app.');
}
@override
@@ -133,8 +130,9 @@ Please make sure you are running "shorebird init" from within your Flutter proje
// don't care about which flavors are new.
if (!force && newFlavors.isNotEmpty) {
logger.info('New flavors detected: ${newFlavors.join(', ')}');
final updateShorebirdYamlProgress =
logger.progress('Adding flavors to shorebird.yaml');
final updateShorebirdYamlProgress = logger.progress(
'Adding flavors to shorebird.yaml',
);
final AppMetadata existingApp;
try {
@@ -180,14 +178,16 @@ Please make sure you are running "shorebird init" from within your Flutter proje
final needsConfirmation = !force && shorebirdEnv.canAcceptUserInput;
final pubspecName = shorebirdEnv.getPubspecYaml()!.name;
var displayName = results['display-name'] as String?;
displayName ??= needsConfirmation
displayName ??=
needsConfirmation
? logger.prompt(
'${lightGreen.wrap('?')} How should we refer to this app?',
defaultValue: pubspecName,
)
: pubspecName;
final hasNoFlavors = productFlavors.isEmpty;
final hasSomeFlavors = productFlavors.isNotEmpty &&
final hasSomeFlavors =
productFlavors.isNotEmpty &&
((androidFlavors?.isEmpty ?? false) ||
(iosFlavors?.isEmpty ?? false));
@@ -45,10 +45,8 @@ class LoginCiCommand extends ShorebirdCommand {
ciToken = await auth.loginCI(provider, prompt: prompt);
} on UserNotFoundException catch (error) {
logger
..err(
'''
We could not find a Shorebird account for ${error.email}.''',
)
..err('''
We could not find a Shorebird account for ${error.email}.''')
..info(
'''If you have not yet created an account, go to "${link(uri: Uri.parse('https://console.shorebird.dev'))}" to create one. If you believe this is an error, please reach out to us via Discord, we're happy to help!''',
);
@@ -53,10 +53,8 @@ class LoginCommand extends ShorebirdCommand {
} on UserNotFoundException catch (error) {
final consoleUri = Uri.https('console.shorebird.dev');
logger
..err(
'''
We could not find a Shorebird account for ${error.email}.''',
)
..err('''
We could not find a Shorebird account for ${error.email}.''')
..info(
"""If you have not yet created an account, you can do so at "${link(uri: consoleUri)}". If you believe this is an error, please reach out to us via Discord, we're happy to help!""",
);
@@ -66,8 +66,7 @@ class AarPatcher extends Patcher {
required ReleaseArtifact releaseArtifact,
required File releaseArchive,
required File patchArchive,
}) =>
patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
}) => patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
localArchive: patchArchive,
releaseArchive: releaseArchive,
archiveDiffer: const AndroidArchiveDiffer(),
@@ -78,8 +77,9 @@ class AarPatcher extends Patcher {
@override
Future<File> buildPatchArtifact({String? releaseVersion}) async {
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
final buildProgress =
logger.progress('Building patch with Flutter $flutterVersionString');
final buildProgress = logger.progress(
'Building patch with Flutter $flutterVersionString',
);
try {
await artifactBuilder.buildAar(
@@ -57,8 +57,7 @@ See more info about the issue ${link(uri: Uri.parse('https://github.com/shorebir
required ReleaseArtifact releaseArtifact,
required File releaseArchive,
required File patchArchive,
}) =>
patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
}) => patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
localArchive: patchArchive,
releaseArchive: releaseArchive,
archiveDiffer: const AndroidArchiveDiffer(),
@@ -91,14 +90,16 @@ See more info about the issue ${link(uri: Uri.parse('https://github.com/shorebir
logger.warn(updaterPatchErrorWarning);
}
final buildProgress = logger
.detailProgress('Building patch with Flutter $flutterVersionString');
final buildProgress = logger.detailProgress(
'Building patch with Flutter $flutterVersionString',
);
try {
aabFile = await artifactBuilder.buildAppBundle(
flavor: flavor,
target: target,
args: argResults.forwardedArgs +
args:
argResults.forwardedArgs +
buildNameAndNumberArgsFromReleaseVersion(releaseVersion),
base64PublicKey: argResults.encodedPublicKey,
buildProgress: buildProgress,
@@ -117,8 +118,7 @@ See more info about the issue ${link(uri: Uri.parse('https://github.com/shorebir
if (patchArchsBuildDir == null) {
logger
..err('Cannot find patch build artifacts.')
..info(
'''
..info('''
Please run `shorebird cache clean` and try again. If the issue persists, please
file a bug report at https://github.com/shorebirdtech/shorebird/issues/new.
@@ -126,8 +126,7 @@ Looked in:
- build/app/intermediates/stripped_native_libs/stripReleaseDebugSymbols/release/out/lib
- build/app/intermediates/stripped_native_libs/strip{flavor}ReleaseDebugSymbols/{flavor}Release/out/lib
- build/app/intermediates/stripped_native_libs/release/out/lib
- build/app/intermediates/stripped_native_libs/{flavor}Release/out/lib''',
);
- build/app/intermediates/stripped_native_libs/{flavor}Release/out/lib''');
throw ProcessExit(ExitCode.software.code);
}
return aabFile;
@@ -154,8 +153,7 @@ Looked in:
// until we can provide a better solution.
var artifactsDownloadCompleted = false;
unawaited(
Future<void>.delayed(downloadMessageTimeout).then(
(_) {
Future<void>.delayed(downloadMessageTimeout).then((_) {
if (artifactsDownloadCompleted) {
return;
}
@@ -164,14 +162,13 @@ Looked in:
It seems like your download is taking longer than expected. If you are on Windows, this is a known issue.
Please refer to ${link(uri: Uri.parse('https://github.com/shorebirdtech/shorebird/issues/2532'))} for potential workarounds.''',
);
},
),
}),
);
for (final (i, releaseArtifact) in releaseArtifacts.entries.indexed) {
try {
final releaseArtifactFile =
await artifactManager.downloadWithProgressUpdates(
final releaseArtifactFile = await artifactManager
.downloadWithProgressUpdates(
Uri.parse(releaseArtifact.value.url),
message: 'Downloading release artifact ${i + 1}/$numArtifacts',
);
@@ -208,7 +205,8 @@ Please refer to ${link(uri: Uri.parse('https://github.com/shorebirdtech/shorebir
final privateKeyFile = argResults.file(
CommonArguments.privateKeyArg.name,
);
final hashSignature = privateKeyFile != null
final hashSignature =
privateKeyFile != null
? codeSigner.sign(
message: hash,
privateKeyPemFile: privateKeyFile,
@@ -56,15 +56,11 @@ class IosFrameworkPatcher extends Patcher {
'App.class_table.json',
);
String get _vmcodeOutputPath => p.join(
shorebirdEnv.buildDirectory.path,
'out.vmcode',
);
String get _vmcodeOutputPath =>
p.join(shorebirdEnv.buildDirectory.path, 'out.vmcode');
String get _appDillCopyPath => p.join(
shorebirdEnv.buildDirectory.path,
'app.dill',
);
String get _appDillCopyPath =>
p.join(shorebirdEnv.buildDirectory.path, 'app.dill');
@override
String get primaryReleaseArtifactArch => 'xcframework';
@@ -109,8 +105,7 @@ class IosFrameworkPatcher extends Patcher {
required ReleaseArtifact releaseArtifact,
required File releaseArchive,
required File patchArchive,
}) =>
patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
}) => patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
localArchive: patchArchive,
releaseArchive: releaseArchive,
archiveDiffer: const AppleArchiveDiffer(),
@@ -208,20 +203,11 @@ class IosFrameworkPatcher extends Patcher {
'Extracted release artifact to $releaseXcframeworkPath',
);
final releaseArtifactFile = File(
p.join(
releaseXcframeworkPath,
'ios-arm64',
'App.framework',
'App',
),
p.join(releaseXcframeworkPath, 'ios-arm64', 'App.framework', 'App'),
);
final aotSnapshotFile = File(
p.join(
shorebirdEnv.getShorebirdProjectRoot()!.path,
'build',
'out.aot',
),
p.join(shorebirdEnv.getShorebirdProjectRoot()!.path, 'build', 'out.aot'),
);
final useLinker = AotTools.usesLinker(shorebirdEnv.flutterRevision);
if (useLinker) {
@@ -240,9 +226,9 @@ class IosFrameworkPatcher extends Patcher {
// Copy the patch's class table link info file to the build directory
// so that it can be used to generate a patch.
File(_patchClassTableLinkInfoFile).copySync(
p.join(shorebirdEnv.buildDirectory.path, 'out.ct.link'),
);
File(
_patchClassTableLinkInfoFile,
).copySync(p.join(shorebirdEnv.buildDirectory.path, 'out.ct.link'));
File(_patchClassTableLinkDebugInfoPath).copySync(
p.join(shorebirdEnv.buildDirectory.path, 'out.class_table.json'),
);
@@ -311,8 +297,7 @@ class IosFrameworkPatcher extends Patcher {
@override
Future<CreatePatchMetadata> updatedCreatePatchMetadata(
CreatePatchMetadata metadata,
) async =>
metadata.copyWith(
) async => metadata.copyWith(
linkPercentage: lastBuildLinkPercentage,
environment: metadata.environment.copyWith(
xcodeVersion: await xcodeBuild.version(),
@@ -126,8 +126,8 @@ class IosPatcher extends Patcher {
// can be nondeterministic. So we still have some hope of alerting users of
// unpatchable native changes, we compare the Podfile.lock hash between the
// patch and the release.
final diffStatus =
await patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
final diffStatus = await patchDiffChecker
.confirmUnpatchableDiffsIfNecessary(
localArchive: patchArchive,
releaseArchive: releaseArchive,
archiveDiffer: const AppleArchiveDiffer(),
@@ -142,7 +142,8 @@ class IosPatcher extends Patcher {
final String? podfileLockHash;
if (shorebirdEnv.iosPodfileLockFile.existsSync()) {
podfileLockHash = sha256
podfileLockHash =
sha256
.convert(shorebirdEnv.iosPodfileLockFile.readAsBytesSync())
.toString();
} else {
@@ -175,18 +176,17 @@ This may indicate that the patch contains native changes, which cannot be applie
Future<File> buildPatchArtifact({String? releaseVersion}) async {
try {
final shouldCodesign = argResults['codesign'] == true;
final (flutterVersionAndRevision, flutterVersion) = await (
final (flutterVersionAndRevision, flutterVersion) =
await (
shorebirdFlutter.getVersionAndRevision(),
shorebirdFlutter.getVersion(),
).wait;
if ((flutterVersion ?? minimumSupportedIosFlutterVersion) <
minimumSupportedIosFlutterVersion) {
logger.err(
'''
logger.err('''
iOS patches are not supported with Flutter versions older than $minimumSupportedIosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
);
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.software.code);
}
@@ -201,7 +201,8 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
codesign: shouldCodesign,
flavor: flavor,
target: target,
args: argResults.forwardedArgs +
args:
argResults.forwardedArgs +
buildNameAndNumberArgsFromReleaseVersion(releaseVersion),
base64PublicKey: argResults.encodedPublicKey,
buildProgress: buildProgress,
@@ -299,12 +300,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
throw ProcessExit(ExitCode.software.code);
}
final releaseArtifactFile = File(
p.join(
appDirectory.path,
'Frameworks',
'App.framework',
'App',
),
p.join(appDirectory.path, 'Frameworks', 'App.framework', 'App'),
);
final useLinker = AotTools.usesLinker(shorebirdEnv.flutterRevision);
@@ -324,9 +320,9 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
// Copy the patch's class table link info file to the build directory
// so that it can be used to generate a patch.
File(_patchClassTableLinkInfoPath).copySync(
p.join(shorebirdEnv.buildDirectory.path, 'out.ct.link'),
);
File(
_patchClassTableLinkInfoPath,
).copySync(p.join(shorebirdEnv.buildDirectory.path, 'out.ct.link'));
File(_patchClassTableLinkDebugInfoPath).copySync(
p.join(shorebirdEnv.buildDirectory.path, 'out.class_table.json'),
);
@@ -383,11 +379,9 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final patchFileSize = patchFile.statSync().size;
final privateKeyFile = argResults.file(CommonArguments.privateKeyArg.name);
final hash = sha256.convert(patchBuildFile.readAsBytesSync()).toString();
final hashSignature = privateKeyFile != null
? codeSigner.sign(
message: hash,
privateKeyPemFile: privateKeyFile,
)
final hashSignature =
privateKeyFile != null
? codeSigner.sign(message: hash, privateKeyPemFile: privateKeyFile)
: null;
return {
@@ -429,8 +423,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
@override
Future<CreatePatchMetadata> updatedCreatePatchMetadata(
CreatePatchMetadata metadata,
) async =>
metadata.copyWith(
) async => metadata.copyWith(
linkPercentage: lastBuildLinkPercentage,
environment: metadata.environment.copyWith(
xcodeVersion: await xcodeBuild.version(),
@@ -99,14 +99,10 @@ class LinuxPatcher extends Patcher {
// build/linux/x64/release/bundle
final appSoPath = p.join(tempDir.path, 'lib', 'libapp.so');
final privateKeyFile = argResults.file(
CommonArguments.privateKeyArg.name,
);
final hashSignature = privateKeyFile != null
? codeSigner.sign(
message: hash,
privateKeyPemFile: privateKeyFile,
)
final privateKeyFile = argResults.file(CommonArguments.privateKeyArg.name);
final hashSignature =
privateKeyFile != null
? codeSigner.sign(message: hash, privateKeyPemFile: privateKeyFile)
: null;
final String diffPath;
@@ -42,21 +42,15 @@ class MacosPatcher extends Patcher {
});
// The elf snapshot built for Apple Silicon macs.
String get _arm64AotOutputPath => p.join(
shorebirdEnv.buildDirectory.path,
'out.arm64.aot',
);
String get _arm64AotOutputPath =>
p.join(shorebirdEnv.buildDirectory.path, 'out.arm64.aot');
// The elf snapshot built for Intel macs.
String get _x64AotOutputPath => p.join(
shorebirdEnv.buildDirectory.path,
'out.x64.aot',
);
String get _x64AotOutputPath =>
p.join(shorebirdEnv.buildDirectory.path, 'out.x64.aot');
String get _appDillCopyPath => p.join(
shorebirdEnv.buildDirectory.path,
'app.dill',
);
String get _appDillCopyPath =>
p.join(shorebirdEnv.buildDirectory.path, 'app.dill');
@override
ReleaseType get releaseType => ReleaseType.macos;
@@ -93,8 +87,8 @@ class MacosPatcher extends Patcher {
// can be nondeterministic. So we still have some hope of alerting users of
// unpatchable native changes, we compare the Podfile.lock hash between the
// patch and the release.
final diffStatus =
await patchDiffChecker.confirmUnpatchableDiffsIfNecessary(
final diffStatus = await patchDiffChecker
.confirmUnpatchableDiffsIfNecessary(
localArchive: patchArchive,
releaseArchive: releaseArchive,
archiveDiffer: const AppleArchiveDiffer(),
@@ -109,7 +103,8 @@ class MacosPatcher extends Patcher {
final String? podfileLockHash;
if (shorebirdEnv.macosPodfileLockFile.existsSync()) {
podfileLockHash = sha256
podfileLockHash =
sha256
.convert(shorebirdEnv.macosPodfileLockFile.readAsBytesSync())
.toString();
} else {
@@ -141,18 +136,17 @@ This may indicate that the patch contains native changes, which cannot be applie
@override
Future<File> buildPatchArtifact({String? releaseVersion}) async {
try {
final (flutterVersionAndRevision, flutterVersion) = await (
final (flutterVersionAndRevision, flutterVersion) =
await (
shorebirdFlutter.getVersionAndRevision(),
shorebirdFlutter.getVersion(),
).wait;
if ((flutterVersion ?? minimumSupportedMacosFlutterVersion) <
minimumSupportedMacosFlutterVersion) {
logger.err(
'''
logger.err('''
macOS patches are not supported with Flutter versions older than $minimumSupportedMacosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
);
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.software.code);
}
@@ -167,7 +161,8 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
codesign: codesign,
flavor: flavor,
target: target,
args: argResults.forwardedArgs +
args:
argResults.forwardedArgs +
buildNameAndNumberArgsFromReleaseVersion(releaseVersion),
base64PublicKey: argResults.encodedPublicKey,
buildProgress: buildProgress,
@@ -244,16 +239,10 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final patchFile = File(patchFilePath);
final patchFileSize = patchFile.statSync().size;
final privateKeyFile = argResults.file(CommonArguments.privateKeyArg.name);
final hash = sha256
.convert(
patchArtifact.readAsBytesSync(),
)
.toString();
final hashSignature = privateKeyFile != null
? codeSigner.sign(
message: hash,
privateKeyPemFile: privateKeyFile,
)
final hash = sha256.convert(patchArtifact.readAsBytesSync()).toString();
final hashSignature =
privateKeyFile != null
? codeSigner.sign(message: hash, privateKeyPemFile: privateKeyFile)
: null;
return PatchArtifactBundle(
@@ -303,10 +292,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
);
createDiffProgress.complete();
return {
Arch.x86_64: x64Bundle,
Arch.arm64: arm64Bundle,
};
return {Arch.x86_64: x64Bundle, Arch.arm64: arm64Bundle};
}
@override
@@ -337,8 +323,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
@override
Future<CreatePatchMetadata> updatedCreatePatchMetadata(
CreatePatchMetadata metadata,
) async =>
metadata.copyWith(
) async => metadata.copyWith(
environment: metadata.environment.copyWith(
xcodeVersion: await xcodeBuild.version(),
),
@@ -36,9 +36,7 @@ typedef ResolvePatcher = Patcher Function(ReleaseType releaseType);
/// {@endtemplate}
class PatchCommand extends ShorebirdCommand {
/// {@macro patch_command}
PatchCommand({
ResolvePatcher? resolvePatcher,
}) {
PatchCommand({ResolvePatcher? resolvePatcher}) {
_resolvePatcher = resolvePatcher ?? getPatcher;
argParser
..addMultiOption(
@@ -219,8 +217,9 @@ NOTE: this is ${styleBold.wrap('not')} recommended. Asset changes cannot be incl
return ExitCode.usage.code;
}
final patcherFutures =
results.releaseTypes.map(_resolvePatcher).map(createPatch);
final patcherFutures = results.releaseTypes
.map(_resolvePatcher)
.map(createPatch);
for (final patcherFuture in patcherFutures) {
await patcherFuture;
@@ -371,8 +370,11 @@ NOTE: this is ${styleBold.wrap('not')} recommended. Asset changes cannot be incl
releaseArtifact: releaseArtifact,
);
final supplementArchive = supplementalArtifact != null
? await downloadReleaseArtifact(releaseArtifact: supplementalArtifact)
final supplementArchive =
supplementalArtifact != null
? await downloadReleaseArtifact(
releaseArtifact: supplementalArtifact,
)
: null;
final releaseFlutterShorebirdEnv = shorebirdEnv.copyWith(
@@ -444,17 +446,13 @@ NOTE: this is ${styleBold.wrap('not')} recommended. Asset changes cannot be incl
artifacts: patchArtifactBundles,
);
},
values: {
shorebirdEnvRef.overrideWith(() => releaseFlutterShorebirdEnv),
},
values: {shorebirdEnvRef.overrideWith(() => releaseFlutterShorebirdEnv)},
);
}
/// Prompts the user for the specific release to patch.
Future<Release> promptForRelease(ReleasePlatform platform) async {
final releases = await codePushClientWrapper.getReleases(
appId: appId,
);
final releases = await codePushClientWrapper.getReleases(appId: appId);
final releasesForPlatform = releases.where(
(release) => release.platformStatuses.keys.contains(platform),
@@ -537,7 +535,8 @@ Please re-run the release command for this version or create a new release.''');
final size = formatBytes(patchArtifactBundles[arch]!.size);
return '${arch.name} ($size)';
});
final trackSummary = (() {
final trackSummary =
(() {
return switch (track) {
DeploymentTrack.staging => '🟠 Track: ${lightCyan.wrap('Staging')}',
DeploymentTrack.beta => '🔵 Track: ${lightCyan.wrap('Beta')}',
@@ -567,14 +566,12 @@ Please re-run the release command for this version or create a new release.''');
'''🔍 Debug Info: ${lightCyan.wrap(Patcher.debugInfoFile.path)}''',
];
logger.info(
'''
logger.info('''
${styleBold.wrap(lightGreen.wrap('🚀 Ready to publish a new patch!'))}
${summary.join('\n')}
''',
);
''');
if (shorebirdEnv.canAcceptUserInput && !noConfirm) {
final confirm = logger.confirm('Would you like to continue?');
@@ -188,9 +188,6 @@ More info: ${troubleshootingUrl.toLink()}.
}
final parts = releaseVersion.split('+');
return [
'--build-name=${parts[0]}',
'--build-number=${parts[1]}',
];
return ['--build-name=${parts[0]}', '--build-number=${parts[1]}'];
}
}
@@ -72,9 +72,7 @@ class WindowsPatcher extends Patcher {
}
@override
Future<File> buildPatchArtifact({
String? releaseVersion,
}) async {
Future<File> buildPatchArtifact({String? releaseVersion}) async {
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
final buildAppBundleProgress = logger.detailProgress(
@@ -123,14 +121,10 @@ class WindowsPatcher extends Patcher {
// build/windows/x64/runner/Release
final appSoPath = p.join(tempDir.path, 'data', 'app.so');
final privateKeyFile = argResults.file(
CommonArguments.privateKeyArg.name,
);
final hashSignature = privateKeyFile != null
? codeSigner.sign(
message: hash,
privateKeyPemFile: privateKeyFile,
)
final privateKeyFile = argResults.file(CommonArguments.privateKeyArg.name);
final hashSignature =
privateKeyFile != null
? codeSigner.sign(message: hash, privateKeyPemFile: privateKeyFile)
: null;
final String diffPath;
@@ -164,10 +158,9 @@ class WindowsPatcher extends Patcher {
zipFile: artifact,
outputDirectory: outputDirectory,
);
final exeFile = outputDirectory
.listSync()
.whereType<File>()
.firstWhere((file) => p.extension(file.path) == '.exe');
final exeFile = outputDirectory.listSync().whereType<File>().firstWhere(
(file) => p.extension(file.path) == '.exe',
);
return powershell.getExeVersionString(exeFile);
}
}
@@ -165,18 +165,21 @@ This is only applicable when previewing Android releases.''',
//
// With these two lists, we can now determine if a platform is previewable
// or not, by making a difference between the two lists.
final (allReleases, sideloadableReleases) = await (
final (allReleases, sideloadableReleases) =
await (
codePushClientWrapper.getReleases(appId: appId),
codePushClientWrapper.getReleases(
appId: appId,
sideloadableOnly: true,
)
),
).wait;
final maybePlatform = results['platform'] != null
final maybePlatform =
results['platform'] != null
? ReleasePlatform.values.byName(results['platform'] as String)
: null;
final platformReleases = sideloadableReleases
final platformReleases =
sideloadableReleases
.where(
(r) =>
maybePlatform == null ||
@@ -195,7 +198,8 @@ This is only applicable when previewing Android releases.''',
return ExitCode.usage.code;
}
final releaseVersion = results['release-version'] as String? ??
final releaseVersion =
results['release-version'] as String? ??
await promptForReleaseVersion(platformReleases);
final release = platformReleases.firstWhereOrNull(
@@ -207,13 +211,15 @@ This is only applicable when previewing Android releases.''',
return ExitCode.usage.code;
}
final availablePlatforms = release.activePlatforms
final availablePlatforms =
release.activePlatforms
.where((p) => supportedReleasePlatforms.contains(p))
.toList();
if (availablePlatforms.isEmpty) {
final activePlatformsString =
release.activePlatforms.map((p) => p.displayName).join(', ');
final activePlatformsString = release.activePlatforms
.map((p) => p.displayName)
.join(', ');
logger.err(
'''This release can only be previewed on platforms that support $activePlatformsString''',
);
@@ -447,10 +453,9 @@ This is only applicable when previewing Android releases.''',
channel: track.name,
);
final exeFile = appDirectory
.listSync()
.whereType<File>()
.firstWhere((file) => file.path.endsWith('.exe'));
final exeFile = appDirectory.listSync().whereType<File>().firstWhere(
(file) => file.path.endsWith('.exe'),
);
final proc = await process.start(exeFile.path, []);
proc.stdout.listen((log) => logger.info(utf8.decode(log)));
@@ -540,17 +545,14 @@ This is only applicable when previewing Android releases.''',
return line.trim().replaceFirst(prefixRegex, '').trim();
}
logs.listen(
(log) {
logs.listen((log) {
final logLine = utf8.decode(log);
if (logFilters.any((filter) => filter.hasMatch(logLine))) {
return;
}
logger.info(removeLogPrefix(logLine));
},
onDone: completer.complete,
);
}, onDone: completer.complete);
return completer.future.then((_) => ExitCode.success.code);
}
@@ -798,7 +800,8 @@ This is only applicable when previewing Android releases.''',
}
final shouldUseDeviceCtl = deviceForLaunch != null;
final progressCompleteMessage = deviceForLaunch != null
final progressCompleteMessage =
deviceForLaunch != null
? 'Using device ${deviceForLaunch.name}'
: '''No iOS 17+ device found, looking for devices running iOS 16 or lower''';
deviceLocateProgress.complete(progressCompleteMessage);
@@ -850,12 +853,7 @@ This is only applicable when previewing Android releases.''',
required String channel,
}) async {
final shorebirdYamlFile = File(
p.join(
appDirectory.path,
'data',
'flutter_assets',
'shorebird.yaml',
),
p.join(appDirectory.path, 'data', 'flutter_assets', 'shorebird.yaml'),
);
await _maybeSetChannelInShorebirdYaml(
@@ -979,12 +977,7 @@ This is only applicable when previewing Android releases.''',
required Directory bundleDirectory,
}) async {
final shorebirdYamlFile = File(
p.join(
bundleDirectory.path,
'data',
'flutter_assets',
'shorebird.yaml',
),
p.join(bundleDirectory.path, 'data', 'flutter_assets', 'shorebird.yaml'),
);
await _maybeSetChannelInShorebirdYaml(
@@ -1027,7 +1020,8 @@ This is only applicable when previewing Android releases.''',
/// that can be previewed).
extension Previewable on Release {
/// Returns the platforms that can be previewed.
List<ReleasePlatform> get activePlatforms => platformStatuses.entries
List<ReleasePlatform> get activePlatforms =>
platformStatuses.entries
.where((e) => e.value == ReleaseStatus.active)
.map((e) => e.key)
.toList();
@@ -33,10 +33,12 @@ class AarReleaser extends Releaser {
String get buildNumber => argResults['build-number'] as String;
/// The architectures to build the aar for.
Set<Arch> get architectures => (argResults['target-platform'] as List<String>)
Set<Arch> get architectures =>
(argResults['target-platform'] as List<String>)
.map(
(platform) => AndroidArch.availableAndroidArchs
.firstWhere((arch) => arch.targetPlatformCliArg == platform),
(platform) => AndroidArch.availableAndroidArchs.firstWhere(
(arch) => arch.targetPlatformCliArg == platform,
),
)
.toSet();
@@ -92,10 +94,7 @@ class AarReleaser extends Releaser {
final targetLibraryDirectory = Directory(
p.join(shorebirdEnv.getShorebirdProjectRoot()!.path, 'release'),
);
await copyPath(
sourceLibraryDirectory.path,
targetLibraryDirectory.path,
);
await copyPath(sourceLibraryDirectory.path, targetLibraryDirectory.path);
return targetLibraryDirectory;
}
@@ -34,10 +34,12 @@ class AndroidReleaser extends Releaser {
String get artifactDisplayName => 'Android app bundle';
/// The architectures to build for.
Set<Arch> get architectures => (argResults['target-platform'] as List<String>)
Set<Arch> get architectures =>
(argResults['target-platform'] as List<String>)
.map(
(platform) => AndroidArch.availableAndroidArchs
.firstWhere((arch) => arch.targetPlatformCliArg == platform),
(platform) => AndroidArch.availableAndroidArchs.firstWhere(
(arch) => arch.targetPlatformCliArg == platform,
),
)
.toSet();
@@ -93,10 +95,12 @@ Please comment and upvote ${link(uri: Uri.parse('https://github.com/shorebirdtec
Future<FileSystemEntity> buildReleaseArtifacts({
DetailProgress? progress,
}) async {
final architectures = (argResults['target-platform'] as List<String>)
final architectures =
(argResults['target-platform'] as List<String>)
.map(
(platform) => AndroidArch.availableAndroidArchs
.firstWhere((arch) => arch.targetPlatformCliArg == platform),
(platform) => AndroidArch.availableAndroidArchs.firstWhere(
(arch) => arch.targetPlatformCliArg == platform,
),
)
.toSet();
@@ -128,15 +132,14 @@ Please comment and upvote ${link(uri: Uri.parse('https://github.com/shorebirdtec
Future<String> getReleaseVersion({
required FileSystemEntity releaseArtifactRoot,
}) async {
final releaseVersionProgress =
logger.progress('Determining release version');
final releaseVersionProgress = logger.progress(
'Determining release version',
);
final String releaseVersion;
try {
releaseVersion =
await shorebirdAndroidArtifacts.extractReleaseVersionFromAppBundle(
releaseArtifactRoot.path,
);
releaseVersion = await shorebirdAndroidArtifacts
.extractReleaseVersionFromAppBundle(releaseArtifactRoot.path);
releaseVersionProgress.complete('Release version: $releaseVersion');
} on Exception catch (error) {
releaseVersionProgress.fail('$error');
@@ -169,8 +172,7 @@ Please comment and upvote ${link(uri: Uri.parse('https://github.com/shorebirdtec
@override
Future<UpdateReleaseMetadata> updatedReleaseMetadata(
UpdateReleaseMetadata metadata,
) async =>
metadata.copyWith(generatedApks: generateApk);
) async => metadata.copyWith(generatedApks: generateApk);
@override
String get postReleaseInstructions {
@@ -185,7 +187,8 @@ Please comment and upvote ${link(uri: Uri.parse('https://github.com/shorebirdtec
project: projectRoot,
flavor: flavor,
);
apkText = generateApk
apkText =
generateApk
? '''
Or distribute the apk:
@@ -35,10 +35,7 @@ class IosFrameworkReleaser extends Releaser {
/// The directory where the release artifacts are stored.
Directory get releaseDirectory => Directory(
p.join(
shorebirdEnv.getShorebirdProjectRoot()!.path,
'release',
),
p.join(shorebirdEnv.getShorebirdProjectRoot()!.path, 'release'),
);
@override
@@ -73,23 +70,20 @@ class IosFrameworkReleaser extends Releaser {
final flutterVersionArg = argResults['flutter-version'] as String?;
if (flutterVersionArg != null) {
final version =
await shorebirdFlutter.resolveFlutterVersion(flutterVersionArg);
if (version != null && version < minimumSupportedIosFlutterVersion) {
logger.err(
'''
iOS releases are not supported with Flutter versions older than $minimumSupportedIosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final version = await shorebirdFlutter.resolveFlutterVersion(
flutterVersionArg,
);
if (version != null && version < minimumSupportedIosFlutterVersion) {
logger.err('''
iOS releases are not supported with Flutter versions older than $minimumSupportedIosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.usage.code);
}
}
}
@override
Future<FileSystemEntity> buildReleaseArtifacts({
Progress? progress,
}) async {
Future<FileSystemEntity> buildReleaseArtifacts({Progress? progress}) async {
// Delete the Shorebird supplement directory if it exists.
// This is to ensure that we don't accidentally upload stale artifacts
// when building with older versions of Flutter.
@@ -110,23 +104,14 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
if (targetLibraryDirectory.existsSync()) {
targetLibraryDirectory.deleteSync(recursive: true);
}
await copyPath(
sourceLibraryDirectory.path,
targetLibraryDirectory.path,
);
await copyPath(sourceLibraryDirectory.path, targetLibraryDirectory.path);
// Rename Flutter.xcframework to ShorebirdFlutter.xcframework to avoid
// Xcode warning users about the .xcframework signature changing.
Directory(
p.join(
targetLibraryDirectory.path,
'Flutter.xcframework',
),
p.join(targetLibraryDirectory.path, 'Flutter.xcframework'),
).renameSync(
p.join(
targetLibraryDirectory.path,
'ShorebirdFlutter.xcframework',
),
p.join(targetLibraryDirectory.path, 'ShorebirdFlutter.xcframework'),
);
return targetLibraryDirectory;
@@ -155,8 +140,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
@override
Future<UpdateReleaseMetadata> updatedReleaseMetadata(
UpdateReleaseMetadata metadata,
) async =>
metadata.copyWith(
) async => metadata.copyWith(
environment: metadata.environment.copyWith(
xcodeVersion: await xcodeBuild.version(),
),
@@ -82,14 +82,13 @@ To change the version of this release, change your app's version in your pubspec
final flutterVersionArg = argResults['flutter-version'] as String?;
if (flutterVersionArg != null) {
final version =
await shorebirdFlutter.resolveFlutterVersion(flutterVersionArg);
if (version != null && version < minimumSupportedIosFlutterVersion) {
logger.err(
'''
iOS releases are not supported with Flutter versions older than $minimumSupportedIosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final version = await shorebirdFlutter.resolveFlutterVersion(
flutterVersionArg,
);
if (version != null && version < minimumSupportedIosFlutterVersion) {
logger.err('''
iOS releases are not supported with Flutter versions older than $minimumSupportedIosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.usage.code);
}
}
@@ -173,7 +172,8 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final xcarchiveDirectory = artifactManager.getXcarchiveDirectory()!;
final String? podfileLockHash;
if (shorebirdEnv.iosPodfileLockFile.existsSync()) {
podfileLockHash = sha256
podfileLockHash =
sha256
.convert(shorebirdEnv.iosPodfileLockFile.readAsBytesSync())
.toString();
} else {
@@ -183,7 +183,8 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
appId: appId,
releaseId: release.id,
xcarchivePath: xcarchiveDirectory.path,
runnerPath: artifactManager
runnerPath:
artifactManager
.getIosAppDirectory(xcarchiveDirectory: xcarchiveDirectory)!
.path,
isCodesigned: codesign,
@@ -195,8 +196,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
@override
Future<UpdateReleaseMetadata> updatedReleaseMetadata(
UpdateReleaseMetadata metadata,
) async =>
metadata.copyWith(
) async => metadata.copyWith(
environment: metadata.environment.copyWith(
xcodeVersion: await xcodeBuild.version(),
),
@@ -61,14 +61,13 @@ To change the version of this release, change your app's version in your pubspec
}
final flutterVersionArg = argResults['flutter-version'] as String?;
if (flutterVersionArg != null) {
final version =
await shorebirdFlutter.resolveFlutterVersion(flutterVersionArg);
if (version != null && version < minimumSupportedLinuxFlutterVersion) {
logger.err(
'''
Linux releases are not supported with Flutter versions older than $minimumSupportedLinuxFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final version = await shorebirdFlutter.resolveFlutterVersion(
flutterVersionArg,
);
if (version != null && version < minimumSupportedLinuxFlutterVersion) {
logger.err('''
Linux releases are not supported with Flutter versions older than $minimumSupportedLinuxFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.usage.code);
}
}
@@ -88,8 +87,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
@override
Future<String> getReleaseVersion({
required FileSystemEntity releaseArtifactRoot,
}) async =>
linux.versionFromLinuxBundle(
}) async => linux.versionFromLinuxBundle(
bundleRoot: releaseArtifactRoot as Directory,
);
@@ -103,8 +101,7 @@ Linux release created at ${artifactManager.linuxBundleDirectory.path}.
Future<void> uploadReleaseArtifacts({
required Release release,
required String appId,
}) =>
codePushClientWrapper.createLinuxReleaseArtifacts(
}) => codePushClientWrapper.createLinuxReleaseArtifacts(
appId: appId,
releaseId: release.id,
bundle: artifactManager.linuxBundleDirectory,
@@ -83,14 +83,13 @@ To change the version of this release, change your app's version in your pubspec
final flutterVersionArg = argResults['flutter-version'] as String?;
if (flutterVersionArg != null) {
final version =
await shorebirdFlutter.resolveFlutterVersion(flutterVersionArg);
if (version != null && version < minimumSupportedMacosFlutterVersion) {
logger.err(
'''
macOS releases are not supported with Flutter versions older than $minimumSupportedMacosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final version = await shorebirdFlutter.resolveFlutterVersion(
flutterVersionArg,
);
if (version != null && version < minimumSupportedMacosFlutterVersion) {
logger.err('''
macOS releases are not supported with Flutter versions older than $minimumSupportedMacosFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.usage.code);
}
}
@@ -163,7 +162,8 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
final String? podfileLockHash;
if (shorebirdEnv.macosPodfileLockFile.existsSync()) {
podfileLockHash = sha256
podfileLockHash =
sha256
.convert(shorebirdEnv.macosPodfileLockFile.readAsBytesSync())
.toString();
} else {
@@ -182,8 +182,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
@override
Future<UpdateReleaseMetadata> updatedReleaseMetadata(
UpdateReleaseMetadata metadata,
) async =>
metadata.copyWith(
) async => metadata.copyWith(
environment: metadata.environment.copyWith(
xcodeVersion: await xcodeBuild.version(),
),
@@ -114,7 +114,8 @@ class ReleaseCommand extends ShorebirdCommand {
)
..addFlag(
'split-per-abi',
help: 'Whether to split the APKs per ABIs (Android only). '
help:
'Whether to split the APKs per ABIs (Android only). '
'To learn more, see: https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split',
hide: true,
negatable: false,
@@ -164,8 +165,9 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
return ExitCode.usage.code;
}
final releaserFutures =
results.releaseTypes.map(_resolveReleaser).map(createRelease);
final releaserFutures = results.releaseTypes
.map(_resolveReleaser)
.map(createRelease);
for (final future in releaserFutures) {
await future;
@@ -179,11 +181,7 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
Releaser getReleaser(ReleaseType releaseType) {
switch (releaseType) {
case ReleaseType.aar:
return AarReleaser(
argResults: results,
flavor: flavor,
target: target,
);
return AarReleaser(argResults: results, flavor: flavor, target: target);
case ReleaseType.android:
return AndroidReleaser(
argResults: results,
@@ -191,11 +189,7 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
target: target,
);
case ReleaseType.ios:
return IosReleaser(
argResults: results,
flavor: flavor,
target: target,
);
return IosReleaser(argResults: results, flavor: flavor, target: target);
case ReleaseType.iosFramework:
return IosFrameworkReleaser(
argResults: results,
@@ -269,8 +263,7 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
final releaseFlutterShorebirdEnv = shorebirdEnv.copyWith(
flutterRevisionOverride: targetFlutterRevision,
);
return await runScoped(
() async {
return await runScoped(() async {
await cache.updateAll();
final flutterVersionString =
@@ -295,8 +288,7 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
if (!e.fixRecommendation.isNullOrEmpty) {
logger.info(e.fixRecommendation);
}
if (e.fixRecommendation.isNullOrEmpty &&
e.flutterError.isNullOrEmpty) {
if (e.fixRecommendation.isNullOrEmpty && e.flutterError.isNullOrEmpty) {
// If we have no fix recommendation or were unable to parse a
// flutter error, fall back to printing the raw stderr.
logger.info(e.stderr.join(Platform.lineTerminator));
@@ -356,11 +348,7 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
flavor: flavor,
target: target,
);
},
values: {
shorebirdEnvRef.overrideWith(() => releaseFlutterShorebirdEnv),
},
);
}, values: {shorebirdEnvRef.overrideWith(() => releaseFlutterShorebirdEnv)});
}
/// Validates arguments that are common to all release types.
@@ -383,11 +371,9 @@ of the iOS app that is using this module. (aar and ios-framework only)''',
flutterVersionArg!,
);
} on Exception catch (error) {
logger.err(
'''
logger.err('''
Unable to determine revision for Flutter version: $flutterVersionArg.
$error''',
);
$error''');
throw ProcessExit(ExitCode.software.code);
}
@@ -398,12 +384,10 @@ $error''',
),
message: 'open an issue',
);
logger.err(
'''
logger.err('''
Version $flutterVersionArg not found. Please $openIssueLink to request a new version.
Use `shorebird flutter versions list` to list available versions.
''',
);
''');
throw ProcessExit(ExitCode.software.code);
}
@@ -452,7 +436,8 @@ Use `shorebird flutter versions list` to list available versions.
..err('''
${styleBold.wrap(lightRed.wrap('A release with version $version already exists but was built using a different Flutter revision.'))}
''')
..info('''
..info(
'''
Existing release built with: ${lightCyan.wrap(formattedExistingReleaseVersion)}
Current release built with: ${lightCyan.wrap(formattedCurrentReleaseVersion)}
@@ -462,7 +447,8 @@ ${styleBold.wrap(lightRed.wrap('All platforms for a given release must be built
To resolve this issue, you can:
* Re-run the release command with "${lightCyan.wrap('--flutter-version=${existingRelease.flutterRevision}')}".
* Delete the existing release and re-run the release command with the desired Flutter version.
* Bump the release version and re-run the release command with the desired Flutter version.''');
* Bump the release version and re-run the release command with the desired Flutter version.''',
);
throw ProcessExit(ExitCode.software.code);
}
}
@@ -583,12 +569,10 @@ ${summary.join('\n')}
);
if (!releaser.requiresReleaseVersionArg) {
logger.info(
'''
logger.info('''
Note: ${lightCyan.wrap(baseCommand)} without the --release-version option will patch the current version of the app.
''',
);
''');
}
}
}
@@ -66,27 +66,25 @@ To change the version of this release, change your app's version in your pubspec
final flutterVersionArg = argResults['flutter-version'] as String?;
if (flutterVersionArg != null) {
final version =
await shorebirdFlutter.resolveFlutterVersion(flutterVersionArg);
final gitHash =
await shorebirdFlutter.getRevisionForVersion(flutterVersionArg);
final version = await shorebirdFlutter.resolveFlutterVersion(
flutterVersionArg,
);
final gitHash = await shorebirdFlutter.getRevisionForVersion(
flutterVersionArg,
);
if (version != null &&
version < minimumSupportedWindowsFlutterVersion &&
!windowsFlutterGitHashesBelowMinVersion.contains(gitHash)) {
logger.err(
'''
logger.err('''
Windows releases are not supported with Flutter versions older than $minimumSupportedWindowsFlutterVersion.
For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
);
For more information see: ${supportedFlutterVersionsUrl.toLink()}''');
throw ProcessExit(ExitCode.usage.code);
}
}
}
@override
Future<FileSystemEntity> buildReleaseArtifacts({
DetailProgress? progress,
}) {
Future<FileSystemEntity> buildReleaseArtifacts({DetailProgress? progress}) {
return artifactBuilder.buildWindowsApp(
flavor: flavor,
target: target,
@@ -142,10 +142,7 @@ class GetApksCommand extends ShorebirdCommand {
)..createSync(recursive: true);
}
await extractFileToDisk(
apksZipFile.path,
outputDirectory.path,
);
await extractFileToDisk(apksZipFile.path, outputDirectory.path);
logger.info('apk(s) generated at ${lightCyan.wrap(outputDirectory.path)}');
return ExitCode.success.code;
@@ -169,9 +166,7 @@ class GetApksCommand extends ShorebirdCommand {
);
}
Future<File> _downloadAab({
required ReleaseArtifact releaseArtifact,
}) async {
Future<File> _downloadAab({required ReleaseArtifact releaseArtifact}) async {
final File artifactFile;
try {
artifactFile = await artifactManager.downloadWithProgressUpdates(
@@ -16,11 +16,7 @@ class RunCommand extends ShorebirdCommand {
/// {@macro run_command}
RunCommand() {
argParser
..addOption(
'device-id',
abbr: 'd',
help: 'Target device id or name.',
)
..addOption('device-id', abbr: 'd', help: 'Target device id or name.')
..addOption(
'target',
abbr: 't',
@@ -28,7 +24,8 @@ class RunCommand extends ShorebirdCommand {
)
..addMultiOption(
'dart-define',
help: 'Additional key-value pairs that will be available as constants '
help:
'Additional key-value pairs that will be available as constants '
'''from the String.fromEnvironment, bool.fromEnvironment, and int.fromEnvironment '''
'constructors.\n'
'''Multiple defines can be passed by repeating "--dart-define" multiple times.''',
@@ -52,11 +49,9 @@ class RunCommand extends ShorebirdCommand {
@override
Future<int> run() async {
logger.warn(
'''
logger.warn('''
This command is deprecated and will be removed in a future release.
Please use "shorebird preview" instead.''',
);
Please use "shorebird preview" instead.''');
// TODO(bryanoltman): check run target and run either
// doctor.iosValidators or doctor.androidValidators as appropriate.
@@ -75,9 +70,7 @@ Please use "shorebird preview" instead.''',
final flavor = results['flavor'] as String?;
final target = results['target'] as String?;
final dartDefines = results['dart-define'] as List<String>?;
final flutter = await process.start(
'flutter',
[
final flutter = await process.start('flutter', [
'run',
// Eventually we should support running in both debug and release mode.
'--release',
@@ -86,9 +79,7 @@ Please use "shorebird preview" instead.''',
if (target != null) '--target=$target',
if (dartDefines != null) ...dartDefines.map((e) => '--dart-define=$e'),
...results.rest,
],
runInShell: true,
);
], runInShell: true);
flutter.stdout.listen((event) {
logger.info(utf8.decode(event));
@@ -5,10 +5,7 @@ part 'shorebird_yaml.g.dart';
/// {@template shorebird_yaml}
/// A Shorebird configuration file which contains metadata about the app.
/// {@endtemplate}
@JsonSerializable(
anyMap: true,
disallowUnrecognizedKeys: true,
)
@JsonSerializable(anyMap: true, disallowUnrecognizedKeys: true)
class ShorebirdYaml {
/// {@macro shorebird_yaml}
const ShorebirdYaml({
@@ -20,9 +20,8 @@ ShorebirdYaml _$ShorebirdYamlFromJson(Map json) => $checkedCreate(
appId: $checkedConvert('app_id', (v) => v as String),
flavors: $checkedConvert(
'flavors',
(v) => (v as Map?)?.map(
(k, e) => MapEntry(k as String, e as String),
)),
(v) => (v as Map?)?.map((k, e) => MapEntry(k as String, e as String)),
),
baseUrl: $checkedConvert('base_url', (v) => v as String?),
autoUpdate: $checkedConvert('auto_update', (v) => v as bool?),
);
@@ -31,7 +30,7 @@ ShorebirdYaml _$ShorebirdYamlFromJson(Map json) => $checkedCreate(
fieldKeyMap: const {
'appId': 'app_id',
'baseUrl': 'base_url',
'autoUpdate': 'auto_update'
'autoUpdate': 'auto_update',
},
);
+1 -3
View File
@@ -28,9 +28,7 @@ class Doctor {
final List<Validator> linuxCommandValidators = [];
/// Validators that verify shorebird will work on macOS.
final List<Validator> macosCommandValidators = [
MacosEntitlementsValidator(),
];
final List<Validator> macosCommandValidators = [MacosEntitlementsValidator()];
/// Validators that verify shorebird will work on Windows.
final List<Validator> windowsCommandValidators = [
@@ -43,10 +43,7 @@ class Adb {
}
/// Starts the app with the given [package] name.
Future<void> startApp({
required String package,
String? deviceId,
}) async {
Future<void> startApp({required String package, String? deviceId}) async {
final args = [
if (deviceId != null) ...['-s', deviceId],
'shell',
@@ -64,10 +61,7 @@ class Adb {
}
/// Runs `adb logcat`.
Future<Process> logcat({
String? filter,
String? deviceId,
}) async {
Future<Process> logcat({String? filter, String? deviceId}) async {
final args = [
if (deviceId != null) ...['-s', deviceId],
'logcat',
@@ -152,19 +152,19 @@ class AotTools {
final stdout = StringBuffer();
final stderr = StringBuffer();
final stdoutSubscription = subprocess.stdout.map(utf8.decode).listen(
(data) {
final stdoutSubscription = subprocess.stdout.map(utf8.decode).listen((
data,
) {
logger.detail(data);
stdout.write(data);
},
);
});
final stderrSubscription = subprocess.stderr.map(utf8.decode).listen(
(data) {
final stderrSubscription = subprocess.stderr.map(utf8.decode).listen((
data,
) {
logger.detail(data);
stderr.write(data);
},
);
});
final exitCode = await subprocess.exitCode;
@@ -189,11 +189,11 @@ class AotTools {
);
} else {
// local engine versions use .dart and we distribute aot-tools as a .dill
result = await execute(
shorebirdEnv.dartBinaryFile.path,
['run', artifactPath, ...command],
workingDirectory: workingDirectory,
);
result = await execute(shorebirdEnv.dartBinaryFile.path, [
'run',
artifactPath,
...command,
], workingDirectory: workingDirectory);
}
if (throwOnError && result.exitCode != 0) {
@@ -249,8 +249,7 @@ class AotTools {
const linkJson = 'link.jsonl';
final outputDir = p.dirname(outputPath);
final linkerUsesGenSnapshot = await _linkerUsesGenSnapshot();
await _exec(
[
await _exec([
'link',
'--base=$base',
'--patch=$patch',
@@ -264,13 +263,8 @@ class AotTools {
'--redirect-to=${p.join(outputDir, linkJson)}',
],
if (dumpDebugInfoPath != null) '--dump-debug-info=$dumpDebugInfoPath',
if (additionalArgs.isNotEmpty) ...[
'--',
...additionalArgs,
],
],
workingDirectory: workingDirectory,
);
if (additionalArgs.isNotEmpty) ...['--', ...additionalArgs],
], workingDirectory: workingDirectory);
return linkerUsesGenSnapshot
? _extractLinkPercentage(File(p.join(workingDirectory!, linkJson)))
@@ -279,7 +273,8 @@ class AotTools {
double? _extractLinkPercentage(File file) {
if (!file.existsSync()) return null;
final status = const LineSplitter()
final status =
const LineSplitter()
.convert(file.readAsStringSync())
.map(json.decode)
.cast<Map<String, dynamic>>()
@@ -312,14 +307,12 @@ class AotTools {
}) async {
final tmpDir = Directory.systemTemp.createTempSync();
final outFile = File(p.join(tmpDir.path, 'diff_base'));
await _exec(
[
await _exec([
'dump_blobs',
'--analyze-snapshot=$analyzeSnapshotPath',
'--output=${outFile.path}',
'--snapshot=${releaseSnapshot.path}',
],
);
]);
if (!outFile.existsSync()) {
throw Exception(
@@ -50,8 +50,7 @@ class Bundletool {
String? keyPassword,
String? keyAlias,
}) async {
final result = await _exec(
[
final result = await _exec([
'build-apks',
'--overwrite',
'--bundle=$bundle',
@@ -61,8 +60,7 @@ class Bundletool {
if (keystorePassword != null) '--ks-pass=$keystorePassword',
if (keyPassword != null) '--key-pass=$keyPassword',
if (keyAlias != null) '--ks-key-alias=$keyAlias',
],
);
]);
if (result.exitCode != 0) {
throw Exception('Failed to build apks: ${result.stderr}');
}
@@ -73,10 +71,7 @@ class Bundletool {
/// e.g. `bundletool install-apks --apks=/MyApp/my_app.apks --allow-downgrade`
///
/// https://developer.android.com/tools/bundletool#deploy_with_bundletool
Future<void> installApks({
required String apks,
String? deviceId,
}) async {
Future<void> installApks({required String apks, String? deviceId}) async {
final args = [
'install-apks',
'--apks=$apks',
@@ -91,15 +86,13 @@ class Bundletool {
/// Extract the package name from an app bundle.
Future<String> getPackageName(String appBundlePath) async {
final result = await _exec(
[
final result = await _exec([
'dump',
'manifest',
'--bundle=$appBundlePath',
'--xpath',
'/manifest/@package',
],
);
]);
if (result.exitCode != 0) {
throw Exception(
@@ -112,15 +105,13 @@ class Bundletool {
/// Extract the version name from an app bundle.
Future<String> getVersionName(String appBundlePath) async {
final result = await _exec(
[
final result = await _exec([
'dump',
'manifest',
'--bundle=$appBundlePath',
'--xpath',
'/manifest/@android:versionName',
],
);
]);
if (result.exitCode != 0) {
throw Exception(
@@ -133,15 +124,13 @@ class Bundletool {
/// Extract the version code from an app bundle.
Future<String> getVersionCode(String appBundlePath) async {
final result = await _exec(
[
final result = await _exec([
'dump',
'manifest',
'--bundle=$appBundlePath',
'--xpath',
'/manifest/@android:versionCode',
],
);
]);
if (result.exitCode != 0) {
throw Exception(
@@ -10,7 +10,6 @@ part 'apple_device.g.dart';
/// `xcrun devicectl list devices`.
/// {@endtemplate}
@JsonSerializable(createToJson: false, fieldRename: FieldRename.none)
/// {@macro apple_device}
class AppleDevice {
/// {@macro apple_device}
@@ -8,59 +8,52 @@ part of 'apple_device.dart';
// JsonSerializableGenerator
// **************************************************************************
AppleDevice _$AppleDeviceFromJson(Map<String, dynamic> json) => $checkedCreate(
'AppleDevice',
json,
($checkedConvert) {
AppleDevice _$AppleDeviceFromJson(Map<String, dynamic> json) =>
$checkedCreate('AppleDevice', json, ($checkedConvert) {
final val = AppleDevice(
deviceProperties: $checkedConvert('deviceProperties',
(v) => DeviceProperties.fromJson(v as Map<String, dynamic>)),
hardwareProperties: $checkedConvert('hardwareProperties',
(v) => HardwareProperties.fromJson(v as Map<String, dynamic>)),
connectionProperties: $checkedConvert('connectionProperties',
(v) => ConnectionProperties.fromJson(v as Map<String, dynamic>)),
deviceProperties: $checkedConvert(
'deviceProperties',
(v) => DeviceProperties.fromJson(v as Map<String, dynamic>),
),
hardwareProperties: $checkedConvert(
'hardwareProperties',
(v) => HardwareProperties.fromJson(v as Map<String, dynamic>),
),
connectionProperties: $checkedConvert(
'connectionProperties',
(v) => ConnectionProperties.fromJson(v as Map<String, dynamic>),
),
);
return val;
},
);
});
HardwareProperties _$HardwarePropertiesFromJson(Map<String, dynamic> json) =>
$checkedCreate(
'HardwareProperties',
json,
($checkedConvert) {
$checkedCreate('HardwareProperties', json, ($checkedConvert) {
final val = HardwareProperties(
platform: $checkedConvert('platform', (v) => v as String),
udid: $checkedConvert('udid', (v) => v as String),
);
return val;
},
);
});
DeviceProperties _$DevicePropertiesFromJson(Map<String, dynamic> json) =>
$checkedCreate(
'DeviceProperties',
json,
($checkedConvert) {
$checkedCreate('DeviceProperties', json, ($checkedConvert) {
final val = DeviceProperties(
name: $checkedConvert('name', (v) => v as String),
osVersionNumber:
$checkedConvert('osVersionNumber', (v) => v as String?),
osVersionNumber: $checkedConvert(
'osVersionNumber',
(v) => v as String?,
),
);
return val;
},
);
});
ConnectionProperties _$ConnectionPropertiesFromJson(
Map<String, dynamic> json) =>
$checkedCreate(
'ConnectionProperties',
json,
($checkedConvert) {
Map<String, dynamic> json,
) => $checkedCreate('ConnectionProperties', json, ($checkedConvert) {
final val = ConnectionProperties(
tunnelState: $checkedConvert('tunnelState', (v) => v as String),
transportType: $checkedConvert('transportType', (v) => v as String?),
);
return val;
},
);
});
@@ -22,10 +22,7 @@ typedef BundleId = String;
/// {@endtemplate}
class DevicectlException implements Exception {
/// {@macro devicectl_exception}
DevicectlException({
required this.message,
this.underlyingException,
});
DevicectlException({required this.message, this.underlyingException});
/// A message describing this exception.
final String message;
@@ -116,10 +113,10 @@ class Devicectl {
final String bundleId;
try {
final maybeBundleId =
JsonPath(r'$.result.installedApplications[0].bundleID')
.read(jsonResult)
.firstOrNull
?.value as String?;
JsonPath(
r'$.result.installedApplications[0].bundleID',
).read(jsonResult).firstOrNull?.value
as String?;
if (maybeBundleId == null) {
throw Exception(
'Unable to find installed app bundleID in devicectl output',
@@ -42,11 +42,7 @@ NSError(
)''';
@override
List<Object> get props => [
code,
domain,
userInfo,
];
List<Object> get props => [code, domain, userInfo];
}
/// {@template user_info}
@@ -8,19 +8,18 @@ part of 'nserror.dart';
// JsonSerializableGenerator
// **************************************************************************
NSError _$NSErrorFromJson(Map<String, dynamic> json) => $checkedCreate(
'NSError',
json,
($checkedConvert) {
NSError _$NSErrorFromJson(Map<String, dynamic> json) =>
$checkedCreate('NSError', json, ($checkedConvert) {
final val = NSError(
code: $checkedConvert('code', (v) => (v as num).toInt()),
domain: $checkedConvert('domain', (v) => v as String),
userInfo: $checkedConvert(
'userInfo', (v) => UserInfo.fromJson(v as Map<String, dynamic>)),
'userInfo',
(v) => UserInfo.fromJson(v as Map<String, dynamic>),
),
);
return val;
},
);
});
Map<String, dynamic> _$NSErrorToJson(NSError instance) => <String, dynamic>{
'code': instance.code,
@@ -35,24 +34,32 @@ UserInfo _$UserInfoFromJson(Map<String, dynamic> json) => $checkedCreate(
final val = UserInfo(
description: $checkedConvert(
'NSDescription',
(v) => v == null
(v) =>
v == null
? null
: StringContainer.fromJson(v as Map<String, dynamic>)),
: StringContainer.fromJson(v as Map<String, dynamic>),
),
localizedDescription: $checkedConvert(
'NSLocalizedDescription',
(v) => v == null
(v) =>
v == null
? null
: StringContainer.fromJson(v as Map<String, dynamic>)),
: StringContainer.fromJson(v as Map<String, dynamic>),
),
localizedFailureReason: $checkedConvert(
'NSLocalizedFailureReason',
(v) => v == null
(v) =>
v == null
? null
: StringContainer.fromJson(v as Map<String, dynamic>)),
: StringContainer.fromJson(v as Map<String, dynamic>),
),
underlyingError: $checkedConvert(
'NSUnderlyingError',
(v) => v == null
(v) =>
v == null
? null
: NSUnderlyingError.fromJson(v as Map<String, dynamic>)),
: NSUnderlyingError.fromJson(v as Map<String, dynamic>),
),
);
return val;
},
@@ -60,7 +67,7 @@ UserInfo _$UserInfoFromJson(Map<String, dynamic> json) => $checkedCreate(
'description': 'NSDescription',
'localizedDescription': 'NSLocalizedDescription',
'localizedFailureReason': 'NSLocalizedFailureReason',
'underlyingError': 'NSUnderlyingError'
'underlyingError': 'NSUnderlyingError',
},
);
@@ -72,39 +79,26 @@ Map<String, dynamic> _$UserInfoToJson(UserInfo instance) => <String, dynamic>{
};
StringContainer _$StringContainerFromJson(Map<String, dynamic> json) =>
$checkedCreate(
'StringContainer',
json,
($checkedConvert) {
$checkedCreate('StringContainer', json, ($checkedConvert) {
final val = StringContainer(
$checkedConvert('string', (v) => v as String),
);
return val;
},
);
});
Map<String, dynamic> _$StringContainerToJson(StringContainer instance) =>
<String, dynamic>{
'string': instance.string,
};
<String, dynamic>{'string': instance.string};
NSUnderlyingError _$NSUnderlyingErrorFromJson(Map<String, dynamic> json) =>
$checkedCreate(
'NSUnderlyingError',
json,
($checkedConvert) {
$checkedCreate('NSUnderlyingError', json, ($checkedConvert) {
final val = NSUnderlyingError(
error: $checkedConvert(
'error',
(v) => v == null
? null
: NSError.fromJson(v as Map<String, dynamic>)),
(v) => v == null ? null : NSError.fromJson(v as Map<String, dynamic>),
),
);
return val;
},
);
});
Map<String, dynamic> _$NSUnderlyingErrorToJson(NSUnderlyingError instance) =>
<String, dynamic>{
'error': instance.error?.toJson(),
};
<String, dynamic>{'error': instance.error?.toJson()};
@@ -45,15 +45,7 @@ class Git {
required String outputDirectory,
List<String>? args,
}) async {
await git(
[
'clone',
url,
...?args,
outputDirectory,
],
runInShell: true,
);
await git(['clone', url, ...?args, outputDirectory], runInShell: true);
}
/// Checks out the git repository located at [directory] to the [revision].
@@ -61,17 +53,14 @@ class Git {
required String directory,
required String revision,
}) async {
await git(
[
await git([
'-C',
directory,
'-c',
'advice.detachedHead=false',
'checkout',
revision,
],
runInShell: true,
);
], runInShell: true);
}
/// Fetch branches/tags from the repository at [directory].
@@ -80,10 +69,7 @@ class Git {
}
/// Run `git remote` at [directory].
Future<void> remote({
required String directory,
List<String>? args,
}) async {
Future<void> remote({required String directory, List<String>? args}) async {
await git(['remote', ...?args], workingDirectory: directory);
}
@@ -95,16 +81,13 @@ class Git {
required String pattern,
String? contains,
}) async {
final result = await git(
[
final result = await git([
'for-each-ref',
if (contains != null) ...['--contains', contains],
'--format',
format,
pattern,
],
workingDirectory: directory,
);
], workingDirectory: directory);
return '${result.stdout}'.trim();
}
@@ -122,10 +105,11 @@ class Git {
required String revision,
required String directory,
}) async {
final result = await git(
['rev-parse', '--verify', revision],
workingDirectory: directory,
);
final result = await git([
'rev-parse',
'--verify',
revision,
], workingDirectory: directory);
return '${result.stdout}'.trim();
}
@@ -141,18 +125,19 @@ class Git {
required Directory directory,
String revision = 'HEAD',
}) async {
final result = await git(
['symbolic-ref', revision],
workingDirectory: directory.path,
);
final result = await git([
'symbolic-ref',
revision,
], workingDirectory: directory.path);
return '${result.stdout}'.trim();
}
/// Returns the name of the branch the git repository located at [directory]
/// is currently on.
Future<String> currentBranch({required Directory directory}) async {
return (await symbolicRef(directory: directory))
.replaceAll('refs/heads/', '');
return (await symbolicRef(
directory: directory,
)).replaceAll('refs/heads/', '');
}
/// Whether [directory] is part of a git repository.
@@ -160,10 +145,7 @@ class Git {
try {
// [git] throws if the command's exit code is nonzero, which is what we're
// checking for here.
await git(
['status'],
workingDirectory: directory.path,
);
await git(['status'], workingDirectory: directory.path);
} on Exception {
return false;
}
@@ -176,14 +158,11 @@ class Git {
try {
// [git] throws if the command's exit code is nonzero, which is what we're
// checking for here.
await git(
[
await git([
'ls-files',
'--error-unmatch',
file.absolute.path,
],
workingDirectory: file.parent.path,
);
], workingDirectory: file.parent.path);
} on Exception {
return false;
}
@@ -107,15 +107,13 @@ class Gradlew {
args,
runInShell: true,
workingDirectory: p.dirname(executablePath),
environment: {
if (!javaHome.isNullOrEmpty) 'JAVA_HOME': javaHome!,
},
environment: {if (!javaHome.isNullOrEmpty) 'JAVA_HOME': javaHome!},
);
if (result.exitCode != ExitCode.success.code) {
if (result.stderr
.toString()
.contains(IncompatibleGradleException.errorPattern)) {
if (result.stderr.toString().contains(
IncompatibleGradleException.errorPattern,
)) {
throw IncompatibleGradleException();
}
}
@@ -142,10 +140,11 @@ class Gradlew {
/// Return the set of product flavors configured for the app at [projectRoot].
/// Returns an empty set for apps that do not use product flavors.
Future<Set<String>> productFlavors(String projectRoot) async {
final result = await _run(
['app:tasks', '--all', '--console=auto'],
projectRoot,
);
final result = await _run([
'app:tasks',
'--all',
'--console=auto',
], projectRoot);
if (result.exitCode != 0) {
throw Exception('${result.stdout}\n${result.stderr}');
@@ -32,9 +32,8 @@ class IDeviceSysLog {
);
/// The location of the idevicesyslog executable.
static File get idevicesyslogExecutable => File(
p.join(libimobiledeviceDirectory.path, 'idevicesyslog'),
);
static File get idevicesyslogExecutable =>
File(p.join(libimobiledeviceDirectory.path, 'idevicesyslog'));
/// The libraries that idevicesyslog depends on.
@visibleForTesting
@@ -86,9 +85,7 @@ class IDeviceSysLog {
// network flag.
if (!device.isWired) '--network',
],
environment: {
'DYLD_LIBRARY_PATH': _dyldPathEntry,
},
environment: {'DYLD_LIBRARY_PATH': _dyldPathEntry},
);
loggerProcess.stdout
@@ -17,11 +17,7 @@ final iosDeployRef = create(IOSDeploy.new);
IOSDeploy get iosDeploy => read(iosDeployRef);
/// lldb debugger state.
enum _DebuggerState {
detached,
launching,
attached,
}
enum _DebuggerState { detached, launching, attached }
/// {@template ios_deploy}
/// Wrapper around the `ios-deploy` command cached by the Flutter tool.
@@ -52,8 +48,9 @@ class IOSDeploy {
// (lldb) platform select remote-'ios' --sysroot
// This regex is to get the configurable lldb prompt.
// By default this prompt will be "lldb".
static final _lldbPlatformSelect =
RegExp(r"\s*platform select remote-'ios' --sysroot");
static final _lldbPlatformSelect = RegExp(
r"\s*platform select remote-'ios' --sysroot",
);
// (lldb) run
static final _lldbProcessExit = RegExp(r'Process \d* exited with status =');
@@ -172,16 +169,13 @@ Or run on an iOS simulator without code signing
});
try {
launchProcess = await process.start(
iosDeployExecutable.path,
[
launchProcess = await process.start(iosDeployExecutable.path, [
'--debug',
if (deviceId != null) ...['--id', deviceId],
'-r', // uninstall the app before reinstalling and clear app data
'--bundle',
bundlePath,
],
);
]);
void detach() {
if (debuggerState.isNotAttached) return;
@@ -294,11 +288,13 @@ Or run on an iOS simulator without code signing
logger.detail(line);
}
final stdoutSubscription =
launchProcess.stdout.asLines().listen(onStdout);
final stdoutSubscription = launchProcess.stdout.asLines().listen(
onStdout,
);
final stderrSubscription =
launchProcess.stderr.asLines().listen(onStderr);
final stderrSubscription = launchProcess.stderr.asLines().listen(
onStderr,
);
final status = await launchProcess.exitCode;
logger.detail('[ios-deploy] exited with code: $exitCode');
@@ -351,7 +347,8 @@ String detectFailures(String line, Logger logger) {
return line;
}
final isDeviceLocked = line.contains(IOSDeploy.deviceLockedError) ||
final isDeviceLocked =
line.contains(IOSDeploy.deviceLockedError) ||
line.contains(IOSDeploy.deviceLockedErrorMessage);
if (isDeviceLocked) {
@@ -378,7 +375,8 @@ extension on _DebuggerState {
extension on Stream<List<int>> {
Stream<String> asLines() {
return transform<String>(utf8.decoder)
.transform<String>(const LineSplitter());
return transform<String>(
utf8.decoder,
).transform<String>(const LineSplitter());
}
}
@@ -17,21 +17,18 @@ class Open {
/// Opens a new application at the provided [path] and streams the stdout and
/// stderr.
Future<Stream<List<int>>> newApplication({required String path}) async {
final app = Directory(p.join(path, 'Contents', 'MacOS'))
.listSync()
.firstWhere((f) => f is File);
final app = Directory(
p.join(path, 'Contents', 'MacOS'),
).listSync().firstWhere((f) => f is File);
await process.start('open', ['-n', path]);
final logStreamProcess = await process.start(
'log',
[
final logStreamProcess = await process.start('log', [
'stream',
'--style=compact',
'--process',
p.basenameWithoutExtension(app.path),
],
);
]);
return logStreamProcess.stdout;
}
@@ -41,11 +41,7 @@ class PatchExecutable {
cache.getArtifactDirectory('patch').path,
'patch',
);
final diffArguments = [
releaseArtifactPath,
patchArtifactPath,
diffPath,
];
final diffArguments = [releaseArtifactPath, patchArtifactPath, diffPath];
final result = await process.run(diffExecutable, diffArguments);
@@ -67,12 +63,10 @@ Please try again once you have installed this software.
}
if (result.exitCode != ExitCode.success.code) {
throw PatchFailedException(
'''
throw PatchFailedException('''
Failed to create diff (exit code ${result.exitCode}). $messageDetails
stdout: ${result.stdout}
stderr: ${result.stderr}''',
);
stderr: ${result.stderr}''');
}
}
}
@@ -21,11 +21,7 @@ class Powershell {
String? workingDirectory,
bool runInShell = false,
}) async {
final result = await process.run(
executable,
arguments,
runInShell: true,
);
final result = await process.run(executable, arguments, runInShell: true);
if (result.exitCode != ExitCode.success.code) {
throw ProcessException(
executable,
@@ -42,10 +38,7 @@ class Powershell {
final exePath = exeFile.path;
final pwshCommand = '(Get-Item -Path $exePath).VersionInfo.ProductVersion';
final result = await pwsh(
['-Command', pwshCommand],
runInShell: true,
);
final result = await pwsh(['-Command', pwshCommand], runInShell: true);
var versionString = (result.stdout as String).trim();
if (!versionString.contains('+')) {
@@ -41,11 +41,7 @@ class ShorebirdTools {
/// The directory containing the `shorebird_tools` package.
Directory get shorebirdToolsDirectory {
final dir = Directory(
p.join(
shorebirdEnv.flutterDirectory.path,
'packages',
'shorebird_tools',
),
p.join(shorebirdEnv.flutterDirectory.path, 'packages', 'shorebird_tools'),
);
return dir;
}
@@ -53,12 +49,7 @@ class ShorebirdTools {
Future<ShorebirdProcessResult> _run(List<String> args) {
return process.run(
shorebirdEnv.dartBinaryFile.path,
[
'run',
'shorebird_tools',
'package',
...args,
],
['run', 'shorebird_tools', 'package', ...args],
workingDirectory: shorebirdToolsDirectory.path,
);
}
@@ -70,22 +61,15 @@ class ShorebirdTools {
required String patchPath,
required String outputPath,
}) async {
final packageArguments = [
'-p',
patchPath,
'-o',
outputPath,
];
final packageArguments = ['-p', patchPath, '-o', outputPath];
final result = await _run(packageArguments);
if (result.exitCode != ExitCode.success.code) {
throw PackageFailedException(
'''
throw PackageFailedException('''
Failed to create package (exit code ${result.exitCode}).
stdout: ${result.stdout}
stderr: ${result.stderr}''',
);
stderr: ${result.stderr}''');
}
}
}
@@ -14,10 +14,7 @@ import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.da
extension OptionFinder on ArgResults {
/// Detects flags even when passed to underlying commands via a `--`
/// separator.
String? findOption(
String name, {
required ArgParser argParser,
}) {
String? findOption(String name, {required ArgParser argParser}) {
if (wasParsed(name)) {
return this[name] as String?;
}
@@ -25,14 +22,12 @@ extension OptionFinder on ArgResults {
// We would ideally check for abbreviations here as well, but ArgResults
// doesn't expose its parser (which we could use to get the list of
// [Options] being parsed) or an abbreviations map.
final abbr = argParser.options.values
final abbr =
argParser.options.values
.firstWhereOrNull((option) => option.name == name)
?.abbr;
final flagsToCheck = [
'--$name',
if (abbr != null) '-$abbr',
];
final flagsToCheck = ['--$name', if (abbr != null) '-$abbr'];
for (var i = 0; i < rest.length; i++) {
for (final flag in flagsToCheck) {
@@ -133,8 +128,7 @@ extension ForwardedArgs on ArgResults {
forwarded = rest.toList();
}
forwarded.addAll(
[
forwarded.addAll([
..._argsNamed(CommonArguments.dartDefineArg.name),
..._argsNamed(CommonArguments.dartDefineFromFileArg.name),
..._argsNamed(CommonArguments.buildNameArg.name),
@@ -142,8 +136,7 @@ extension ForwardedArgs on ArgResults {
..._argsNamed(CommonArguments.splitDebugInfoArg.name),
..._argsNamed(CommonArguments.exportMethodArg.name),
..._argsNamed(CommonArguments.exportOptionsPlistArg.name),
],
);
]);
return forwarded;
}
@@ -9,9 +9,7 @@ extension FileValidations on File {
/// Logs an error and exits with [ExitCode.usage] if this file does not exist.
void assertExists() {
if (!existsSync()) {
logger.err(
'No file found at $path',
);
logger.err('No file found at $path');
throw ProcessExit(ExitCode.usage.code);
}
}
@@ -7,7 +7,8 @@ String formatBytes(int bytes, {int decimals = 2}) {
final i = (log(bytes) / log(1024)).floor();
final value = bytes / pow(1024, i);
final suffix = suffixes[i];
final formattedValue = value % 1 == 0 || decimals <= 0
final formattedValue =
value % 1 == 0 || decimals <= 0
? '${value.toInt()} $suffix'
: '${value.toStringAsFixed(decimals)} $suffix';
return formattedValue;
@@ -8,9 +8,7 @@ export 'retrying_client.dart';
/// A reference to a [http.Client] instance.
final httpClientRef = create(
() => retryingHttpClient(
LoggingClient(httpClient: http.Client()),
),
() => retryingHttpClient(LoggingClient(httpClient: http.Client())),
);
/// The [http.Client] instance available in the current zone.
@@ -7,10 +7,8 @@ import 'package:mason_logger/mason_logger.dart';
/// {@endtemplate}
class DetailProgress implements Progress {
/// {@macro detail_progress}
DetailProgress._({
required Progress progress,
required String primaryMessage,
}) : _progress = progress,
DetailProgress._({required Progress progress, required String primaryMessage})
: _progress = progress,
_primaryMessage = primaryMessage;
String _primaryMessage;
@@ -16,14 +16,12 @@ const _logFileName = 'shorebird.log';
/// Where logs are written for the current Shorebird CLI run. A new file will
/// be created for every run of the Shorebird CLI, and will have the name
/// `timestamp_shorebird.log`.
final File currentRunLogFile = (() {
final File currentRunLogFile =
(() {
// TODO(bryanoltman): use package:clock to test for the correct timestamp
final timestamp = DateTime.now().millisecondsSinceEpoch;
final file = File(
p.join(
shorebirdEnv.logsDirectory.path,
'${timestamp}_$_logFileName',
),
p.join(shorebirdEnv.logsDirectory.path, '${timestamp}_$_logFileName'),
);
if (!file.existsSync()) {
@@ -36,8 +36,7 @@ class BuildEnvironmentMetadata extends Equatable {
String operatingSystemVersion = '1.2.3',
ShorebirdYaml shorebirdYaml = const ShorebirdYaml(appId: '123'),
String? xcodeVersion = '15.0',
}) =>
BuildEnvironmentMetadata(
}) => BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
shorebirdVersion: shorebirdVersion,
operatingSystem: operatingSystem,
@@ -63,8 +62,7 @@ class BuildEnvironmentMetadata extends Equatable {
String? operatingSystemVersion,
ShorebirdYaml? shorebirdYaml,
String? xcodeVersion,
}) =>
BuildEnvironmentMetadata(
}) => BuildEnvironmentMetadata(
flutterRevision: flutterRevision ?? this.flutterRevision,
shorebirdVersion: shorebirdVersion ?? this.shorebirdVersion,
operatingSystem: operatingSystem ?? this.operatingSystem,
@@ -9,22 +9,26 @@ part of 'build_environment_metadata.dart';
// **************************************************************************
BuildEnvironmentMetadata _$BuildEnvironmentMetadataFromJson(
Map<String, dynamic> json) =>
$checkedCreate(
Map<String, dynamic> json,
) => $checkedCreate(
'BuildEnvironmentMetadata',
json,
($checkedConvert) {
final val = BuildEnvironmentMetadata(
flutterRevision:
$checkedConvert('flutter_revision', (v) => v as String),
shorebirdVersion:
$checkedConvert('shorebird_version', (v) => v as String),
operatingSystem:
$checkedConvert('operating_system', (v) => v as String),
operatingSystemVersion:
$checkedConvert('operating_system_version', (v) => v as String),
shorebirdYaml: $checkedConvert('shorebird_yaml',
(v) => ShorebirdYaml.fromJson(v as Map<String, dynamic>)),
flutterRevision: $checkedConvert('flutter_revision', (v) => v as String),
shorebirdVersion: $checkedConvert(
'shorebird_version',
(v) => v as String,
),
operatingSystem: $checkedConvert('operating_system', (v) => v as String),
operatingSystemVersion: $checkedConvert(
'operating_system_version',
(v) => v as String,
),
shorebirdYaml: $checkedConvert(
'shorebird_yaml',
(v) => ShorebirdYaml.fromJson(v as Map<String, dynamic>),
),
xcodeVersion: $checkedConvert('xcode_version', (v) => v as String?),
);
return val;
@@ -35,13 +39,13 @@ BuildEnvironmentMetadata _$BuildEnvironmentMetadataFromJson(
'operatingSystem': 'operating_system',
'operatingSystemVersion': 'operating_system_version',
'shorebirdYaml': 'shorebird_yaml',
'xcodeVersion': 'xcode_version'
'xcodeVersion': 'xcode_version',
},
);
Map<String, dynamic> _$BuildEnvironmentMetadataToJson(
BuildEnvironmentMetadata instance) =>
<String, dynamic>{
BuildEnvironmentMetadata instance,
) => <String, dynamic>{
'flutter_revision': instance.flutterRevision,
'shorebird_version': instance.shorebirdVersion,
'operating_system': instance.operatingSystem,
@@ -39,8 +39,7 @@ class CreatePatchMetadata extends Equatable {
bool hasNativeChanges = false,
double? linkPercentage,
BuildEnvironmentMetadata? environment,
}) =>
CreatePatchMetadata(
}) => CreatePatchMetadata(
releasePlatform: releasePlatform,
usedIgnoreAssetChangesFlag: usedIgnoreAssetChangesFlag,
hasAssetChanges: hasAssetChanges,
@@ -68,8 +67,7 @@ class CreatePatchMetadata extends Equatable {
bool? hasNativeChanges,
double? linkPercentage,
BuildEnvironmentMetadata? environment,
}) =>
CreatePatchMetadata(
}) => CreatePatchMetadata(
releasePlatform: releasePlatform ?? this.releasePlatform,
usedIgnoreAssetChangesFlag:
usedIgnoreAssetChangesFlag ?? this.usedIgnoreAssetChangesFlag,
@@ -8,28 +8,35 @@ part of 'create_patch_metadata.dart';
// JsonSerializableGenerator
// **************************************************************************
CreatePatchMetadata _$CreatePatchMetadataFromJson(Map<String, dynamic> json) =>
$checkedCreate(
CreatePatchMetadata _$CreatePatchMetadataFromJson(
Map<String, dynamic> json,
) => $checkedCreate(
'CreatePatchMetadata',
json,
($checkedConvert) {
final val = CreatePatchMetadata(
releasePlatform: $checkedConvert('release_platform',
(v) => $enumDecode(_$ReleasePlatformEnumMap, v)),
releasePlatform: $checkedConvert(
'release_platform',
(v) => $enumDecode(_$ReleasePlatformEnumMap, v),
),
usedIgnoreAssetChangesFlag: $checkedConvert(
'used_ignore_asset_changes_flag', (v) => v as bool),
hasAssetChanges:
$checkedConvert('has_asset_changes', (v) => v as bool),
'used_ignore_asset_changes_flag',
(v) => v as bool,
),
hasAssetChanges: $checkedConvert('has_asset_changes', (v) => v as bool),
usedIgnoreNativeChangesFlag: $checkedConvert(
'used_ignore_native_changes_flag', (v) => v as bool),
hasNativeChanges:
$checkedConvert('has_native_changes', (v) => v as bool),
'used_ignore_native_changes_flag',
(v) => v as bool,
),
hasNativeChanges: $checkedConvert('has_native_changes', (v) => v as bool),
environment: $checkedConvert(
'environment',
(v) =>
BuildEnvironmentMetadata.fromJson(v as Map<String, dynamic>)),
(v) => BuildEnvironmentMetadata.fromJson(v as Map<String, dynamic>),
),
linkPercentage: $checkedConvert(
'link_percentage', (v) => (v as num?)?.toDouble()),
'link_percentage',
(v) => (v as num?)?.toDouble(),
),
);
return val;
},
@@ -39,13 +46,13 @@ CreatePatchMetadata _$CreatePatchMetadataFromJson(Map<String, dynamic> json) =>
'hasAssetChanges': 'has_asset_changes',
'usedIgnoreNativeChangesFlag': 'used_ignore_native_changes_flag',
'hasNativeChanges': 'has_native_changes',
'linkPercentage': 'link_percentage'
'linkPercentage': 'link_percentage',
},
);
Map<String, dynamic> _$CreatePatchMetadataToJson(
CreatePatchMetadata instance) =>
<String, dynamic>{
CreatePatchMetadata instance,
) => <String, dynamic>{
'release_platform': _$ReleasePlatformEnumMap[instance.releasePlatform]!,
'used_ignore_asset_changes_flag': instance.usedIgnoreAssetChangesFlag,
'has_asset_changes': instance.hasAssetChanges,
@@ -33,8 +33,7 @@ class UpdateReleaseMetadata extends Equatable {
String? flutterVersionOverride = '1.2.3',
bool? generatedApks = false,
BuildEnvironmentMetadata? environment,
}) =>
UpdateReleaseMetadata(
}) => UpdateReleaseMetadata(
releasePlatform: releasePlatform,
flutterVersionOverride: flutterVersionOverride,
generatedApks: generatedApks,
@@ -56,8 +55,7 @@ class UpdateReleaseMetadata extends Equatable {
String? flutterVersionOverride,
bool? generatedApks,
BuildEnvironmentMetadata? environment,
}) =>
UpdateReleaseMetadata(
}) => UpdateReleaseMetadata(
releasePlatform: releasePlatform ?? this.releasePlatform,
flutterVersionOverride:
flutterVersionOverride ?? this.flutterVersionOverride,
@@ -9,20 +9,24 @@ part of 'update_release_metadata.dart';
// **************************************************************************
UpdateReleaseMetadata _$UpdateReleaseMetadataFromJson(
Map<String, dynamic> json) =>
$checkedCreate(
Map<String, dynamic> json,
) => $checkedCreate(
'UpdateReleaseMetadata',
json,
($checkedConvert) {
final val = UpdateReleaseMetadata(
releasePlatform: $checkedConvert('release_platform',
(v) => $enumDecode(_$ReleasePlatformEnumMap, v)),
flutterVersionOverride:
$checkedConvert('flutter_version_override', (v) => v as String?),
releasePlatform: $checkedConvert(
'release_platform',
(v) => $enumDecode(_$ReleasePlatformEnumMap, v),
),
flutterVersionOverride: $checkedConvert(
'flutter_version_override',
(v) => v as String?,
),
environment: $checkedConvert(
'environment',
(v) =>
BuildEnvironmentMetadata.fromJson(v as Map<String, dynamic>)),
(v) => BuildEnvironmentMetadata.fromJson(v as Map<String, dynamic>),
),
generatedApks: $checkedConvert('generated_apks', (v) => v as bool?),
);
return val;
@@ -30,13 +34,13 @@ UpdateReleaseMetadata _$UpdateReleaseMetadataFromJson(
fieldKeyMap: const {
'releasePlatform': 'release_platform',
'flutterVersionOverride': 'flutter_version_override',
'generatedApks': 'generated_apks'
'generatedApks': 'generated_apks',
},
);
Map<String, dynamic> _$UpdateReleaseMetadataToJson(
UpdateReleaseMetadata instance) =>
<String, dynamic>{
UpdateReleaseMetadata instance,
) => <String, dynamic>{
'release_platform': _$ReleasePlatformEnumMap[instance.releasePlatform]!,
'flutter_version_override': instance.flutterVersionOverride,
'generated_apks': instance.generatedApks,
@@ -35,7 +35,8 @@ class NetworkCheckerException implements Exception {
/// {@endtemplate}
class NetworkChecker {
/// The URLs to check for network reachability.
static final urlsToCheck = [
static final urlsToCheck =
[
'https://api.shorebird.dev',
'https://console.shorebird.dev',
'https://oauth2.googleapis.com',
@@ -81,12 +82,10 @@ class NetworkChecker {
final end = clock.now();
final fileSize = file.existsSync() ? file.lengthSync() : 0;
if (fileSize != 16000000) {
throw NetworkCheckerException(
'''
throw NetworkCheckerException('''
Unexpected file size.
Expected: 16MB
Actual: ${formatBytes(fileSize)}''',
);
Actual: ${formatBytes(fileSize)}''');
}
return fileSize / (end.difference(start).inMilliseconds * 1000);
} finally {
@@ -113,7 +112,9 @@ Actual: ${formatBytes(fileSize)}''',
final start = clock.now();
final file = await http.MultipartFile.fromPath('file', testFile.path);
final uploadRequest = http.MultipartRequest('POST', uri)..files.add(file);
final uploadResponse = await httpClient.send(uploadRequest).timeout(
final uploadResponse = await httpClient
.send(uploadRequest)
.timeout(
timeout,
onTimeout: () {
throw const NetworkCheckerException('Upload timed out');

Some files were not shown because too many files have changed in this diff Show More