feat(shorebird_cli): refactor publish command flow (#159)
This commit is contained in:
committed by
Felix Angelov
parent
29e1793f86
commit
d96c75adbd
@@ -43,11 +43,4 @@ abstract class ShorebirdCommand extends Command<int> {
|
||||
|
||||
/// [ArgResults] for the current command.
|
||||
ArgResults get results => testArgResults ?? argResults!;
|
||||
|
||||
/// [CommandRunner] used for testing purposes only.
|
||||
@visibleForTesting
|
||||
CommandRunner<int>? testCommandRunner;
|
||||
|
||||
@override
|
||||
CommandRunner<int>? get runner => testCommandRunner ?? super.runner;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:shorebird_cli/src/command.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_config_mixin.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_engine_mixin.dart';
|
||||
|
||||
@@ -17,7 +18,7 @@ typedef RunProcess = Future<ProcessResult> Function(
|
||||
/// Build a new release of your application.
|
||||
/// {@endtemplate}
|
||||
class BuildCommand extends ShorebirdCommand
|
||||
with ShorebirdConfigMixin, ShorebirdEngineMixin {
|
||||
with ShorebirdConfigMixin, ShorebirdEngineMixin, ShorebirdBuildMixin {
|
||||
/// {@macro build_command}
|
||||
BuildCommand({
|
||||
required super.logger,
|
||||
@@ -50,7 +51,7 @@ class BuildCommand extends ShorebirdCommand
|
||||
|
||||
final buildProgress = logger.progress('Building release ');
|
||||
try {
|
||||
await _build(shorebirdEnginePath);
|
||||
await buildRelease();
|
||||
buildProgress.complete();
|
||||
} on ProcessException catch (error) {
|
||||
buildProgress.fail('Failed to build: ${error.message}');
|
||||
@@ -59,37 +60,4 @@ class BuildCommand extends ShorebirdCommand
|
||||
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
Future<void> _build(String shorebirdEnginePath) async {
|
||||
const executable = 'flutter';
|
||||
final arguments = [
|
||||
'build',
|
||||
// This is temporary because the Shorebird engine currently
|
||||
// only supports Android.
|
||||
'appbundle',
|
||||
'--release',
|
||||
'--local-engine-src-path',
|
||||
shorebirdEnginePath,
|
||||
'--local-engine',
|
||||
// This is temporary because the Shorebird engine currently
|
||||
// only supports Android arm64.
|
||||
'android_release_arm64',
|
||||
...results.rest,
|
||||
];
|
||||
|
||||
final result = await runProcess(
|
||||
executable,
|
||||
arguments,
|
||||
runInShell: true,
|
||||
);
|
||||
|
||||
if (result.exitCode != ExitCode.success.code) {
|
||||
throw ProcessException(
|
||||
'flutter',
|
||||
arguments,
|
||||
result.stderr.toString(),
|
||||
result.exitCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/command.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_config_mixin.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_engine_mixin.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
/// {@template publish_command}
|
||||
///
|
||||
/// `shorebird publish <path/to/artifact>`
|
||||
/// Publish new releases to the Shorebird CodePush server.
|
||||
/// {@endtemplate}
|
||||
class PublishCommand extends ShorebirdCommand with ShorebirdConfigMixin {
|
||||
class PublishCommand extends ShorebirdCommand
|
||||
with ShorebirdConfigMixin, ShorebirdEngineMixin, ShorebirdBuildMixin {
|
||||
/// {@macro publish_command}
|
||||
PublishCommand({
|
||||
required super.logger,
|
||||
super.auth,
|
||||
super.buildCodePushClient,
|
||||
super.runProcess,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -39,55 +45,123 @@ class PublishCommand extends ShorebirdCommand with ShorebirdConfigMixin {
|
||||
return ExitCode.noUser.code;
|
||||
}
|
||||
|
||||
final args = results.rest;
|
||||
if (args.length > 1) {
|
||||
usageException('A single file path must be specified.');
|
||||
}
|
||||
|
||||
final artifactPath = args.isEmpty
|
||||
? p.join(
|
||||
Directory.current.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
)
|
||||
: args.first;
|
||||
|
||||
final artifact = File(artifactPath);
|
||||
if (!artifact.existsSync()) {
|
||||
logger.err('Artifact not found: "${artifact.path}"');
|
||||
return ExitCode.noInput.code;
|
||||
}
|
||||
|
||||
try {
|
||||
final pubspecYaml = getPubspecYaml()!;
|
||||
final shorebirdYaml = getShorebirdYaml()!;
|
||||
final codePushClient = buildCodePushClient(
|
||||
apiKey: session.apiKey,
|
||||
hostedUri: hostedUri,
|
||||
);
|
||||
logger.detail(
|
||||
'''Deploying ${artifact.path} to ${shorebirdYaml.appId} (${pubspecYaml.version})''',
|
||||
);
|
||||
final version = pubspecYaml.version!;
|
||||
await codePushClient.createPatch(
|
||||
artifactPath: artifact.path,
|
||||
releaseVersion: '${version.major}.${version.minor}.${version.patch}',
|
||||
appId: shorebirdYaml.appId,
|
||||
channel: 'stable',
|
||||
);
|
||||
await ensureEngineExists();
|
||||
} catch (error) {
|
||||
logger.err('$error');
|
||||
logger.err(error.toString());
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
logger.success('Successfully deployed.');
|
||||
final buildProgress = logger.progress('Building release');
|
||||
try {
|
||||
await buildRelease();
|
||||
buildProgress.complete();
|
||||
} on ProcessException catch (error) {
|
||||
buildProgress.fail('Failed to build: ${error.message}');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
final artifactPath = p.join(
|
||||
Directory.current.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
|
||||
final artifact = File(artifactPath);
|
||||
|
||||
if (!artifact.existsSync()) {
|
||||
logger.err('Artifact not found: "${artifact.path}"');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
final pubspecYaml = getPubspecYaml()!;
|
||||
final shorebirdYaml = getShorebirdYaml()!;
|
||||
final codePushClient = buildCodePushClient(
|
||||
apiKey: session.apiKey,
|
||||
hostedUri: hostedUri,
|
||||
);
|
||||
final version = pubspecYaml.version!;
|
||||
final versionString = '${version.major}.${version.minor}.${version.patch}';
|
||||
|
||||
logger.info(
|
||||
'''
|
||||
Ready to publish the following patch:
|
||||
App: ${pubspecYaml.name} (${shorebirdYaml.appId})
|
||||
Release Version: $versionString
|
||||
Patch Number: [NEW]
|
||||
''',
|
||||
);
|
||||
|
||||
final confirm = logger.confirm('Are you sure you want to continue?');
|
||||
|
||||
if (!confirm) {
|
||||
logger.info('Aborting.');
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
late final List<Release> releases;
|
||||
final fetchReleasesProgress = logger.progress('Fetching releases');
|
||||
try {
|
||||
releases = await codePushClient.getReleases(
|
||||
appId: shorebirdYaml.appId,
|
||||
);
|
||||
fetchReleasesProgress.complete();
|
||||
} catch (error) {
|
||||
fetchReleasesProgress.fail('$error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
var release = releases.firstWhereOrNull(
|
||||
(r) => r.version == versionString,
|
||||
);
|
||||
|
||||
if (release == null) {
|
||||
final createReleaseProgress = logger.progress('Creating release');
|
||||
try {
|
||||
release = await codePushClient.createRelease(
|
||||
appId: shorebirdYaml.appId,
|
||||
version: versionString,
|
||||
);
|
||||
createReleaseProgress.complete();
|
||||
} catch (error) {
|
||||
createReleaseProgress.fail('$error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
}
|
||||
|
||||
late final Patch patch;
|
||||
final createPatchProgress = logger.progress('Creating patch');
|
||||
try {
|
||||
patch = await codePushClient.createPatch(releaseId: release.id);
|
||||
createPatchProgress.complete();
|
||||
} catch (error) {
|
||||
createPatchProgress.fail('$error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
final createArtifactProgress = logger.progress('Creating artifact');
|
||||
try {
|
||||
await codePushClient.createArtifact(
|
||||
patchId: patch.id,
|
||||
artifactPath: artifact.path,
|
||||
arch: 'aarch64',
|
||||
platform: 'android',
|
||||
hash: '#',
|
||||
);
|
||||
createArtifactProgress.complete();
|
||||
} catch (error) {
|
||||
createArtifactProgress.fail('$error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
logger.success('Published!');
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_engine_mixin.dart';
|
||||
|
||||
mixin ShorebirdBuildMixin on ShorebirdEngineMixin {
|
||||
Future<void> buildRelease() async {
|
||||
const executable = 'flutter';
|
||||
final arguments = [
|
||||
'build',
|
||||
// This is temporary because the Shorebird engine currently
|
||||
// only supports Android.
|
||||
'appbundle',
|
||||
'--release',
|
||||
'--local-engine-src-path',
|
||||
shorebirdEnginePath,
|
||||
'--local-engine',
|
||||
// This is temporary because the Shorebird engine currently
|
||||
// only supports Android arm64.
|
||||
'android_release_arm64',
|
||||
...results.rest,
|
||||
];
|
||||
|
||||
final result = await runProcess(
|
||||
executable,
|
||||
arguments,
|
||||
runInShell: true,
|
||||
);
|
||||
|
||||
if (result.exitCode != ExitCode.success.code) {
|
||||
throw ProcessException(
|
||||
'flutter',
|
||||
arguments,
|
||||
result.stderr.toString(),
|
||||
result.exitCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ mixin ShorebirdEngineMixin on ShorebirdConfigMixin {
|
||||
String path,
|
||||
) async {
|
||||
final engine = await codePushClient.downloadEngine(
|
||||
requiredFlutterEngineRevision,
|
||||
revision: requiredFlutterEngineRevision,
|
||||
);
|
||||
final targetFile = File(path);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ void main() {
|
||||
|
||||
when(() => argResults.rest).thenReturn([]);
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenAnswer((_) async => Uint8List.fromList([]));
|
||||
when(() => logger.progress(any())).thenReturn(_MockProgress());
|
||||
});
|
||||
@@ -71,7 +71,7 @@ void main() {
|
||||
|
||||
test('exits with code 70 when pulling engine fails', () async {
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenThrow(Exception('oops'));
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
|
||||
@@ -103,7 +103,7 @@ void main() {
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenAnswer((_) async => Uint8List.fromList([]));
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -19,18 +19,30 @@ class _MockLogger extends Mock implements Logger {}
|
||||
|
||||
class _MockProgress extends Mock implements Progress {}
|
||||
|
||||
class _MockCodePushClient extends Mock implements CodePushClient {}
|
||||
class _MockProcessResult extends Mock implements ProcessResult {}
|
||||
|
||||
class _FakeCommandRunner extends Fake implements CommandRunner<int> {
|
||||
@override
|
||||
String get executableName => 'shorebird_test';
|
||||
}
|
||||
class _MockCodePushClient extends Mock implements CodePushClient {}
|
||||
|
||||
void main() {
|
||||
group('publish', () {
|
||||
const session = Session(apiKey: 'test-api-key');
|
||||
const appId = 'test-app-id';
|
||||
const version = '1.2.3';
|
||||
const artifact = Artifact(
|
||||
id: 0,
|
||||
patchId: 0,
|
||||
arch: 'aarch64',
|
||||
platform: 'android',
|
||||
hash: '#',
|
||||
url: 'https://example.com',
|
||||
);
|
||||
const release = Release(
|
||||
id: 0,
|
||||
appId: appId,
|
||||
version: version,
|
||||
displayName: '1.2.3',
|
||||
);
|
||||
const patch = Patch(id: 0, number: 1);
|
||||
const pubspecYamlContent = '''
|
||||
name: example
|
||||
version: $version
|
||||
@@ -43,7 +55,9 @@ flutter:
|
||||
|
||||
late ArgResults argResults;
|
||||
late Auth auth;
|
||||
late Progress progress;
|
||||
late Logger logger;
|
||||
late ProcessResult processResult;
|
||||
late CodePushClient codePushClient;
|
||||
late PublishCommand command;
|
||||
late Uri? capturedHostedUri;
|
||||
@@ -62,7 +76,9 @@ flutter:
|
||||
setUp(() {
|
||||
argResults = _MockArgResults();
|
||||
auth = _MockAuth();
|
||||
progress = _MockProgress();
|
||||
logger = _MockLogger();
|
||||
processResult = _MockProcessResult();
|
||||
codePushClient = _MockCodePushClient();
|
||||
command = PublishCommand(
|
||||
auth: auth,
|
||||
@@ -70,22 +86,41 @@ flutter:
|
||||
capturedHostedUri = hostedUri;
|
||||
return codePushClient;
|
||||
},
|
||||
runProcess: (executable, arguments, {bool runInShell = false}) async {
|
||||
return processResult;
|
||||
},
|
||||
logger: logger,
|
||||
)
|
||||
..testArgResults = argResults
|
||||
..testCommandRunner = _FakeCommandRunner();
|
||||
)..testArgResults = argResults;
|
||||
|
||||
when(() => argResults.rest).thenReturn([]);
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
when(() => logger.progress(any())).thenReturn(_MockProgress());
|
||||
when(() => logger.progress(any())).thenReturn(progress);
|
||||
when(() => logger.confirm(any())).thenReturn(true);
|
||||
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
|
||||
when(
|
||||
() => codePushClient.createPatch(
|
||||
releaseVersion: any(named: 'releaseVersion'),
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
channel: any(named: 'channel'),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenAnswer((_) async => Uint8List.fromList([]));
|
||||
when(
|
||||
() => codePushClient.getReleases(appId: any(named: 'appId')),
|
||||
).thenAnswer((_) async => [release]);
|
||||
when(
|
||||
() => codePushClient.createRelease(
|
||||
appId: any(named: 'appId'),
|
||||
version: any(named: 'version'),
|
||||
),
|
||||
).thenAnswer((_) async {});
|
||||
).thenAnswer((_) async => release);
|
||||
when(
|
||||
() => codePushClient.createPatch(releaseId: any(named: 'releaseId')),
|
||||
).thenAnswer((_) async => patch);
|
||||
when(
|
||||
() => codePushClient.createArtifact(
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
patchId: any(named: 'patchId'),
|
||||
arch: any(named: 'arch'),
|
||||
platform: any(named: 'platform'),
|
||||
hash: any(named: 'hash'),
|
||||
),
|
||||
).thenAnswer((_) async => artifact);
|
||||
});
|
||||
|
||||
test('throws config error when shorebird is not initialized', () async {
|
||||
@@ -112,21 +147,48 @@ flutter:
|
||||
expect(exitCode, equals(ExitCode.noUser.code));
|
||||
});
|
||||
|
||||
test('throws usage error when multiple args are passed.', () async {
|
||||
when(() => argResults.rest).thenReturn(['arg1', 'arg2']);
|
||||
test('exits with code 70 when pulling engine fails', () async {
|
||||
when(
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenThrow(Exception('oops'));
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
final tempDir = setUpTempDir();
|
||||
await expectLater(
|
||||
IOOverrides.runZoned(
|
||||
() => command.run(),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
),
|
||||
throwsA(isA<UsageException>()),
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(exitCode, equals(ExitCode.software.code));
|
||||
});
|
||||
|
||||
test('throws no input error when artifact is not found (default).',
|
||||
test('exits with code 70 when building fails', () async {
|
||||
when(() => processResult.exitCode).thenReturn(1);
|
||||
when(() => processResult.stderr).thenReturn('oops');
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
() async => command.run(),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
|
||||
expect(exitCode, equals(ExitCode.software.code));
|
||||
});
|
||||
|
||||
test('throws software error when artifact is not found (default).',
|
||||
() async {
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
@@ -134,66 +196,216 @@ flutter:
|
||||
verify(
|
||||
() => logger.err(any(that: contains('Artifact not found:'))),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.noInput.code);
|
||||
});
|
||||
|
||||
test('throws no input error when artifact is not found (custom).',
|
||||
() async {
|
||||
final tempDir = setUpTempDir();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'));
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(
|
||||
() => logger.err(
|
||||
any(
|
||||
that: contains('Artifact not found: "${artifact.path}"'),
|
||||
),
|
||||
),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.noInput.code);
|
||||
});
|
||||
|
||||
test('throws error when publish fails.', () async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.createPatch(
|
||||
releaseVersion: any(named: 'releaseVersion'),
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
channel: any(named: 'channel'),
|
||||
appId: any(named: 'appId'),
|
||||
),
|
||||
).thenThrow(error);
|
||||
final tempDir = setUpTempDir();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => logger.err(error)).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('succeeds when publish is successful using existing app id', () async {
|
||||
test('aborts when user opts out', () async {
|
||||
when(() => logger.confirm(any())).thenReturn(false);
|
||||
final tempDir = setUpTempDir();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => logger.success('Successfully deployed.')).called(1);
|
||||
verify(
|
||||
() => codePushClient.createPatch(
|
||||
releaseVersion: version,
|
||||
appId: appId,
|
||||
artifactPath: artifact.path,
|
||||
channel: 'stable',
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
verify(() => logger.info('Aborting.')).called(1);
|
||||
});
|
||||
|
||||
test('throws error when fetching releases fails.', () async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.getReleases(appId: any(named: 'appId')),
|
||||
).thenThrow(error);
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => progress.fail(error)).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('throws error when creating release fails.', () async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.getReleases(appId: any(named: 'appId')),
|
||||
).thenAnswer((_) async => []);
|
||||
when(
|
||||
() => codePushClient.createRelease(
|
||||
appId: any(named: 'appId'),
|
||||
version: any(named: 'version'),
|
||||
displayName: any(named: 'displayName'),
|
||||
),
|
||||
).called(1);
|
||||
).thenThrow(error);
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => progress.fail(error)).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('throws error when creating patch fails.', () async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.getReleases(appId: any(named: 'appId')),
|
||||
).thenAnswer((_) async => []);
|
||||
when(
|
||||
() => codePushClient.createPatch(releaseId: any(named: 'releaseId')),
|
||||
).thenThrow(error);
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => progress.fail(error)).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('throws error when uploading artifact fails.', () async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.getReleases(appId: any(named: 'appId')),
|
||||
).thenAnswer((_) async => []);
|
||||
when(
|
||||
() => codePushClient.createArtifact(
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
patchId: any(named: 'patchId'),
|
||||
arch: any(named: 'arch'),
|
||||
platform: any(named: 'platform'),
|
||||
hash: any(named: 'hash'),
|
||||
),
|
||||
).thenThrow(error);
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => progress.fail(error)).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('succeeds when publish is successful', () async {
|
||||
final tempDir = setUpTempDir();
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => logger.success('Published!')).called(1);
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
expect(capturedHostedUri, isNull);
|
||||
});
|
||||
@@ -208,8 +420,25 @@ flutter:
|
||||
app_id: $appId
|
||||
base_url: $baseUrl''',
|
||||
);
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/engine',
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
'${tempDir.path}/.shorebird/cache',
|
||||
).createSync(recursive: true);
|
||||
final artifactPath = p.join(
|
||||
tempDir.path,
|
||||
'build',
|
||||
'app',
|
||||
'intermediates',
|
||||
'stripped_native_libs',
|
||||
'release',
|
||||
'out',
|
||||
'lib',
|
||||
'arm64-v8a',
|
||||
'libapp.so',
|
||||
);
|
||||
File(artifactPath).createSync(recursive: true);
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
|
||||
@@ -74,7 +74,7 @@ void main() {
|
||||
final error = Exception('oops');
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenThrow(error);
|
||||
final progress = _MockProgress();
|
||||
when(() => logger.progress(any())).thenReturn(progress);
|
||||
@@ -98,7 +98,7 @@ void main() {
|
||||
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenAnswer((_) async => Uint8List(0));
|
||||
final progress = _MockProgress();
|
||||
when(() => logger.progress(any())).thenReturn(progress);
|
||||
@@ -129,7 +129,7 @@ void main() {
|
||||
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenAnswer((_) async => Uint8List(0));
|
||||
|
||||
final progress = _MockProgress();
|
||||
@@ -163,7 +163,7 @@ void main() {
|
||||
.createSync(recursive: true);
|
||||
when(() => auth.currentSession).thenReturn(session);
|
||||
when(
|
||||
() => codePushClient.downloadEngine(any()),
|
||||
() => codePushClient.downloadEngine(revision: any(named: 'revision')),
|
||||
).thenAnswer((_) async => Uint8List(0));
|
||||
|
||||
final progress = _MockProgress();
|
||||
|
||||
Reference in New Issue
Block a user