From 71279877dfcb01af9e8d2ef44b1a8c9ab5584362 Mon Sep 17 00:00:00 2001 From: Zyy Date: Fri, 3 Jul 2026 10:25:44 +0800 Subject: [PATCH] ci: refine release workflow Co-authored-by: Codex --- .github/workflows/release.yml | 36 ++++++++++++++++++- docs/architecture.md | 4 +-- release/publish | 4 +-- release/version.yaml | 1 - .../codex-proxy-rs/references/repo-guide.md | 2 +- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff4b7490c..9ec217f88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,10 @@ permissions: contents: write packages: write +concurrency: + group: release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} + cancel-in-progress: false + env: REGISTRY: ghcr.io IMAGE_NAME: zyycn/codex-proxy-rs @@ -29,12 +33,14 @@ jobs: git_sha: ${{ steps.version.outputs.git_sha }} platform_matrix: ${{ steps.version.outputs.platform_matrix }} docker_platforms: ${{ steps.version.outputs.docker_platforms }} + previous_tag: ${{ steps.version.outputs.previous_tag }} steps: - name: Checkout uses: actions/checkout@v7 with: ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} + fetch-depth: 0 - name: Read release metadata id: version @@ -45,6 +51,7 @@ jobs: ruby <<'RUBY' >> "$GITHUB_OUTPUT" require "json" require "open3" + require "rubygems" require "time" require "yaml" @@ -62,13 +69,19 @@ jobs: value.strip end + def version_sort_key(version) + Gem::Version.new(version.split("+", 2).first) + rescue ArgumentError + nil + end + version_path = "release/version.yaml" platforms_path = "release/platforms.yaml" version_data = YAML.load_file(version_path) platforms_data = YAML.load_file(platforms_path) version = require_string(version_data, "version", version_path).delete_prefix("v") - tag_prefix = require_string(version_data, "tagPrefix", version_path) + tag_prefix = "v" expected_tag = "#{tag_prefix}#{version}" release_tag = ENV.fetch("RELEASE_TAG") fail("Release tag #{release_tag} does not match #{version_path} tag #{expected_tag}") unless release_tag == expected_tag @@ -101,6 +114,23 @@ jobs: git_sha, status = Open3.capture2("git", "rev-parse", "HEAD") fail("failed to resolve git sha") unless status.success? matrix = JSON.generate({ "include" => platforms }) + current_sort_key = version_sort_key(version) + tags, status = Open3.capture2("git", "tag", "--list", "#{tag_prefix}*", "--merged", "HEAD") + fail("failed to list git tags") unless status.success? + previous_tag = tags + .lines + .map(&:strip) + .reject(&:empty?) + .reject { |tag| tag == release_tag } + .filter_map do |tag| + tag_version = tag.delete_prefix(tag_prefix) + next unless tag_version.match?(SEMVER) + sort_key = version_sort_key(tag_version) + next unless sort_key && current_sort_key && sort_key < current_sort_key + [sort_key, tag] + end + .max_by(&:first) + &.last puts "tag=#{release_tag}" puts "version=#{version}" @@ -108,6 +138,7 @@ jobs: puts "git_sha=#{git_sha.strip}" puts "platform_matrix=#{matrix}" puts "docker_platforms=#{docker_platforms.join(",")}" + puts "previous_tag=#{previous_tag}" RUBY build-frontend: @@ -149,6 +180,7 @@ jobs: retention-days: 1 build-binary: + name: build-binary (${{ matrix.os }}-${{ matrix.arch }}) needs: [release-info, build-frontend] strategy: fail-fast: false @@ -416,6 +448,7 @@ jobs: with: tag_name: ${{ needs.release-info.outputs.tag }} generate_release_notes: true + previous_tag: ${{ needs.release-info.outputs.previous_tag }} body: | ## 📥 Installation @@ -432,5 +465,6 @@ jobs: - [GitHub Repository](https://github.com/${{ github.repository }}) - [Installation Guide](https://github.com/${{ github.repository }}/blob/main/deploy/README.md) + fail_on_unmatched_files: true files: | dist/release/* diff --git a/docs/architecture.md b/docs/architecture.md index 90be03148..53026d780 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -100,12 +100,12 @@ flowchart LR 版本和平台发布契约集中在 `release/`: -- `release/version.yaml`:产品版本和 tag 前缀。 +- `release/version.yaml`:产品版本,发布 tag 固定使用 `v` 前缀。 - `release/platforms.yaml`:Release asset 构建平台、归档格式和 Docker 平台。 日常发布只需要执行 `release/publish 0.1.4`。该命令会更新 `release/version.yaml`、生成带署名的版本提交、创建 `v0.1.4` tag,并 push 分支和 tag 触发发布 workflow。 -发布 workflow 会校验触发 tag 必须等于 `tagPrefix + version`。发布构建时通过 `CPR_VERSION`、`CPR_GIT_SHA`、`CPR_BUILD_TIME` 和 `CPR_BUILD_TYPE=release` 注入编译期元数据。 +发布 workflow 会校验触发 tag 必须等于 `v` + `version`。发布构建时通过 `CPR_VERSION`、`CPR_GIT_SHA`、`CPR_BUILD_TIME` 和 `CPR_BUILD_TYPE=release` 注入编译期元数据。 `backend/build/build.rs` 负责把这些值写入二进制。没有 `CPR_VERSION` 时,版本回落到 `release/version.yaml`,最后才回落到 Cargo 的 `CARGO_PKG_VERSION`。 diff --git a/release/publish b/release/publish index 1e18d4f34..21fe2e2d6 100755 --- a/release/publish +++ b/release/publish @@ -3,6 +3,7 @@ set -euo pipefail APP_NAME="codex-proxy-rs" VERSION_FILE="release/version.yaml" +TAG_PREFIX="v" SEMVER='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$' SIGNATURE="Co-authored-by: Codex " @@ -94,8 +95,7 @@ current_version="$(read_version_key version)" current_version="${current_version#v}" [[ "$version" != "$current_version" ]] || die "$VERSION_FILE is already $version" -tag_prefix="$(read_version_key tagPrefix)" -tag="${tag_prefix}${version}" +tag="${TAG_PREFIX}${version}" if git rev-parse --quiet --verify "refs/tags/$tag" >/dev/null; then die "local tag already exists: $tag" diff --git a/release/version.yaml b/release/version.yaml index 04c38fce3..5a2558592 100644 --- a/release/version.yaml +++ b/release/version.yaml @@ -1,2 +1 @@ version: "0.1.4" -tagPrefix: "v" diff --git a/skills/codex-proxy-rs/references/repo-guide.md b/skills/codex-proxy-rs/references/repo-guide.md index c70684f31..2789900d7 100644 --- a/skills/codex-proxy-rs/references/repo-guide.md +++ b/skills/codex-proxy-rs/references/repo-guide.md @@ -90,7 +90,7 @@ docker compose -f deploy/docker-compose.yml up -d - Release workflow:`.github/workflows/release.yml`。 - `v*` tag 触发发布。 - 日常发布入口:`release/publish `。该命令会更新 `release/version.yaml`、提交版本变更、创建 tag,并 push 分支和 tag 触发 workflow。 -- 产品版本来源是 `release/version.yaml`;workflow 会校验触发 tag 等于 `tagPrefix + version`,并通过 `CPR_VERSION` build arg 写入编译期元数据。 +- 产品版本来源是 `release/version.yaml`;workflow 会校验触发 tag 等于 `v` + `version`,并通过 `CPR_VERSION` build arg 写入编译期元数据。 - 发布平台来源是 `release/platforms.yaml`;新增/删除 Release asset 平台时优先改这个文件。 - 运行时版本接口展示编译期元数据:`CPR_VERSION`、`CPR_GIT_SHA`、`CPR_BUILD_TIME`。 - 当前发布的 Docker 镜像平台是 `linux/amd64` 和 `linux/arm64`。