fix(shorebird_cli): switch to release flutter revision in patch (#1233)
Co-authored-by: Felix Angelov <felix@shorebird.dev>
This commit is contained in:
@@ -17,6 +17,7 @@ import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/patch_diff_checker.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.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_release_version_mixin.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
@@ -103,6 +104,7 @@ class PatchAndroidCommand extends ShorebirdCommand
|
||||
final appId = shorebirdYaml.getAppId(flavor: flavor);
|
||||
final app = await codePushClientWrapper.getApp(appId: appId);
|
||||
|
||||
final originalFlutterRevision = shorebirdEnv.flutterRevision;
|
||||
final buildProgress = logger.progress('Building patch');
|
||||
try {
|
||||
await buildAppBundle(flavor: flavor, target: target);
|
||||
@@ -144,26 +146,33 @@ 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) {
|
||||
logger
|
||||
..err('''
|
||||
Flutter revision mismatch.
|
||||
|
||||
if (release.flutterRevision != originalFlutterRevision) {
|
||||
logger.info('''
|
||||
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 android')}
|
||||
Current Flutter Revision: $originalFlutterRevision''');
|
||||
|
||||
Or change your Flutter version and try again using:
|
||||
${lightCyan.wrap('shorebird flutter versions use ${release.flutterRevision}')}''',
|
||||
var flutterVersionProgress = logger.progress(
|
||||
'Switching to Flutter revision ${release.flutterRevision}',
|
||||
);
|
||||
await shorebirdFlutter.useRevision(revision: release.flutterRevision);
|
||||
flutterVersionProgress.complete();
|
||||
|
||||
final buildProgress = logger.progress('Building patch');
|
||||
try {
|
||||
await buildAppBundle(flavor: flavor, target: target);
|
||||
buildProgress.complete();
|
||||
} on ProcessException catch (error) {
|
||||
buildProgress.fail('Failed to build: ${error.message}');
|
||||
return ExitCode.software.code;
|
||||
} finally {
|
||||
flutterVersionProgress = logger.progress(
|
||||
'Reverting to Flutter revision $originalFlutterRevision',
|
||||
);
|
||||
return ExitCode.software.code;
|
||||
await shorebirdFlutter.useRevision(revision: originalFlutterRevision);
|
||||
flutterVersionProgress.complete();
|
||||
}
|
||||
}
|
||||
|
||||
final releaseArtifacts = await codePushClientWrapper.getReleaseArtifacts(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:io' hide Platform;
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
|
||||
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
|
||||
@@ -16,6 +17,7 @@ import 'package:shorebird_cli/src/patch_diff_checker.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.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
@@ -93,36 +95,18 @@ class PatchIosCommand extends ShorebirdCommand
|
||||
const arch = 'aarch64';
|
||||
const channelName = 'stable';
|
||||
const releasePlatform = ReleasePlatform.ios;
|
||||
final target = results['target'] as String?;
|
||||
final flavor = results['flavor'] as String?;
|
||||
|
||||
final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!;
|
||||
final appId = shorebirdYaml.getAppId(flavor: flavor);
|
||||
final app = await codePushClientWrapper.getApp(appId: appId);
|
||||
|
||||
final buildProgress = logger.progress('Building release');
|
||||
try {
|
||||
await buildIpa(flavor: flavor, target: target);
|
||||
} on ProcessException catch (error) {
|
||||
buildProgress.fail('Failed to build: ${error.message}');
|
||||
return ExitCode.software.code;
|
||||
} on BuildException catch (error) {
|
||||
buildProgress.fail('Failed to build IPA');
|
||||
logger.err(error.message);
|
||||
await _buildPatch();
|
||||
} catch (_) {
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
final File aotFile;
|
||||
try {
|
||||
final newestDillFile = newestAppDill();
|
||||
aotFile = await buildElfAotSnapshot(appDillPath: newestDillFile.path);
|
||||
} catch (error) {
|
||||
buildProgress.fail('$error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
buildProgress.complete();
|
||||
|
||||
final detectReleaseVersionProgress = logger.progress(
|
||||
'Detecting release version',
|
||||
);
|
||||
@@ -159,26 +143,31 @@ 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) {
|
||||
logger
|
||||
..err('''
|
||||
Flutter revision mismatch.
|
||||
|
||||
final originalFlutterRevision = shorebirdEnv.flutterRevision;
|
||||
if (release.flutterRevision != originalFlutterRevision) {
|
||||
logger.info('''
|
||||
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 ios-alpha')}
|
||||
Current Flutter Revision: $originalFlutterRevision''');
|
||||
|
||||
Or change your Flutter version and try again using:
|
||||
${lightCyan.wrap('shorebird flutter versions use ${release.flutterRevision}')}''',
|
||||
var flutterVersionProgress = logger.progress(
|
||||
'Switching to Flutter revision ${release.flutterRevision}',
|
||||
);
|
||||
await shorebirdFlutter.useRevision(revision: release.flutterRevision);
|
||||
flutterVersionProgress.complete();
|
||||
|
||||
try {
|
||||
await _buildPatch();
|
||||
} catch (_) {
|
||||
return ExitCode.software.code;
|
||||
} finally {
|
||||
flutterVersionProgress = logger.progress(
|
||||
'Switching back to original Flutter revision $originalFlutterRevision',
|
||||
);
|
||||
return ExitCode.software.code;
|
||||
await shorebirdFlutter.useRevision(revision: originalFlutterRevision);
|
||||
flutterVersionProgress.complete();
|
||||
}
|
||||
}
|
||||
|
||||
final releaseArtifact = await codePushClientWrapper.getReleaseArtifact(
|
||||
@@ -209,6 +198,7 @@ Or change your Flutter version and try again using:
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
final aotFile = File(_aotOutputPath);
|
||||
final aotFileSize = aotFile.statSync().size;
|
||||
|
||||
final summary = [
|
||||
@@ -257,4 +247,36 @@ ${summary.join('\n')}
|
||||
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
String get _aotOutputPath =>
|
||||
p.join(Directory.current.path, 'build', 'out.aot');
|
||||
|
||||
Future<void> _buildPatch() async {
|
||||
final target = results['target'] as String?;
|
||||
final flavor = results['flavor'] as String?;
|
||||
final buildProgress = logger.progress('Building patch');
|
||||
try {
|
||||
await buildIpa(flavor: flavor, target: target);
|
||||
} on ProcessException catch (error) {
|
||||
buildProgress.fail('Failed to build: ${error.message}');
|
||||
rethrow;
|
||||
} on BuildException catch (error) {
|
||||
buildProgress.fail('Failed to build IPA');
|
||||
logger.err(error.message);
|
||||
rethrow;
|
||||
}
|
||||
|
||||
try {
|
||||
final newestDillFile = newestAppDill();
|
||||
await buildElfAotSnapshot(
|
||||
appDillPath: newestDillFile.path,
|
||||
outFilePath: _aotOutputPath,
|
||||
);
|
||||
} catch (error) {
|
||||
buildProgress.fail('$error');
|
||||
rethrow;
|
||||
}
|
||||
|
||||
buildProgress.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,10 @@ Please re-run the release command for this version or create a new release.''');
|
||||
final File aotFile;
|
||||
try {
|
||||
final newestDillFile = newestAppDill();
|
||||
aotFile = await buildElfAotSnapshot(appDillPath: newestDillFile.path);
|
||||
aotFile = await buildElfAotSnapshot(
|
||||
appDillPath: newestDillFile.path,
|
||||
outFilePath: p.join(Directory.current.path, 'build', 'out.aot'),
|
||||
);
|
||||
} catch (error) {
|
||||
buildProgress.fail('$error');
|
||||
return ExitCode.software.code;
|
||||
|
||||
@@ -374,11 +374,13 @@ Either run `flutter pub get` manually, or follow the steps in ${link(uri: Uri.pa
|
||||
return diffPath;
|
||||
}
|
||||
|
||||
/// Creates an AOT snapshot of the given [appDillPath] and returns the
|
||||
/// resulting snapshot file.
|
||||
/// Creates an AOT snapshot of the given [appDillPath] at [outFilePath] and
|
||||
/// returns the resulting file.
|
||||
// TODO(bryanoltman): make this work with the --local-engine flag.
|
||||
Future<File> buildElfAotSnapshot({required String appDillPath}) async {
|
||||
final outFilePath = p.join(Directory.current.path, 'build', 'out.aot');
|
||||
Future<File> buildElfAotSnapshot({
|
||||
required String appDillPath,
|
||||
required String outFilePath,
|
||||
}) async {
|
||||
final arguments = [
|
||||
'--deterministic',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
|
||||
@@ -61,11 +61,11 @@ class _MockHttpClient extends Mock implements http.Client {}
|
||||
|
||||
class _MockShorebirdEnv extends Mock implements ShorebirdEnv {}
|
||||
|
||||
class _MockShorebirdFlutter extends Mock implements ShorebirdFlutter {}
|
||||
|
||||
class _MockShorebirdFlutterValidator extends Mock
|
||||
implements ShorebirdFlutterValidator {}
|
||||
|
||||
class _MockShorebirdFlutter extends Mock implements ShorebirdFlutter {}
|
||||
|
||||
class _MockShorebirdProcess extends Mock implements ShorebirdProcess {}
|
||||
|
||||
class _MockShorebirdValidator extends Mock implements ShorebirdValidator {}
|
||||
@@ -244,6 +244,8 @@ flutter:
|
||||
when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot);
|
||||
when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory);
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn(flutterRevision);
|
||||
when(() => shorebirdFlutter.useRevision(revision: any(named: 'revision')))
|
||||
.thenAnswer((_) async {});
|
||||
when(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
@@ -499,8 +501,8 @@ Please re-run the release command for this version or create a new release.'''),
|
||||
});
|
||||
|
||||
test(
|
||||
'errors when shorebird flutter revision '
|
||||
'does not match release revision', () async {
|
||||
'''switches to release flutter revision when shorebird flutter revision does not match''',
|
||||
() async {
|
||||
const otherRevision = 'other-revision';
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
|
||||
final tempDir = setUpTempDir();
|
||||
@@ -511,17 +513,49 @@ Please re-run the release command for this version or create a new release.'''),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
// Verify that we switch back to the original revision once we're done.
|
||||
verifyInOrder([
|
||||
() => shorebirdFlutter.useRevision(revision: release.flutterRevision),
|
||||
() => shorebirdFlutter.useRevision(revision: otherRevision),
|
||||
]);
|
||||
verify(
|
||||
() => logger.info('''
|
||||
Either create a new release using:
|
||||
${lightCyan.wrap('shorebird release android')}
|
||||
|
||||
Or change your Flutter version and try again using:
|
||||
${lightCyan.wrap('shorebird flutter versions use ${release.flutterRevision}')}'''),
|
||||
() => logger.info(
|
||||
any(
|
||||
that: stringContainsInOrder([
|
||||
'''The release you are trying to patch was built with a different version of Flutter.''',
|
||||
'Release Flutter Revision: ${release.flutterRevision}',
|
||||
'Current Flutter Revision: $otherRevision',
|
||||
]),
|
||||
),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test(
|
||||
'exits with code 70 if build fails after switching flutter versions',
|
||||
() async {
|
||||
const otherRevision = 'other-revision';
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
|
||||
when(
|
||||
() => shorebirdFlutter.useRevision(revision: any(named: 'revision')),
|
||||
).thenAnswer((invocation) async {
|
||||
// Cause builds to fail after switching flutter versions.
|
||||
when(() => flutterBuildProcessResult.exitCode).thenReturn(1);
|
||||
when(() => flutterBuildProcessResult.stderr).thenReturn('oops');
|
||||
});
|
||||
final tempDir = setUpTempDir();
|
||||
setUpTempArtifacts(tempDir);
|
||||
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
() => runWithOverrides(command.run),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
|
||||
expect(exitCode, equals(ExitCode.software.code));
|
||||
},
|
||||
);
|
||||
|
||||
test('errors when detecting release version name fails', () async {
|
||||
final exception = Exception(
|
||||
'Failed to extract version name from app bundle: oops',
|
||||
|
||||
@@ -292,6 +292,8 @@ flutter:
|
||||
when(() => shorebirdEnv.genSnapshotFile).thenReturn(genSnapshotFile);
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn(flutterRevision);
|
||||
when(() => shorebirdEnv.isRunningOnCI).thenReturn(false);
|
||||
when(() => shorebirdFlutter.useRevision(revision: any(named: 'revision')))
|
||||
.thenAnswer((_) async {});
|
||||
when(
|
||||
() => aotBuildProcessResult.exitCode,
|
||||
).thenReturn(ExitCode.success.code);
|
||||
@@ -568,8 +570,8 @@ Please re-run the release command for this version or create a new release.'''),
|
||||
});
|
||||
|
||||
test(
|
||||
'errors when shorebird flutter revision '
|
||||
'does not match release revision', () async {
|
||||
'''switches to release flutter revision when shorebird flutter revision does not match''',
|
||||
() async {
|
||||
const otherRevision = 'other-revision';
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
|
||||
final tempDir = setUpTempDir();
|
||||
@@ -580,17 +582,50 @@ Please re-run the release command for this version or create a new release.'''),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
verify(
|
||||
() => logger.info('''
|
||||
Either create a new release using:
|
||||
${lightCyan.wrap('shorebird release ios-alpha')}
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
// Verify that we switch back to the original revision once we're done.
|
||||
verifyInOrder([
|
||||
() => shorebirdFlutter.useRevision(revision: release.flutterRevision),
|
||||
() => shorebirdFlutter.useRevision(revision: otherRevision),
|
||||
]);
|
||||
|
||||
Or change your Flutter version and try again using:
|
||||
${lightCyan.wrap('shorebird flutter versions use ${release.flutterRevision}')}'''),
|
||||
verify(
|
||||
() => logger.info(
|
||||
any(
|
||||
that: stringContainsInOrder([
|
||||
'''The release you are trying to patch was built with a different version of Flutter.''',
|
||||
'Release Flutter Revision: ${release.flutterRevision}',
|
||||
'Current Flutter Revision: $otherRevision',
|
||||
]),
|
||||
),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test(
|
||||
'exits with code 70 if build fails after switching flutter versions',
|
||||
() async {
|
||||
const otherRevision = 'other-revision';
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn(otherRevision);
|
||||
when(
|
||||
() => shorebirdFlutter.useRevision(revision: any(named: 'revision')),
|
||||
).thenAnswer((invocation) async {
|
||||
// Cause builds to fail after switching flutter versions.
|
||||
when(() => flutterBuildProcessResult.exitCode).thenReturn(1);
|
||||
when(() => flutterBuildProcessResult.stderr).thenReturn('oops');
|
||||
});
|
||||
final tempDir = setUpTempDir();
|
||||
setUpTempArtifacts(tempDir);
|
||||
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
() => runWithOverrides(command.run),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
|
||||
expect(exitCode, equals(ExitCode.software.code));
|
||||
},
|
||||
);
|
||||
|
||||
test('exits with code 70 when release version cannot be determiend',
|
||||
() async {
|
||||
when(() => ipa.versionNumber).thenThrow(Exception('oops'));
|
||||
|
||||
Reference in New Issue
Block a user