Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 23 additions & 90 deletions .github/workflows/pages-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ jobs:
branch: ${{ steps.identity.outputs.branch }}
head_sha: ${{ steps.identity.outputs.head_sha }}
pull_number: ${{ steps.identity.outputs.pull_number }}
run_id: ${{ steps.identity.outputs.run_id }}
steps:
- name: Resolve exact internal pull request
id: identity
Expand All @@ -38,7 +37,7 @@ jobs:
run_id: runId,
})
if (run.name !== 'Website checks' || run.path !== '.github/workflows/website-check.yml') {
core.setFailed('Preview artifact must come from the trusted Website checks workflow.')
core.setFailed('Preview must be orchestrated by the trusted Website checks workflow.')
return
}
if (run.conclusion !== 'success' || run.event !== 'pull_request') {
Expand Down Expand Up @@ -69,7 +68,6 @@ jobs:
core.setOutput('branch', `preview/docs/pr-${pull.number}`)
core.setOutput('head_sha', run.head_sha)
core.setOutput('pull_number', String(pull.number))
core.setOutput('run_id', String(runId))

deploy:
name: Deploy isolated pull-request preview
Expand All @@ -78,12 +76,13 @@ jobs:
group: pages-preview-docs-${{ needs.identity.outputs.pull_number }}
cancel-in-progress: true
permissions:
actions: read
contents: read
deployments: write
pull-requests: read
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: preview
url: ${{ steps.pages.outputs.pages-deployment-alias-url }}
steps:
- name: Checkout the trusted preview controller
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -92,73 +91,29 @@ jobs:
path: controller
persist-credentials: false

- name: Set up Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
- name: Checkout the exact pull-request head
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
node-version-file: controller/website/package.json
ref: ${{ needs.identity.outputs.head_sha }}
path: candidate
persist-credentials: false

- name: Download the exact checked artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- name: Install the pinned pnpm release
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
name: inkcre-website-dist
path: .pages-preview/dist
run-id: ${{ needs.identity.outputs.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
package_json_file: candidate/website/package.json

- name: Reverify pull-request identity before delivery
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PREVIEW_BRANCH: ${{ needs.identity.outputs.branch }}
PREVIEW_HEAD_SHA: ${{ needs.identity.outputs.head_sha }}
PULL_NUMBER: ${{ needs.identity.outputs.pull_number }}
- name: Set up Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
script: |
const expectedBranch = `preview/docs/pr-${process.env.PULL_NUMBER}`
if (process.env.PREVIEW_BRANCH !== expectedBranch) {
core.setFailed(`Unexpected Pages preview branch: ${process.env.PREVIEW_BRANCH}`)
return
}
const { data: pull } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: Number(process.env.PULL_NUMBER),
})
if (
pull.state !== 'open' ||
pull.base.ref !== 'main' ||
pull.head.sha !== process.env.PREVIEW_HEAD_SHA ||
pull.head.repo?.full_name !== `${context.repo.owner}/${context.repo.repo}`
) {
core.setFailed('Pull-request identity changed after artifact selection.')
}
node-version-file: candidate/website/package.json
cache: pnpm
cache-dependency-path: candidate/website/pnpm-lock.yaml

- name: Register the pull-request deployment
id: github-deployment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PREVIEW_HEAD_SHA: ${{ needs.identity.outputs.head_sha }}
PULL_NUMBER: ${{ needs.identity.outputs.pull_number }}
with:
script: |
const { data: deployment } = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: process.env.PREVIEW_HEAD_SHA,
environment: 'preview',
description: `Cloudflare Pages preview for PR #${process.env.PULL_NUMBER}`,
auto_merge: false,
required_contexts: [],
transient_environment: true,
production_environment: false,
})
core.setOutput('deployment_id', String(deployment.id))
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: deployment.id,
state: 'in_progress',
log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
})
- name: Build the exact-head website preview
run: |
pnpm --dir candidate/website install --frozen-lockfile
pnpm --dir candidate/website check

- name: Deploy to the isolated Pages preview branch
id: pages
Expand All @@ -168,9 +123,9 @@ jobs:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
packageManager: npm
wranglerVersion: 4.114.0
workingDirectory: .pages-preview
workingDirectory: candidate/website
command: >-
pages deploy dist
pages deploy .vitepress/dist
--project-name=${{ vars.CLOUDFLARE_PAGES_PROJECT }}
--branch=${{ needs.identity.outputs.branch }}
--commit-hash=${{ needs.identity.outputs.head_sha }}
Expand All @@ -182,25 +137,3 @@ jobs:
CLOUDFLARE_PAGES_DEPLOYMENT_ID: ${{ steps.pages.outputs.pages-deployment-id }}
CLOUDFLARE_PAGES_DEPLOYMENT_URL: ${{ steps.pages.outputs.pages-deployment-alias-url }}
INKCRE_PAGES_SMOKE_MODE: preview

- name: Report the pull-request deployment
if: ${{ always() && steps.github-deployment.outputs.deployment_id != '' }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
DEPLOYMENT_ID: ${{ steps.github-deployment.outputs.deployment_id }}
JOB_STATUS: ${{ job.status }}
PREVIEW_URL: ${{ steps.pages.outputs.pages-deployment-alias-url }}
with:
script: |
const success = process.env.JOB_STATUS === 'success'
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: Number(process.env.DEPLOYMENT_ID),
state: success ? 'success' : 'failure',
description: success ? 'Pull-request preview is ready' : 'Pull-request preview failed',
log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
...(success && process.env.PREVIEW_URL
? { environment_url: process.env.PREVIEW_URL }
: {}),
})
9 changes: 0 additions & 9 deletions .github/workflows/website-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,3 @@ jobs:

- name: Verify the website contract
run: pnpm --dir website check

- name: Upload the checked preview artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: inkcre-website-dist
path: website/.vitepress/dist
if-no-files-found: error
include-hidden-files: true
retention-days: 7
17 changes: 9 additions & 8 deletions website/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ an artificial Hub mirror.

`Website checks` validates pull-request candidates and supports manual diagnostics. It proves that
the website can be built and that the generated site satisfies the repository contract, but it owns
neither the canonical production artifact nor production delivery. A successful same-repository pull
request may hand its checked artifact to a trusted controller for an isolated, deterministic,
short-lived preview. Fork pull requests receive no preview credentials, preview origins remain
`noindex`, and closing the pull request replaces the live preview with a trusted closed-preview
tombstone. The stable `preview-docs-pr-N` branch alias is the user-facing preview URL and is
recorded against the pull-request head in GitHub; Cloudflare retains the underlying immutable
deployments in its history. If automatic retirement fails, the cleanup workflow can be run manually
for the closed pull-request number. A preview artifact is never promoted to production.
neither a preview delivery input nor canonical production delivery. After a successful
same-repository run, the trusted Preview workflow checks out that exact head, builds it itself, and
publishes an isolated, deterministic, short-lived preview. Fork pull requests receive no preview
credentials, preview origins remain `noindex`, and closing the pull request replaces the live
preview with a trusted closed-preview tombstone. The stable `preview-docs-pr-N` branch alias is the
user-facing preview URL and is recorded against the pull-request head in GitHub; Cloudflare retains
the underlying immutable deployments in its history. If automatic retirement fails, the cleanup
workflow can be run manually for the closed pull-request number. A preview build is never promoted
to production.

Protected `main` is the publication authority. `Pages deployment` runs for a push to `main`; failed
runs can be rerun for the same commit, while rollback starts by reverting `main` through a pull
Expand Down