diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 67ae751c7..ff4b7490c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,8 @@ jobs: version: ${{ steps.version.outputs.version }} build_time: ${{ steps.version.outputs.build_time }} git_sha: ${{ steps.version.outputs.git_sha }} + platform_matrix: ${{ steps.version.outputs.platform_matrix }} + docker_platforms: ${{ steps.version.outputs.docker_platforms }} steps: - name: Checkout @@ -34,39 +36,79 @@ jobs: with: ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} - - name: Derive version + - name: Read release metadata id: version shell: bash env: RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} run: | - VERSION="${RELEASE_TAG#v}" - BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" - GIT_SHA="$(git rev-parse HEAD)" - { - echo "tag=${RELEASE_TAG}" - echo "version=${VERSION}" - echo "build_time=${BUILD_TIME}" - echo "git_sha=${GIT_SHA}" - } >> "$GITHUB_OUTPUT" + ruby <<'RUBY' >> "$GITHUB_OUTPUT" + require "json" + require "open3" + require "time" + require "yaml" - - name: Verify Cargo package version - env: - RELEASE_VERSION: ${{ steps.version.outputs.version }} - run: | - PACKAGE_VERSION="$(python3 - <<'PY' - import pathlib - import tomllib + APP_NAME = "codex-proxy-rs" + SEMVER = /\A(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?\z/ - manifest = tomllib.loads(pathlib.Path("backend/Cargo.toml").read_text()) - print(manifest["package"]["version"]) - PY - )" + def fail(message) + warn(message) + exit(1) + end - if [ "${PACKAGE_VERSION}" != "${RELEASE_VERSION}" ]; then - echo "Tag version ${RELEASE_VERSION} does not match backend/Cargo.toml package version ${PACKAGE_VERSION}" >&2 - exit 1 - fi + def require_string(data, key, path) + value = data[key] + fail("#{path}: #{key} must be a non-empty string") unless value.is_a?(String) && !value.strip.empty? + value.strip + 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) + 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 + fail("#{version_path}: version #{version} is not a valid semver") unless version.match?(SEMVER) + + platforms = platforms_data["platforms"] + fail("#{platforms_path}: platforms must be a non-empty array") unless platforms.is_a?(Array) && !platforms.empty? + + required_platform_keys = %w[os arch runner target binary archive] + seen_platforms = {} + docker_platforms = [] + platforms.each_with_index do |platform, index| + fail("#{platforms_path}: platforms[#{index}] must be a table") unless platform.is_a?(Hash) + required_platform_keys.each do |key| + require_string(platform, key, "#{platforms_path}: platforms[#{index}]") + end + archive = platform["archive"] + fail("#{platforms_path}: platforms[#{index}].archive must be tar.gz or zip") unless ["tar.gz", "zip"].include?(archive) + platform_key = "#{platform["os"]}-#{platform["arch"]}" + fail("#{platforms_path}: duplicate platform #{platform_key}") if seen_platforms[platform_key] + seen_platforms[platform_key] = true + platform["artifact"] = "#{APP_NAME}-#{platform_key}" + if platform["docker"] == true + docker_platforms << require_string(platform, "dockerPlatform", "#{platforms_path}: platforms[#{index}]") + end + end + + fail("#{platforms_path}: at least one platform must set docker = true") if docker_platforms.empty? + + git_sha, status = Open3.capture2("git", "rev-parse", "HEAD") + fail("failed to resolve git sha") unless status.success? + matrix = JSON.generate({ "include" => platforms }) + + puts "tag=#{release_tag}" + puts "version=#{version}" + puts "build_time=#{Time.now.utc.iso8601}" + puts "git_sha=#{git_sha.strip}" + puts "platform_matrix=#{matrix}" + puts "docker_platforms=#{docker_platforms.join(",")}" + RUBY build-frontend: needs: [release-info] @@ -110,33 +152,7 @@ jobs: needs: [release-info, build-frontend] strategy: fail-fast: false - matrix: - include: - - os: linux - arch: amd64 - runner: ubuntu-latest - target: x86_64-unknown-linux-gnu - binary: codex-proxy-rs - - os: linux - arch: arm64 - runner: ubuntu-24.04-arm - target: aarch64-unknown-linux-gnu - binary: codex-proxy-rs - - os: darwin - arch: amd64 - runner: macos-15-intel - target: x86_64-apple-darwin - binary: codex-proxy-rs - - os: darwin - arch: arm64 - runner: macos-15 - target: aarch64-apple-darwin - binary: codex-proxy-rs - - os: windows - arch: amd64 - runner: windows-latest - target: x86_64-pc-windows-msvc - binary: codex-proxy-rs.exe + matrix: ${{ fromJson(needs.release-info.outputs.platform_matrix) }} runs-on: ${{ matrix.runner }} steps: @@ -183,7 +199,7 @@ jobs: uses: actions/upload-artifact@v7 with: name: codex-proxy-rs-${{ matrix.os }}-${{ matrix.arch }} - path: dist/binaries/${{ matrix.os }}-${{ matrix.arch }}/${{ matrix.binary }} + path: dist/binaries/${{ matrix.os }}-${{ matrix.arch }}/ if-no-files-found: error retention-days: 1 @@ -192,44 +208,22 @@ jobs: runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v7 + with: + ref: ${{ needs.release-info.outputs.tag }} + - name: Download frontend artifact uses: actions/download-artifact@v8 with: name: frontend-dist path: dist/frontend - - name: Download amd64 binary + - name: Download binary artifacts uses: actions/download-artifact@v8 with: - name: codex-proxy-rs-linux-amd64 - path: dist/binaries/linux-amd64 - - - name: Download arm64 binary - uses: actions/download-artifact@v8 - with: - name: codex-proxy-rs-linux-arm64 - path: dist/binaries/linux-arm64 - - - name: Download darwin amd64 binary - uses: actions/download-artifact@v8 - with: - name: codex-proxy-rs-darwin-amd64 - path: dist/binaries/darwin-amd64 - - - name: Download darwin arm64 binary - uses: actions/download-artifact@v8 - with: - name: codex-proxy-rs-darwin-arm64 - path: dist/binaries/darwin-arm64 - - - name: Download windows amd64 binary - uses: actions/download-artifact@v8 - with: - name: codex-proxy-rs-windows-amd64 - path: dist/binaries/windows-amd64 - - - name: Install packaging tools - run: sudo apt-get update && sudo apt-get install -y zip + pattern: codex-proxy-rs-* + path: dist/binaries - name: Package release assets shell: bash @@ -237,43 +231,84 @@ jobs: VERSION: ${{ needs.release-info.outputs.version }} run: | set -euo pipefail + ruby <<'RUBY' > release-platforms.json + require "json" + require "yaml" - rm -rf package checksums.txt + print JSON.generate(YAML.load_file("release/platforms.yaml")) + RUBY + python3 - <<'PY' + import hashlib + import json + import os + import pathlib + import shutil + import tarfile + import zipfile - for platform in linux-amd64 linux-arm64 darwin-amd64 darwin-arm64; do - os="${platform%-*}" - arch="${platform#*-}" - rm -rf package/codex-proxy-rs - mkdir -p package/codex-proxy-rs/web/dist - cp "dist/binaries/${platform}/codex-proxy-rs" package/codex-proxy-rs/codex-proxy-rs - chmod +x package/codex-proxy-rs/codex-proxy-rs - cp -R dist/frontend/. package/codex-proxy-rs/web/dist/ - tar -czf "codex-proxy-rs_${VERSION}_${os}_${arch}.tar.gz" -C package codex-proxy-rs - done + APP_NAME = "codex-proxy-rs" - rm -rf package/codex-proxy-rs - mkdir -p package/codex-proxy-rs/web/dist - cp "dist/binaries/windows-amd64/codex-proxy-rs.exe" package/codex-proxy-rs/codex-proxy-rs.exe - cp -R dist/frontend/. package/codex-proxy-rs/web/dist/ - (cd package && zip -qr "../codex-proxy-rs_${VERSION}_windows_amd64.zip" codex-proxy-rs) + def sha256(path: pathlib.Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as file: + for chunk in iter(lambda: file.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() - sha256sum "codex-proxy-rs_${VERSION}_linux_amd64.tar.gz" \ - "codex-proxy-rs_${VERSION}_linux_arm64.tar.gz" \ - "codex-proxy-rs_${VERSION}_darwin_amd64.tar.gz" \ - "codex-proxy-rs_${VERSION}_darwin_arm64.tar.gz" \ - "codex-proxy-rs_${VERSION}_windows_amd64.zip" > checksums.txt + version = os.environ["VERSION"] + platforms = json.loads(pathlib.Path("release-platforms.json").read_text())["platforms"] + frontend_dir = pathlib.Path("dist/frontend") + binary_artifacts_dir = pathlib.Path("dist/binaries") + release_dir = pathlib.Path("dist/release") + package_dir = pathlib.Path("package") + + shutil.rmtree(release_dir, ignore_errors=True) + release_dir.mkdir(parents=True) + archives = [] + + for platform in platforms: + platform_name = f"{platform['os']}-{platform['arch']}" + artifact_name = f"{APP_NAME}-{platform_name}" + binary_src = binary_artifacts_dir / artifact_name / platform["binary"] + if not binary_src.is_file(): + raise SystemExit(f"Missing binary artifact: {binary_src}") + + shutil.rmtree(package_dir, ignore_errors=True) + package_root = package_dir / APP_NAME + web_dist = package_root / "web" / "dist" + web_dist.mkdir(parents=True) + + binary_dest = package_root / platform["binary"] + shutil.copy2(binary_src, binary_dest) + if platform["os"] != "windows": + binary_dest.chmod(0o755) + shutil.copytree(frontend_dir, web_dist, dirs_exist_ok=True) + + archive_name = f"{APP_NAME}_{version}_{platform['os']}_{platform['arch']}.{platform['archive']}" + archive_path = release_dir / archive_name + if platform["archive"] == "tar.gz": + with tarfile.open(archive_path, "w:gz") as archive: + archive.add(package_root, arcname=APP_NAME) + elif platform["archive"] == "zip": + with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as archive: + for path in sorted(package_root.rglob("*")): + if path.is_file(): + archive.write(path, path.relative_to(package_dir)) + else: + raise SystemExit(f"Unsupported archive format: {platform['archive']}") + archives.append(archive_path) + + checksum_path = release_dir / "checksums.txt" + with checksum_path.open("w", encoding="utf-8") as file: + for archive in archives: + file.write(f"{sha256(archive)} {archive.name}\n") + PY - name: Upload release assets uses: actions/upload-artifact@v7 with: name: release-assets - path: | - codex-proxy-rs_${{ needs.release-info.outputs.version }}_linux_amd64.tar.gz - codex-proxy-rs_${{ needs.release-info.outputs.version }}_linux_arm64.tar.gz - codex-proxy-rs_${{ needs.release-info.outputs.version }}_darwin_amd64.tar.gz - codex-proxy-rs_${{ needs.release-info.outputs.version }}_darwin_arm64.tar.gz - codex-proxy-rs_${{ needs.release-info.outputs.version }}_windows_amd64.zip - checksums.txt + path: dist/release if-no-files-found: error retention-days: 1 @@ -293,17 +328,11 @@ jobs: name: frontend-dist path: dist/docker/web/dist - - name: Download amd64 binary + - name: Download Docker binary artifacts uses: actions/download-artifact@v8 with: - name: codex-proxy-rs-linux-amd64 - path: dist/docker/linux-amd64 - - - name: Download arm64 binary - uses: actions/download-artifact@v8 - with: - name: codex-proxy-rs-linux-arm64 - path: dist/docker/linux-arm64 + pattern: codex-proxy-rs-linux-* + path: dist/docker-artifacts - name: Download release assets uses: actions/download-artifact@v8 @@ -311,6 +340,42 @@ jobs: name: release-assets path: dist/release + - name: Prepare Docker prebuilt assets + shell: bash + run: | + set -euo pipefail + ruby <<'RUBY' > release-platforms.json + require "json" + require "yaml" + + print JSON.generate(YAML.load_file("release/platforms.yaml")) + RUBY + python3 - <<'PY' + import json + import pathlib + import shutil + + APP_NAME = "codex-proxy-rs" + platforms = json.loads(pathlib.Path("release-platforms.json").read_text())["platforms"] + source_root = pathlib.Path("dist/docker-artifacts") + target_root = pathlib.Path("dist/docker") + + for platform in platforms: + if platform.get("docker") is not True: + continue + platform_name = f"{platform['os']}-{platform['arch']}" + artifact_name = f"{APP_NAME}-{platform_name}" + binary_src = source_root / artifact_name / platform["binary"] + if not binary_src.is_file(): + raise SystemExit(f"Missing Docker binary artifact: {binary_src}") + + target_dir = target_root / platform_name + target_dir.mkdir(parents=True, exist_ok=True) + binary_dest = target_dir / APP_NAME + shutil.copy2(binary_src, binary_dest) + binary_dest.chmod(0o755) + PY + - name: Setup QEMU uses: docker/setup-qemu-action@v4 @@ -330,7 +395,7 @@ jobs: context: . file: deploy/Dockerfile target: runtime-prebuilt - platforms: linux/amd64,linux/arm64 + platforms: ${{ needs.release-info.outputs.docker_platforms }} push: true build-args: | CPR_PREBUILT_DIR=dist/docker @@ -368,9 +433,4 @@ jobs: - [GitHub Repository](https://github.com/${{ github.repository }}) - [Installation Guide](https://github.com/${{ github.repository }}/blob/main/deploy/README.md) files: | - dist/release/codex-proxy-rs_${{ needs.release-info.outputs.version }}_linux_amd64.tar.gz - dist/release/codex-proxy-rs_${{ needs.release-info.outputs.version }}_linux_arm64.tar.gz - dist/release/codex-proxy-rs_${{ needs.release-info.outputs.version }}_darwin_amd64.tar.gz - dist/release/codex-proxy-rs_${{ needs.release-info.outputs.version }}_darwin_arm64.tar.gz - dist/release/codex-proxy-rs_${{ needs.release-info.outputs.version }}_windows_amd64.zip - dist/release/checksums.txt + dist/release/* diff --git a/backend/Cargo.lock b/backend/Cargo.lock index fe390dd68..055643a77 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -315,7 +315,7 @@ checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" [[package]] name = "codex-proxy-rs" -version = "0.1.3" +version = "0.0.0" dependencies = [ "argon2", "async-trait", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 53dc43da5..ff12f5d15 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "codex-proxy-rs" -version = "0.1.3" edition = "2021" license = "MIT" publish = false diff --git a/backend/build/build.rs b/backend/build/build.rs index 415071378..99ffd56fc 100644 --- a/backend/build/build.rs +++ b/backend/build/build.rs @@ -1,14 +1,19 @@ -use std::env; +use std::{env, fs}; + +const RELEASE_VERSION_FILE: &str = "../release/version.yaml"; fn main() { + println!("cargo:rerun-if-changed={RELEASE_VERSION_FILE}"); println!("cargo:rerun-if-env-changed=CPR_VERSION"); println!("cargo:rerun-if-env-changed=CPR_GIT_SHA"); println!("cargo:rerun-if-env-changed=CPR_BUILD_TIME"); println!("cargo:rerun-if-env-changed=CPR_BUILD_TYPE"); - let version = version_env_value("CPR_VERSION").unwrap_or_else(|| { - env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.0.0-dev".to_string()) - }); + let version = version_env_value("CPR_VERSION") + .or_else(release_version) + .unwrap_or_else(|| { + env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.0.0-dev".to_string()) + }); let git_sha = env_value("CPR_GIT_SHA").unwrap_or_else(|| "unknown".to_string()); let build_time = env_value("CPR_BUILD_TIME").unwrap_or_else(|| "unknown".to_string()); let build_type = env_value("CPR_BUILD_TYPE").unwrap_or_else(|| "source".to_string()); @@ -29,3 +34,36 @@ fn env_value(key: &str) -> Option { fn version_env_value(key: &str) -> Option { env_value(key).map(|value| value.trim_start_matches('v').to_string()) } + +fn release_version() -> Option { + let data = fs::read_to_string(RELEASE_VERSION_FILE).ok()?; + yaml_string_value(&data, "version").map(|value| value.trim_start_matches('v').to_string()) +} + +fn yaml_string_value(data: &str, key: &str) -> Option { + for line in data.lines() { + let line = line.split_once('#').map_or(line, |(value, _)| value).trim(); + let Some((name, value)) = line.split_once(':') else { + continue; + }; + if name.trim() != key { + continue; + } + let value = value.trim(); + if value.is_empty() { + continue; + } + let first = value.chars().next()?; + let parsed = if first == '"' || first == '\'' { + let rest = &value[first.len_utf8()..]; + let end = rest.find(first)?; + rest[..end].trim() + } else { + value.trim() + }; + if !parsed.is_empty() { + return Some(parsed.to_string()); + } + } + None +} diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 04f01e9ab..03e60e75e 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -24,6 +24,7 @@ ENV CPR_BUILD_TYPE=${CPR_BUILD_TYPE} COPY backend/Cargo.toml backend/Cargo.lock ./ COPY backend/build ./build COPY backend/src ./src +COPY release/version.yaml /app/release/version.yaml COPY --from=frontend-builder /app/frontend/dist ./web/dist RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/usr/local/cargo/git \ diff --git a/deploy/README.md b/deploy/README.md index af875b3fc..47901bc8a 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -51,7 +51,7 @@ docker compose -f deploy/docker-compose.yml build 需要注入版本信息时: ```bash -CPR_VERSION=0.1.1 \ +CPR_VERSION="$(ruby -ryaml -e 'puts YAML.load_file("release/version.yaml").fetch("version").delete_prefix("v")')" \ CPR_GIT_SHA="$(git rev-parse HEAD)" \ CPR_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ docker compose -f deploy/docker-compose.yml build diff --git a/docs/architecture.md b/docs/architecture.md index d8f592b52..cc9494bda 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -98,20 +98,23 @@ flowchart LR ## 版本和发布 -版本只有两个来源层级: +版本和平台发布契约集中在 `release/`: -- 源码默认版本:`backend/Cargo.toml` 的 `[package].version`。 -- 发布版本:Git tag 去掉 `v` 前缀后得到的版本。 +- `release/version.yaml`:产品版本和 tag 前缀。 +- `release/platforms.yaml`:Release asset 构建平台、归档格式和 Docker 平台。 -发布 workflow 会校验 tag 版本必须等于 `backend/Cargo.toml` 的 package version。发布构建时通过 `CPR_VERSION`、`CPR_GIT_SHA`、`CPR_BUILD_TIME` 和 `CPR_BUILD_TYPE=release` 注入编译期元数据。 +发布 workflow 会校验触发 tag 必须等于 `tagPrefix + version`。发布构建时通过 `CPR_VERSION`、`CPR_GIT_SHA`、`CPR_BUILD_TIME` 和 `CPR_BUILD_TYPE=release` 注入编译期元数据。 -`backend/build/build.rs` 负责把这些值写入二进制。没有 `CPR_VERSION` 时,版本回落到 Cargo 的 `CARGO_PKG_VERSION`。不要重新引入独立 `VERSION` 文件。 +`backend/build/build.rs` 负责把这些值写入二进制。没有 `CPR_VERSION` 时,版本回落到 `release/version.yaml`,最后才回落到 Cargo 的 `CARGO_PKG_VERSION`。 GitHub Release 产物: ```text codex-proxy-rs__linux_amd64.tar.gz codex-proxy-rs__linux_arm64.tar.gz +codex-proxy-rs__darwin_amd64.tar.gz +codex-proxy-rs__darwin_arm64.tar.gz +codex-proxy-rs__windows_amd64.zip checksums.txt ``` diff --git a/frontend/src/composables/useSystemUpdate.ts b/frontend/src/composables/useSystemUpdate.ts index 18d51b43b..4bfe4205b 100644 --- a/frontend/src/composables/useSystemUpdate.ts +++ b/frontend/src/composables/useSystemUpdate.ts @@ -156,7 +156,8 @@ async function checkUpdates(force = true) { } async function updateNow() { - if (!canUpdate.value || !updateInfo.value || updating.value) return null + const currentInfo = updateInfo.value + if (!canUpdate.value || !currentInfo || updating.value) return null clearUpdateLogs() connectUpdateEvents() @@ -167,6 +168,11 @@ async function updateNow() { const result = await performSystemUpdate() updateSuccess.value = true needRestart.value = result.needRestart + updateInfo.value = { + ...currentInfo, + latestVersion: result.targetVersion, + hasUpdate: false, + } return result } catch (error: any) { updateError.value = error.message || '更新失败' diff --git a/release/platforms.yaml b/release/platforms.yaml new file mode 100644 index 000000000..4544b137b --- /dev/null +++ b/release/platforms.yaml @@ -0,0 +1,39 @@ +platforms: + - os: linux + arch: amd64 + runner: ubuntu-latest + target: x86_64-unknown-linux-gnu + binary: codex-proxy-rs + archive: tar.gz + docker: true + dockerPlatform: linux/amd64 + + - os: linux + arch: arm64 + runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + binary: codex-proxy-rs + archive: tar.gz + docker: true + dockerPlatform: linux/arm64 + + - os: darwin + arch: amd64 + runner: macos-15-intel + target: x86_64-apple-darwin + binary: codex-proxy-rs + archive: tar.gz + + - os: darwin + arch: arm64 + runner: macos-15 + target: aarch64-apple-darwin + binary: codex-proxy-rs + archive: tar.gz + + - os: windows + arch: amd64 + runner: windows-latest + target: x86_64-pc-windows-msvc + binary: codex-proxy-rs.exe + archive: zip diff --git a/release/version.yaml b/release/version.yaml new file mode 100644 index 000000000..058549487 --- /dev/null +++ b/release/version.yaml @@ -0,0 +1,2 @@ +version: "0.1.3" +tagPrefix: "v" diff --git a/skills/codex-proxy-rs/references/repo-guide.md b/skills/codex-proxy-rs/references/repo-guide.md index 309413f9d..486b0027f 100644 --- a/skills/codex-proxy-rs/references/repo-guide.md +++ b/skills/codex-proxy-rs/references/repo-guide.md @@ -89,8 +89,8 @@ docker compose -f deploy/docker-compose.yml up -d - Release workflow:`.github/workflows/release.yml`。 - `v*` tag 触发发布。 -- release 时版本来源是去掉 `v` 前缀的 Git tag;workflow 通过 `CPR_VERSION` build arg 写入编译期元数据。 -- 源码默认版本使用 `backend/Cargo.toml` 的 `[package].version`;不要重新引入独立 `VERSION` 文件。 +- 产品版本来源是 `release/version.yaml`;workflow 会校验触发 tag 等于 `tagPrefix + version`,并通过 `CPR_VERSION` build arg 写入编译期元数据。 +- 发布平台来源是 `release/platforms.yaml`;新增/删除 Release asset 平台时优先改这个文件。 - 运行时版本接口展示编译期元数据:`CPR_VERSION`、`CPR_GIT_SHA`、`CPR_BUILD_TIME`。 - 当前发布的 Docker 镜像平台是 `linux/amd64` 和 `linux/arm64`。 - Release asset 命名必须匹配在线更新选择器: @@ -98,6 +98,9 @@ docker compose -f deploy/docker-compose.yml up -d ```text codex-proxy-rs__linux_amd64.tar.gz codex-proxy-rs__linux_arm64.tar.gz +codex-proxy-rs__darwin_amd64.tar.gz +codex-proxy-rs__darwin_arm64.tar.gz +codex-proxy-rs__windows_amd64.zip checksums.txt ```