Skip to content
55 changes: 55 additions & 0 deletions .github/workflows/docs-preview-main.yml
Original file line number Diff line number Diff line change
@@ -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
76 changes: 76 additions & 0 deletions .github/workflows/docs-preview-pr.yml
Original file line number Diff line number Diff line change
@@ -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}"
13 changes: 13 additions & 0 deletions .github/workflows/release-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
4 changes: 2 additions & 2 deletions apps/docs/getting-started/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
</Card>
<Card
title="AI Agents"
icon="sparkles"
href="/getting-started/ai-agents"
>
Headless mode and LLM workflows
Headless mode and agent workflows
</Card>
<Card
title="Examples"
Expand Down
8 changes: 8 additions & 0 deletions scripts/release-local-stable.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Loading