Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add bin/internal/last_engine_commit.sh and tests. #168387

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

Merged
merged 1 commit into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 28 additions & 0 deletions bin/internal/last_engine_commit.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2014 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Based on the current repository state, writes on stdout the last commit in the
# git tree that edited either `DEPS` or any file in the `engine/` sub-folder,
# which is used to ensure `bin/internal/engine.version` is set correctly.

# ---------------------------------- NOTE ---------------------------------- #
#
# Please keep the logic in this file consistent with the logic in the
# `last_engine_commit.sh` script in the same directory to ensure that Flutter
# continues to work across all platforms!
#
# https://github.com/flutter/flutter/blob/main/docs/tool/Engine-artifacts.md.
#
# Want to test this script?
# $ cd dev/tools
# $ dart test test/last_engine_commit_test.dart
#
# -------------------------------------------------------------------------- #

$ErrorActionPreference = "Stop"

$progName = Split-Path -parent $MyInvocation.MyCommand.Definition
$flutterRoot = (Get-Item $progName).parent.parent.FullName

cmd /c "git log -1 --pretty=format:%H -- ""$(git rev-parse --show-toplevel)/DEPS"" ""$(git rev-parse --show-toplevel)/engine"""
29 changes: 29 additions & 0 deletions bin/internal/last_engine_commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Copyright 2014 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Based on the current repository state, writes on stdout the last commit in the
# git tree that edited either `DEPS` or any file in the `engine/` sub-folder,
# which is used to ensure `bin/internal/engine.version` is set correctly.
#

# ---------------------------------- NOTE ---------------------------------- #
#
# Please keep the logic in this file consistent with the logic in the
# `last_engine_commit.ps1` script in the same directory to ensure that Flutter
# continues to work across all platforms!
#
# https://github.com/flutter/flutter/blob/main/docs/tool/Engine-artifacts.md.
#
# Want to test this script?
# $ cd dev/tools
# $ dart test test/last_engine_commit_test.dart
#
# -------------------------------------------------------------------------- #

set -e

FLUTTER_ROOT="$(dirname "$(dirname "$(dirname "${BASH_SOURCE[0]}")")")"

git log -1 --pretty=format:%H -- "$(git rev-parse --show-toplevel)/DEPS" "$(git rev-parse --show-toplevel)/engine"
1 change: 1 addition & 0 deletions dev/bots/analyze.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2660,6 +2660,7 @@ const Set<String> kExecutableAllowlist = <String>{
'bin/dart',
'bin/flutter',
'bin/flutter-dev',
'bin/internal/last_engine_commit.sh',
'bin/internal/update_dart_sdk.sh',
'bin/internal/update_engine_version.sh',
'bin/internal/content_aware_hash.sh',
Expand Down
259 changes: 259 additions & 0 deletions dev/tools/test/last_engine_commit_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@TestOn('vm')
library;

import 'dart:io' as io;

import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:platform/platform.dart';
import 'package:test/test.dart';

//////////////////////////////////////////////////////////////////////
// //
// ✨ THINKING OF MOVING/REFACTORING THIS FILE? READ ME FIRST! ✨ //
// //
// There is a link to this file in //docs/tool/Engine-artfiacts.md //
// and it would be very kind of you to update the link, if needed. //
// //
//////////////////////////////////////////////////////////////////////

void main() {
// Want to test the powershell (update_engine_version.ps1) file, but running
// a macOS or Linux machine? You can install powershell and then opt-in to
// running `pwsh bin/internal/update_engine_version.ps1`.
//
// macOS: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos
// linux: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux
//
// Then, set this variable to true:
final bool usePowershellOnPosix = () {
// Intentionally not a const so that linting doesn't go wild across the test.
return false;
}();

const FileSystem localFs = LocalFileSystem();
final _FlutterRootUnderTest flutterRoot = _FlutterRootUnderTest.findWithin(
forcePowershell: usePowershellOnPosix,
);

late Directory tmpDir;
late _FlutterRootUnderTest testRoot;
late Map<String, String> environment;

void printIfNotEmpty(String prefix, String string) {
if (string.isNotEmpty) {
string.split(io.Platform.lineTerminator).forEach((String s) {
print('$prefix:>$s<');
});
}
}

io.ProcessResult run(String executable, List<String> args) {
print('Running "$executable ${args.join(" ")}');
final io.ProcessResult result = io.Process.runSync(
executable,
args,
environment: environment,
workingDirectory: testRoot.root.absolute.path,
includeParentEnvironment: false,
);
if (result.exitCode != 0) {
fail(
'Failed running "$executable $args" (exit code = ${result.exitCode}),'
'\nstdout: ${result.stdout}'
'\nstderr: ${result.stderr}',
);
}
printIfNotEmpty('stdout', (result.stdout as String).trim());
printIfNotEmpty('stderr', (result.stderr as String).trim());
return result;
}

setUpAll(() async {
if (usePowershellOnPosix) {
final io.ProcessResult result = io.Process.runSync('pwsh', <String>['--version']);
print('Using Powershell (${result.stdout}) on POSIX for local debugging and testing');
}
});

/// Initializes a blank git repo in [testRoot.root].
void initGitRepoWithBlankInitialCommit() {
run('git', <String>['init', '--initial-branch', 'master']);
run('git', <String>['config', '--local', 'user.email', '[email protected]']);
run('git', <String>['config', '--local', 'user.name', 'Test User']);
run('git', <String>['add', '.']);
run('git', <String>['commit', '--allow-empty', '-m', 'Initial commit']);
}

late int commitCount;

setUp(() async {
commitCount = 0;

tmpDir = localFs.systemTempDirectory.createTempSync('last_engine_commit_test.');
testRoot = _FlutterRootUnderTest.fromPath(
tmpDir.childDirectory('flutter').path,
forcePowershell: usePowershellOnPosix,
);

environment = <String, String>{};

if (const LocalPlatform().isWindows || usePowershellOnPosix) {
// Copy a minimal set of environment variables needed to run the update_engine_version script in PowerShell.
const List<String> powerShellVariables = <String>['SystemRoot', 'Path', 'PATHEXT'];
for (final String key in powerShellVariables) {
final String? value = io.Platform.environment[key];
if (value != null) {
environment[key] = value;
}
}
}

// Copy the update_engine_version script and create a rough directory structure.
flutterRoot.binInternalLastEngineCommit.copySyncRecursive(
testRoot.binInternalLastEngineCommit.path,
);

initGitRepoWithBlankInitialCommit();
});

tearDown(() {
tmpDir.deleteSync(recursive: true);
});

/// Runs `bin/internal/last_engine_commit.{sh|ps1}` and returns the stdout.
///
/// - On Windows, `powershell` is used (to run `last_engine_commit.ps1`);
/// - On POSIX, if [usePowershellOnPosix] is set, `pwsh` is used (to run `last_engine_commit.ps1`);
/// - Otherwise, `last_engine_commit.sh` is used.
String getLastEngineCommit() {
final String executable;
final List<String> args;
if (const LocalPlatform().isWindows) {
executable = 'powershell';
args = <String>[testRoot.binInternalLastEngineCommit.path];
} else if (usePowershellOnPosix) {
executable = 'pwsh';
args = <String>[testRoot.binInternalLastEngineCommit.path];
} else {
executable = testRoot.binInternalLastEngineCommit.path;
args = <String>[];
}
return run(executable, args).stdout as String;
}

void writeCommit(Iterable<String> files) {
commitCount++;
for (final String relativePath in files) {
localFs.file(localFs.path.join(testRoot.root.path, relativePath))
..createSync(recursive: true)
..writeAsStringSync('$commitCount');
}
run('git', <String>['add', '.']);
run('git', <String>['commit', '-m', 'Wrote ${files.length} files']);
}

test('returns the last engine commit', () {
writeCommit(<String>['DEPS', 'engine/README.md']);

final String lastEngine = getLastEngineCommit();
expect(lastEngine, isNotEmpty);

writeCommit(<String>['CHANGELOG.md', 'dev/folder/called/engine/README.md']);
expect(getLastEngineCommit(), lastEngine);
});

test('considers DEPS an engine change', () {
writeCommit(<String>['DEPS', 'engine/README.md']);

final String lastEngineA = getLastEngineCommit();
expect(lastEngineA, isNotEmpty);

writeCommit(<String>['DEPS']);
final String lastEngineB = getLastEngineCommit();
expect(lastEngineB, allOf(isNotEmpty, isNot(equals(lastEngineA))));
});
}

extension on File {
void copySyncRecursive(String newPath) {
fileSystem.directory(fileSystem.path.dirname(newPath)).createSync(recursive: true);
copySync(newPath);
}
}

/// A FrUT, or "Flutter Root"-Under Test (parallel to a SUT, System Under Test).
///
/// For the intent of this test case, the "Flutter Root" is a directory
/// structure with the following elements:
///
/// ```txt
/// ├── DEPS
/// ├── engine/
/// ├── bin/
/// │ ├── internal/
/// │ │ └── last_engine_commit.{sh|ps1}
/// ```
final class _FlutterRootUnderTest {
/// Creates a root-under test using [path] as the root directory.
///
/// It is assumed the files already exist or will be created if needed.
factory _FlutterRootUnderTest.fromPath(
String path, {
FileSystem fileSystem = const LocalFileSystem(),
Platform platform = const LocalPlatform(),
bool forcePowershell = false,
}) {
final Directory root = fileSystem.directory(path);
return _FlutterRootUnderTest._(
root,
depsFile: root.childFile('DEPS'),
engineDirectory: root.childDirectory('engine'),
binInternalLastEngineCommit: root.childFile(
fileSystem.path.join(
'bin',
'internal',
'last_engine_commit.${platform.isWindows || forcePowershell ? 'ps1' : 'sh'}',
),
),
);
}

factory _FlutterRootUnderTest.findWithin({
String? path,
FileSystem fileSystem = const LocalFileSystem(),
bool forcePowershell = false,
}) {
path ??= fileSystem.currentDirectory.path;
Directory current = fileSystem.directory(path);
while (!current.childFile('DEPS').existsSync()) {
if (current.path == current.parent.path) {
throw ArgumentError.value(path, 'path', 'Could not resolve flutter root');
}
current = current.parent;
}
return _FlutterRootUnderTest.fromPath(current.path, forcePowershell: forcePowershell);
}

const _FlutterRootUnderTest._(
this.root, {
required this.binInternalLastEngineCommit,
required this.depsFile,
required this.engineDirectory,
});

final Directory root;

/// `DEPS`.
final File depsFile;

/// The `engine/` directory.
final Directory engineDirectory;

/// `bin/internal/last_engine_commit.{sh|ps1}`.
final File binInternalLastEngineCommit;
}
17 changes: 14 additions & 3 deletions docs/tool/Engine-artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ On Cocoon (Flutter's internal CI/CD) we _often_ set
| `main` | `commit.sha` | _Uses normal flow_ | _Uses normal flow_ |
| `flutter-x.x-candidate.x` | `commit.sha` | N/A[^1] | `commit.sha` |

> NOTE: `engine.version` is intentionally ignored in release candidate
> post-submit builds. See
> [#167010](https://github.com/flutter/flutter/issues/167010).
> IMPORTANT: `engine.version` is intentionally ignored in release candidate
> post-submit builds.
>
> To generate a new `engine.version`:
>
> ```sh
> ./bin/internal/last_engine_commit.sh > ./bin/internal/engine.version
> ```
>
> At the moment this needs to be manually done, and manually verified, before
> making a release. See [#168273](https://github.com/flutter/flutter/issues/168273).

[^1]: Release candidates do not use a merge queue.

Expand All @@ -74,6 +82,9 @@ The script(s) that compute (and test the computation of) the engine version:
- [`bin/internal/update_engine_version.sh`](../../bin/internal/update_engine_version.sh)
- [`bin/internal/update_engine_version.ps1`](../../bin/internal/update_engine_version.ps1)
- [`dev/tools/test/update_engine_version_test.dart`](../../dev/tools/test/update_engine_version_test.dart)
- [`bin/internal/last_engine_commit.sh`](../../bin/internal/last_engine_commit.sh)
- [`bin/internal/last_engine_commit.ps1`](../../bin/internal/last_engine_commit.ps1)
- [`dev/tools/test/last_engine_commit_test.dart`](../../dev/tools/test/last_engine_commit_test.dart)
- [`bin/internal/content_aware_hash.sh`](../../bin/internal/content_aware_hash.sh)
- [`bin/internal/content_aware_hash.ps1`](../../bin/internal/content_aware_hash.ps1)
- [`dev/tools/test/content_aware_hash_test.dart`](../../dev/tools/test/content_aware_hash_test.dart)
Expand Down