From 32b178a8cf991a268e3c1b320ea40b6f58f5478c Mon Sep 17 00:00:00 2001 From: discord9 Date: Mon, 27 Jul 2026 19:05:41 +0800 Subject: [PATCH] ci: keep prereleases off stable release channels (#8653) * ci: keep prereleases off stable release channels Signed-off-by: discord9 * ci: run release version tests with uv Signed-off-by: discord9 --------- Signed-off-by: discord9 --- .github/scripts/check-version-test.py | 150 ++++++++++++++++++++++++++ .github/scripts/check-version.sh | 10 ++ .github/workflows/develop.yml | 8 +- .github/workflows/release.yml | 12 ++- 4 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 .github/scripts/check-version-test.py diff --git a/.github/scripts/check-version-test.py b/.github/scripts/check-version-test.py new file mode 100644 index 0000000000..f20e4198f4 --- /dev/null +++ b/.github/scripts/check-version-test.py @@ -0,0 +1,150 @@ +# 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 json +import os +import stat +import subprocess +import sys +import tempfile +import textwrap +import unittest +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +CHECK_VERSION_SCRIPT = SCRIPT_DIR / "check-version.sh" + + +def write_python_executable(path: Path, source: str) -> None: + path.write_text( + f"#!{sys.executable}\n{textwrap.dedent(source)}", + encoding="utf-8", + ) + path.chmod(path.stat().st_mode | stat.S_IXUSR) + + +class CheckVersionTest(unittest.TestCase): + def run_check_version( + self, current_version: str, latest_version: str + ) -> tuple[subprocess.CompletedProcess[str], dict[str, str]]: + with tempfile.TemporaryDirectory(prefix="check-version-test-") as temp_dir: + temp_path = Path(temp_dir) + mock_bin = temp_path / "bin" + mock_bin.mkdir() + output_path = temp_path / "github-output" + output_path.touch() + + write_python_executable( + mock_bin / "curl", + """ + import os + import sys + + sys.stdout.write(os.environ["MOCK_CURL_RESPONSE"]) + """, + ) + write_python_executable( + mock_bin / "jq", + """ + import json + import sys + + payload = json.load(sys.stdin) + query = sys.argv[-1] + if query == ".message": + value = payload.get("message") + elif query == ".tag_name": + value = payload.get("tag_name") + else: + raise SystemExit(f"unsupported jq query: {query}") + print("null" if value is None else value) + """, + ) + + environment = os.environ.copy() + environment.update( + { + "GITHUB_OUTPUT": str(output_path), + "MOCK_CURL_RESPONSE": json.dumps({"tag_name": latest_version}), + "PATH": f"{mock_bin}{os.pathsep}{environment['PATH']}", + } + ) + completed = subprocess.run( + [str(CHECK_VERSION_SCRIPT), current_version], + cwd=SCRIPT_DIR.parent.parent, + env=environment, + capture_output=True, + text=True, + check=False, + ) + outputs = dict( + line.split("=", 1) + for line in output_path.read_text(encoding="utf-8").splitlines() + if line + ) + + return completed, outputs + + def assert_version_outputs( + self, + name: str, + current_version: str, + latest_version: str, + expected_stable: str, + expected_latest: str, + ) -> None: + completed, outputs = self.run_check_version(current_version, latest_version) + failure_details = ( + f"{name}: current={current_version}, latest={latest_version}, " + f"returncode={completed.returncode}, stdout={completed.stdout!r}, " + f"stderr={completed.stderr!r}, outputs={outputs!r}" + ) + self.assertEqual(completed.returncode, 0, failure_details) + self.assertEqual( + outputs, + { + "is-current-version-stable": expected_stable, + "is-current-version-latest": expected_latest, + }, + failure_details, + ) + + def test_version_classification(self) -> None: + cases = [ + ("stable-newer", "v1.2.4", "v1.2.3", "true", "true"), + ("stable-older", "v1.2.2", "v1.2.3", "true", "false"), + ("beta-newer-base", "v1.2.4-beta.1", "v1.2.3", "false", "true"), + ("beta-against-same-stable", "v1.2.3-beta.1", "v1.2.3", "false", "false"), + ("rc", "v1.2.4-rc.1", "v1.2.3", "false", "true"), + ("nightly", "v1.2.4-nightly-20250101", "v1.2.3", "false", "true"), + ("build-suffix", "v1.2.4-build.1", "v1.2.3", "false", "true"), + ("invalid-version", "v1.2", "v1.2.3", "false", "false"), + ] + + for case in cases: + with self.subTest(name=case[0]): + self.assert_version_outputs(*case) + + def test_empty_input_fails_without_outputs(self) -> None: + completed, outputs = self.run_check_version("", "v1.2.3") + failure_details = ( + f"empty-input: returncode={completed.returncode}, stdout={completed.stdout!r}, " + f"stderr={completed.stderr!r}, outputs={outputs!r}" + ) + self.assertNotEqual(completed.returncode, 0, failure_details) + self.assertEqual(outputs, {}, failure_details) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/.github/scripts/check-version.sh b/.github/scripts/check-version.sh index 1efa3bb4db..7fdca23ca8 100755 --- a/.github/scripts/check-version.sh +++ b/.github/scripts/check-version.sh @@ -7,6 +7,16 @@ if [ -z "$CURRENT_VERSION" ]; then exit 1 fi +if [[ "$CURRENT_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + IS_CURRENT_VERSION_STABLE=true +else + IS_CURRENT_VERSION_STABLE=false +fi + +if [ -n "$GITHUB_OUTPUT" ]; then + echo "is-current-version-stable=$IS_CURRENT_VERSION_STABLE" >> "$GITHUB_OUTPUT" +fi + # Get the latest version from GitHub Releases API_RESPONSE=$(curl -s "https://api.github.com/repos/GreptimeTeam/greptimedb/releases/latest") diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index a161a0d496..d32583e0b9 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -63,8 +63,14 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 24 - - name: Run GitHub script tests + - name: Run Node GitHub script tests run: node --test .github/scripts/query-regression-comment.test.cjs + - name: Setup uv + uses: astral-sh/setup-uv@v3 + with: + version: "latest" + - name: Run check-version script tests + run: uv run --no-project python .github/scripts/check-version-test.py check: if: ${{ github.repository == 'GreptimeTeam/greptimedb' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 375521f36c..ee1754dd6b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -119,6 +119,7 @@ jobs: # The 'is-current-version-latest' determines whether to update 'latest' Docker tags and downstream repositories. is-current-version-latest: ${{ steps.check-version.outputs.is-current-version-latest }} + is-current-version-stable: ${{ steps.check-version.outputs.is-current-version-stable }} steps: - name: Checkout uses: actions/checkout@v4 @@ -504,7 +505,7 @@ jobs: image-registry-username: ${{ secrets.DOCKERHUB_USERNAME }} image-registry-password: ${{ secrets.DOCKERHUB_TOKEN }} version: ${{ needs.allocate-runners.outputs.version }} - push-latest-tag: ${{ needs.allocate-runners.outputs.is-current-version-latest == 'true' && github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' }} + push-latest-tag: ${{ needs.allocate-runners.outputs.is-current-version-latest == 'true' && needs.allocate-runners.outputs.is-current-version-stable == 'true' && github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' }} - name: Set build image result id: set-build-image-result @@ -550,7 +551,7 @@ jobs: dev-mode: false upload-to-s3: true update-version-info: true - push-latest-tag: ${{ needs.allocate-runners.outputs.is-current-version-latest == 'true' && github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' }} + push-latest-tag: ${{ needs.allocate-runners.outputs.is-current-version-latest == 'true' && needs.allocate-runners.outputs.is-current-version-stable == 'true' && github.ref_type == 'tag' && !contains(github.ref_name, 'nightly') && github.event_name != 'schedule' }} publish-github-release: name: Create GitHub release and upload artifacts @@ -573,6 +574,7 @@ jobs: (github.event_name == 'workflow_dispatch' && inputs.release_images == false && needs.release-images-to-dockerhub.result == 'skipped')) && (github.event_name != 'workflow_dispatch' || github.ref_type != 'tag' || + needs.allocate-runners.outputs.is-current-version-stable != 'true' || needs.allocate-runners.outputs.is-current-version-latest == 'true') needs: [ # The job have to wait for all the artifacts are built. allocate-runners, @@ -654,7 +656,7 @@ jobs: bump-downstream-repo-versions: name: Bump downstream repo versions - if: ${{ github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref_type == 'tag') }} + 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') }} needs: [allocate-runners, publish-github-release] runs-on: ubuntu-latest # Permission reference: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs @@ -680,7 +682,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-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: @@ -701,7 +703,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-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: