From 25d49ba092b3a8a997ffded5332ba50d75e69a7f Mon Sep 17 00:00:00 2001 From: Weny Xu Date: Thu, 3 Sep 2026 06:09:57 +0000 Subject: [PATCH] ci: trigger downstream updates for prereleases (#9008) * ci: trigger downstream updates for prereleases Signed-off-by: WenyXu * test: cover docs prerelease dispatch Signed-off-by: WenyXu --------- Signed-off-by: WenyXu --- .github/workflows/release.yml | 6 +-- cyborg/bin/bump-versions.ts | 72 +++++++++++++++------------------ cyborg/package.json | 3 ++ cyborg/src/docs-version.test.ts | 49 ++++++++++++++++++++++ cyborg/src/docs-version.ts | 49 ++++++++++++++++++++++ 5 files changed, 137 insertions(+), 42 deletions(-) create mode 100644 cyborg/src/docs-version.test.ts create mode 100644 cyborg/src/docs-version.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a9c39c903..122c349d8a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -715,7 +715,7 @@ jobs: bump-downstream-repo-versions: name: Bump downstream repo versions - if: ${{ github.event_name == 'schedule' || ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref_type == 'tag' && needs.allocate-runners.outputs.is-current-version-stable == 'true') }} + if: ${{ github.event_name == 'schedule' || ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref_type == 'tag' && !contains(github.ref_name, 'nightly')) }} needs: [allocate-runners, publish-github-release] runs-on: ubuntu-latest # Permission reference: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs @@ -741,7 +741,7 @@ jobs: bump-helm-charts-version: name: Bump helm charts version - if: ${{ github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' && needs.allocate-runners.outputs.is-current-version-stable == 'true' && needs.allocate-runners.outputs.is-current-version-latest == 'true' }} + if: ${{ github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' && (needs.allocate-runners.outputs.is-current-version-stable != 'true' || needs.allocate-runners.outputs.is-current-version-latest == 'true') }} needs: [allocate-runners, publish-github-release] runs-on: ubuntu-latest permissions: @@ -762,7 +762,7 @@ jobs: bump-homebrew-greptime-version: name: Bump homebrew greptime version - if: ${{ github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' && needs.allocate-runners.outputs.is-current-version-stable == 'true' && needs.allocate-runners.outputs.is-current-version-latest == 'true' }} + if: ${{ github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' && (needs.allocate-runners.outputs.is-current-version-stable != 'true' || needs.allocate-runners.outputs.is-current-version-latest == 'true') }} needs: [allocate-runners, publish-github-release] runs-on: ubuntu-latest permissions: diff --git a/cyborg/bin/bump-versions.ts b/cyborg/bin/bump-versions.ts index eec77ad3a3..53fe0e557b 100644 --- a/cyborg/bin/bump-versions.ts +++ b/cyborg/bin/bump-versions.ts @@ -16,11 +16,12 @@ import * as core from "@actions/core"; import {obtainClient} from "@/common"; +import {docsWorkflowDispatch, WorkflowDispatch} from "@/docs-version"; interface RepoConfig { tokenEnv: string; repo: string; - workflowLogic: (version: string) => [string, string] | null; + workflowLogic: (version: string) => WorkflowDispatch | null | Promise; } const REPO_CONFIGS: Record = { @@ -33,7 +34,10 @@ const REPO_CONFIGS: Record = { console.log('Nightly version detected for website, skipping workflow trigger.'); return null; } - return ['bump-patch-version.yml', version]; + return { + workflowId: 'bump-patch-version.yml', + inputs: {version}, + }; } }, demo: { @@ -45,62 +49,53 @@ const REPO_CONFIGS: Record = { console.log('Nightly version detected for demo, skipping workflow trigger.'); return null; } - return ['bump-patch-version.yml', version]; + return { + workflowId: 'bump-patch-version.yml', + inputs: {version}, + }; } }, docs: { tokenEnv: "DOCS_REPO_TOKEN", repo: "docs", - workflowLogic: (version: string) => { - // Check if it's a nightly version + workflowLogic: async (version: string): Promise => { + // Nightly versions do not need the current docs version list. if (version.includes('nightly')) { - return ['bump-nightly-version.yml', version]; + return docsWorkflowDispatch(version, []); } - // Check for prerelease versions (e.g., 1.0.0-beta.3, 1.0.0-rc.1) - const prereleaseMatch = version.match(/^(\d+)\.(\d+)\.(\d+)-(beta|rc)\.(\d+)$/); - if (prereleaseMatch) { - const [, major, minor, patch, prereleaseType, prereleaseNum] = prereleaseMatch; - - // If it's beta.1 and patch version is 0, treat as major version - if (prereleaseType === 'beta' && prereleaseNum === '1' && patch === '0') { - return ['bump-version.yml', `${major}.${minor}`]; - } - - // Otherwise (beta.x where x > 1, or rc.x), treat as patch version - return ['bump-patch-version.yml', version]; + const client = obtainClient('DOCS_REPO_TOKEN'); + const {data} = await client.rest.repos.getContent({ + owner: 'GreptimeTeam', + repo: 'docs', + path: 'versions.json', + ref: 'main', + }); + if (Array.isArray(data) || data.type !== 'file') { + throw new Error('Expected docs versions.json to be a file'); } - const parts = version.split('.'); - if (parts.length !== 3) { - throw new Error('Invalid version format'); + const versions = JSON.parse(Buffer.from(data.content, 'base64').toString('utf-8')); + if (!Array.isArray(versions) || !versions.every((entry) => typeof entry === 'string')) { + throw new Error('Expected docs versions.json to be a string array'); } - // If patch version (last number) is 0, it's a major version - // Return only major.minor version - if (parts[2] === '0') { - return ['bump-version.yml', `${parts[0]}.${parts[1]}`]; - } - - // Otherwise it's a patch version, use full version - return ['bump-patch-version.yml', version]; + return docsWorkflowDispatch(version, versions); } } }; -async function triggerWorkflow(repoConfig: RepoConfig, workflowId: string, version: string) { +async function triggerWorkflow(repoConfig: RepoConfig, dispatch: WorkflowDispatch) { const client = obtainClient(repoConfig.tokenEnv); try { await client.rest.actions.createWorkflowDispatch({ owner: "GreptimeTeam", repo: repoConfig.repo, - workflow_id: workflowId, + workflow_id: dispatch.workflowId, ref: "main", - inputs: { - version, - }, + inputs: dispatch.inputs, }); - console.log(`Successfully triggered ${workflowId} workflow for ${repoConfig.repo} with version ${version}`); + console.log(`Successfully triggered ${dispatch.workflowId} workflow for ${repoConfig.repo} with version ${dispatch.inputs.version}`); } catch (error) { core.setFailed(`Failed to trigger workflow for ${repoConfig.repo}: ${error.message}`); throw error; @@ -114,14 +109,13 @@ async function processRepo(repoName: string, version: string) { } try { - const workflowResult = repoConfig.workflowLogic(version); - if (workflowResult === null) { + const dispatch = await repoConfig.workflowLogic(version); + if (dispatch === null) { // Skip this repo (e.g., nightly version for website) return; } - const [workflowId, apiVersion] = workflowResult; - await triggerWorkflow(repoConfig, workflowId, apiVersion); + await triggerWorkflow(repoConfig, dispatch); } catch (error) { core.setFailed(`Error processing ${repoName} with version ${version}: ${error.message}`); throw error; diff --git a/cyborg/package.json b/cyborg/package.json index 742c6f06bf..3332623874 100644 --- a/cyborg/package.json +++ b/cyborg/package.json @@ -4,6 +4,9 @@ "description": "Automator for GreptimeDB Repository Management", "private": true, "packageManager": "pnpm@8.15.5", + "scripts": { + "test": "tsx --test src/*.test.ts" + }, "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", diff --git a/cyborg/src/docs-version.test.ts b/cyborg/src/docs-version.test.ts new file mode 100644 index 0000000000..86a53204c5 --- /dev/null +++ b/cyborg/src/docs-version.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Greptime Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import {docsWorkflowDispatch} from './docs-version'; + +test('cuts a missing documentation line for stable and prerelease versions', () => { + for (const version of ['1.3.0', '1.3.0-alpha.1', '1.3.0-beta.2', '1.3.0-rc.1', '1.3.0-alpha.1+build.7']) { + assert.deepEqual(docsWorkflowDispatch(version, ['1.2']), { + workflowId: 'bump-version.yml', + inputs: {version: '1.3'}, + }); + } +}); + +test('bumps the exact version when its documentation line exists', () => { + assert.deepEqual(docsWorkflowDispatch('1.3.0-beta.2', ['1.3']), { + workflowId: 'bump-patch-version.yml', + inputs: {version: '1.3.0-beta.2'}, + }); +}); + +test('keeps nightly versions on the nightly workflow', () => { + assert.deepEqual(docsWorkflowDispatch('1.3.0-nightly-20260903', []), { + workflowId: 'bump-nightly-version.yml', + inputs: {version: '1.3.0-nightly-20260903'}, + }); +}); + +test('rejects malformed non-nightly versions', () => { + for (const version of ['1.3', 'v1.3.0', '1.3.0-', '1.3.0-alpha..1']) { + assert.throws(() => docsWorkflowDispatch(version, []), /Invalid version format/); + } +}); diff --git a/cyborg/src/docs-version.ts b/cyborg/src/docs-version.ts new file mode 100644 index 0000000000..d90f22a315 --- /dev/null +++ b/cyborg/src/docs-version.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Greptime Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface WorkflowDispatch { + workflowId: string; + inputs: Record; +} + +const DOCS_VERSION_RE = /^(\d+)\.(\d+)\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/; + +export function docsWorkflowDispatch(version: string, versions: string[]): WorkflowDispatch { + if (version.includes('nightly')) { + return { + workflowId: 'bump-nightly-version.yml', + inputs: {version}, + }; + } + + const match = version.match(DOCS_VERSION_RE); + if (!match) { + throw new Error('Invalid version format'); + } + + const docsVersion = `${match[1]}.${match[2]}`; + if (versions.includes(docsVersion)) { + return { + workflowId: 'bump-patch-version.yml', + inputs: {version}, + }; + } + + return { + workflowId: 'bump-version.yml', + inputs: {version: docsVersion}, + }; +}