feat(shorebird_cli): shorebird init (#72)
This commit is contained in:
@@ -14,9 +14,37 @@ dart pub global activate --source git https://github.com/shorebirdtech/shorebird
|
||||
|
||||
## Commands
|
||||
|
||||
### Init
|
||||
|
||||
Get started by initializing shorebird in your current project.
|
||||
|
||||
```bash
|
||||
shorebird init
|
||||
```
|
||||
|
||||
**Sample**
|
||||
|
||||
```
|
||||
shorebird init
|
||||
✓ Initialized Shorebird (27ms)
|
||||
|
||||
🐦 Shorebird initialized successfully!
|
||||
|
||||
✅ A "shorebird.yaml" has been created.
|
||||
✅ The "pubspec.yaml" has been updated to include "shorebird.yaml" as an asset.
|
||||
|
||||
Reference the following commands to get started:
|
||||
|
||||
🚙 To run your project use: "shorebird run".
|
||||
📦 To build your project use: "shorebird build".
|
||||
🚀 To publish a new update use: "shorebird publish".
|
||||
|
||||
For more information, visit https://shorebird.dev
|
||||
```
|
||||
|
||||
### Login
|
||||
|
||||
Get started by requesting an API key and using `shorebird login` to authenticate:
|
||||
Request an API key and use `shorebird login` to authenticate:
|
||||
|
||||
```bash
|
||||
shorebird login
|
||||
@@ -24,7 +52,7 @@ shorebird login
|
||||
|
||||
**Sample**
|
||||
|
||||
```bash
|
||||
```
|
||||
shorebird login
|
||||
? Please enter your API Key: <API-KEY>
|
||||
✓ Logging into shorebird.dev (7ms)
|
||||
@@ -41,11 +69,27 @@ shorebird logout
|
||||
|
||||
**Sample**
|
||||
|
||||
```bash
|
||||
```
|
||||
shorebird logout
|
||||
✓ Logging out of shorebird.dev (1ms)
|
||||
```
|
||||
|
||||
### List Apps
|
||||
|
||||
List all existing apps in Shorebird using the `shorebird apps list` command:
|
||||
|
||||
```bash
|
||||
shorebird apps list
|
||||
```
|
||||
|
||||
**Sample**
|
||||
|
||||
```
|
||||
shorebird apps list
|
||||
my-counter: v1.0.0 (patch #1)
|
||||
my-example: v2.1.0 (patch #2)
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
Run an existing application using the Shorebird Engine via the `shorebird run` command:
|
||||
@@ -54,7 +98,7 @@ Run an existing application using the Shorebird Engine via the `shorebird run` c
|
||||
shorebird run
|
||||
```
|
||||
|
||||
**❗️Note**: If it's the first time, `shorebird run` will download and build the shorebird engine which may take some time. The shorebird engine will be cached for subsequent runs.
|
||||
**❗️Note**: If it's the first time using shorebird, `shorebird run` will download and build the shorebird engine which may take some time. The shorebird engine will be cached for subsequent runs.
|
||||
|
||||
### Build
|
||||
|
||||
@@ -64,6 +108,8 @@ Build a new release of your application using the `shorebird build` command:
|
||||
shorebird build
|
||||
```
|
||||
|
||||
**❗️Note**: If it's the first time using shorebird, `shorebird build` will download and build the shorebird engine which may take some time. The shorebird engine will be cached for subsequent runs.
|
||||
|
||||
### Publish
|
||||
|
||||
The publish command allows developers to publish new releases of their Flutter application to the Shorebird CodePush API. These updates are then pushed directly to users' devices.
|
||||
@@ -89,7 +135,9 @@ Global options:
|
||||
--[no-]verbose Noisy logging, including all shell commands executed.
|
||||
|
||||
Available commands:
|
||||
apps Manage your Shorebird apps.
|
||||
build Build a new release of your application.
|
||||
init Initialize Shorebird.
|
||||
login Login as a new Shorebird user.
|
||||
logout Logout of the current Shorebird user
|
||||
publish Publish an update.
|
||||
|
||||
@@ -37,6 +37,7 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
|
||||
|
||||
addCommand(AppsCommand(logger: _logger));
|
||||
addCommand(BuildCommand(logger: _logger));
|
||||
addCommand(InitCommand(logger: _logger));
|
||||
addCommand(LoginCommand(logger: _logger));
|
||||
addCommand(LogoutCommand(logger: _logger));
|
||||
addCommand(PublishCommand(logger: _logger));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export 'apps/apps.dart';
|
||||
export 'build_command.dart';
|
||||
export 'init_command.dart';
|
||||
export 'login_command.dart';
|
||||
export 'logout_command.dart';
|
||||
export 'publish_command.dart';
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import 'dart:io';
|
||||
|
||||
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/config/config.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_config_mixin.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import 'package:yaml_edit/yaml_edit.dart';
|
||||
|
||||
/// {@template init_command}
|
||||
///
|
||||
/// `shorebird init`
|
||||
/// Initialize Shorebird.
|
||||
/// {@endtemplate}
|
||||
class InitCommand extends ShorebirdCommand with ShorebirdConfigMixin {
|
||||
/// {@macro init_command}
|
||||
InitCommand({required super.logger, super.buildUuid});
|
||||
|
||||
@override
|
||||
String get description => 'Initialize Shorebird.';
|
||||
|
||||
@override
|
||||
String get name => 'init';
|
||||
|
||||
@override
|
||||
Future<int> run() async {
|
||||
final progress = logger.progress('Initializing Shorebird');
|
||||
|
||||
try {
|
||||
if (!hasPubspecYaml) {
|
||||
logger.err('Could not find a "pubspec.yaml".');
|
||||
return ExitCode.noInput.code;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.err('Error parsing "pubspec.yaml": $error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
progress.update('Creating "shorebird.yaml"');
|
||||
|
||||
try {
|
||||
if (hasShorebirdYaml) {
|
||||
progress.update('"shorebird.yaml" already exists.');
|
||||
} else {
|
||||
_addShorebirdYamlToProject();
|
||||
progress.update('Generated a "shorebird.yaml".');
|
||||
}
|
||||
} catch (error) {
|
||||
progress.fail();
|
||||
logger.err('Error creating "shorebird.yaml".\n$error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
progress.update('Adding "shorebird.yaml" to "pubspec.yaml" assets');
|
||||
|
||||
if (pubspecContainsShorebirdYaml) {
|
||||
progress.update('"shorebird.yaml" already in "pubspec.yaml" assets.');
|
||||
} else {
|
||||
_addShorebirdYamlToPubspecAssets();
|
||||
}
|
||||
|
||||
progress.complete('Initialized Shorebird');
|
||||
|
||||
logger.info(
|
||||
'''
|
||||
|
||||
${lightGreen.wrap('🐦 Shorebird initialized successfully!')}
|
||||
|
||||
✅ A "shorebird.yaml" has been created.
|
||||
✅ The "pubspec.yaml" has been updated to include "shorebird.yaml" as an asset.
|
||||
|
||||
Reference the following commands to get started:
|
||||
|
||||
🚙 To run your project use: "${lightCyan.wrap('shorebird run')}".
|
||||
📦 To build your project use: "${lightCyan.wrap('shorebird build')}".
|
||||
🚀 To publish a new update use: "${lightCyan.wrap('shorebird publish')}".
|
||||
|
||||
For more information about Shorebird, visit ${link(uri: Uri.parse('https://shorebird.dev'))}''',
|
||||
);
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
ShorebirdYaml _addShorebirdYamlToProject() {
|
||||
final productId = buildUuid();
|
||||
File(
|
||||
p.join(Directory.current.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('''
|
||||
# This file is used to configure the Shorebird CLI.
|
||||
# Learn more at https://shorebird.dev
|
||||
|
||||
# This is the unique identifier for your app.
|
||||
product_id: $productId
|
||||
''');
|
||||
|
||||
return ShorebirdYaml(productId: productId);
|
||||
}
|
||||
|
||||
void _addShorebirdYamlToPubspecAssets() {
|
||||
final pubspecFile = File(p.join(Directory.current.path, 'pubspec.yaml'));
|
||||
final pubspecContents = pubspecFile.readAsStringSync();
|
||||
final yaml = loadYaml(pubspecContents, sourceUrl: pubspecFile.uri) as Map;
|
||||
final editor = YamlEditor(pubspecContents);
|
||||
|
||||
if (!yaml.containsKey('flutter')) {
|
||||
editor.update(
|
||||
['flutter'],
|
||||
{
|
||||
'assets': ['shorebird.yaml']
|
||||
},
|
||||
);
|
||||
} else {
|
||||
if (!(yaml['flutter'] as Map).containsKey('assets')) {
|
||||
editor.update(['flutter', 'assets'], ['shorebird.yaml']);
|
||||
} else {
|
||||
final assets = (yaml['flutter'] as Map)['assets'] as List;
|
||||
if (!assets.contains('shorebird.yaml')) {
|
||||
editor.update(['flutter', 'assets'], [...assets, 'shorebird.yaml']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editor.edits.isEmpty) return;
|
||||
|
||||
pubspecFile.writeAsStringSync(editor.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,16 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:checked_yaml/checked_yaml.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:pubspec_parse/pubspec_parse.dart';
|
||||
import 'package:shorebird_cli/src/command.dart';
|
||||
import 'package:shorebird_cli/src/config/shorebird_yaml.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import 'package:yaml_edit/yaml_edit.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_config_mixin.dart';
|
||||
|
||||
/// {@template publish_command}
|
||||
///
|
||||
/// `shorebird publish <path/to/artifact>`
|
||||
/// Publish new releases to the Shorebird CodePush server.
|
||||
/// {@endtemplate}
|
||||
class PublishCommand extends ShorebirdCommand {
|
||||
class PublishCommand extends ShorebirdCommand with ShorebirdConfigMixin {
|
||||
/// {@macro publish_command}
|
||||
PublishCommand({
|
||||
required super.logger,
|
||||
@@ -31,6 +27,13 @@ class PublishCommand extends ShorebirdCommand {
|
||||
|
||||
@override
|
||||
Future<int> run() async {
|
||||
if (!isShorebirdInitialized) {
|
||||
logger.err(
|
||||
'Shorebird is not initialized. Did you run "shorebird init"?',
|
||||
);
|
||||
return ExitCode.config.code;
|
||||
}
|
||||
|
||||
final session = auth.currentSession;
|
||||
if (session == null) {
|
||||
logger.err('You must be logged in to publish.');
|
||||
@@ -42,45 +45,6 @@ class PublishCommand extends ShorebirdCommand {
|
||||
usageException('A single file path must be specified.');
|
||||
}
|
||||
|
||||
late final Pubspec? pubspecYaml;
|
||||
try {
|
||||
pubspecYaml = _readPubspecYaml();
|
||||
if (pubspecYaml == null) {
|
||||
logger.err('Could not find a "pubspec.yaml".');
|
||||
return ExitCode.noInput.code;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.err('Error parsing "pubspec.yaml": $error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
late final String productId;
|
||||
late final ShorebirdYaml? shorebirdYaml;
|
||||
try {
|
||||
shorebirdYaml = _readShorebirdYaml();
|
||||
} catch (error) {
|
||||
logger.err('Error parsing "shorebird.yaml": $error');
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
if (shorebirdYaml == null) {
|
||||
productId = buildUuid();
|
||||
File(
|
||||
p.join(Directory.current.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('''
|
||||
# This file is used to configure the Shorebird CLI.
|
||||
# Learn more at https://shorebird.dev
|
||||
|
||||
# This is the unique identifier for your app.
|
||||
product_id: $productId
|
||||
''');
|
||||
logger.info('Generated a "shorebird.yaml".');
|
||||
} else {
|
||||
productId = shorebirdYaml.productId;
|
||||
}
|
||||
|
||||
_addShorebirdYamlToAssets();
|
||||
|
||||
final artifactPath = args.isEmpty
|
||||
? p.join(
|
||||
Directory.current.path,
|
||||
@@ -103,15 +67,17 @@ product_id: $productId
|
||||
}
|
||||
|
||||
try {
|
||||
final pubspecYaml = getPubspecYaml()!;
|
||||
final shorebirdYaml = getShorebirdYaml()!;
|
||||
final codePushClient = buildCodePushClient(apiKey: session.apiKey);
|
||||
logger.detail(
|
||||
'Deploying ${artifact.path} to $productId (${pubspecYaml.version})',
|
||||
'''Deploying ${artifact.path} to ${shorebirdYaml.productId} (${pubspecYaml.version})''',
|
||||
);
|
||||
final version = pubspecYaml.version!;
|
||||
await codePushClient.createPatch(
|
||||
artifactPath: artifact.path,
|
||||
baseVersion: '${version.major}.${version.minor}.${version.patch}',
|
||||
productId: productId,
|
||||
productId: shorebirdYaml.productId,
|
||||
channel: 'stable',
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -122,48 +88,4 @@ product_id: $productId
|
||||
logger.success('Successfully deployed.');
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
ShorebirdYaml? _readShorebirdYaml() {
|
||||
final file = File(p.join(Directory.current.path, 'shorebird.yaml'));
|
||||
if (!file.existsSync()) return null;
|
||||
final yaml = file.readAsStringSync();
|
||||
return checkedYamlDecode(yaml, (m) => ShorebirdYaml.fromJson(m!));
|
||||
}
|
||||
|
||||
Pubspec? _readPubspecYaml() {
|
||||
final file = File(p.join(Directory.current.path, 'pubspec.yaml'));
|
||||
if (!file.existsSync()) return null;
|
||||
final yaml = file.readAsStringSync();
|
||||
return Pubspec.parse(yaml);
|
||||
}
|
||||
|
||||
void _addShorebirdYamlToAssets() {
|
||||
final pubspecFile = File(p.join(Directory.current.path, 'pubspec.yaml'));
|
||||
final pubspecContents = pubspecFile.readAsStringSync();
|
||||
final yaml = loadYaml(pubspecContents, sourceUrl: pubspecFile.uri) as Map;
|
||||
final editor = YamlEditor(pubspecContents);
|
||||
|
||||
if (!yaml.containsKey('flutter')) {
|
||||
editor.update(
|
||||
['flutter'],
|
||||
{
|
||||
'assets': ['shorebird.yaml']
|
||||
},
|
||||
);
|
||||
} else {
|
||||
if (!(yaml['flutter'] as Map).containsKey('assets')) {
|
||||
editor.update(['flutter', 'assets'], ['shorebird.yaml']);
|
||||
} else {
|
||||
final assets = (yaml['flutter'] as Map)['assets'] as List;
|
||||
if (!assets.contains('shorebird.yaml')) {
|
||||
editor.update(['flutter', 'assets'], [...assets, 'shorebird.yaml']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editor.edits.isNotEmpty) {
|
||||
pubspecFile.writeAsStringSync(editor.toString());
|
||||
logger.info('Added "shorebird.yaml" to "pubspec.yaml".');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export 'shorebird_yaml.dart';
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:checked_yaml/checked_yaml.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:pubspec_parse/pubspec_parse.dart';
|
||||
import 'package:shorebird_cli/src/command.dart';
|
||||
import 'package:shorebird_cli/src/config/config.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
mixin ShorebirdConfigMixin on ShorebirdCommand {
|
||||
bool get hasShorebirdYaml => getShorebirdYaml() != null;
|
||||
|
||||
bool get hasPubspecYaml => getPubspecYaml() != null;
|
||||
|
||||
bool get isShorebirdInitialized {
|
||||
return hasShorebirdYaml && pubspecContainsShorebirdYaml;
|
||||
}
|
||||
|
||||
bool get pubspecContainsShorebirdYaml {
|
||||
final file = File(p.join(Directory.current.path, 'pubspec.yaml'));
|
||||
final pubspecContents = file.readAsStringSync();
|
||||
final yaml = loadYaml(pubspecContents, sourceUrl: file.uri) as Map;
|
||||
if (!yaml.containsKey('flutter')) return false;
|
||||
if (!(yaml['flutter'] as Map).containsKey('assets')) return false;
|
||||
final assets = (yaml['flutter'] as Map)['assets'] as List;
|
||||
return assets.contains('shorebird.yaml');
|
||||
}
|
||||
|
||||
ShorebirdYaml? getShorebirdYaml() {
|
||||
final file = File(p.join(Directory.current.path, 'shorebird.yaml'));
|
||||
if (!file.existsSync()) return null;
|
||||
final yaml = file.readAsStringSync();
|
||||
return checkedYamlDecode(yaml, (m) => ShorebirdYaml.fromJson(m!));
|
||||
}
|
||||
|
||||
Pubspec? getPubspecYaml() {
|
||||
final file = File(p.join(Directory.current.path, 'pubspec.yaml'));
|
||||
if (!file.existsSync()) return null;
|
||||
final yaml = file.readAsStringSync();
|
||||
return Pubspec.parse(yaml);
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,6 @@ void main() {
|
||||
test(
|
||||
'ensure_build',
|
||||
() => expectBuildClean(packageRelativeDirectory: 'packages/shorebird_cli'),
|
||||
timeout: const Timeout.factor(2),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/commands/init_command.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockLogger extends Mock implements Logger {}
|
||||
|
||||
class _MockProgress extends Mock implements Progress {}
|
||||
|
||||
void main() {
|
||||
group('init', () {
|
||||
const version = '1.2.3';
|
||||
const pubspecYamlContent = '''
|
||||
name: example
|
||||
version: $version
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"''';
|
||||
const productId = 'test-product-id';
|
||||
|
||||
late Logger logger;
|
||||
late Progress progress;
|
||||
late InitCommand command;
|
||||
|
||||
setUp(() {
|
||||
logger = _MockLogger();
|
||||
progress = _MockProgress();
|
||||
command = InitCommand(
|
||||
logger: logger,
|
||||
buildUuid: () => productId,
|
||||
);
|
||||
|
||||
when(() => logger.progress(any())).thenReturn(progress);
|
||||
});
|
||||
|
||||
test('throws no input error when pubspec.yaml is not found.', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => logger.err('Could not find a "pubspec.yaml".')).called(1);
|
||||
expect(exitCode, ExitCode.noInput.code);
|
||||
});
|
||||
|
||||
test('throws software error when pubspec.yaml is malformed.', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).createSync();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(
|
||||
() => logger.err(any(that: contains('Error parsing "pubspec.yaml":'))),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('throws software error when shorebird.yaml is malformed.', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(p.join(tempDir.path, 'shorebird.yaml')).createSync();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(
|
||||
() => logger.err(
|
||||
any(that: contains('Error creating "shorebird.yaml".')),
|
||||
),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('detects existing shorebird.yaml', () async {
|
||||
const existingProductId = 'existing-product-id';
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(
|
||||
p.join(tempDir.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('product_id: $existingProductId');
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(),
|
||||
contains('product_id: $existingProductId'),
|
||||
);
|
||||
verify(() => progress.update('"shorebird.yaml" already exists.'));
|
||||
});
|
||||
|
||||
test('creates shorebird.yaml', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(),
|
||||
contains('product_id: $productId'),
|
||||
);
|
||||
});
|
||||
|
||||
test('detects existing shorebird.yaml in pubspec.yaml assets', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
''');
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(),
|
||||
contains('product_id: $productId'),
|
||||
);
|
||||
verify(
|
||||
() => progress.update(
|
||||
'"shorebird.yaml" already in "pubspec.yaml" assets.',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('creates flutter.assets and adds shorebird.yaml', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(),
|
||||
equals('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
'''),
|
||||
);
|
||||
});
|
||||
|
||||
test('creates assets and adds shorebird.yaml', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
''');
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(),
|
||||
equals('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
uses-material-design: true
|
||||
'''),
|
||||
);
|
||||
});
|
||||
|
||||
test('adds shorebird.yaml to assets', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- some/asset.txt
|
||||
''');
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(),
|
||||
equals('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- some/asset.txt
|
||||
- shorebird.yaml
|
||||
'''),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -38,7 +38,11 @@ void main() {
|
||||
name: example
|
||||
version: $version
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"''';
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
|
||||
flutter:
|
||||
assets:
|
||||
- shorebird.yaml''';
|
||||
|
||||
late ArgResults argResults;
|
||||
late Auth auth;
|
||||
@@ -46,6 +50,17 @@ environment:
|
||||
late CodePushClient codePushClient;
|
||||
late PublishCommand command;
|
||||
|
||||
Directory setUpTempDir() {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(
|
||||
p.join(tempDir.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('product_id: $productId');
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
argResults = _MockArgResults();
|
||||
auth = _MockAuth();
|
||||
@@ -72,67 +87,45 @@ environment:
|
||||
).thenAnswer((_) async {});
|
||||
});
|
||||
|
||||
test('throws no user error when session does not exist', () async {
|
||||
when(() => auth.currentSession).thenReturn(null);
|
||||
final exitCode = await command.run();
|
||||
expect(exitCode, equals(ExitCode.noUser.code));
|
||||
});
|
||||
|
||||
test('throws usage error when multiple args are passed.', () async {
|
||||
when(() => argResults.rest).thenReturn(['arg1', 'arg2']);
|
||||
await expectLater(command.run, throwsA(isA<UsageException>()));
|
||||
});
|
||||
|
||||
test('throws no input error when pubspec.yaml is not found.', () async {
|
||||
test('throws config error when shorebird is not initialized', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => logger.err('Could not find a "pubspec.yaml".')).called(1);
|
||||
expect(exitCode, ExitCode.noInput.code);
|
||||
});
|
||||
|
||||
test('throws software error when pubspec.yaml is malformed.', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).createSync();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(
|
||||
() => logger.err(any(that: contains('Error parsing "pubspec.yaml":'))),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
});
|
||||
|
||||
test('throws software error when shorebird.yaml is malformed.', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(p.join(tempDir.path, 'shorebird.yaml')).createSync();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(
|
||||
() => logger.err(
|
||||
any(that: contains('Error parsing "shorebird.yaml":')),
|
||||
'Shorebird is not initialized. Did you run "shorebird init"?',
|
||||
),
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.software.code);
|
||||
expect(exitCode, ExitCode.config.code);
|
||||
});
|
||||
|
||||
test('throws no user error when session does not exist', () async {
|
||||
when(() => auth.currentSession).thenReturn(null);
|
||||
final tempDir = setUpTempDir();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
() => command.run(),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(exitCode, equals(ExitCode.noUser.code));
|
||||
});
|
||||
|
||||
test('throws usage error when multiple args are passed.', () async {
|
||||
when(() => argResults.rest).thenReturn(['arg1', 'arg2']);
|
||||
final tempDir = setUpTempDir();
|
||||
await expectLater(
|
||||
IOOverrides.runZoned(
|
||||
() => command.run(),
|
||||
getCurrentDirectory: () => tempDir,
|
||||
),
|
||||
throwsA(isA<UsageException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('throws no input error when artifact is not found (default).',
|
||||
() async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(
|
||||
p.join(tempDir.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('product_id: $productId');
|
||||
final tempDir = setUpTempDir();
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
@@ -145,15 +138,9 @@ environment:
|
||||
|
||||
test('throws no input error when artifact is not found (custom).',
|
||||
() async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final tempDir = setUpTempDir();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'));
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(
|
||||
p.join(tempDir.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('product_id: $productId');
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
@@ -178,15 +165,9 @@ environment:
|
||||
productId: any(named: 'productId'),
|
||||
),
|
||||
).thenThrow(error);
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final tempDir = setUpTempDir();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(
|
||||
p.join(tempDir.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('product_id: $productId');
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
@@ -197,15 +178,9 @@ environment:
|
||||
|
||||
test('succeeds when publish is successful using existing product id',
|
||||
() async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final tempDir = setUpTempDir();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
File(
|
||||
p.join(tempDir.path, 'shorebird.yaml'),
|
||||
).writeAsStringSync('product_id: $productId');
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
@@ -221,106 +196,5 @@ environment:
|
||||
).called(1);
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
});
|
||||
|
||||
test('succeeds when publish is successful using newly generated product id',
|
||||
() async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
final exitCode = await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
verify(() => logger.success('Successfully deployed.')).called(1);
|
||||
verify(
|
||||
() => codePushClient.createPatch(
|
||||
baseVersion: version,
|
||||
productId: any(named: 'productId', that: isNotEmpty),
|
||||
artifactPath: artifact.path,
|
||||
channel: 'stable',
|
||||
),
|
||||
).called(1);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'shorebird.yaml')).existsSync(),
|
||||
isTrue,
|
||||
);
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
});
|
||||
|
||||
test('creates flutter.assets and adds shorebird.yaml', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(
|
||||
p.join(tempDir.path, 'pubspec.yaml'),
|
||||
).writeAsStringSync(pubspecYamlContent);
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(),
|
||||
equals('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
'''),
|
||||
);
|
||||
});
|
||||
|
||||
test('creates assets and adds shorebird.yaml', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
''');
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(),
|
||||
equals('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
uses-material-design: true
|
||||
'''),
|
||||
);
|
||||
});
|
||||
|
||||
test('adds shorebird.yaml to assets', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final artifact = File(p.join(tempDir.path, 'patch.txt'))..createSync();
|
||||
when(() => argResults.rest).thenReturn([artifact.path]);
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- some/asset.txt
|
||||
''');
|
||||
await IOOverrides.runZoned(
|
||||
command.run,
|
||||
getCurrentDirectory: () => tempDir,
|
||||
);
|
||||
expect(
|
||||
File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(),
|
||||
equals('''
|
||||
$pubspecYamlContent
|
||||
flutter:
|
||||
assets:
|
||||
- some/asset.txt
|
||||
- shorebird.yaml
|
||||
'''),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user