Files
orca/.github/workflows/docs.yml
T

329 lines
12 KiB
YAML

name: Docs site
on:
# Stable desktop releases are the production publication boundary. The
# release gate below excludes mobile and prerelease tags from this trigger.
release:
types: [published]
pull_request:
paths:
- 'docs/site/**'
- '.github/workflows/docs.yml'
workflow_dispatch:
inputs:
tag:
description: 'Stable desktop release tag to redeploy (vX.Y.Z)'
required: true
type: string
permissions:
contents: read
concurrency:
group: docs-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('release-{0}', github.event.release.tag_name || inputs.tag) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
defaults:
run:
# The Vercel project is rooted at `.`; this package directory is the
# complete upload and build context.
working-directory: docs/site
jobs:
# This job deliberately has no deployment credentials and runs for fork PRs
# as well as same-repository PRs.
check:
name: Build and test
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout pull request
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10.24.0
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: docs/site/pnpm-lock.yaml
- name: Install site dependencies
run: pnpm --ignore-workspace install --frozen-lockfile
- name: Run package tests
run: pnpm --ignore-workspace test
- name: Lint site
run: pnpm --ignore-workspace lint
- name: Typecheck site
run: pnpm --ignore-workspace exec tsc --noEmit --incremental false
- name: Build site
run: pnpm --ignore-workspace build
release_gate:
name: Authorize release
if: >-
github.repository == 'stablyai/orca' &&
(github.event_name == 'release' || github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
deploy: ${{ steps.validate.outputs.deploy }}
steps:
- name: Validate stable desktop tag
id: validate
env:
EVENT_NAME: ${{ github.event_name }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
INPUT_TAG: ${{ inputs.tag }}
RELEASE_PRERELEASE: ${{ github.event.release.prerelease }}
RELEASE_DRAFT: ${{ github.event.release.draft }}
RELEASE_AUTHOR: ${{ github.event.release.author.login }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
WORKFLOW_REF: ${{ github.ref }}
GH_TOKEN: ${{ github.token }}
shell: bash
working-directory: .
run: |
set -euo pipefail
tag="$INPUT_TAG"
[[ "$EVENT_NAME" == "release" ]] && tag="$RELEASE_TAG"
# Match the stable desktop format used by release-policy.yml. This
# intentionally rejects mobile-* and all -rc.* tags.
stable_tag=false
[[ "$tag" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] && stable_tag=true
if [[ "$stable_tag" != "true" ]]; then
echo "Release $tag is not a stable desktop release; skipping docs deployment."
echo "deploy=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [[ "$EVENT_NAME" == "release" ]]; then
authorized_ref=true
authorized_author=false
[[ "$RELEASE_AUTHOR" == "github-actions[bot]" ]] && authorized_author=true
release_state_ok=false
[[ "$RELEASE_PRERELEASE" == "false" && "$RELEASE_DRAFT" == "false" ]] && release_state_ok=true
else
authorized_ref=false
[[ "$WORKFLOW_REF" == "refs/heads/$DEFAULT_BRANCH" ]] && authorized_ref=true
release_json="$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$tag")"
authorized_author=false
[[ "$(jq -r '.author.login' <<<"$release_json")" == "github-actions[bot]" ]] && authorized_author=true
release_state_ok=false
if [[ "$(jq -r '.tag_name' <<<"$release_json")" == "$tag" &&
"$(jq -r '.prerelease' <<<"$release_json")" == "false" &&
"$(jq -r '.draft' <<<"$release_json")" == "false" ]]; then
release_state_ok=true
fi
fi
deploy=false
if [[ "$authorized_ref" == "true" && "$authorized_author" == "true" && "$release_state_ok" == "true" ]]; then
deploy=true
else
echo "Release $tag is not an authorized stable desktop release; skipping docs deployment."
fi
echo "deploy=$deploy" >> "$GITHUB_OUTPUT"
preview:
name: Preview
# Only trusted branches can access the Vercel preview environment. Forks
# still receive the credential-free check job above.
needs: check
if: >-
github.event_name == 'pull_request' &&
needs.check.result == 'success' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
issues: write
pull-requests: write
environment:
name: docs-preview
env:
VERCEL_TELEMETRY_DISABLED: '1'
steps:
- name: Checkout pull request
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10.24.0
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: docs/site/pnpm-lock.yaml
- name: Install site dependencies
run: pnpm --ignore-workspace install --frozen-lockfile
- name: Verify Vercel credentials
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
set -euo pipefail
test -n "${VERCEL_TOKEN:-}" || { echo '::error::VERCEL_TOKEN is not configured for docs-preview.'; exit 1; }
test -n "${VERCEL_ORG_ID:-}" || { echo '::error::VERCEL_ORG_ID is not configured for docs-preview.'; exit 1; }
test -n "${VERCEL_PROJECT_ID:-}" || { echo '::error::VERCEL_PROJECT_ID is not configured for docs-preview.'; exit 1; }
- name: Pull Vercel preview settings
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: pnpm --ignore-workspace exec vercel pull --yes --non-interactive --environment=preview
- name: Build preview
run: pnpm --ignore-workspace exec vercel build --non-interactive
- name: Deploy preview
id: deploy
shell: bash
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
set -euo pipefail
# `--non-interactive` returns a JSON status envelope, not a bare URL.
deployment_json="$(pnpm --ignore-workspace exec vercel deploy --prebuilt --yes --non-interactive --format json)"
printf '%s\n' "$deployment_json"
url="$(jq -er '.deployment.url // .url // empty' <<<"$deployment_json")"
[[ "$url" =~ ^https://[^[:space:]]+$ ]]
printf '%s\n' "$url"
echo "url=$url" >> "$GITHUB_OUTPUT"
echo "Preview deployed to $url" >> "$GITHUB_STEP_SUMMARY"
- name: Link preview in pull request
uses: actions/github-script@v8
env:
PREVIEW_URL: ${{ steps.deploy.outputs.url }}
with:
script: |
const marker = '<!-- docs-preview -->'
const url = process.env.PREVIEW_URL
if (!url) throw new Error('Preview URL was not produced')
const body = `${marker}\nDocs preview: [${url}](${url})`
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100
})
const existing = comments.find((comment) => comment.body?.includes(marker))
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body
})
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
})
}
production:
name: Production
if: >-
(github.event_name == 'release' || github.event_name == 'workflow_dispatch') &&
needs.release_gate.result == 'success' &&
needs.release_gate.outputs.deploy == 'true'
needs: release_gate
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: docs-production
url: https://www.onorca.dev/docs
env:
VERCEL_TELEMETRY_DISABLED: '1'
steps:
- name: Checkout released tag
uses: actions/checkout@v6
with:
ref: ${{ github.event.release.tag_name || inputs.tag }}
persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10.24.0
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: docs/site/pnpm-lock.yaml
- name: Install site dependencies
run: pnpm --ignore-workspace install --frozen-lockfile
- name: Verify Vercel credentials
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
set -euo pipefail
test -n "${VERCEL_TOKEN:-}" || { echo '::error::VERCEL_TOKEN is not configured for docs-production.'; exit 1; }
test -n "${VERCEL_ORG_ID:-}" || { echo '::error::VERCEL_ORG_ID is not configured for docs-production.'; exit 1; }
test -n "${VERCEL_PROJECT_ID:-}" || { echo '::error::VERCEL_PROJECT_ID is not configured for docs-production.'; exit 1; }
- name: Pull Vercel production settings
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: pnpm --ignore-workspace exec vercel pull --yes --non-interactive --environment=production
- name: Run package tests
run: pnpm --ignore-workspace test
- name: Lint site
run: pnpm --ignore-workspace lint
- name: Typecheck site
run: pnpm --ignore-workspace exec tsc --noEmit --incremental false
- name: Build production site
run: pnpm --ignore-workspace exec vercel build --prod --non-interactive
- name: Deploy production site
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: pnpm --ignore-workspace exec vercel deploy --prebuilt --prod --yes --non-interactive