mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-19 01:45:48 +08:00
ci: Ensure bundle branches post-sync (#36594)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user