feat(shorebird_cli): shorebird patch ios-framework-alpha supports dynamic Flutter revision (#1019)

This commit is contained in:
Felix Angelov
2023-08-03 16:53:52 -05:00
committed by GitHub
parent 4b00cfbed9
commit 3efa2b0535
2 changed files with 198 additions and 112 deletions
@@ -3,6 +3,7 @@ import 'dart:io' hide Platform;
import 'package:crypto/crypto.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:platform/platform.dart';
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/config/shorebird_yaml.dart';
@@ -13,6 +14,7 @@ import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/shorebird_artifact_mixin.dart';
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_flutter_manager.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -75,7 +77,6 @@ of the iOS app that is using this module.''',
final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!;
final appId = shorebirdYaml.getAppId();
final app = await codePushClientWrapper.getApp(appId: appId);
final release = await codePushClientWrapper.getRelease(
appId: appId,
releaseVersion: releaseVersion,
@@ -89,9 +90,34 @@ Please re-run the release command for this version or create a new release.''');
return ExitCode.software.code;
}
final shorebirdFlutterRevision = shorebirdEnv.flutterRevision;
if (release.flutterRevision != shorebirdFlutterRevision) {
final installFlutterRevisionProgress = logger.progress(
'Switching to Flutter revision ${release.flutterRevision}',
);
try {
await shorebirdFlutterManager.installRevision(
revision: release.flutterRevision,
);
installFlutterRevisionProgress.complete();
} catch (error) {
installFlutterRevisionProgress.fail('$error');
return ExitCode.software.code;
}
}
final buildProgress = logger.progress('Building patch');
try {
await buildIosFramework();
await runScoped(
buildIosFramework,
values: {
shorebirdEnvRef.overrideWith(
() => ShorebirdEnv(
flutterRevisionOverride: release.flutterRevision,
),
),
},
);
buildProgress.complete();
} on ProcessException catch (error) {
buildProgress.fail('Failed to build: ${error.message}');
@@ -109,33 +135,6 @@ Please re-run the release command for this version or create a new release.''');
buildProgress.complete();
final shorebirdFlutterRevision = shorebirdEnv.flutterRevision;
if (release.flutterRevision != shorebirdFlutterRevision) {
logger
..err('''
Flutter revision mismatch.
The release you are trying to patch was built with a different version of Flutter.
Release Flutter Revision: ${release.flutterRevision}
Current Flutter Revision: $shorebirdFlutterRevision
''')
..info(
'''
Either create a new release using:
${lightCyan.wrap('shorebird release')}
Or downgrade your Flutter version and try again using:
${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')}
${lightCyan.wrap('git checkout ${release.flutterRevision}')}
Shorebird plans to support this automatically, let us know if it's important to you:
https://github.com/shorebirdtech/shorebird/issues/472
''',
);
return ExitCode.software.code;
}
if (dryRun) {
logger
..info('No issues detected.')
@@ -15,6 +15,7 @@ import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/process.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_flutter_manager.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
import 'package:shorebird_cli/src/validators/validators.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -37,11 +38,16 @@ class _MockProgress extends Mock implements Progress {}
class _MockProcessResult extends Mock implements ShorebirdProcessResult {}
class _MockProcessWrapper extends Mock implements ProcessWrapper {}
class _MockShorebirdEnv extends Mock implements ShorebirdEnv {}
class _MockShorebirdFlutterValidator extends Mock
implements ShorebirdFlutterValidator {}
class _MockShorebirdFlutterManager extends Mock
implements ShorebirdFlutterManager {}
class _MockShorebirdProcess extends Mock implements ShorebirdProcess {}
class _MockShorebirdValidator extends Mock implements ShorebirdValidator {}
@@ -88,6 +94,7 @@ flutter:
late ShorebirdProcessResult aotBuildProcessResult;
late ShorebirdProcessResult flutterBuildProcessResult;
late ShorebirdEnv shorebirdEnv;
late ShorebirdFlutterManager shorebirdFlutterManager;
late ShorebirdFlutterValidator flutterValidator;
late ShorebirdProcess shorebirdProcess;
late ShorebirdValidator shorebirdValidator;
@@ -104,6 +111,8 @@ flutter:
platformRef.overrideWith(() => platform),
processRef.overrideWith(() => shorebirdProcess),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
shorebirdFlutterManagerRef
.overrideWith(() => shorebirdFlutterManager),
shorebirdValidatorRef.overrideWith(() => shorebirdValidator),
},
);
@@ -169,6 +178,7 @@ flutter:
aotBuildProcessResult = _MockProcessResult();
flutterBuildProcessResult = _MockProcessResult();
shorebirdEnv = _MockShorebirdEnv();
shorebirdFlutterManager = _MockShorebirdFlutterManager();
flutterValidator = _MockShorebirdFlutterValidator();
shorebirdProcess = _MockShorebirdProcess();
shorebirdValidator = _MockShorebirdValidator();
@@ -225,6 +235,11 @@ flutter:
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).thenAnswer((_) async {});
when(
() => shorebirdFlutterManager.installRevision(
revision: any(named: 'revision'),
),
).thenAnswer((_) async {});
when(
() => shorebirdValidator.validatePreconditions(
checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'),
@@ -279,66 +294,167 @@ flutter:
});
test(
'''exits with code 70 if release is in draft state for the ios platform''',
() async {
when(
() => codePushClientWrapper.getRelease(
appId: any(named: 'appId'),
releaseVersion: any(named: 'releaseVersion'),
),
).thenAnswer(
(_) async => const Release(
id: 0,
appId: appId,
version: version,
flutterRevision: flutterRevision,
displayName: '1.2.3+1',
platformStatuses: {ReleasePlatform.ios: ReleaseStatus.draft},
),
);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.software.code);
verify(
() => logger.err('''
'''exits with code 70 if release is in draft state for the ios platform''',
() async {
when(
() => codePushClientWrapper.getRelease(
appId: any(named: 'appId'),
releaseVersion: any(named: 'releaseVersion'),
),
).thenAnswer(
(_) async => const Release(
id: 0,
appId: appId,
version: version,
flutterRevision: flutterRevision,
displayName: '1.2.3+1',
platformStatuses: {ReleasePlatform.ios: ReleaseStatus.draft},
),
);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.software.code);
verify(
() => logger.err('''
Release 1.2.3+1 is in an incomplete state. It's possible that the original release was terminated or failed to complete.
Please re-run the release command for this version or create a new release.'''),
).called(1);
},
);
).called(1);
});
test('proceeds if release is in draft state for a non-ios platform',
() async {
when(
() => codePushClientWrapper.getRelease(
appId: any(named: 'appId'),
releaseVersion: any(named: 'releaseVersion'),
),
).thenAnswer(
(_) async => const Release(
id: 0,
appId: appId,
version: version,
flutterRevision: flutterRevision,
displayName: '1.2.3+1',
platformStatuses: {ReleasePlatform.android: ReleaseStatus.draft},
),
);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.success.code);
});
test(
'proceeds if release is in draft state for a non-ios platform',
() async {
when(
() => codePushClientWrapper.getRelease(
appId: any(named: 'appId'),
releaseVersion: any(named: 'releaseVersion'),
'installs correct flutter revision '
'when release flutter revision differs', () async {
const otherRevision = 'other-revision';
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.success.code));
verify(
() => logger.progress(
'Switching to Flutter revision ${release.flutterRevision}',
),
).called(1);
verify(
() => shorebirdFlutterManager.installRevision(
revision: release.flutterRevision,
),
).called(1);
});
test(
'builds using correct flutter revision '
'when release flutter revision differs', () async {
when(
() => platform.script,
).thenReturn(Uri.parse('file:///bin/cache/shorebird.snapshot'));
const otherRevision = 'other-revision';
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
final processWrapper = _MockProcessWrapper();
when(
() => processWrapper.run(
any(),
any(),
runInShell: any(named: 'runInShell'),
workingDirectory: any(named: 'workingDirectory'),
environment: any(named: 'environment'),
),
).thenAnswer((_) async => flutterBuildProcessResult);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
await IOOverrides.runZoned(
() => runWithOverrides(
() => runScoped(
() => command.run(),
values: {
processRef.overrideWith(
() => ShorebirdProcess(
logger: logger,
processWrapper: processWrapper,
),
),
},
),
).thenAnswer(
(_) async => const Release(
id: 0,
appId: appId,
version: version,
flutterRevision: flutterRevision,
displayName: '1.2.3+1',
platformStatuses: {ReleasePlatform.android: ReleaseStatus.draft},
),
);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.success.code);
},
);
),
getCurrentDirectory: () => tempDir,
);
verify(
() => processWrapper.run(
'/bin/cache/flutter/${release.flutterRevision}/bin/flutter',
any(),
runInShell: true,
workingDirectory: any(named: 'workingDirectory'),
environment: any(named: 'environment'),
),
).called(1);
});
test(
'exits with code 70 when '
'unable to install correct flutter revision', () async {
final exception = Exception('oops');
const otherRevision = 'other-revision';
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
when(
() => shorebirdFlutterManager.installRevision(
revision: any(named: 'revision'),
),
).thenThrow(exception);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => logger.progress(
'Switching to Flutter revision ${release.flutterRevision}',
),
).called(1);
verify(
() => shorebirdFlutterManager.installRevision(
revision: release.flutterRevision,
),
).called(1);
verify(() => progress.fail('$exception')).called(1);
});
test('exits with code 70 when build fails', () async {
when(() => flutterBuildProcessResult.exitCode).thenReturn(1);
@@ -354,35 +470,6 @@ Please re-run the release command for this version or create a new release.'''),
verify(() => progress.fail('Failed to build: oh no')).called(1);
});
test(
'errors when shorebird flutter revision '
'does not match release revision', () async {
const otherRevision = 'other-revision';
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.software.code);
verify(
() => logger.err('''
Either create a new release using:
${lightCyan.wrap('shorebird release aar')}
Or downgrade your Flutter version and try again using:
${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')}
${lightCyan.wrap('git checkout ${release.flutterRevision}')}
Shorebird plans to support this automatically, let us know if it's important to you:
https://github.com/shorebirdtech/shorebird/issues/472
'''),
).called(1);
});
test('throws error when creating aot snapshot fails', () async {
const error = 'oops something went wrong';
when(() => aotBuildProcessResult.exitCode).thenReturn(1);