diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c73084ae..51301bfd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,32 +2,17 @@ name: Build & Release on: push: - branches: - - main - paths: - - "src/**" - - "Cargo.toml" - - "Cargo.lock" - - "Dockerfile" - - "Dockerfile.*" + tags: + - "v*" workflow_dispatch: inputs: - chart_bump: - description: 'Chart version bump type' + tag: + description: 'Version tag (e.g. v0.7.0-beta.1 or v0.7.0)' required: true - type: choice - options: - - patch - - minor - - major - default: patch - release: - description: 'Stable release (no beta suffix)' - required: false - type: boolean - default: false + type: string + default: 'v' dry_run: - description: 'Dry run (show changes without committing)' + description: 'Dry run (build only, no push)' required: false type: boolean default: false @@ -37,7 +22,46 @@ env: IMAGE_NAME: ${{ github.repository }} jobs: + resolve-tag: + runs-on: ubuntu-latest + outputs: + tag: ${{ steps.resolve.outputs.tag }} + chart_version: ${{ steps.resolve.outputs.chart_version }} + is_prerelease: ${{ steps.resolve.outputs.is_prerelease }} + steps: + - name: Resolve and validate tag + id: resolve + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + TAG="${{ inputs.tag }}" + else + TAG="${GITHUB_REF_NAME}" + fi + + # Validate tag format + if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "::error::Invalid tag format '${TAG}'. Expected v{major}.{minor}.{patch}[-prerelease]" + exit 1 + fi + + CHART_VERSION="${TAG#v}" + + # Pre-release if version contains '-' (e.g. 0.7.0-beta.1) + if [[ "$CHART_VERSION" == *-* ]]; then + IS_PRERELEASE="true" + else + IS_PRERELEASE="false" + fi + + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "chart_version=${CHART_VERSION}" >> "$GITHUB_OUTPUT" + echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT" + + # ── Pre-release path: full build ────────────────────────────── + build-image: + needs: resolve-tag + if: ${{ needs.resolve-tag.outputs.is_prerelease == 'true' }} strategy: matrix: variant: @@ -53,11 +77,11 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 + - uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} @@ -65,7 +89,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ matrix.variant.suffix }} @@ -96,8 +120,8 @@ jobs: retention-days: 1 merge-manifests: - needs: build-image - if: inputs.dry_run != true + needs: [resolve-tag, build-image] + if: ${{ inputs.dry_run != true && needs.resolve-tag.outputs.is_prerelease == 'true' }} strategy: matrix: variant: @@ -109,8 +133,6 @@ jobs: permissions: contents: read packages: write - outputs: - version: ${{ steps.meta.outputs.version }} steps: - name: Download digests uses: actions/download-artifact@v4 @@ -121,7 +143,7 @@ jobs: - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 + - uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} @@ -129,12 +151,12 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ matrix.variant.suffix }} tags: | type=sha,prefix= - type=raw,value=latest + type=semver,pattern={{version}},value=${{ needs.resolve-tag.outputs.tag }} - name: Create manifest list working-directory: /tmp/digests @@ -142,88 +164,97 @@ jobs: docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ matrix.variant.suffix }}@sha256:%s ' *) - bump-chart: - needs: merge-manifests - if: inputs.dry_run != true + # ── Stable path: promote pre-release image (no rebuild) ────── + + promote-stable: + needs: resolve-tag + if: ${{ inputs.dry_run != true && needs.resolve-tag.outputs.is_prerelease == 'false' }} + strategy: + matrix: + variant: + - { suffix: "" } + - { suffix: "-codex" } + - { suffix: "-claude" } + - { suffix: "-gemini" } runs-on: ubuntu-latest permissions: - contents: write - pull-requests: write + contents: read + packages: write steps: - - name: Generate App token - id: app-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Get current chart version - id: current - run: | - chart_version=$(grep '^version:' charts/openab/Chart.yaml | awk '{print $2}') - echo "chart_version=$chart_version" >> "$GITHUB_OUTPUT" + - uses: docker/setup-buildx-action@v3 - - name: Bump chart version - id: bump + - uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Find pre-release image + id: find-prerelease run: | - current="${{ steps.current.outputs.chart_version }}" - # Strip any existing pre-release suffix for base version - base="${current%%-*}" - IFS='.' read -r major minor patch <<< "$base" - bump_type="${{ inputs.chart_bump }}" - bump_type="${bump_type:-patch}" - case "$bump_type" in - major) major=$((major + 1)); minor=0; patch=0 ;; - minor) minor=$((minor + 1)); patch=0 ;; - patch) patch=$((patch + 1)) ;; - esac - # Stable release: clean version. Otherwise: beta with run number. - if [ "${{ inputs.release }}" = "true" ]; then - new_version="${major}.${minor}.${patch}" - else - new_version="${major}.${minor}.${patch}-beta.${GITHUB_RUN_NUMBER}" + CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" + # Find latest pre-release tag matching this version (e.g. v0.7.0-beta.1) + PRERELEASE_TAG=$(git tag -l "v${CHART_VERSION}-*" --sort=-v:refname | head -1) + if [ -z "$PRERELEASE_TAG" ]; then + echo "::error::No pre-release tag found for v${CHART_VERSION}-*. Run a pre-release build first." + exit 1 fi - echo "new_version=$new_version" >> "$GITHUB_OUTPUT" + PRERELEASE_VERSION="${PRERELEASE_TAG#v}" + echo "Found pre-release: ${PRERELEASE_TAG} (${PRERELEASE_VERSION})" + echo "prerelease_version=${PRERELEASE_VERSION}" >> "$GITHUB_OUTPUT" - - name: Resolve image SHA - id: image-sha + - name: Verify pre-release image exists run: | - # Use the commit SHA that triggered this build — this is the SHA - # that merge-manifests tagged the Docker image with (type=sha,prefix=). - # We capture it here explicitly so it survives the bump commit. - IMAGE_SHA="${{ github.sha }}" - IMAGE_SHA="${IMAGE_SHA:0:7}" - echo "sha=${IMAGE_SHA}" >> "$GITHUB_OUTPUT" - - - name: Update Chart.yaml and values.yaml + IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ matrix.variant.suffix }}" + PRERELEASE_VERSION="${{ steps.find-prerelease.outputs.prerelease_version }}" + echo "Checking ${IMAGE}:${PRERELEASE_VERSION} ..." + docker buildx imagetools inspect "${IMAGE}:${PRERELEASE_VERSION}" || \ + { echo "::error::Image ${IMAGE}:${PRERELEASE_VERSION} not found — build the pre-release first"; exit 1; } + + - name: Promote to stable tags run: | - IMAGE_SHA="${{ steps.image-sha.outputs.sha }}" - sed -i "s/^version: .*/version: ${{ steps.bump.outputs.new_version }}/" charts/openab/Chart.yaml - sed -i "s/^appVersion: .*/appVersion: \"${IMAGE_SHA}\"/" charts/openab/Chart.yaml - sed -i "s|repository: .*|repository: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}|" charts/openab/values.yaml - sed -i "s/tag: .*/tag: \"${IMAGE_SHA}\"/" charts/openab/values.yaml - - - name: Create and auto-merge bump PR - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} + IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ matrix.variant.suffix }}" + PRERELEASE_VERSION="${{ steps.find-prerelease.outputs.prerelease_version }}" + CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" + MAJOR_MINOR="${CHART_VERSION%.*}" + + echo "Promoting ${IMAGE}:${PRERELEASE_VERSION} → ${CHART_VERSION}, ${MAJOR_MINOR}, latest" + docker buildx imagetools create \ + -t "${IMAGE}:${CHART_VERSION}" \ + -t "${IMAGE}:${MAJOR_MINOR}" \ + -t "${IMAGE}:latest" \ + "${IMAGE}:${PRERELEASE_VERSION}" + + # ── Chart release (runs after either path) ─────────────────── + + release-chart: + needs: [resolve-tag, merge-manifests, promote-stable] + if: >- + ${{ always() && inputs.dry_run != true && + needs.resolve-tag.result == 'success' && + (needs.merge-manifests.result == 'success' || needs.promote-stable.result == 'success') }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v6 + + - name: Install Helm + uses: azure/setup-helm@v4 + + - uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push chart to OCI run: | - VERSION="${{ steps.bump.outputs.new_version }}" - IMAGE_SHA="${{ steps.image-sha.outputs.sha }}" - BRANCH="chore/chart-${VERSION}" - git config user.name "openab-app[bot]" - git config user.email "274185012+openab-app[bot]@users.noreply.github.com" - git checkout -b "$BRANCH" - git add charts/openab/Chart.yaml charts/openab/values.yaml - git commit -m "chore: bump chart to ${VERSION} - - image: ${IMAGE_SHA}" - git push origin "$BRANCH" - PR_URL=$(gh pr create \ - --title "chore: bump chart to ${VERSION}" \ - --body "Auto-generated chart version bump for image \`${IMAGE_SHA}\`." \ - --base main --head "$BRANCH") - gh pr merge "$PR_URL" --squash --delete-branch + CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" + helm package charts/openab + helm push openab-${CHART_VERSION}.tgz oci://ghcr.io/${{ github.repository_owner }}/charts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..4239edd9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + pull_request: + paths: + - "src/**" + - "Cargo.toml" + - "Cargo.lock" + - "Dockerfile*" + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - uses: Swatinem/rust-cache@v2 + + - name: cargo check + run: cargo check + + - name: cargo clippy + run: cargo clippy -- -D warnings + + - name: cargo test + run: cargo test diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml new file mode 100644 index 00000000..b6c3658f --- /dev/null +++ b/.github/workflows/release-pr.yml @@ -0,0 +1,83 @@ +name: Release PR + +on: + workflow_dispatch: + inputs: + version: + description: "Version (leave empty for auto bump, or specify e.g. 0.8.0-beta.1)" + required: false + type: string + bump: + description: "Auto bump type (ignored when version is specified)" + required: false + type: choice + options: + - patch + - minor + - major + default: patch + +jobs: + create-release-pr: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Generate App token + id: app-token + uses: actions/create-github-app-token@v3 + with: + client-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - uses: actions/checkout@v6 + with: + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - name: Resolve version + id: version + run: | + if [ -n "${{ inputs.version }}" ]; then + VERSION="${{ inputs.version }}" + else + CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') + BASE="${CURRENT%%-*}" + IFS='.' read -r major minor patch <<< "$BASE" + case "${{ inputs.bump }}" in + major) major=$((major + 1)); minor=0; patch=0 ;; + minor) minor=$((minor + 1)); patch=0 ;; + patch) patch=$((patch + 1)) ;; + esac + VERSION="${major}.${minor}.${patch}-beta.1" + fi + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "::notice::Release version: ${VERSION}" + + - uses: dtolnay/rust-toolchain@stable + + - name: Update version files + run: | + VERSION="${{ steps.version.outputs.version }}" + sed -i "s/^version = .*/version = \"${VERSION}\"/" Cargo.toml + sed -i "s/^version: .*/version: ${VERSION}/" charts/openab/Chart.yaml + sed -i "s/^appVersion: .*/appVersion: \"${VERSION}\"/" charts/openab/Chart.yaml + cargo generate-lockfile + + - name: Create release PR + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + VERSION="${{ steps.version.outputs.version }}" + BRANCH="release/v${VERSION}" + git config user.name "openab-app[bot]" + git config user.email "274185012+openab-app[bot]@users.noreply.github.com" + git checkout -b "$BRANCH" + git add -A + git commit -m "release: v${VERSION}" + git push origin "$BRANCH" + gh pr create \ + --title "release: v${VERSION}" \ + --body "Merge this PR to tag \`v${VERSION}\` and trigger the build pipeline." \ + --base main --head "$BRANCH" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 534bb7ab..c2be4c13 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,10 +14,9 @@ jobs: permissions: contents: write pages: write - packages: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -29,13 +28,6 @@ jobs: - name: Install Helm uses: azure/setup-helm@v4 - - name: Login to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Run chart-releaser uses: helm/chart-releaser-action@v1.6.0 with: @@ -43,15 +35,7 @@ jobs: env: CR_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Push chart to OCI registry - run: | - CHART=charts/openab - NAME=$(grep '^name:' ${CHART}/Chart.yaml | awk '{print $2}') - VERSION=$(grep '^version:' ${CHART}/Chart.yaml | awk '{print $2}') - helm package ${CHART} - helm push ${NAME}-${VERSION}.tgz oci://ghcr.io/${{ github.repository_owner }}/charts - - - name: Append OCI install instructions to release notes + - name: Append install instructions to release notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/.github/workflows/tag-on-merge.yml b/.github/workflows/tag-on-merge.yml new file mode 100644 index 00000000..e414d933 --- /dev/null +++ b/.github/workflows/tag-on-merge.yml @@ -0,0 +1,38 @@ +name: Tag on Release PR merge + +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + tag: + if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Generate App token + id: app-token + uses: actions/create-github-app-token@v3 + with: + client-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - uses: actions/checkout@v6 + with: + token: ${{ steps.app-token.outputs.token }} + + - name: Create and push tag + run: | + # release/v0.8.0-beta.1 → v0.8.0-beta.1 + VERSION="${GITHUB_HEAD_REF#release/}" + if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "::error::Invalid version format '${VERSION}'. Expected v{major}.{minor}.{patch}[-prerelease]" + exit 1 + fi + git config user.name "openab-app[bot]" + git config user.email "274185012+openab-app[bot]@users.noreply.github.com" + git tag "$VERSION" + git push origin "$VERSION" + echo "::notice::Tagged ${VERSION}" diff --git a/Cargo.lock b/Cargo.lock index ac944efe..5fad25ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -763,7 +763,7 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "openab" -version = "0.1.0" +version = "0.6.0" dependencies = [ "anyhow", "base64", diff --git a/Cargo.toml b/Cargo.toml index 11f7dadc..27f30527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openab" -version = "0.1.0" +version = "0.6.0" edition = "2021" [dependencies] diff --git a/RELEASING.md b/RELEASING.md index 7061d0ec..8e2f35d6 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -2,55 +2,201 @@ ## Version Scheme -Chart versions follow SemVer with beta pre-releases: +Versions follow SemVer (e.g. `0.7.0`). Version bumps are controlled via `workflow_dispatch`: -- **Beta**: `0.2.1-beta.12345` — auto-generated on every push to main -- **Stable**: `0.2.1` — manually triggered, visible to `helm install` +| Method | 效果 | 範例 | +|---|---|---| +| Auto patch (default) | patch bump + beta | `0.6.0 → 0.6.1-beta.1` | +| Auto minor | minor bump + beta | `0.6.0 → 0.7.0-beta.1` | +| Auto major | major bump + beta | `0.6.0 → 1.0.0-beta.1` | +| Manual | 自行指定 | `0.8.0-beta.1` or `0.8.0` | -Users running `helm install` only see stable versions. Beta versions require `--devel` or explicit `--version`. +## Release Flow (Tag-Driven) -## Development Flow +> **核心原則:測過什麼就發什麼 (what you tested is what you ship)** +> stable release 不重新 build,直接 promote pre-release 驗證過的 image。 + +##### Step 1 — 建立 Release PR ``` - PR merged to main - │ - ▼ - ┌─────────────┐ ┌──────────────────┐ ┌─────────────────────┐ - │ CI: Build │────>│ CI: Bump PR │────>│ Merge bump PR │ - │ 3 images │ │ 0.2.1-beta.12345 │ │ → publishes beta │ - └─────────────┘ └──────────────────┘ └─────────────────────┘ - │ - ┌───────────────────────────────────────────────┘ - ▼ - helm install ... --version 0.2.1-beta.12345 (explicit only) - helm install ... (still gets 0.2.0 stable) + ┌─────────────────────────────────────────────────────────────────┐ + │ Maintainer 到 Actions → Release PR → Run workflow │ + │ │ + │ 選項 A: 留空 version,選 bump type → 自動算 (e.g. 0.7.0-beta.1) │ + │ 選項 B: 手動填 version (e.g. 0.8.0-beta.1 or 0.8.0) │ + │ │ + │ → release-pr.yml 觸發 │ + │ → 更新 Cargo.toml + Chart.yaml version/appVersion │ + │ → 建立 Release PR (branch: release/v0.7.0-beta.1) │ + └─────────────────────────────────────────────────────────────────┘ ``` -## Stable Release +##### Step 2 — Merge Release PR → 自動打 Tag → Build ``` - Actions → Build & Release → Run workflow - [bump: patch] [✅ Stable release] + ┌─────────────────────────────────────────────────────────────────┐ + │ Maintainer review & merge Release PR │ + │ │ + │ → tag-on-merge.yml 偵測 release/ branch merge │ + │ → 自動打 tag (e.g. v0.7.0-beta.1) │ + │ → build.yml 觸發 (is_prerelease=true) │ + │ → build-image: 4 variants × 2 platforms (amd64 + arm64) │ + │ → merge-manifests: image tags = + 0.7.0-beta.1 │ + │ → release-chart: helm chart → OCI registry │ + └─────────────────────────────────────────────────────────────────┘ │ ▼ - ┌─────────────┐ ┌──────────────────┐ ┌─────────────────────┐ - │ CI: Build │────>│ CI: Bump PR │────>│ Merge bump PR │ - │ 3 images │ │ 0.2.1 │ │ → publishes stable │ - └─────────────┘ └──────────────────┘ └─────────────────────┘ - │ - ┌───────────────────────────────────────────────┘ - ▼ - helm install ... (gets 0.2.1 🎉) + ┌─────────────────────────────────────────────────────────────────┐ + │ 部署 pre-release 進行測試: │ + │ │ + │ helm install openab \ │ + │ oci://ghcr.io/openabdev/charts/openab \ │ + │ --version 0.7.0-beta.1 │ + │ │ + │ 發現 bug?→ 修復 PR merge → 再跑一次 Release PR workflow │ + │ → 手動指定 v0.7.0-beta.2 → merge → 重新測試 │ + └─────────────────────────────────────────────────────────────────┘ ``` -## Image Tags +##### Step 3 — Stable Release(Promote) -Each build produces three multi-arch images tagged with the git short SHA: +``` + ┌─────────────────────────────────────────────────────────────────┐ + │ 測試通過後,再跑一次 Release PR workflow │ + │ → 手動指定 version: 0.7.0 (不帶 rc) │ + │ → merge Release PR │ + │ → tag-on-merge.yml 打 tag v0.7.0 │ + │ │ + │ → build.yml 觸發 (is_prerelease=false) │ + │ → promote-stable: │ + │ 1. 找到最新的 pre-release tag (v0.7.0-beta.2) │ + │ 2. 驗證 pre-release image 存在 │ + │ 3. re-tag 0.7.0-beta.2 → 0.7.0 / 0.7 / latest │ + │ ⚠️ 不 rebuild,跟 pre-release 是同一個 artifact │ + │ → release-chart: helm chart → OCI registry │ + └─────────────────────────────────────────────────────────────────┘ +``` + +##### Step 4 — Chart Release(自動) ``` -ghcr.io/openabdev/openab: # kiro-cli -ghcr.io/openabdev/openab-codex: # codex -ghcr.io/openabdev/openab-claude: # claude + ┌─────────────────────────────────────────────────────────────────┐ + │ release.yml 偵測到 Chart.yaml 變更 push to main │ + │ → chart-releaser 更新 GitHub Pages helm repo index │ + │ → 附加 install instructions 到 chart release notes │ + └─────────────────────────────────────────────────────────────────┘ +``` + +## 快速指令參考 + +```bash +# ── Pre-release ─────────────────────────────────────── +# 到 Actions → Release PR → Run workflow +# 留空 version,選 patch → 自動算 0.7.0-beta.1 +# 或手動填 version: 0.7.0-beta.1 +# → merge 產生的 Release PR → 自動打 tag → build + +# ── 第二輪 pre-release(beta.1 有 bug 時)───────────── +# 修 bug → PR merge to main +# 再跑 Release PR workflow,手動填 version: 0.7.0-beta.2 +# → merge → 自動打 tag → build + +# ── Stable release ──────────────────────────────────── +# 跑 Release PR workflow,手動填 version: 0.7.0 +# → merge → 自動打 tag → promote beta image (不 rebuild) + +# ── 手動重跑(build 失敗時)────────────────────────── +gh workflow run build.yml -f tag=v0.7.0-beta.1 +gh workflow run build.yml -f tag=v0.7.0 ``` -The `latest` tag always points to the most recent build. +## GitHub Releases + +| Release | Tag 格式 | 內容 | +|---|---|---| +| chart-releaser | `openab-0.7.0` | Version Info + Installation instructions | + +## Workflow 對應表 + +| Workflow | 觸發條件 | 用途 | +|---|---|---| +| `ci.yml` | pull_request (src/Cargo/Dockerfile) | cargo check + clippy + test | +| `release-pr.yml` | workflow_dispatch | 建立 Release PR(更新版本檔案) | +| `tag-on-merge.yml` | release/ PR merge to main | 自動打 tag | +| `build.yml` | tag push `v*` | pre-release: 完整 build / stable: promote | +| `release.yml` | Chart.yaml 變更 push to main | chart-releaser 更新 GitHub Pages index | + +## Version 同步 + +release-pr.yml 在 Release PR 中自動更新以下檔案的版本: + +| 檔案 | 欄位 | +|---|---| +| `Cargo.toml` | `version` | +| `charts/openab/Chart.yaml` | `version` | +| `charts/openab/Chart.yaml` | `appVersion` | + +三者統一為同一個 semver(e.g. `0.7.0`)。 + +## Image Variants + +每次 build 產出 4 個 multi-arch image (linux/amd64 + linux/arm64): + +``` +ghcr.io/openabdev/openab # default (kiro-cli) +ghcr.io/openabdev/openab-codex # codex +ghcr.io/openabdev/openab-claude # claude +ghcr.io/openabdev/openab-gemini # gemini +``` + +Image tags 依 release 類型不同: + +| Tag | Stable (`v0.7.0`) | Pre-release (`v0.7.0-beta.1`) | +|---|---|---| +| `` | v (from pre-release) | v | +| `0.7.0` / `0.7.0-beta.1` | v | v | +| `0.7` | v | x | +| `latest` | v | x | + +## Installation + +##### Helm Repository (GitHub Pages) + +```bash +helm repo add openab https://openabdev.github.io/openab +helm repo update +helm install openab openab/openab --version 0.7.0 +``` + +##### OCI Registry + +```bash +helm install openab oci://ghcr.io/openabdev/charts/openab --version 0.7.0 +``` + +## 手動操作 + +| 時機 | 做什麼 | +|---|---| +| 準備 release | Actions → Release PR → Run workflow | +| 需要 beta 測試 | 指定 version 如 `0.7.0-beta.1` | +| 測試通過 | 指定 stable version 如 `0.7.0` → promote | +| build 失敗或需重跑 | `gh workflow run build.yml -f tag=` | + +## GitHub App 權限 + +release-pr.yml 和 tag-on-merge.yml 使用 GitHub App token 來建立 PR 和推送 tag。App 需要以下 Repository permissions: + +| Permission | Access | +|---|---| +| Contents | Read and write | +| Metadata | Read-only (mandatory) | +| Pull requests | Read and write | + +對應的 secrets:`APP_ID`(Client ID)、`APP_PRIVATE_KEY`。 + +## 限制與注意事項 + +- **Stable release 必須先有 pre-release**:promote-stable 會查找 `v{version}-*` 的 pre-release tag,找不到就失敗 +- **promote 用 version tag 找 image**:不依賴 commit SHA,pre-release 和 stable 可以在不同 commit 上 +- **外部用戶不會裝到 pre-release**:`helm install` 預設只拿 stable 版本,pre-release 需明確指定 `--version` diff --git a/charts/openab/Chart.yaml b/charts/openab/Chart.yaml index af5733cb..bf2f1b38 100644 --- a/charts/openab/Chart.yaml +++ b/charts/openab/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: openab description: Discord ↔ ACP coding CLI bridge (Kiro CLI, Claude Code, Codex, Gemini) type: application -version: 0.6.3-beta.39 -appVersion: "94253a5" +version: 0.6.3 +appVersion: "0.6.3" diff --git a/src/discord.rs b/src/discord.rs index b8d7e53e..77539173 100644 --- a/src/discord.rs +++ b/src/discord.rs @@ -54,7 +54,7 @@ impl EventHandler for Handler { Ok(serenity::model::channel::Channel::Guild(gc)) => { let result = gc .parent_id - .map_or(false, |pid| self.allowed_channels.contains(&pid.get())); + .is_some_and(|pid| self.allowed_channels.contains(&pid.get())); tracing::debug!(channel_id = %msg.channel_id, parent_id = ?gc.parent_id, result, "thread check"); result }