mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-29 18:00:59 +08:00
ci: Introduce backport workflow and compute-backport-targets script (#26518)
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
// Creates backport PR's according to labels on merged PR
|
||||
|
||||
import { readPrLabels, resolveRcBranchForTrack, writeGithubOutput } from './github-helpers.mjs';
|
||||
|
||||
/** @type { Record<string, import('./github-helpers.mjs').ReleaseTrack> } */
|
||||
const BACKPORT_BY_TAG_MAP = {
|
||||
'Backport to Beta': 'beta',
|
||||
'Backport to Stable': 'stable',
|
||||
};
|
||||
|
||||
const BACKPORT_BY_BRANCH_MAP = {
|
||||
'Backport to v1': '1.x',
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Set<string>} labels
|
||||
*
|
||||
* @returns { Set<string> }
|
||||
*/
|
||||
export function labelsToReleaseCandidateBranches(labels) {
|
||||
const targets = new Set();
|
||||
|
||||
// Backport by tag map includes mapping of label to git tag to resolve
|
||||
for (const [label, tag] of Object.entries(BACKPORT_BY_TAG_MAP)) {
|
||||
// Check if backport label is present
|
||||
if (!labels.has(label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const branch = resolveRcBranchForTrack(tag);
|
||||
// Make sure our backport branch exists
|
||||
if (!branch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targets.add(branch);
|
||||
}
|
||||
|
||||
// Backport by branch map includes mapping of label to git branch. This is used for
|
||||
// older versions of n8n. v1, etc.
|
||||
for (const [label, branch] of Object.entries(BACKPORT_BY_BRANCH_MAP)) {
|
||||
// Check if backport label is present
|
||||
if (!labels.has(label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targets.add(branch);
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const labels = new Set(readPrLabels());
|
||||
if (!labels || labels.size === 0) {
|
||||
console.log('No labels on PR. Exiting...');
|
||||
return;
|
||||
}
|
||||
|
||||
const backportBranches = labelsToReleaseCandidateBranches(labels);
|
||||
|
||||
if (backportBranches.size === 0) {
|
||||
console.log('No backports needed. Exiting...');
|
||||
return;
|
||||
}
|
||||
|
||||
const target_branches = [...backportBranches].join(' '); // korthout/backport-action@v4 uses space-delimited branch list
|
||||
writeGithubOutput({ target_branches });
|
||||
}
|
||||
|
||||
// only run when executed directly, not when imported by tests
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
await main();
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { describe, it, mock, before } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
/**
|
||||
* Run these tests by running
|
||||
*
|
||||
* node --test --experimental-test-module-mocks ./.github/scripts/compute-backport-targets.test.mjs
|
||||
* */
|
||||
|
||||
// mock.module must be called before the module under test is imported,
|
||||
// because static imports are hoisted and resolve before any code runs.
|
||||
mock.module('./github-helpers.mjs', {
|
||||
namedExports: {
|
||||
ensureEnvVar: () => {}, // no-op
|
||||
readPrLabels: () => {}, // no-op
|
||||
resolveRcBranchForTrack: mockResolveRcBranchForTrack,
|
||||
writeGithubOutput: () => {}, //no-op
|
||||
},
|
||||
});
|
||||
|
||||
function mockResolveRcBranchForTrack(track) {
|
||||
switch (track) {
|
||||
case 'beta':
|
||||
return 'release-candidate/2.10.1';
|
||||
case 'stable':
|
||||
return 'release-candidate/2.9.4';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let labelsToReleaseCandidateBranches;
|
||||
before(async () => {
|
||||
({ labelsToReleaseCandidateBranches } = await import('./compute-backport-targets.mjs'));
|
||||
});
|
||||
|
||||
describe('Compute backport targets', () => {
|
||||
it('Finds backport branches for pointer tag labels', () => {
|
||||
const labels = new Set(['Backport to Beta', 'Backport to Stable']);
|
||||
/** @type { Set<string> } */
|
||||
const result = labelsToReleaseCandidateBranches(labels);
|
||||
|
||||
assert.equal(result.size, 2);
|
||||
assert.ok(result.has('release-candidate/2.10.1'));
|
||||
assert.ok(result.has('release-candidate/2.9.4'));
|
||||
});
|
||||
|
||||
it("Doesn't parse other labes to backport branches", () => {
|
||||
const labels = new Set(['n8n team', 'release']);
|
||||
/** @type { Set<string> } */
|
||||
const result = labelsToReleaseCandidateBranches(labels);
|
||||
|
||||
assert.equal(result.size, 0);
|
||||
});
|
||||
|
||||
it("Doesn't parse malformed backport labels", () => {
|
||||
const labels = new Set(['Backport to Fork', 'Backport to my Home']);
|
||||
/** @type { Set<string> } */
|
||||
const result = labelsToReleaseCandidateBranches(labels);
|
||||
|
||||
assert.equal(result.size, 0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user