diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 5bfec3da..f059f0f7 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -1,3 +1,4 @@ +--- name: "Claude Code Review" "on": diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 9fa7c1f2..c86a6239 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,3 +1,4 @@ +--- name: "Claude Code" # Note: only users with write permissions can invoke Claude jobs diff --git a/.github/workflows/link-checker-full.yaml b/.github/workflows/link-checker-full.yaml index c1887532..e7ab021d 100644 --- a/.github/workflows/link-checker-full.yaml +++ b/.github/workflows/link-checker-full.yaml @@ -1,7 +1,7 @@ --- name: "Link Checker with External Links" -on: +"on": schedule: - cron: "0 18 * * *" # Daily at 18:00 UTC workflow_dispatch: diff --git a/.github/workflows/link-checker.yaml b/.github/workflows/link-checker.yaml index 8c294567..af27c842 100644 --- a/.github/workflows/link-checker.yaml +++ b/.github/workflows/link-checker.yaml @@ -1,7 +1,7 @@ --- name: "Link Checker" -on: +"on": push: branches: ["main"] pull_request: diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 02f8ddd5..18a0f807 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,6 +1,6 @@ --- name: "Lint" -on: +"on": push: branches: - "main" @@ -25,7 +25,5 @@ jobs: cache: "pnpm" - name: "Install dependencies" run: "pnpm install" - - name: "Lint Markdown" - run: "pnpm run lint:markdown" - name: "Check Formatting" run: "pnpm run format:check" diff --git a/.github/workflows/spellcheck.yaml b/.github/workflows/spellcheck.yaml index b7e88795..fabe4dc0 100644 --- a/.github/workflows/spellcheck.yaml +++ b/.github/workflows/spellcheck.yaml @@ -1,6 +1,6 @@ --- name: "Spellcheck" -on: +"on": push: branches: - "main" diff --git a/.github/workflows/vercel-preview.yml b/.github/workflows/vercel-preview.yml new file mode 100644 index 00000000..6a38b94c --- /dev/null +++ b/.github/workflows/vercel-preview.yml @@ -0,0 +1,192 @@ +--- +name: "Vercel Preview" + +"on": + pull_request: + types: + - "opened" + - "synchronize" + - "reopened" + +jobs: + deploy-preview: + name: "Deploy Preview" + runs-on: "depot-ubuntu-latest" + timeout-minutes: 15 + concurrency: + group: "vercel-preview-ga-${{ github.event.pull_request.number }}" + cancel-in-progress: true + permissions: + contents: "read" + deployments: "write" + pull-requests: "write" + steps: + - name: "Checkout code" + uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 + + - name: "Check collaborator write access" + id: "permission" + uses: "actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b" # v7 + with: + result-encoding: "string" + script: | + const author = context.payload.pull_request.user.login; + try { + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: author, + }); + const hasWrite = ['write', 'maintain', 'admin'].includes(data.permission); + core.info(`${author} has permission '${data.permission}' — hasWrite: ${hasWrite}`); + return String(hasWrite); + } catch (e) { + if (e.status === 404) { + core.info(`${author} is not a collaborator — hasWrite: false`); + return 'false'; + } + throw e; + } + + - name: "Find existing comment" + uses: "peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e" # v3 + id: "find-comment" + with: + issue-number: "${{ github.event.pull_request.number }}" + comment-author: "github-actions[bot]" + body-includes: "" + + - name: "Post no-permission comment" + if: "steps.permission.outputs.result == 'false'" + uses: "peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043" # v4 + with: + comment-id: "${{ steps.find-comment.outputs.comment-id }}" + issue-number: "${{ github.event.pull_request.number }}" + edit-mode: "replace" + body: | + + Preview deployment skipped — **@${{ github.event.pull_request.user.login }}** does not have write access to this repository. + + A maintainer can trigger a preview deployment by [re-running this workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). + + - name: "Setup Node.js" + if: "steps.permission.outputs.result == 'true'" + uses: "actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238" # v6 + with: + node-version: 22 + + - name: "Generate timestamp" + if: "steps.permission.outputs.result == 'true'" + id: "timestamp-building" + run: | + echo "time=$(date -u '+%b %d, %Y %I:%M%P')" >> $GITHUB_OUTPUT + + - name: "Post initial building comment" + if: "steps.permission.outputs.result == 'true'" + uses: "peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043" # v4 + id: "building-comment" + with: + comment-id: "${{ steps.find-comment.outputs.comment-id }}" + issue-number: "${{ github.event.pull_request.number }}" + edit-mode: "replace" + body: | + + Preview deployment status for this pull request. + + | Name | Status | Preview | Updated (UTC) | + | :--- | :----- | :------ | :------------ | + | **docs** | 🟡 [Building](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | [View Workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ${{ steps.timestamp-building.outputs.time }} | + + - name: "Deploy to Vercel" + if: "steps.permission.outputs.result == 'true'" + id: "deploy" + env: + VERCEL_TOKEN: "${{ secrets.VERCEL_TOKEN }}" + VERCEL_ORG_ID: "${{ secrets.VERCEL_ORG_ID }}" + VERCEL_PROJECT_ID: "${{ secrets.VERCEL_PROJECT_ID }}" + run: | + npm install --global vercel@latest + + DEPLOY_URL=$(vercel deploy --token "$VERCEL_TOKEN" --yes | tail -1) + echo "preview-url=$DEPLOY_URL" >> $GITHUB_OUTPUT + + # Sanitize branch name for DNS: lowercase, non-alphanumeric to hyphens, collapse runs + # Truncate to 46 chars so the full alias (docs-git--authzed) stays within the 63-char DNS label limit + BRANCH=$(echo "${{ github.head_ref }}" \ + | tr '[:upper:]' '[:lower:]' \ + | sed 's/[^a-z0-9]/-/g' \ + | sed 's/-\+/-/g' \ + | sed 's/^-//; s/-$//' \ + | cut -c1-46 \ + | sed 's/-$//') + + vercel alias set "$DEPLOY_URL" "docs-git-${BRANCH}-authzed.vercel.app" \ + --token "$VERCEL_TOKEN" + + - name: "Create GitHub Deployment" + if: "steps.permission.outputs.result == 'true'" + uses: "chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1" # v2 + id: "deployment" + with: + token: "${{ secrets.GITHUB_TOKEN }}" + environment: "Preview (GitHub Actions)" + environment-url: "${{ steps.deploy.outputs.preview-url }}" + transient-environment: true + auto-inactive: true + + - name: "Update deployment status to success" + if: "steps.permission.outputs.result == 'true' && success()" + uses: "chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806" # v2 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + deployment-id: "${{ steps.deployment.outputs.deployment_id }}" + state: "success" + environment-url: "${{ steps.deploy.outputs.preview-url }}" + + - name: "Update deployment status to failure" + if: "steps.permission.outputs.result == 'true' && failure()" + uses: "chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806" # v2 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + deployment-id: "${{ steps.deployment.outputs.deployment_id }}" + state: "failure" + + - name: "Generate timestamp for success" + if: "steps.permission.outputs.result == 'true' && success()" + id: "timestamp-success" + run: | + echo "time=$(date -u '+%b %d, %Y %I:%M%P')" >> $GITHUB_OUTPUT + + - name: "Update comment with success" + if: "steps.permission.outputs.result == 'true' && success()" + uses: "peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043" # v4 + with: + comment-id: "${{ steps.building-comment.outputs.comment-id }}" + edit-mode: "replace" + body: | + + Preview deployment status for this pull request. + + | Name | Status | Preview | Updated (UTC) | + | :--- | :----- | :------ | :------------ | + | **docs** | 🟢 [Ready](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | [Visit Preview](${{ steps.deploy.outputs.preview-url }}) | ${{ steps.timestamp-success.outputs.time }} | + + - name: "Generate timestamp for failure" + if: "steps.permission.outputs.result == 'true' && failure()" + id: "timestamp-failure" + run: | + echo "time=$(date -u '+%b %d, %Y %I:%M%P')" >> $GITHUB_OUTPUT + + - name: "Update comment with failure" + if: "steps.permission.outputs.result == 'true' && failure()" + uses: "peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043" # v4 + with: + comment-id: "${{ steps.building-comment.outputs.comment-id }}" + edit-mode: "replace" + body: | + + Preview deployment status for this pull request. + + | Name | Status | Preview | Updated (UTC) | + | :--- | :----- | :------ | :------------ | + | **docs** | 🔴 [Failed](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | [View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ${{ steps.timestamp-failure.outputs.time }} | diff --git a/.github/workflows/vercel-production.yml b/.github/workflows/vercel-production.yml new file mode 100644 index 00000000..4e59c308 --- /dev/null +++ b/.github/workflows/vercel-production.yml @@ -0,0 +1,95 @@ +--- +name: "Vercel Production Deployment" + +"on": + push: + branches: ["main"] + workflow_dispatch: + +jobs: + deploy-production: + name: "Deploy to Production" + runs-on: "depot-ubuntu-latest" + timeout-minutes: 15 + permissions: + contents: "read" + deployments: "write" + steps: + - name: "Checkout code" + uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 + + - name: "Setup Node.js" + uses: "actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238" # v6 + with: + node-version: 22 + + - name: "Start deployment" + run: | + echo "🚀 Starting production deployment to authzed.com/docs" + echo "📦 Commit: ${{ github.sha }}" + echo "👤 Triggered by: ${{ github.actor }}" + echo "⏰ Timestamp: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" + + - name: "Deploy to Vercel Production" + id: "deploy" + env: + VERCEL_TOKEN: "${{ secrets.VERCEL_TOKEN }}" + VERCEL_ORG_ID: "${{ secrets.VERCEL_ORG_ID }}" + VERCEL_PROJECT_ID: "${{ secrets.VERCEL_PROJECT_ID }}" + run: | + npm install --global vercel@latest + + DEPLOY_URL=$(vercel deploy --token "$VERCEL_TOKEN" --prod --yes | tail -1) + echo "preview-url=$DEPLOY_URL" >> $GITHUB_OUTPUT + + - name: "Create GitHub Deployment" + uses: "chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1" # v2 + id: "github-deployment" + with: + token: "${{ secrets.GITHUB_TOKEN }}" + environment: "Production" + environment-url: "https://authzed.com/docs" + auto-inactive: false + + - name: "Update deployment status to success" + if: "success()" + uses: "chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806" # v2 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + deployment-id: "${{ steps.github-deployment.outputs.deployment_id }}" + state: "success" + environment-url: "https://authzed.com/docs" + + - name: "Update deployment status to failure" + if: "failure()" + uses: "chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806" # v2 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + deployment-id: "${{ steps.github-deployment.outputs.deployment_id }}" + state: "failure" + + - name: "Generate deployment summary" + if: "success()" + run: | + echo "## ✅ Production Deployment Successful" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Deployment URL:** https://authzed.com/docs" >> $GITHUB_STEP_SUMMARY + echo "**Vercel URL:** ${{ steps.deploy.outputs.preview-url }}" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Deployed by:** @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY + echo "**Timestamp:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "🎉 Production docs site is now live at https://authzed.com/docs" >> $GITHUB_STEP_SUMMARY + + - name: "Generate failure summary" + if: "failure()" + run: | + echo "## ❌ Production Deployment Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Triggered by:** @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY + echo "**Timestamp:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "⚠️ Check the workflow logs above for error details." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 88af06d9..75706f8d 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -1,3 +1,4 @@ +--- config: fenced-code-language: false first-line-h1: false diff --git a/.yamllint b/.yamllint index d84f32e8..41717624 100644 --- a/.yamllint +++ b/.yamllint @@ -12,3 +12,4 @@ extends: "default" rules: quoted-strings: "enable" line-length: "disable" + comments: "disable" diff --git a/package.json b/package.json index d47b9db8..a29a770c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build": "next build --webpack", "postbuild": "./scripts/postbuild.sh", "start": "next start", - "lint:markdown": "markdownlint-cli2", + "lint:yaml": "yamllint .", "gen:pagefind": "node --experimental-strip-types scripts/buildSearchIndex.mts", "format:check": "oxfmt --check", "format": "oxfmt" diff --git a/vercel.json b/vercel.json index 8d5d13fa..e32695cb 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,13 @@ { + "$schema": "https://openapi.vercel.sh/vercel.json", "version": 2, + "git": { + "deploymentEnabled": false + }, + "github": { + "silent": false, + "autoJobCancelation": true + }, "headers": [ { "source": "/(.*)",