mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
a50e1f7b06
* feat(release): 自动化 release 切版 + backport + 准入闸 引入一套基于 GitHub Action 的 release 流程自动化(配套飞书《发布期间 fix PR 处理手册》): - cut-release: 每周二 09:00(北京)从 main HEAD 切 release/vX.Y.Z + bump 版本 + 建 backport label - backport: 合进 main 的 PR 带 backport label → 自动 cherry-pick 到 release 分支(冲突转 draft) - backport-label-guard: 校验贴标人 write+ 权限,否则撤销 label - release-gate: 发版前检查目标 release 分支无未落地 backport 全部用 GitHub App (open-design-release-bot) token,非 GITHUB_TOKEN, 以便其 push/PR 能正常触发下游 workflow(notify-release-feishu / ci)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(release): translate workflow comments and messages to English Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(release): validate cut-release version input via env; release-gate fails closed on missing branch Addresses review (PerishCode): - cut-release: pass manual version through env + strict x.y.z validation to prevent shell injection; downstream steps consume validated outputs via env (no inline ${{ }} in run). - release-gate: verify the release branch exists before checking PRs, so a mistyped / not-yet-cut branch fails the gate instead of falsely passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(release): constrain backport/gate target to the release/vX.Y.Z grammar Addresses review (PerishCode, round 2): - backport: tighten label_pattern to ^backport (release/v[0-9]+\.[0-9]+\.[0-9]+)$ so a typo like 'backport main' / 'backport feat/foo' no longer matches and is never run by the bot. - release-gate: validate the input branch against ^release/vX.Y.Z before the existence check, so an arbitrary existing branch (e.g. main) with no open PRs cannot falsely pass the gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(release): cut-release always checks out main HEAD Addresses review (PerishCode, round 3): pin actions/checkout to ref: main so a manual workflow_dispatch from another branch still cuts the release branch from main HEAD, not from whatever ref the run was started on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
2.5 KiB
YAML
57 lines
2.5 KiB
YAML
name: release-gate
|
|
# Pre-release gate: confirm the target release branch has no un-landed backports.
|
|
# Fails if any -> do not tag. Run it before tagging (or wire it as a required pre-release job).
|
|
#
|
|
# The check = open PRs whose base is the release branch. That single check covers every
|
|
# "not landed yet" case:
|
|
# - Conflict -> backport-action opens a draft PR (state open, caught here)
|
|
# - Clean but not merged / CI still running -> also an open PR, caught here
|
|
# Once everything is merged, open PRs targeting the branch hit zero -> the gate passes.
|
|
# (korthout has no "label on failure" feature, so this does not rely on any *-failed label.)
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
release_branch:
|
|
description: 'Release branch, e.g. release/v0.12.0'
|
|
required: true
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
|
|
jobs:
|
|
gate:
|
|
if: github.repository == 'nexu-io/open-design'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check for un-landed backports
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPO: ${{ github.repository }}
|
|
BR: ${{ inputs.release_branch }}
|
|
run: |
|
|
set -euo pipefail
|
|
# Constrain the input to the release/vX.Y.Z grammar first. Any other branch (e.g. main)
|
|
# must never pass the gate, even if it exists and happens to have no open PRs targeting it.
|
|
if ! printf '%s' "$BR" | grep -Eq '^release/v[0-9]+\.[0-9]+\.[0-9]+$'; then
|
|
echo "::error::Invalid release branch '$BR' (expected release/vX.Y.Z)"
|
|
exit 1
|
|
fi
|
|
# Fail closed: the branch must actually exist. A mistyped or not-yet-cut branch makes
|
|
# `gh pr list` return an empty list, which would otherwise look like "clean / safe".
|
|
if ! gh api "repos/$REPO/git/ref/heads/$BR" >/dev/null 2>&1; then
|
|
echo "::error::Release branch '$BR' does not exist — failing closed (refusing to pass the gate)."
|
|
exit 1
|
|
fi
|
|
echo "Checking $BR for un-landed backports (open PRs)…"
|
|
open=$(gh pr list --repo "$REPO" --base "$BR" --state open \
|
|
--json number,title,isDraft \
|
|
--jq '.[] | " #\(.number) \(.title)\(if .isDraft then " [draft/conflict]" else "" end)"')
|
|
if [ -n "$open" ]; then
|
|
echo "::error::$BR has un-landed backports, do not release:"
|
|
echo "$open"
|
|
exit 1
|
|
fi
|
|
echo "✅ $BR has no pending backports, safe to tag and release."
|