From 59362acfd3fb100f7e5d859412544214445ecb00 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Wed, 29 Apr 2026 07:15:42 -0300 Subject: [PATCH 1/2] ci(release): gate production docs on stable release success Mintlify currently deploys from main, so docs ship as soon as a doc commit lands, often weeks before the underlying packages reach npm via the weekly stable release. Move production docs onto a `docs-stable` branch that the release workflow only advances after every publish step succeeds, so docs and packages move together. - release-stable.yml pushes stable -> docs-stable as its final step, only reachable on full success - new preview-stable-docs.yml triggers a Mintlify preview for any PR targeting stable so the promote-stable PR shows reviewers the exact docs that will go live - requires manual setup before this can take effect: create the docs-stable branch from current stable, switch Mintlify's deployment branch in the dashboard, and add MINTLIFY_API_KEY / MINTLIFY_PROJECT_ID repo secrets (preview workflow no-ops without them) --- .github/workflows/preview-stable-docs.yml | 56 +++++++++++++++++++++++ .github/workflows/release-stable.yml | 11 +++++ 2 files changed, 67 insertions(+) create mode 100644 .github/workflows/preview-stable-docs.yml diff --git a/.github/workflows/preview-stable-docs.yml b/.github/workflows/preview-stable-docs.yml new file mode 100644 index 0000000000..5677bf7a3e --- /dev/null +++ b/.github/workflows/preview-stable-docs.yml @@ -0,0 +1,56 @@ +# Triggers a Mintlify preview deployment for any PR targeting `stable`. +# Promote-stable PRs target this branch, so reviewers see the docs that will +# go live the moment the release succeeds. +name: 👀 Preview docs for stable PRs + +on: + pull_request: + types: [opened, reopened, synchronize] + branches: + - stable + +permissions: + pull-requests: write + +concurrency: + group: preview-stable-docs-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + trigger-preview: + runs-on: ubuntu-latest + steps: + - name: Trigger Mintlify preview + id: preview + env: + MINTLIFY_API_KEY: ${{ secrets.MINTLIFY_API_KEY }} + MINTLIFY_PROJECT_ID: ${{ secrets.MINTLIFY_PROJECT_ID }} + BRANCH: ${{ github.head_ref }} + run: | + set -euo pipefail + if [ -z "${MINTLIFY_API_KEY}" ] || [ -z "${MINTLIFY_PROJECT_ID}" ]; then + echo "MINTLIFY_API_KEY or MINTLIFY_PROJECT_ID not set; skipping." + echo "preview_url=" >> "$GITHUB_OUTPUT" + exit 0 + fi + + response=$(curl -sS -X POST \ + -H "Authorization: Bearer ${MINTLIFY_API_KEY}" \ + -H "Content-Type: application/json" \ + -d "{\"branch\": \"${BRANCH}\"}" \ + "https://api.mintlify.com/v1/project/preview/${MINTLIFY_PROJECT_ID}") + + echo "Mintlify response: ${response}" + preview_url=$(echo "${response}" | jq -r '.previewUrl // empty') + echo "preview_url=${preview_url}" >> "$GITHUB_OUTPUT" + + # Comment only on first open. Mintlify reuses the same URL for the same + # branch on redeploys, so subsequent pushes refresh content in place. + - name: Comment preview URL on PR + if: steps.preview.outputs.preview_url != '' && github.event.action == 'opened' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PREVIEW_URL: ${{ steps.preview.outputs.preview_url }} + run: | + gh pr comment "$PR_NUMBER" --body "📖 Docs preview: ${PREVIEW_URL}" diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml index 8709d73df6..1549d86bd1 100644 --- a/.github/workflows/release-stable.yml +++ b/.github/workflows/release-stable.yml @@ -176,3 +176,14 @@ jobs: with: packages-dir: packages/sdk/langs/python/dist/ skip-existing: true + + # Stable docs deploy from `docs-stable`. Promoting only after every release + # step above has succeeded keeps published docs in lockstep with the + # packages users can actually install. + - name: Promote stable docs + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + run: | + set -euo pipefail + git fetch origin stable + git push origin refs/remotes/origin/stable:refs/heads/docs-stable From 5bd6e25e79a1488fce6e2814c2938c4bc51a41d1 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Wed, 29 Apr 2026 07:25:38 -0300 Subject: [PATCH 2/2] fix(release): gate docs-stable promote on verified release SHA Address review on PR 2989. Two issues: - The promote step refetched `origin/stable` and pushed it, so a newer commit landing on stable while the release was still publishing would be promoted to docs-stable before the next run actually published it. The orchestrator now emits `promote_sha` (current HEAD) only when no package failed and no deferral happened; the workflow gates the push on that output and pushes the explicit SHA instead of refetching. - The preview workflow called `gh pr comment` without checking out the repo, so gh had no repo context. Added `--repo "$GITHUB_REPOSITORY"`. --- .github/workflows/preview-stable-docs.yml | 2 +- .github/workflows/release-stable.yml | 12 +++++++----- scripts/release-local-stable.mjs | 8 ++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/preview-stable-docs.yml b/.github/workflows/preview-stable-docs.yml index 5677bf7a3e..f52f8041b6 100644 --- a/.github/workflows/preview-stable-docs.yml +++ b/.github/workflows/preview-stable-docs.yml @@ -53,4 +53,4 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} PREVIEW_URL: ${{ steps.preview.outputs.preview_url }} run: | - gh pr comment "$PR_NUMBER" --body "📖 Docs preview: ${PREVIEW_URL}" + gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body "📖 Docs preview: ${PREVIEW_URL}" diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml index 1549d86bd1..b56f944904 100644 --- a/.github/workflows/release-stable.yml +++ b/.github/workflows/release-stable.yml @@ -177,13 +177,15 @@ jobs: packages-dir: packages/sdk/langs/python/dist/ skip-existing: true - # Stable docs deploy from `docs-stable`. Promoting only after every release - # step above has succeeded keeps published docs in lockstep with the - # packages users can actually install. + # Stable docs deploy from `docs-stable`. The orchestrator emits + # `promote_sha` only when the run released (or no-op'd) on the SHA it + # checked out; it stays empty on failure or deferral, so production docs + # never advance past a commit whose packages were not actually published. - name: Promote stable docs + if: steps.stable_release.outputs.promote_sha != '' env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + PROMOTE_SHA: ${{ steps.stable_release.outputs.promote_sha }} run: | set -euo pipefail - git fetch origin stable - git push origin refs/remotes/origin/stable:refs/heads/docs-stable + git push origin "${PROMOTE_SHA}:refs/heads/docs-stable" diff --git a/scripts/release-local-stable.mjs b/scripts/release-local-stable.mjs index b4fe84ca04..b1e4697b39 100644 --- a/scripts/release-local-stable.mjs +++ b/scripts/release-local-stable.mjs @@ -696,6 +696,7 @@ for (const arg of process.argv.slice(2)) { setStepOutput('sdk_python_snapshot_tag', ''); setStepOutput('sdk_python_snapshot_companion_dir', ''); setStepOutput('sdk_python_snapshot_main_dir', ''); +setStepOutput('promote_sha', ''); // --------------------------------------------------------------------------- // Branch guard @@ -923,6 +924,13 @@ if (deferredReason && !hasFailed) { console.log('\nCurrent run stopped before publishing from a stale checkout. The next queued stable run should continue from the latest branch head.'); } +// Emit the SHA the docs-stable promotion step should publish. Stays empty when +// the run failed or was deferred, so production docs never advance past a +// commit whose packages are not actually published. +if (!hasFailed && !deferredReason) { + setStepOutput('promote_sha', getCurrentHead()); +} + // Remind operator about @semantic-release/git behavior on stable const anyReleased = [...results.values()].some((r) => r.status === 'released'); if (anyReleased && !isDryRun) {