From adb9b26cb10ffd955d7af2a452f762bb711d025d Mon Sep 17 00:00:00 2001 From: Matsu Date: Tue, 10 Mar 2026 15:24:06 +0200 Subject: [PATCH] ci: Call Cloud DB populate workflow from actions (#26808) --- .../determine-release-version-changes.mjs | 46 ++++++++++++ ...determine-release-version-changes.test.mjs | 47 ++++++++++++ .github/scripts/determine-version-info.mjs | 5 +- .../scripts/determine-version-info.test.mjs | 5 ++ .github/scripts/github-helpers.mjs | 2 +- .github/scripts/populate-cloud-databases.mjs | 32 +++++++++ .../release-populate-cloud-with-releases.yml | 72 +++++++++++++++++++ .../release-publish-post-release.yml | 13 ++++ .github/workflows/release-publish.yml | 2 + .gitignore | 1 + 10 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 .github/scripts/determine-release-version-changes.mjs create mode 100644 .github/scripts/determine-release-version-changes.test.mjs create mode 100644 .github/scripts/populate-cloud-databases.mjs create mode 100644 .github/workflows/release-populate-cloud-with-releases.yml diff --git a/.github/scripts/determine-release-version-changes.mjs b/.github/scripts/determine-release-version-changes.mjs new file mode 100644 index 00000000000..cb2b40bacd7 --- /dev/null +++ b/.github/scripts/determine-release-version-changes.mjs @@ -0,0 +1,46 @@ +import { ensureEnvVar, sh, writeGithubOutput } from './github-helpers.mjs'; + +function determineReleaseVersionChanges() { + const previousVersion = ensureEnvVar('PREVIOUS_VERSION_TAG'); + const releaseVersion = ensureEnvVar('RELEASE_VERSION_TAG'); + + const log = sh('git', [ + '--no-pager', + 'log', + '--format="%s (%h)"', + `${previousVersion}..${releaseVersion}`, + ]); + + writeGithubOutput({ + has_node_enhancements: hasNodeEnhancements(log), + has_core_changes: hasCoreChanges(log), + }); +} + +/** + * Matches commit messages with + * + * fix(nodes) + * fix(xyz Node) + * feat(nodes) + * feat(xyz Node) + * + * @param {string} log + */ +export function hasNodeEnhancements(log) { + return /(fix|feat)\((.*Node|nodes)\)/.test(log); +} + +/** + * Matches commit messages with feat(core) or feat(editor) + * + * @param {string} log + */ +export function hasCoreChanges(log) { + return /feat\((core|editor)\)/.test(log); +} + +// only run when executed directly, not when imported by tests +if (import.meta.url === `file://${process.argv[1]}`) { + determineReleaseVersionChanges(); +} diff --git a/.github/scripts/determine-release-version-changes.test.mjs b/.github/scripts/determine-release-version-changes.test.mjs new file mode 100644 index 00000000000..5a1cbb5eb0e --- /dev/null +++ b/.github/scripts/determine-release-version-changes.test.mjs @@ -0,0 +1,47 @@ +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/determine-release-version-changes.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 + sh: () => {}, // no-op + writeGithubOutput: () => {}, // no-op + }, +}); + +let hasNodeEnhancements, hasCoreChanges; +before(async () => { + ({ hasNodeEnhancements, hasCoreChanges } = await import( + './determine-release-version-changes.mjs' + )); +}); + +describe('Determine release version changes', () => { + it('Matches nodes feature', () => { + assert.ok(hasNodeEnhancements('feat(nodes): Added a utility for node')); + }); + it('Matches nodes fix', () => { + assert.ok(hasNodeEnhancements('fix(nodes): Fix said utility')); + }); + it('Matches named node feature', () => { + assert.ok(hasNodeEnhancements('feat(Github Actions Node): Add ability to call webhooks')); + }); + it('Matches named node fix', () => { + assert.ok(hasNodeEnhancements('fix(OpenAI Node): Allow credentials to pass through')); + }); + + it('Matches core changes', () => { + assert.ok(hasCoreChanges('feat(core): Add cli flag')); + }); + it('Matches editor changes', () => { + assert.ok(hasCoreChanges('feat(editor): Add button')); + }); +}); diff --git a/.github/scripts/determine-version-info.mjs b/.github/scripts/determine-version-info.mjs index bd73e8cffa1..19852bf67f8 100644 --- a/.github/scripts/determine-version-info.mjs +++ b/.github/scripts/determine-version-info.mjs @@ -56,7 +56,10 @@ export function determineTrack(packageVersion) { tag: /** @type {import('./github-helpers.mjs').ReleaseVersion} */ (`n8n@${packageVersion}`), }); + const previousVersion = trackToReleaseMap[track]?.version; + const output = { + previous_version: previousVersion, version: packageVersion, track, bump, @@ -67,7 +70,7 @@ export function determineTrack(packageVersion) { writeGithubOutput(output); console.log( - `Determined track info: track=${track}, version=${packageVersion}, new_stable_version=${newStable}, release_type=${releaseType}, rc_branch=${rc_branch}`, + `Determined track info: track=${track}, version=${packageVersion}, previous_version=${previousVersion}, new_stable_version=${newStable}, release_type=${releaseType}, rc_branch=${rc_branch}`, ); return output; diff --git a/.github/scripts/determine-version-info.test.mjs b/.github/scripts/determine-version-info.test.mjs index 352e80f6615..9010f826039 100644 --- a/.github/scripts/determine-version-info.test.mjs +++ b/.github/scripts/determine-version-info.test.mjs @@ -37,6 +37,7 @@ describe('determine-tracks', () => { assert.equal(output.track, 'stable'); assert.equal(output.version, '2.9.3'); + assert.equal(output.previous_version, '2.9.2'); assert.equal(output.bump, 'patch'); assert.equal(output.new_stable_version, null); assert.equal(output.release_type, 'stable'); @@ -48,6 +49,7 @@ describe('determine-tracks', () => { assert.equal(output.track, 'beta'); assert.equal(output.version, '2.10.2'); + assert.equal(output.previous_version, '2.10.1'); assert.equal(output.bump, 'patch'); assert.equal(output.new_stable_version, null); assert.equal(output.release_type, 'stable'); @@ -60,6 +62,7 @@ describe('determine-tracks', () => { assert.equal(output.track, 'stable'); assert.equal(output.version, '2.9.4'); + assert.equal(output.previous_version, '2.9.2'); assert.equal(output.bump, 'patch'); assert.equal(output.new_stable_version, null); assert.equal(output.release_type, 'stable'); @@ -81,6 +84,7 @@ describe('determine-tracks', () => { assert.equal(output.track, 'beta'); assert.equal(output.version, '2.11.0'); + assert.equal(output.previous_version, '2.10.1'); assert.equal(output.bump, 'minor'); assert.equal(output.new_stable_version, '2.10.1'); assert.equal(output.release_type, 'stable'); @@ -92,6 +96,7 @@ describe('determine-tracks', () => { assert.equal(output.track, 'beta'); assert.equal(output.version, '2.10.2-rc.1'); + assert.equal(output.previous_version, '2.10.1'); assert.equal(output.bump, 'patch'); assert.equal(output.new_stable_version, null); assert.equal(output.release_type, 'rc'); diff --git a/.github/scripts/github-helpers.mjs b/.github/scripts/github-helpers.mjs index f9f55111d45..6359d494860 100644 --- a/.github/scripts/github-helpers.mjs +++ b/.github/scripts/github-helpers.mjs @@ -205,7 +205,7 @@ export function trySh(cmd, args, opts = {}) { /** * Append outputs to GITHUB_OUTPUT if available. * - * @param {Record} obj + * @param {Record} obj */ export function writeGithubOutput(obj) { const path = process.env.GITHUB_OUTPUT; diff --git a/.github/scripts/populate-cloud-databases.mjs b/.github/scripts/populate-cloud-databases.mjs new file mode 100644 index 00000000000..0f8fe1db262 --- /dev/null +++ b/.github/scripts/populate-cloud-databases.mjs @@ -0,0 +1,32 @@ +import { ensureEnvVar } from './github-helpers.mjs'; + +async function populateCloudDatabases() { + const payload = ensureEnvVar('PAYLOAD'); + const webhookData = ensureEnvVar('N8N_POPULATE_CLOUD_WEBHOOK_DATA'); + + const { user, secret, url } = JSON.parse(webhookData); + + console.log('Payload: ', JSON.parse(payload)); + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Basic ' + Buffer.from(`${user}:${secret}`).toString('base64'), + }, + body: payload, + }); + + const status = response.status; + console.log('Webhook call returned status ' + status); + + if (status !== 200) { + const body = await response.text(); + throw new Error(`Webhook call failed:\n\n ${body}`); + } +} + +// only run when executed directly, not when imported by tests +if (import.meta.url === `file://${process.argv[1]}`) { + populateCloudDatabases(); +} diff --git a/.github/workflows/release-populate-cloud-with-releases.yml b/.github/workflows/release-populate-cloud-with-releases.yml new file mode 100644 index 00000000000..01d6a80fbbc --- /dev/null +++ b/.github/workflows/release-populate-cloud-with-releases.yml @@ -0,0 +1,72 @@ +name: 'Release: Populate cloud with releases' +run-name: 'Populate cloud with version n8n@${{ inputs.version }}' + +on: + workflow_dispatch: + inputs: + previous-version: + description: 'The previous release version (e.g. 2.10.2)' + required: true + type: string + version: + description: 'The release version (e.g. 2.11.0)' + required: true + type: string + experimental: + description: 'If publishing experimental version' + type: boolean + default: false + workflow_call: + inputs: + previous-version: + description: 'The previous release version (e.g. 2.10.2)' + required: true + type: string + version: + description: 'The release version (e.g. 2.11.0)' + required: true + type: string + experimental: + description: 'If publishing experimental version' + type: boolean + default: false + +jobs: + determine-changes: + runs-on: ubuntu-slim + environment: release + outputs: + has_node_enhancements: ${{ steps.get-changes.outputs.has_node_enhancements }} + has_core_changes: ${{ steps.get-changes.outputs.has_core_changes }} + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: ./.github/actions/setup-nodejs + with: + build-command: '' + install-command: npm install --prefix=.github/scripts --no-package-lock + + - name: Extract changes + id: get-changes + env: + PREVIOUS_VERSION_TAG: 'n8n@${{ inputs.previous-version }}' + RELEASE_VERSION_TAG: 'n8n@${{ inputs.version }}' + run: node ./.github/scripts/determine-release-version-changes.mjs + + - name: Populate databases + id: populate-databases + env: + N8N_POPULATE_CLOUD_WEBHOOK_DATA: ${{ secrets.N8N_POPULATE_CLOUD_WEBHOOK_DATA }} + PAYLOAD: | + { + "target_version_to_update": "${{ inputs.version }}", + "has_node_enhancements": ${{ steps.get-changes.outputs.has_node_enhancements }}, + "has_core_changes": ${{ steps.get-changes.outputs.has_core_changes }}, + "has_breaking_change": false, + "is_experimental": ${{ inputs.experimental }} + } + run: node ./.github/scripts/populate-cloud-databases.mjs diff --git a/.github/workflows/release-publish-post-release.yml b/.github/workflows/release-publish-post-release.yml index b14203b8b83..078fea0a265 100644 --- a/.github/workflows/release-publish-post-release.yml +++ b/.github/workflows/release-publish-post-release.yml @@ -7,6 +7,10 @@ on: description: 'Release track acquired from determine-version-info. (e.g. stable, beta)' required: true type: string + previous_version: + description: 'Previous release version acquired from determine-version-info. (e.g. 2.9.2, 1.123.22)' + required: true + type: string version: description: 'Release version acquired from determine-version-info. (e.g. 2.9.3, 1.123.23)' required: true @@ -51,3 +55,12 @@ jobs: inputs.release_type != 'rc' uses: ./.github/workflows/util-ensure-release-candidate-branches.yml secrets: inherit + + populate-cloud-with-releases: + name: 'Populate cloud database with releases' + uses: ./.github/workflows/release-populate-cloud-with-releases.yml + with: + previous-version: ${{ inputs.previous_version }} + version: ${{ inputs.version }} + experimental: ${{ inputs.release_type == 'rc' }} + secrets: inherit diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index a8c913860c9..f92b9ab6f34 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -14,6 +14,7 @@ jobs: if: github.event.pull_request.merged == true outputs: track: ${{ steps.determine-info.outputs.track }} + previous_version: ${{ steps.determine-info.outputs.previous_version }} version: ${{ steps.determine-info.outputs.version }} bump: ${{ steps.determine-info.outputs.bump }} new_stable_version: ${{ steps.determine-info.outputs.new_stable_version }} @@ -192,6 +193,7 @@ jobs: uses: ./.github/workflows/release-publish-post-release.yml with: track: ${{ needs.determine-version-info.outputs.track }} + previous_version: ${{ needs.determine-version-info.outputs.previous_version }} version: ${{ needs.determine-version-info.outputs.version }} bump: ${{ needs.determine-version-info.outputs.bump }} new_stable_version: ${{ needs.determine-version-info.outputs.new_stable_version }} diff --git a/.gitignore b/.gitignore index d72fd22b745..4c82ea493d3 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ compiled docker-build-manifest.json packages/cli/src/modules/my-feature .secrets +act-event.json packages/testing/**/.cursor/rules/ .venv .ruff_cache