fix: get content hash for master on local engine branches (#172792)

The content hash doesn't exist for local engine changes, except for on
CI. If we detect we're on a branch with committed or uncommitted changes
to engine files; use "master".

towards  #171790
This commit is contained in:
John "codefu" McDole 2025-07-25 17:35:26 -07:00 committed by GitHub
parent 35ebc20b99
commit e2d591cf88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 111 additions and 9 deletions

View File

@ -13,14 +13,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Fetch base commit and origin/master
run: |
git fetch --no-tags --prune --depth=1 origin ${{ github.event.pull_request.base.sha }}
- name: Generate Hash
run: |
engine_content_hash=$(bin/internal/content_aware_hash.sh)
# IMPORTANT: Keep the list of files in sync with bin/internal/content_aware_hash.sh
# We call this directly here as we're expected to be in the merge queue (not master)
engine_content_hash=$(git ls-tree --format "%(objectname) %(path)" HEAD -- DEPS engine bin/internal/release-candidate-branch.version | git hash-object --stdin)
# test notice annotation for retrival from api
echo "::notice ::{\"engine_content_hash\": \"${engine_content_hash}\"}"
# test summary writing
echo "{\"engine_content_hash\": \"${engine_content_hash}\"" >> $GITHUB_STEP_SUMMARY
echo "{\"engine_content_hash\": \"${engine_content_hash}\"" >> $GITHUB_STEP_SUMMARY

View File

@ -36,6 +36,28 @@ $flutterRoot = (Get-Item $progName).parent.parent.FullName
# 3. Out-File -NoNewline -Encoding ascii outputs 8bit ascii
# 4. git hash-object with stdin from a pipeline consumes UTF-16, so consume
#. the contents of hash.txt
(git -C "$flutterRoot" ls-tree --format "%(objectname) %(path)" HEAD DEPS engine bin/internal/release-candidate-branch.version | Out-String) -replace "`r`n", "`n" | Out-File -NoNewline -Encoding ascii hash.txt
$trackedFiles = "DEPS", "engine", "bin/internal/release-candidate-branch.version"
$baseRef = "HEAD"
$ErrorActionPreference = "Continue"
# We will fallback to origin/master if upstream is not detected.
git -C "$flutterRoot" remote get-url upstream *> $null
$exitCode = $?
$ErrorActionPreference = "Stop"
if ($exitCode) {
$mergeBase = (git -C "$flutterRoot" merge-base HEAD upstream/master)
} else {
$mergeBase = (git -C "$flutterRoot" merge-base HEAD origin/master)
}
# Check to see if we're in a local development branch and the branch has any
# changes to engine code - including non-committed changes.
if ((git -C "$flutterRoot" rev-parse --abbrev-ref HEAD) -ne "master") {
git -C "$flutterRoot" diff --quiet "$(git -C "$flutterRoot" merge-base $mergeBase HEAD)" -- $trackedFiles | Out-Null
if ($LASTEXITCODE -ne 0) {
$baseRef = "$mergeBase"
}
}
(git -C "$flutterRoot" ls-tree --format "%(objectname) %(path)" $baseRef -- $trackedFiles | Out-String) -replace "`r`n", "`n" | Out-File -NoNewline -Encoding ascii hash.txt
git hash-object hash.txt
Remove-Item hash.txt

View File

@ -25,4 +25,24 @@ unset GIT_WORK_TREE
# bin/internal/content_aware_hash.ps1: script for calculating the hash on windows
# bin/internal/content_aware_hash.sh: script for calculating the hash on mac/linux
# .github/workflows/content-aware-hash.yml: github action for CI/CD hashing
git -C "$FLUTTER_ROOT" ls-tree --format "%(objectname) %(path)" HEAD DEPS engine bin/internal/release-candidate-branch.version | git hash-object --stdin
TRACKEDFILES="DEPS engine bin/internal/release-candidate-branch.version"
BASEREF="HEAD"
set +e
# We will fallback to origin/master if upstream is not detected.
git -C "$FLUTTER_ROOT" remote get-url upstream >/dev/null 2>&1
exit_code=$?
set -e
if [[ $exit_code -eq 0 ]]; then
MERGEBASE=$(git -C "$FLUTTER_ROOT" merge-base HEAD upstream/master)
else
MERGEBASE=$(git -C "$FLUTTER_ROOT" merge-base HEAD origin/master)
fi
# Check to see if we're in a local development branch and the branch has any
# changes to engine code - including non-committed changes.
if [ "$(git -C "$FLUTTER_ROOT" rev-parse --abbrev-ref HEAD)" != "master" ] && \
! git -C "$FLUTTER_ROOT" diff --quiet "$MERGEBASE" -- $TRACKEDFILES; then
BASEREF="$MERGEBASE"
fi
git -C "$FLUTTER_ROOT" ls-tree --format "%(objectname) %(path)" $BASEREF -- $TRACKEDFILES | git hash-object --stdin

View File

@ -114,8 +114,21 @@ void main() {
return run(executable, args);
}
/// Sets up and fetches a [remote] (such as `upstream` or `origin`) for [testRoot.root].
///
/// The remote points at itself (`testRoot.root.path`) for ease of testing.
void setupRemote({required String remote, String? rootPath}) {
run('git', <String>[
'remote',
'add',
remote,
rootPath ?? testRoot.root.path,
], workingPath: rootPath);
run('git', <String>['fetch', remote], workingPath: rootPath);
}
/// Initializes a blank git repo in [testRoot.root].
void initGitRepoWithBlankInitialCommit({String? workingPath}) {
void initGitRepoWithBlankInitialCommit({String? workingPath, String remote = 'upstream'}) {
run('git', <String>['init', '--initial-branch', 'master'], workingPath: workingPath);
// autocrlf is very important for tests to work on windows.
run('git', 'config --local core.autocrlf true'.split(' '), workingPath: workingPath);
@ -133,6 +146,8 @@ void main() {
'-m',
'Initial commit',
], workingPath: workingPath);
setupRemote(remote: remote, rootPath: workingPath);
}
void writeFileAndCommit(File file, String contents) {
@ -141,11 +156,58 @@ void main() {
run('git', <String>['commit', '--all', '-m', 'changed ${file.basename} to $contents']);
}
void gitSwitchBranch(String branch, {bool create = true}) {
run('git', <String>['switch', if (create) '-c', branch]);
}
test('generates a hash', () async {
initGitRepoWithBlankInitialCommit();
expect(runContentAwareHash(), processStdout('3bbeb6a394378478683ece4f8e8663c42f8dc814'));
});
test('generates a hash for origin', () {
initGitRepoWithBlankInitialCommit(remote: 'origin');
expect(runContentAwareHash(), processStdout('3bbeb6a394378478683ece4f8e8663c42f8dc814'));
});
group('ignores local engine for', () {
test('upstream', () {
initGitRepoWithBlankInitialCommit();
gitSwitchBranch('engineTest');
testRoot.deps.writeAsStringSync('deps changed');
expect(
runContentAwareHash(),
processStdout('3bbeb6a394378478683ece4f8e8663c42f8dc814'),
reason: 'content hash from master for non-committed file',
);
writeFileAndCommit(testRoot.deps, 'deps changed');
expect(
runContentAwareHash(),
processStdout('3bbeb6a394378478683ece4f8e8663c42f8dc814'),
reason: 'content hash from master for committed file',
);
});
test('origin', () {
initGitRepoWithBlankInitialCommit(remote: 'origin');
gitSwitchBranch('engineTest');
testRoot.deps.writeAsStringSync('deps changed');
expect(
runContentAwareHash(),
processStdout('3bbeb6a394378478683ece4f8e8663c42f8dc814'),
reason: 'content hash from master for non-committed file',
);
writeFileAndCommit(testRoot.deps, 'deps changed');
expect(
runContentAwareHash(),
processStdout('3bbeb6a394378478683ece4f8e8663c42f8dc814'),
reason: 'content hash from master for committed file',
);
});
});
group('generates a different hash when', () {
setUp(() {
initGitRepoWithBlankInitialCommit();