diff --git a/.github/workflows/preview-stable-docs.yml b/.github/workflows/preview-stable-docs.yml new file mode 100644 index 0000000000..f52f8041b6 --- /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" --repo "$GITHUB_REPOSITORY" --body "📖 Docs preview: ${PREVIEW_URL}" diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml index 8709d73df6..b56f944904 100644 --- a/.github/workflows/release-stable.yml +++ b/.github/workflows/release-stable.yml @@ -176,3 +176,16 @@ jobs: with: packages-dir: packages/sdk/langs/python/dist/ skip-existing: true + + # 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 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) {