From 0432352ae5f4adb7b0f99c2935e61c80059e718b Mon Sep 17 00:00:00 2001 From: ework-agent Date: Sat, 19 Sep 2026 12:10:31 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(ci):=20one-click=20release=20=E2=80=94?= =?UTF-8?q?=20go=20green=20when=20prepared;=20auto-PR=20is=20best-effort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release-manual.yml | 45 +++++++++++++++++++++++----- AGENTS.md | 10 +++++-- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-manual.yml b/.github/workflows/release-manual.yml index 7d5838c1..c9dd8db5 100644 --- a/.github/workflows/release-manual.yml +++ b/.github/workflows/release-manual.yml @@ -96,6 +96,7 @@ jobs: npm run build - name: Commit and push to master + id: push env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -107,28 +108,56 @@ jobs: echo "pushed release commit to master" exit 0 fi - echo "::error::direct push to master was blocked (branch protection). Fallback: opening a release PR." + echo "::warning::direct push to master was blocked (branch protection). Fallback: release prepared on a branch; a PR is needed to publish." BR="$(date -u +%Y-%m-%d)_release-v${VER}" if ! git push -q origin "HEAD:$BR"; then echo "::error::fallback branch push to ${BR} failed — nothing published, aborting." exit 1 fi + echo "fallback release branch pushed: ${BR}" + echo "needs_pr=true" >> "$GITHUB_OUTPUT" + echo "pr_branch=${BR}" >> "$GITHUB_OUTPUT" + exit 0 + + - name: Open fallback PR (best effort) + if: steps.push.outputs.needs_pr == 'true' + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_BRANCH: ${{ steps.push.outputs.pr_branch }} + run: | + VER="${{ steps.version.outputs.version }}" + LINK="https://github.com/${{ github.repository }}/pull/new/${PR_BRANCH}" RESP_FILE="$(mktemp)" HTTP_CODE=$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -X POST \ -H "Authorization: Bearer $GH_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "Content-Type: application/json" \ "https://api.github.com/repos/${{ github.repository }}/pulls" \ - -d "{\"title\":\"release v${VER}\",\"head\":\"${BR}\",\"base\":\"master\",\"body\":\"Auto-fallback from the one-click release workflow: direct push to master was blocked by branch protection. Merging this PR publishes via the standard release.yml flow.\\n\\n\"}") + -d "{\"title\":\"release v${VER}\",\"head\":\"${PR_BRANCH}\",\"base\":\"master\",\"body\":\"Auto-fallback from the one-click release workflow: direct push to master was blocked by branch protection. Merging this PR publishes via the standard release.yml flow.\\n\\n\"}") PR_URL=$(node -e 'try{console.log(JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).html_url||"")}catch(e){console.log("")}' "$RESP_FILE") echo "POST /pulls HTTP: $HTTP_CODE" - if [ -z "$PR_URL" ]; then - echo "::error::failed to open the fallback PR automatically (HTTP $HTTP_CODE): $(cat "$RESP_FILE")" + if [ -n "$PR_URL" ]; then + { + echo "### :rotating_light: Release v${VER} prepared — NOT PUBLISHED yet" + echo "" + echo "Direct push to master was blocked (branch protection); the release commit is on branch \`${PR_BRANCH}\`." + echo "Release PR opened automatically: **${PR_URL}** — merge it to publish via CI." + } >> "$GITHUB_STEP_SUMMARY" + echo "::notice::Release PR opened automatically: ${PR_URL} — merge it to publish v${VER}" + exit 0 fi - echo "" - echo "::notice::nothing was published yet. Finish the release by merging:" - echo "::notice::${PR_URL:-https://github.com/${{ github.repository }}/pull/new/${BR}}" - exit 1 + { + echo "### :rotating_light: Release v${VER} prepared — NOTHING PUBLISHED YET" + echo "" + echo "The release commit passed the full pre-flight gate and is on branch \`${PR_BRANCH}\`." + echo "Auto-opening the release PR failed (HTTP ${HTTP_CODE}): $(cat "$RESP_FILE")" + echo "" + echo "**Finish with one click:** [open the release PR](${LINK}), then merge it to publish." + } >> "$GITHUB_STEP_SUMMARY" + echo "::warning::NOTHING WAS PUBLISHED YET — auto-PR failed (HTTP ${HTTP_CODE}): $(cat "$RESP_FILE")" + echo "::warning::Finish the release with one click: ${LINK} (then merge to publish v${VER})" + exit 0 - name: Check if prerelease id: pre diff --git a/AGENTS.md b/AGENTS.md index 34e1562a..a4e1c7c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -383,9 +383,13 @@ latest; type a full semver for minor/major/prerelease bumps. The workflow: `release v{VERSION}` — the same one-version-one-commit discipline as the Version Bumps section above. 3. Runs the full pre-flight gate (`npm ci` + typecheck + test + build). -4. Pushes the release commit directly to `master` (GITHUB_TOKEN, fast-forward - only). If branch protection blocks direct pushes, it falls back to opening - a release PR — merge that to publish via the standard flow. + 4. Pushes the release commit directly to `master` (GITHUB_TOKEN, fast-forward + only). If branch protection blocks direct pushes, the release lands on a + release branch instead and the run tries to open the release PR itself + (best-effort — if the account forbids Actions-created PRs the run still + finishes green with a one-click "open the release PR" link in the job + summary). Merging that PR publishes via the standard flow; red is reserved + for real failures (guard trips, gate failures, or a failed branch push). 5. Publishes to npm (`latest`, or `dev` for prerelease), tags `v{VERSION}`, and creates the GitHub Release with notes generated from `git log` since the last tag. From f3895c44c296758ac73d353dfb3d36fa6e84bf7a Mon Sep 17 00:00:00 2001 From: ework-agent Date: Sat, 19 Sep 2026 12:22:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(ci):=20gate=20tag/publish/release=20on?= =?UTF-8?q?=20direct=20push=20=E2=80=94=20fallback=20run=20must=20not=20pu?= =?UTF-8?q?blish=20(#978)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release-manual.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release-manual.yml b/.github/workflows/release-manual.yml index c9dd8db5..41fc1bde 100644 --- a/.github/workflows/release-manual.yml +++ b/.github/workflows/release-manual.yml @@ -106,6 +106,7 @@ jobs: git commit -q -m "release v$VER" if git push -q origin HEAD:master; then echo "pushed release commit to master" + echo "direct=true" >> "$GITHUB_OUTPUT" exit 0 fi echo "::warning::direct push to master was blocked (branch protection). Fallback: release prepared on a branch; a PR is needed to publish." @@ -161,6 +162,7 @@ jobs: - name: Check if prerelease id: pre + if: steps.push.outputs.direct == 'true' run: | if echo "${{ steps.version.outputs.version }}" | grep -q '-'; then echo "tag=dev" >> "$GITHUB_OUTPUT" @@ -171,16 +173,19 @@ jobs: fi - name: Create tag + if: steps.push.outputs.direct == 'true' run: | git tag "v${{ steps.version.outputs.version }}" || true git push origin "v${{ steps.version.outputs.version }}" || true - name: Publish + if: steps.push.outputs.direct == 'true' run: npm publish --tag ${{ steps.pre.outputs.tag }} --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Create GitHub Release + if: steps.push.outputs.direct == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: |