diff --git a/.github/workflows/docs-preview-main.yml b/.github/workflows/docs-preview-main.yml new file mode 100644 index 0000000000..7a2bdfa793 --- /dev/null +++ b/.github/workflows/docs-preview-main.yml @@ -0,0 +1,55 @@ +# Refreshes the persistent Mintlify preview for `main` so the team can see the +# current "next" state of the docs at superdoc-main.mintlify.app without +# opening a draft PR. Mintlify reuses the URL per branch, so each push +# refreshes content in place. Production docs stay on docs-stable; this is +# unrelated to the production gate. +name: 📖 Docs preview (main) + +on: + push: + branches: + - main + paths: + - 'apps/docs/**' + - 'packages/document-api/src/contract/**' + - 'scripts/generate-all.mjs' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: refresh-main-docs-preview + cancel-in-progress: true + +jobs: + trigger-preview: + runs-on: ubuntu-latest + steps: + - name: Trigger Mintlify preview for main + env: + MINTLIFY_API_KEY: ${{ secrets.MINTLIFY_API_KEY }} + MINTLIFY_PROJECT_ID: ${{ secrets.MINTLIFY_PROJECT_ID }} + 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." + exit 0 + fi + + response=$(curl -sS -X POST \ + -H "Authorization: Bearer ${MINTLIFY_API_KEY}" \ + -H "Content-Type: application/json" \ + -d '{"branch": "main"}' \ + "https://api.mintlify.com/v1/project/preview/${MINTLIFY_PROJECT_ID}") + + echo "Mintlify response: ${response}" + preview_url=$(echo "${response}" | jq -r '.previewUrl // empty') + + if [ -n "$preview_url" ]; then + { + echo "## Next docs preview refreshed" + echo + echo "URL: $preview_url" + } >> "$GITHUB_STEP_SUMMARY" + fi diff --git a/.github/workflows/docs-preview-pr.yml b/.github/workflows/docs-preview-pr.yml new file mode 100644 index 0000000000..572b8bc77d --- /dev/null +++ b/.github/workflows/docs-preview-pr.yml @@ -0,0 +1,76 @@ +# Triggers a Mintlify preview deployment for docs-touching PRs targeting +# `main` or `stable`. The `stable` case covers promote-stable PRs so reviewers +# see the docs that will go live the moment the release succeeds. The `main` +# case gives doc authors a hosted preview during normal development, since +# Mintlify's automatic previews only fire on PRs targeting the deployment +# branch (`docs-stable`). Path filter keeps the 5 req/min Mintlify API quota +# from being spent on unrelated code PRs. +name: 📖 Docs preview (PR) + +on: + pull_request: + types: [opened, reopened, synchronize] + branches: + - main + - stable + paths: + - 'apps/docs/**' + - 'packages/document-api/src/contract/**' + - 'scripts/generate-all.mjs' + +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 once per PR. Mintlify reuses the same URL for the same branch + # on redeploys, so subsequent pushes refresh content in place at the same + # URL. We can't gate on `action == 'opened'` because the workflow may + # first run on a `synchronize` event (e.g., when this workflow file + # didn't exist on the base branch when the PR was opened), so we check + # for an existing preview comment instead. + - name: Comment preview URL on PR + if: steps.preview.outputs.preview_url != '' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PREVIEW_URL: ${{ steps.preview.outputs.preview_url }} + run: | + set -euo pipefail + existing=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json comments \ + --jq '[.comments[] | select(.body | startswith("📖 Docs preview:"))] | length') + if [ "$existing" -gt 0 ]; then + echo "Preview comment already exists; skipping." + exit 0 + fi + 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 08611c6a1a..15a9c56422 100644 --- a/.github/workflows/release-stable.yml +++ b/.github/workflows/release-stable.yml @@ -171,3 +171,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/apps/docs/getting-started/introduction.mdx b/apps/docs/getting-started/introduction.mdx index a7bd32e415..ce05d258fb 100644 --- a/apps/docs/getting-started/introduction.mdx +++ b/apps/docs/getting-started/introduction.mdx @@ -28,14 +28,14 @@ SuperDoc renders and edits DOCX files directly in the browser. Built on OOXML icon="download" href="/getting-started/installation" > - Get started in minutes + Get started in seconds - Headless mode and LLM workflows + Headless mode and agent workflows r.status === 'released'); if (anyReleased && !isDryRun) {