ci: Ensure bundle branches post-sync (#36594)

This commit is contained in:
Matsu
2026-08-19 11:03:43 +00:00
committed by GitHub
parent 65e370e5aa
commit 4a3cb8c66a
3 changed files with 95 additions and 0 deletions
+22
View File
@@ -60,6 +60,19 @@ export function annotation(title, message) {
return `::error title=${title}::${message.replace(/\n/g, '%0A')}`;
}
function createBundleBranch({ git, log, bundle, base, baseSha, remote }) {
log(`${bundle} does not exist yet; creating it at ${base} ${baseSha}.`);
git(['checkout', '--force', '-B', bundle, baseSha]);
const push = attempt(git, ['push', remote, `HEAD:refs/heads/${bundle}`]);
if (!push.ok) {
if (push.out) log(push.out);
return { status: 'rejected' };
}
log(`Created ${bundle} at ${base} ${baseSha}.`);
return { status: 'created' };
}
/**
* One fetch-merge-push cycle. `status: 'rejected'` means the branch moved under us — the
* caller re-runs from a fresh fetch rather than forcing anything.
@@ -69,6 +82,15 @@ function mergeBaseIntoBundle({ git, log, bundle, base, remote }) {
// refs, so FETCH_HEAD is the only handle. Base first: the second fetch overwrites it.
git(['fetch', remote, base]);
const baseSha = git(['rev-parse', 'FETCH_HEAD']);
const listed = attempt(git, ['ls-remote', '--heads', remote, `refs/heads/${bundle}`]);
if (!listed.ok) {
throw new Error(`Could not check whether ${bundle} exists on the remote:\n${listed.out}`);
}
if (!listed.out) {
return createBundleBranch({ git, log, bundle, base, baseSha, remote });
}
git(['fetch', remote, bundle]);
git(['checkout', '--force', '-B', bundle, 'FETCH_HEAD']);
const preHead = git(['rev-parse', 'HEAD']);
@@ -50,6 +50,7 @@ const conflictedMergeTree = (...paths) =>
// merge tree is computable, and the merge lands on exactly that tree. git grep exits
// non-zero => no markers.
const baseGitRoutes = [
[(a) => a[0] === 'ls-remote', `${PRE_HEAD}\trefs/heads/bundle/2.x`],
[(a) => a[0] === 'rev-parse' && a[1] === 'FETCH_HEAD', BASE],
[(a) => a[0] === 'rev-parse' && a[1] === 'HEAD', PRE_HEAD],
[(a) => a[0] === 'merge-base', fail()],
@@ -188,6 +189,56 @@ test('a branch that keeps moving fails instead of forcing', () => {
});
});
test('a bundle branch that does not exist yet is created at its base', () => {
const git = makeStub([[(a) => a[0] === 'ls-remote', ''], ...baseGitRoutes]);
const result = syncBundleBranch({ git, env, log: silent });
assert.deepEqual(result, { status: 'created' });
assert.deepEqual(
git.calls.find((a) => a[0] === 'checkout'),
['checkout', '--force', '-B', 'bundle/2.x', BASE],
);
assert.equal(git.calls.some(isMerge), false);
assert.deepEqual(
git.calls.filter((a) => a[0] === 'fetch'),
[['fetch', REMOTE, 'master']],
);
assert.deepEqual(git.calls.find(isPush), ['push', REMOTE, 'HEAD:refs/heads/bundle/2.x']);
});
test('a remote that cannot be listed fails instead of re-creating the branch', () => {
const git = makeStub([[(a) => a[0] === 'ls-remote', fail()], ...baseGitRoutes]);
assert.throws(() => syncBundleBranch({ git, env, log: silent }), {
message: /Could not check whether bundle\/2\.x exists on the remote/,
});
assert.equal(git.calls.some(isPush), false);
});
test('a branch created under us mid-run is merged into on the retry', () => {
let lsRemotes = 0;
let pushes = 0;
const git = makeStub([
[
(a) => a[0] === 'ls-remote',
() => (++lsRemotes === 1 ? '' : `${PRE_HEAD}\trefs/heads/bundle/2.x`),
],
[
isPush,
() => {
if (++pushes === 1) throw Object.assign(new Error('rejected'), { status: 1 });
return '';
},
],
...baseGitRoutes,
]);
const result = syncBundleBranch({ git, env, log: silent });
assert.deepEqual(result, { status: 'merged' });
assert.equal(pushes, 2);
assert.equal(git.calls.filter(isMerge).length, 1);
});
test('annotation keeps a multi-line message on one line', () => {
assert.equal(annotation('t', 'a\nb'), '::error title=t::a%0Ab');
});
@@ -27,6 +27,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- name: Generate App Token
@@ -92,3 +93,24 @@ jobs:
git reset --hard public-1.x
git push origin 1.x --force-with-lease
- name: Re-create missing bundle branches
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
EXISTING=$(git ls-remote --heads origin 'refs/heads/bundle/*')
MISSING=()
for BRANCH in bundle/2.x bundle/1.x; do
if ! grep -qF "refs/heads/$BRANCH" <<<"$EXISTING"; then
MISSING+=("$BRANCH")
fi
done
if [ ${#MISSING[@]} -eq 0 ]; then
echo 'Both bundle branches exist; nothing to dispatch.'
exit 0
fi
echo "Missing: ${MISSING[*]} - dispatching Security: Sync Bundle Branches"
gh workflow run sec-sync-bundle-branches.yml --ref master