feat(shorebird_cli): publish using shorebird_code_push_api_client (#6)
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import 'package:args/args.dart';
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:cli_completion/cli_completion.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:pub_updater/pub_updater.dart';
|
||||
import 'package:shorebird_cli/src/commands/commands.dart';
|
||||
import 'package:shorebird_cli/src/version.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
|
||||
const executableName = 'shorebird';
|
||||
const packageName = 'shorebird_cli';
|
||||
@@ -22,7 +22,7 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
|
||||
/// {@macro shorebird_cli_command_runner}
|
||||
ShorebirdCliCommandRunner({
|
||||
Logger? logger,
|
||||
http.Client? httpClient,
|
||||
ShorebirdCodePushApiClient? codePushApiClient,
|
||||
PubUpdater? pubUpdater,
|
||||
}) : _logger = logger ?? Logger(),
|
||||
_pubUpdater = pubUpdater ?? PubUpdater(),
|
||||
@@ -41,7 +41,9 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
|
||||
);
|
||||
|
||||
// Add sub commands
|
||||
addCommand(PublishCommand(logger: _logger, httpClient: httpClient));
|
||||
addCommand(
|
||||
PublishCommand(logger: _logger, codePushApiClient: codePushApiClient),
|
||||
);
|
||||
addCommand(UpdateCommand(logger: _logger, pubUpdater: _pubUpdater));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
|
||||
/// {@template sample_command}
|
||||
/// {@template publish_command}
|
||||
///
|
||||
/// `shorebird sample`
|
||||
/// A [Command] to exemplify a sub command
|
||||
/// `shorebird publish <path/to/artifact>`
|
||||
/// Publish new releases to the Shorebird CodePush server.
|
||||
/// {@endtemplate}
|
||||
class PublishCommand extends Command<int> {
|
||||
/// {@macro sample_command}
|
||||
PublishCommand({required Logger logger, http.Client? httpClient})
|
||||
: _logger = logger,
|
||||
_httpClient = httpClient ?? http.Client();
|
||||
/// {@macro publish_command}
|
||||
PublishCommand({
|
||||
required Logger logger,
|
||||
ShorebirdCodePushApiClient? codePushApiClient,
|
||||
}) : _logger = logger,
|
||||
_codePushApiClient = codePushApiClient ?? ShorebirdCodePushApiClient();
|
||||
|
||||
@override
|
||||
String get description => 'Publish an update.';
|
||||
@@ -22,7 +24,7 @@ class PublishCommand extends Command<int> {
|
||||
String get name => 'publish';
|
||||
|
||||
final Logger _logger;
|
||||
final http.Client _httpClient;
|
||||
final ShorebirdCodePushApiClient _codePushApiClient;
|
||||
|
||||
@override
|
||||
Future<int> run() async {
|
||||
@@ -37,23 +39,14 @@ class PublishCommand extends Command<int> {
|
||||
return ExitCode.noInput.code;
|
||||
}
|
||||
|
||||
final request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse('http://localhost:8080/api/v1/releases'),
|
||||
);
|
||||
final file = await http.MultipartFile.fromPath('file', artifact.path);
|
||||
request.files.add(file);
|
||||
final response = await _httpClient.send(request);
|
||||
|
||||
if (response.statusCode != HttpStatus.created) {
|
||||
_logger.err(
|
||||
'Failed to deploy: ${response.statusCode} ${response.reasonPhrase}',
|
||||
);
|
||||
try {
|
||||
await _codePushApiClient.createRelease(artifact.path);
|
||||
} catch (error) {
|
||||
_logger.err('Failed to deploy: $error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
_logger.success('Deployed ${artifact.path}!');
|
||||
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,19 @@ name: shorebird_cli
|
||||
description: The shorebird command-line tool
|
||||
version: 0.0.1
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
args: ^2.3.1
|
||||
cli_completion: ^0.2.0
|
||||
http: ^0.13.5
|
||||
mason_logger: ^0.2.4
|
||||
path: ^1.8.3
|
||||
pub_updater: ^0.2.4
|
||||
shorebird_code_push_api_client:
|
||||
path: ../shorebird_code_push_api_client
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.0.0
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/command_runner.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockLogger extends Mock implements Logger {}
|
||||
|
||||
class _MockHttpClient extends Mock implements http.Client {}
|
||||
|
||||
class _FakeBaseRequest extends Fake implements http.BaseRequest {}
|
||||
class _MockShorebirdCodePushApiClient extends Mock
|
||||
implements ShorebirdCodePushApiClient {}
|
||||
|
||||
void main() {
|
||||
group('publish', () {
|
||||
late Logger logger;
|
||||
late http.Client httpClient;
|
||||
late _MockShorebirdCodePushApiClient codePushApiClient;
|
||||
late ShorebirdCliCommandRunner commandRunner;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeBaseRequest());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
logger = _MockLogger();
|
||||
httpClient = _MockHttpClient();
|
||||
codePushApiClient = _MockShorebirdCodePushApiClient();
|
||||
commandRunner = ShorebirdCliCommandRunner(
|
||||
logger: logger,
|
||||
httpClient: httpClient,
|
||||
codePushApiClient: codePushApiClient,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -58,30 +51,18 @@ void main() {
|
||||
});
|
||||
|
||||
test('throws error when release fails.', () async {
|
||||
const statusCode = HttpStatus.internalServerError;
|
||||
const reasonPhrase = 'something went wrong';
|
||||
when(() => httpClient.send(any())).thenAnswer(
|
||||
(_) async => http.StreamedResponse(
|
||||
const Stream.empty(),
|
||||
statusCode,
|
||||
reasonPhrase: reasonPhrase,
|
||||
),
|
||||
);
|
||||
const error = 'something went wrong';
|
||||
when(() => codePushApiClient.createRelease(any())).thenThrow(error);
|
||||
final release = p.join('test', 'fixtures', 'release.txt');
|
||||
final exitCode = await commandRunner.run(['publish', release]);
|
||||
verify(
|
||||
() => logger.err('Failed to deploy: $statusCode $reasonPhrase'),
|
||||
).called(1);
|
||||
verify(() => logger.err('Failed to deploy: $error')).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('succeeds when release is successful.', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer(
|
||||
(_) async => http.StreamedResponse(
|
||||
const Stream.empty(),
|
||||
HttpStatus.created,
|
||||
),
|
||||
);
|
||||
when(
|
||||
() => codePushApiClient.createRelease(any()),
|
||||
).thenAnswer((_) async {});
|
||||
final release = p.join('test', 'fixtures', 'release.txt');
|
||||
final exitCode = await commandRunner.run(['publish', release]);
|
||||
verify(() => logger.success('Deployed $release!')).called(1);
|
||||
|
||||
Reference in New Issue
Block a user