Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shorebird_cli): add channel support to patch and preview commands #1340

Merged
merged 6 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ class PatchAndroidCommand extends ShorebirdCommand
'flavor',
help: 'The product flavor to use when building the app.',
)
..addOption(
'channel',
help: 'The channel to publish the patch to.',
defaultsTo: 'stable',
)
..addFlag(
'force',
abbr: 'f',
Expand Down Expand Up @@ -97,7 +102,7 @@ class PatchAndroidCommand extends ShorebirdCommand
await cache.updateAll();

const platform = ReleasePlatform.android;
const channelName = 'stable';
final channelName = results['channel'] as String;
final flavor = results['flavor'] as String?;
final target = results['target'] as String?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class PatchIosCommand extends ShorebirdCommand
'flavor',
help: 'The product flavor to use when building the app.',
)
..addOption(
'channel',
help: 'The channel to publish the patch to.',
defaultsTo: 'stable',
)
..addFlag(
'codesign',
help: 'Codesign the application bundle.',
Expand Down Expand Up @@ -95,7 +100,7 @@ class PatchIosCommand extends ShorebirdCommand
}

const arch = 'aarch64';
const channelName = 'stable';
final channelName = results['channel'] as String;
const releasePlatform = ReleasePlatform.ios;
final flavor = results['flavor'] as String?;

Expand Down
145 changes: 129 additions & 16 deletions packages/shorebird_cli/lib/src/commands/preview_command.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';

import 'package:archive/archive_io.dart';
import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;
import 'package:mason_logger/mason_logger.dart';
Expand All @@ -17,6 +19,7 @@ import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:yaml_edit/yaml_edit.dart';

/// {@template preview_command}
/// `shorebird preview` command.
Expand Down Expand Up @@ -49,6 +52,11 @@ class PreviewCommand extends ShorebirdCommand {
ReleasePlatform.ios.name: 'iOS',
},
help: 'The platform of the release.',
)
..addOption(
'channel',
defaultsTo: 'stable',
help: 'The channel to preview the release for.',
);
}

Expand Down Expand Up @@ -103,17 +111,20 @@ class PreviewCommand extends ShorebirdCommand {
);

final deviceId = results['device-id'] as String?;
final channel = results['channel'] as String;

return switch (platform) {
ReleasePlatform.android => installAndLaunchAndroid(
appId: appId,
release: release,
deviceId: deviceId,
channel: channel,
),
ReleasePlatform.ios => installAndLaunchIos(
appId: appId,
release: release,
deviceId: deviceId,
channel: channel,
),
};
}
Expand Down Expand Up @@ -151,6 +162,7 @@ class PreviewCommand extends ShorebirdCommand {
Future<int> installAndLaunchAndroid({
required String appId,
required Release release,
required String channel,
String? deviceId,
}) async {
const platform = ReleasePlatform.android;
Expand Down Expand Up @@ -187,6 +199,23 @@ class PreviewCommand extends ShorebirdCommand {
}
}

final apksPath = getArtifactPath(
appId: appId,
release: release,
platform: platform,
extension: 'apks',
);

if (File(apksPath).existsSync()) File(apksPath).deleteSync();
final progress = logger.progress('Using channel $channel');
try {
await setChannelOnAab(aabFile: aabFile, channel: channel);
progress.complete();
} catch (error) {
progress.fail('$error');
return ExitCode.software.code;
}

final extractMetadataProgress = logger.progress('Extracting metadata');
late String package;
try {
Expand All @@ -197,22 +226,13 @@ class PreviewCommand extends ShorebirdCommand {
return ExitCode.software.code;
}

final apksPath = getArtifactPath(
appId: appId,
release: release,
platform: platform,
extension: 'apks',
);

if (!File(apksPath).existsSync()) {
final buildApksProgress = logger.progress('Building apks');
try {
await bundletool.buildApks(bundle: aabFile.path, output: apksPath);
buildApksProgress.complete();
} catch (error) {
buildApksProgress.fail('$error');
return ExitCode.software.code;
}
final buildApksProgress = logger.progress('Building apks');
try {
await bundletool.buildApks(bundle: aabFile.path, output: apksPath);
buildApksProgress.complete();
} catch (error) {
buildApksProgress.fail('$error');
return ExitCode.software.code;
}

final installApksProgress = logger.progress('Installing apks');
Expand Down Expand Up @@ -247,6 +267,7 @@ class PreviewCommand extends ShorebirdCommand {
Future<int> installAndLaunchIos({
required String appId,
required Release release,
required String channel,
String? deviceId,
}) async {
const platform = ReleasePlatform.ios;
Expand Down Expand Up @@ -285,6 +306,18 @@ class PreviewCommand extends ShorebirdCommand {
}
}

final progress = logger.progress('Using channel $channel');
try {
await setChannelOnRunner(
runnerDirectory: runnerDirectory,
channel: channel,
);
progress.complete();
} catch (error) {
progress.fail('$error');
return ExitCode.software.code;
}

try {
final exitCode = await iosDeploy.installAndLaunchApp(
bundlePath: runnerDirectory.path,
Expand All @@ -308,4 +341,84 @@ class PreviewCommand extends ShorebirdCommand {
'${platform.name}_${release.version}.$extension',
);
}

Future<void> setChannelOnRunner({
felangel marked this conversation as resolved.
Show resolved Hide resolved
required Directory runnerDirectory,
required String channel,
}) async {
await Isolate.run(() async {
final shorebirdYaml = File(
p.join(
runnerDirectory.path,
'Frameworks',
'App.framework',
'flutter_assets',
'shorebird.yaml',
),
);

if (!shorebirdYaml.existsSync()) {
throw Exception('Unable to find shorebird.yaml');
}

final yaml = YamlEditor(shorebirdYaml.readAsStringSync())
..update(['channel'], channel);

shorebirdYaml.writeAsStringSync(yaml.toString(), flush: true);
});
}

Future<void> setChannelOnAab({
felangel marked this conversation as resolved.
Show resolved Hide resolved
required File aabFile,
required String channel,
}) async {
// Getting the reference here since we cannot inside the isolate.
felangel marked this conversation as resolved.
Show resolved Hide resolved
final extractZip = artifactManager.extractZip;

await Isolate.run(() async {
final tempDir = Directory.systemTemp.createTempSync();
final basename = p.basenameWithoutExtension(aabFile.path);
final outputPath = p.join(tempDir.path, basename);

await extractZip(
zipFile: aabFile,
outputDirectory: Directory(outputPath),
);

final shorebirdYaml = File(
p.join(
outputPath,
'base',
'assets',
'flutter_assets',
'shorebird.yaml',
),
);

if (!shorebirdYaml.existsSync()) {
throw Exception('Unable to find shorebird.yaml');
}

final yaml = YamlEditor(shorebirdYaml.readAsStringSync())
..update(['channel'], channel);

shorebirdYaml.writeAsStringSync(yaml.toString(), flush: true);

// This is equivalent to `zip --no-dir-entries`
// Which does NOT create entries in the zip archive for directories.
// It's important to do this because bundletool expects the
// .aab not to contain any directories.
final encoder = ZipFileEncoder()..create(aabFile.path);
for (final file in Directory(outputPath).listSync(recursive: true)) {
if (file is File) {
await encoder.addFile(
file,
file.path.replaceFirst('$outputPath/', ''),
);
}
}
Comment on lines +415 to +422
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary, but a (maybe) more readable way of writing this:

Directory(outputPath).listSync(recursive: true))
  .whereType<File>()
  .forEach((file) async => 
    await encoder.addFile(
      file,
      file.path.replaceFirst('$outputPath/', ''),
    ),
  );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just didn't like the having to iterate through the list twice

encoder.close();
tempDir.deleteSync(recursive: true);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void main() {
const version = '$versionName+$versionCode';
const arch = 'aarch64';
const releasePlatform = ReleasePlatform.android;
const channelName = 'stable';
const channelName = 'test-channel';
const appDisplayName = 'Test App';
final appMetadata = AppMetadata(
appId: appId,
Expand Down Expand Up @@ -786,10 +786,10 @@ Please re-run the release command for this version or create a new release.'''),
verifyNever(() => logger.confirm(any()));
verify(
() => codePushClientWrapper.publishPatch(
appId: any(named: 'appId'),
releaseId: any(named: 'releaseId'),
platform: any(named: 'platform'),
channelName: any(named: 'channelName'),
appId: appId,
releaseId: release.id,
platform: releasePlatform,
channelName: channelName,
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).called(1);
Expand All @@ -813,10 +813,10 @@ Please re-run the release command for this version or create a new release.'''),
).called(1);
verify(
() => codePushClientWrapper.publishPatch(
appId: any(named: 'appId'),
releaseId: any(named: 'releaseId'),
platform: any(named: 'platform'),
channelName: any(named: 'channelName'),
appId: appId,
releaseId: release.id,
platform: releasePlatform,
channelName: channelName,
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).called(1);
Expand Down Expand Up @@ -863,10 +863,10 @@ flavors:
);
verify(
() => codePushClientWrapper.publishPatch(
appId: any(named: 'appId'),
releaseId: any(named: 'releaseId'),
platform: any(named: 'platform'),
channelName: any(named: 'channelName'),
appId: appId,
releaseId: release.id,
platform: releasePlatform,
channelName: channelName,
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).called(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ void main() {
const versionCode = '1';
const version = '$versionName+$versionCode';
const arch = 'aarch64';
const channelName = 'test-channel';
const appDisplayName = 'Test App';
const releasePlatform = ReleasePlatform.ios;
const platformName = 'ios';
const elfAotSnapshotFileName = 'out.aot';
const ipaPath = 'build/ios/ipa/Runner.ipa';
Expand Down Expand Up @@ -252,6 +254,7 @@ flutter:
when(() => argResults['force']).thenReturn(false);
when(() => argResults['release-version']).thenReturn(release.version);
when(() => argResults['codesign']).thenReturn(true);
when(() => argResults['channel']).thenReturn(channelName);
when(() => argResults.rest).thenReturn([]);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
Expand Down Expand Up @@ -845,10 +848,10 @@ Please re-run the release command for this version or create a new release.'''),
verifyNever(() => logger.confirm(any()));
verify(
() => codePushClientWrapper.publishPatch(
appId: any(named: 'appId'),
releaseId: any(named: 'releaseId'),
platform: any(named: 'platform'),
channelName: any(named: 'channelName'),
appId: appId,
releaseId: release.id,
platform: releasePlatform,
channelName: channelName,
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).called(1);
Expand All @@ -872,10 +875,10 @@ Please re-run the release command for this version or create a new release.'''),
).called(1);
verify(
() => codePushClientWrapper.publishPatch(
appId: any(named: 'appId'),
releaseId: any(named: 'releaseId'),
platform: any(named: 'platform'),
channelName: any(named: 'channelName'),
appId: appId,
releaseId: release.id,
platform: releasePlatform,
channelName: channelName,
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).called(1);
Expand Down Expand Up @@ -976,10 +979,10 @@ flavors:
expect(exitCode, ExitCode.success.code);
verify(
() => codePushClientWrapper.publishPatch(
appId: any(named: 'appId'),
releaseId: any(named: 'releaseId'),
platform: any(named: 'platform'),
channelName: any(named: 'channelName'),
appId: appId,
releaseId: release.id,
platform: releasePlatform,
channelName: channelName,
patchArtifactBundles: any(named: 'patchArtifactBundles'),
),
).called(1);
Expand Down
Loading