diff --git a/bin/internal/flutter.version b/bin/internal/flutter.version new file mode 100644 index 00000000..fd82bdfe --- /dev/null +++ b/bin/internal/flutter.version @@ -0,0 +1 @@ +45fc514f1a9c347a3af76b02baf980a4d88b7879 \ No newline at end of file diff --git a/bin/shorebird b/bin/shorebird index cfb4d3ac..b40bbd3c 100755 --- a/bin/shorebird +++ b/bin/shorebird @@ -9,6 +9,7 @@ BIN_DIR=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SO PROG_NAME="$BIN_DIR/$(basename "$BASH_SOURCE")" OS="$(uname -s)" +FLUTTER_VERSION=`cat "$BIN_DIR/internal/flutter.version"` source "$BIN_DIR/../third_party/flutter/bin/internal/shared.sh" # We currently depend on a forked (3.7.8 stable) Flutter shared.sh script diff --git a/packages/shorebird_cli/lib/src/cache.dart b/packages/shorebird_cli/lib/src/cache.dart index e5b24673..4a8d0bd6 100644 --- a/packages/shorebird_cli/lib/src/cache.dart +++ b/packages/shorebird_cli/lib/src/cache.dart @@ -5,7 +5,6 @@ import 'package:archive/archive_io.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; -import 'package:shorebird_cli/src/engine_revision.dart'; import 'package:shorebird_cli/src/shorebird_environment.dart'; typedef ArchiveExtracter = Future Function( @@ -137,6 +136,6 @@ class PatchArtifact extends CachedArtifact { artifactName += 'windows-x64.zip'; } - return 'shorebird/$shorebirdEngineRevision/$artifactName'; + return 'shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/$artifactName'; } } diff --git a/packages/shorebird_cli/lib/src/command_runner.dart b/packages/shorebird_cli/lib/src/command_runner.dart index 5fb72aed..2fd8ddd3 100644 --- a/packages/shorebird_cli/lib/src/command_runner.dart +++ b/packages/shorebird_cli/lib/src/command_runner.dart @@ -3,7 +3,7 @@ import 'package:args/command_runner.dart'; import 'package:cli_completion/cli_completion.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; -import 'package:shorebird_cli/src/engine_revision.dart'; +import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/version.dart'; const executableName = 'shorebird'; @@ -99,7 +99,7 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner { _logger.info( ''' Shorebird $packageVersion -Shorebird Engine • revision $shorebirdEngineRevision''', +Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision}''', ); exitCode = ExitCode.success.code; } else { diff --git a/packages/shorebird_cli/lib/src/commands/doctor_command.dart b/packages/shorebird_cli/lib/src/commands/doctor_command.dart index 6a7b997f..35485f78 100644 --- a/packages/shorebird_cli/lib/src/commands/doctor_command.dart +++ b/packages/shorebird_cli/lib/src/commands/doctor_command.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:shorebird_cli/src/command.dart'; +import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/shorebird_version_mixin.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:shorebird_cli/src/version.dart'; @@ -40,7 +41,7 @@ class DoctorCommand extends ShorebirdCommand with ShorebirdVersionMixin { logger.info(''' Shorebird v$packageVersion -'''); +Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision}'''); var numIssues = 0; for (final validator in validators) { diff --git a/packages/shorebird_cli/lib/src/engine_revision.dart b/packages/shorebird_cli/lib/src/engine_revision.dart deleted file mode 100644 index 11172173..00000000 --- a/packages/shorebird_cli/lib/src/engine_revision.dart +++ /dev/null @@ -1,2 +0,0 @@ -/// The hash of the Shorebird engine used by this version of Shorebird CLI. -const shorebirdEngineRevision = '8b89f8bd9fc6982aa9c4557fd0e5e89db1ff9986'; diff --git a/packages/shorebird_cli/lib/src/shorebird_environment.dart b/packages/shorebird_cli/lib/src/shorebird_environment.dart index f335b7a4..e5ef4803 100644 --- a/packages/shorebird_cli/lib/src/shorebird_environment.dart +++ b/packages/shorebird_cli/lib/src/shorebird_environment.dart @@ -17,6 +17,34 @@ abstract class ShorebirdEnvironment { static Directory get shorebirdRoot => File(platform.script.toFilePath()).parent.parent.parent; + static String get shorebirdEngineRevision { + return _shorebirdEngineRevision ?? + File( + p.join(flutterDirectory.path, 'bin', 'internal', 'engine.version'), + ).readAsStringSync(); + } + + static String? _shorebirdEngineRevision; + + @visibleForTesting + static set shorebirdEngineRevision(String revision) { + _shorebirdEngineRevision = revision; + } + + static String? _flutterRevision; + + @visibleForTesting + static set flutterRevision(String revision) { + _flutterRevision = revision; + } + + static String get flutterRevision { + return _flutterRevision ?? + File( + p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version'), + ).readAsStringSync(); + } + /// The root of the Shorebird-vended Flutter git checkout. static Directory get flutterDirectory => Directory( p.join( diff --git a/packages/shorebird_cli/lib/src/shorebird_process.dart b/packages/shorebird_cli/lib/src/shorebird_process.dart index 096fff1e..e231e09c 100644 --- a/packages/shorebird_cli/lib/src/shorebird_process.dart +++ b/packages/shorebird_cli/lib/src/shorebird_process.dart @@ -86,6 +86,8 @@ abstract class ShorebirdProcess { required String executable, }) { if (executable == 'flutter') { + // If this ever changes we also need to update the `shorebird` shell + // wrapper which downloads runs Flutter to fetch artifacts the first time. return {'FLUTTER_STORAGE_BASE_URL': 'https://download.shorebird.dev/'}; } diff --git a/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart b/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart index 2c0e331d..ba8743c6 100644 --- a/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart +++ b/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart @@ -45,10 +45,9 @@ class ShorebirdFlutterValidator extends Validator { ); } - if (!await _flutterDirectoryTracksStable()) { + if (!await _flutterDirectoryTracksCorrectRevision()) { final message = - '${ShorebirdEnvironment.flutterDirectory} is not on the "stable" ' - 'branch'; + '''${ShorebirdEnvironment.flutterDirectory} is not on the correct revision'''; issues.add( ValidationIssue( severity: ValidationIssueSeverity.warning, @@ -102,13 +101,15 @@ This can cause unexpected behavior if you are switching between the tools and th .contains('nothing to commit, working tree clean'); } - Future _flutterDirectoryTracksStable() async { + Future _flutterDirectoryTracksCorrectRevision() async { final result = await runProcess( 'git', - ['--no-pager', 'branch'], + ['rev-parse', 'HEAD'], workingDirectory: ShorebirdEnvironment.flutterDirectory.path, ); - return result.stdout.toString().contains('* stable'); + return result.stdout + .toString() + .contains(ShorebirdEnvironment.flutterRevision); } Future _shorebirdFlutterVersion() => _getFlutterVersion( diff --git a/packages/shorebird_cli/test/src/cache_test.dart b/packages/shorebird_cli/test/src/cache_test.dart index 3ad38215..6e672baf 100644 --- a/packages/shorebird_cli/test/src/cache_test.dart +++ b/packages/shorebird_cli/test/src/cache_test.dart @@ -6,7 +6,6 @@ import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:shorebird_cli/src/cache.dart'; -import 'package:shorebird_cli/src/engine_revision.dart'; import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:test/test.dart'; @@ -43,6 +42,7 @@ void main() { shorebirdRoot = Directory.systemTemp.createTempSync(); ShorebirdEnvironment.platform = platform; + ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision'; when(() => platform.isMacOS).thenReturn(true); when(() => platform.isWindows).thenReturn(false); @@ -121,7 +121,7 @@ void main() { request.url, equals( Uri.parse( - '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/patch-darwin-x64.zip', + '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/patch-darwin-x64.zip', ), ), ); @@ -141,7 +141,7 @@ void main() { request.url, equals( Uri.parse( - '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/patch-windows-x64.zip', + '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/patch-windows-x64.zip', ), ), ); @@ -161,7 +161,7 @@ void main() { request.url, equals( Uri.parse( - '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/patch-linux-x64.zip', + '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/patch-linux-x64.zip', ), ), ); diff --git a/packages/shorebird_cli/test/src/command_runner_test.dart b/packages/shorebird_cli/test/src/command_runner_test.dart index bff640cc..0e0c2b15 100644 --- a/packages/shorebird_cli/test/src/command_runner_test.dart +++ b/packages/shorebird_cli/test/src/command_runner_test.dart @@ -5,7 +5,7 @@ import 'package:cli_completion/cli_completion.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:shorebird_cli/src/command_runner.dart'; -import 'package:shorebird_cli/src/engine_revision.dart'; +import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/version.dart'; import 'package:test/test.dart'; @@ -22,6 +22,7 @@ void main() { setUp(() { logger = _MockLogger(); + ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision'; processResult = _MockProcessResult(); when(() => processResult.exitCode).thenReturn(ExitCode.success.code); commandRunner = ShorebirdCliCommandRunner(logger: logger); @@ -72,7 +73,7 @@ void main() { () => logger.info( ''' Shorebird $packageVersion -Shorebird Engine • revision $shorebirdEngineRevision''', +Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision}''', ), ).called(1); }); diff --git a/packages/shorebird_cli/test/src/commands/doctor_command_test.dart b/packages/shorebird_cli/test/src/commands/doctor_command_test.dart index a736687f..f30fe419 100644 --- a/packages/shorebird_cli/test/src/commands/doctor_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/doctor_command_test.dart @@ -1,6 +1,7 @@ import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:test/test.dart'; @@ -30,6 +31,8 @@ void main() { logger = _MockLogger(); progress = _MockProgress(); + ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision'; + when(() => logger.progress(any())).thenReturn(progress); when(() => logger.info(any())).thenReturn(null); diff --git a/packages/shorebird_cli/test/src/shorebird_environment_test.dart b/packages/shorebird_cli/test/src/shorebird_environment_test.dart new file mode 100644 index 00000000..4b84b06b --- /dev/null +++ b/packages/shorebird_cli/test/src/shorebird_environment_test.dart @@ -0,0 +1,58 @@ +import 'dart:io'; + +import 'package:mocktail/mocktail.dart'; +import 'package:path/path.dart' as p; +import 'package:platform/platform.dart'; +import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:test/test.dart'; + +class _MockPlatform extends Mock implements Platform {} + +void main() { + group('ShorebirdEnvironment', () { + late Platform platform; + late Directory shorebirdRoot; + late Uri platformScript; + + setUp(() { + shorebirdRoot = Directory.systemTemp.createTempSync(); + platformScript = Uri.file( + p.join(shorebirdRoot.path, 'bin', 'cache', 'shorebird.snapshot'), + ); + platform = _MockPlatform(); + ShorebirdEnvironment.platform = platform; + + when(() => platform.script).thenReturn(platformScript); + }); + + group('flutterRevision', () { + test('returns correct revision', () { + const revision = 'test-revision'; + File(p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version')) + ..createSync(recursive: true) + ..writeAsStringSync(revision, flush: true); + expect(ShorebirdEnvironment.flutterRevision, equals(revision)); + }); + }); + + group('shorebirdEngineRevision', () { + test('returns correct revision', () { + const revision = 'test-revision'; + File( + p.join( + shorebirdRoot.path, + 'bin', + 'cache', + 'flutter', + 'bin', + 'internal', + 'engine.version', + ), + ) + ..createSync(recursive: true) + ..writeAsStringSync(revision, flush: true); + expect(ShorebirdEnvironment.shorebirdEngineRevision, equals(revision)); + }); + }); + }); +} diff --git a/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart b/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart index bb66e660..07023796 100644 --- a/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart +++ b/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart @@ -14,16 +14,14 @@ class _MockPlatform extends Mock implements Platform {} void main() { group('ShorebirdFlutterValidator', () { - const gitStatusMessage = """ -On branch stable -Your branch is up to date with 'origin/stable'. - + const flutterRevision = '45fc514f1a9c347a3af76b02baf980a4d88b7879'; + const gitStatusMessage = ''' +HEAD detached at 45fc514f nothing to commit, working tree clean -"""; +'''; - const gitBranchMessage = ''' - main -* stable + const gitRevParseHeadMessage = ''' +$flutterRevision '''; const pathFlutterVersionMessage = ''' @@ -44,7 +42,7 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 late Directory tempDir; late ProcessResult pathFlutterVersionProcessResult; late ProcessResult shorebirdFlutterVersionProcessResult; - late ProcessResult gitBranchProcessResult; + late ProcessResult gitRevParseHeadProcessResult; late ProcessResult gitStatusProcessResult; Directory flutterDirectory(Directory root) => @@ -64,13 +62,14 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 tempDir = setupTempDirectory(); ShorebirdEnvironment.platform = _MockPlatform(); + ShorebirdEnvironment.flutterRevision = flutterRevision; when(() => ShorebirdEnvironment.platform.script) .thenReturn(shorebirdScriptFile(tempDir).uri); when(() => ShorebirdEnvironment.platform.environment).thenReturn({}); pathFlutterVersionProcessResult = _MockProcessResult(); shorebirdFlutterVersionProcessResult = _MockProcessResult(); - gitBranchProcessResult = _MockProcessResult(); + gitRevParseHeadProcessResult = _MockProcessResult(); gitStatusProcessResult = _MockProcessResult(); validator = ShorebirdFlutterValidator( @@ -85,8 +84,8 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 if (executable == 'git') { if (arguments.equals(['status'])) { return gitStatusProcessResult; - } else if (arguments.equals(['--no-pager', 'branch'])) { - return gitBranchProcessResult; + } else if (arguments.equals(['rev-parse', 'HEAD'])) { + return gitRevParseHeadProcessResult; } } else if (executable == 'flutter') { if (arguments.equals(['--version'])) { @@ -109,7 +108,8 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 .thenReturn(shorebirdFlutterVersionMessage); when(() => shorebirdFlutterVersionProcessResult.stderr).thenReturn(''); when(() => shorebirdFlutterVersionProcessResult.exitCode).thenReturn(0); - when(() => gitBranchProcessResult.stdout).thenReturn(gitBranchMessage); + when(() => gitRevParseHeadProcessResult.stdout) + .thenReturn(gitRevParseHeadMessage); when(() => gitStatusProcessResult.stdout).thenReturn(gitStatusMessage); }); @@ -141,16 +141,15 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 }); test('warns when Flutter does not track stable', () async { - when(() => gitBranchProcessResult.stdout).thenReturn(''' -* main - stable + when(() => gitRevParseHeadProcessResult.stdout).thenReturn(''' +62bd79521d '''); final results = await validator.validate(); expect(results, hasLength(1)); expect(results.first.severity, ValidationIssueSeverity.warning); - expect(results.first.message, contains('is not on the "stable" branch')); + expect(results.first.message, contains('is not on the correct revision')); }); test( diff --git a/third_party/flutter/bin/internal/shared.sh b/third_party/flutter/bin/internal/shared.sh index dfa11fb4..a9408229 100755 --- a/third_party/flutter/bin/internal/shared.sh +++ b/third_party/flutter/bin/internal/shared.sh @@ -11,10 +11,14 @@ unset CDPATH # Either clones or pulls the Shorebird Flutter repository, depending on whether FLUTTER_PATH exists. function update_flutter { if [[ -d "$FLUTTER_PATH" ]]; then - git --git-dir="$FLUTTER_PATH/.git" --work-tree="$FLUTTER_PATH" pull + git -C "$FLUTTER_PATH" fetch else - git clone --filter=tree:0 https://github.com/shorebirdtech/flutter.git -b stable "$FLUTTER_PATH" + git clone --filter=tree:0 https://github.com/shorebirdtech/flutter.git --no-checkout "$FLUTTER_PATH" fi + # -c to avoid printing a warning about being in a detached head state. + git -C "$FLUTTER_PATH" -c advice.detachedHead=false checkout "$FLUTTER_VERSION" + SHOREBIRD_ENGINE_VERSION=`cat "$FLUTTER_PATH/bin/internal/engine.version"` + echo "Shorebird Engine • revision $SHOREBIRD_ENGINE_VERSION" # Install Shorebird Flutter Artifacts FLUTTER_STORAGE_BASE_URL=https://download.shorebird.dev/ $FLUTTER_PATH/bin/flutter --version } @@ -23,7 +27,7 @@ function pub_upgrade_with_retry { local total_tries="10" local remaining_tries=$((total_tries - 1)) while [[ "$remaining_tries" -gt 0 ]]; do - (cd "$SHOREBIRD_CLI_DIR" && dart pub upgrade) && break + (cd "$SHOREBIRD_CLI_DIR" && $DART_PATH pub upgrade) && break >&2 echo "Error: Unable to 'pub upgrade' shorebird. Retrying in five seconds... ($remaining_tries tries left)" remaining_tries=$((remaining_tries - 1)) sleep 5 @@ -161,7 +165,7 @@ function upgrade_shorebird () ( fi # Compile... - dart --verbosity=error --disable-dart-dev --snapshot="$SNAPSHOT_PATH" --snapshot-kind="app-jit" --packages="$SHOREBIRD_CLI_DIR/.dart_tool/package_config.json" --no-enable-mirrors "$SCRIPT_PATH" > /dev/null + $DART_PATH --verbosity=error --disable-dart-dev --snapshot="$SNAPSHOT_PATH" --snapshot-kind="app-jit" --packages="$SHOREBIRD_CLI_DIR/.dart_tool/package_config.json" --no-enable-mirrors "$SCRIPT_PATH" > /dev/null echo "$compilekey" > "$STAMP_PATH" # Delete any temporary snapshot path. @@ -185,6 +189,7 @@ function shared::execute() { STAMP_PATH="$SHOREBIRD_ROOT/bin/cache/shorebird.stamp" SCRIPT_PATH="$SHOREBIRD_CLI_DIR/bin/shorebird.dart" FLUTTER_PATH="$SHOREBIRD_ROOT/bin/cache/flutter" + export DART_PATH="$FLUTTER_PATH/bin/cache/dart-sdk/bin/dart" # Test if running as superuser – but don't warn if running within Docker or CI. if [[ "$EUID" == "0" && ! -f /.dockerenv && "$CI" != "true" && "$BOT" != "true" && "$CONTINUOUS_INTEGRATION" != "true" ]]; then @@ -200,18 +205,6 @@ function shared::execute() { exit 1 fi - # Test if Dart is available on the Host - if ! hash dart 2>/dev/null; then - >&2 echo "Error: Unable to find dart in your PATH." - exit 1 - fi - - # Test if Flutter is available on the Host - if ! hash flutter 2>/dev/null; then - >&2 echo "Error: Unable to find flutter in your PATH." - exit 1 - fi - # Test if the shorebird directory is a git clone (otherwise git rev-parse HEAD # would fail) if [[ ! -e "$SHOREBIRD_ROOT/.git" ]]; then @@ -227,7 +220,7 @@ function shared::execute() { BIN_NAME="$(basename "$PROG_NAME")" case "$BIN_NAME" in shorebird*) - exec "dart" "$SNAPSHOT_PATH" "$@" + exec "$DART_PATH" "$SNAPSHOT_PATH" "$@" ;; *) >&2 echo "Error! Executable name $BIN_NAME not recognized!"