ci(website): publish Cloudflare and Pages from one artifact

This commit is contained in:
N0zoM1z0 2026-10-09 12:19:38 +08:00
parent 54c0ed42aa
commit 761881cec3
5 changed files with 127 additions and 21 deletions

View File

@ -19,12 +19,33 @@ jobs:
contents: read
pages: read
steps:
- name: Require Cloudflare publishing credentials
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
if [[ -z "$CLOUDFLARE_API_TOKEN" || -z "$CLOUDFLARE_ACCOUNT_ID" ]]; then
echo "::error::Set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID in repository Actions secrets before publishing."
exit 1
fi
- name: Check out the repository
uses: actions/checkout@v4
- name: Prepare the example download
run: python3 scripts/prepare-website.py
- name: Check website links and publishing configuration
run: python3 scripts/verify-website.py
- name: Record the published commit
run: |
python3 - <<'PY'
import json
import os
from pathlib import Path
Path("website/public/deployment.json").write_text(
json.dumps({"commit": os.environ["GITHUB_SHA"]}) + "\n",
encoding="utf-8",
)
PY
- name: Configure Pages
uses: actions/configure-pages@v5
- name: Upload the website
@ -35,14 +56,81 @@ jobs:
deploy:
needs: prepare
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 15
permissions:
contents: read
actions: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Check out publishing configuration
uses: actions/checkout@v4
- name: Set up Node.js for Wrangler
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: Download the verified website
uses: actions/download-artifact@v4
with:
name: github-pages
path: ${{ runner.temp }}/website-archive
- name: Extract the shared publishing artifact
run: |
mkdir -p "$RUNNER_TEMP/rea-website-public"
tar -xf "$RUNNER_TEMP/website-archive/artifact.tar" -C "$RUNNER_TEMP/rea-website-public"
- name: Publish the website to Cloudflare
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
wranglerVersion: "4.149.0"
packageManager: npm
command: deploy --cwd website --env production --assets "${{ runner.temp }}/rea-website-public"
- name: Publish to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
- name: Verify both published revisions
env:
PAGES_URL: ${{ steps.deployment.outputs.page_url }}
run: |
python3 - <<'PY'
import json
import os
import sys
import time
from urllib.error import URLError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
expected = os.environ["GITHUB_SHA"]
pages_url = os.environ["PAGES_URL"]
if not pages_url:
sys.exit("GitHub Pages returned no published URL.")
pending = {"Cloudflare": "https://rea.tools/", "GitHub Pages": pages_url}
for attempt in range(24):
for name, base in list(pending.items()):
url = urljoin(base, "deployment.json") + "?commit=" + expected
try:
request = Request(url, headers={"Cache-Control": "no-cache"})
with urlopen(request, timeout=10) as response:
actual = json.load(response).get("commit")
if actual == expected:
print(f"{name} serves {expected}: {base}")
del pending[name]
else:
print(f"Waiting for {name}: observed revision {actual}")
except (URLError, TimeoutError, ValueError) as error:
print(f"Waiting for {name}: {error}")
if not pending:
break
if attempt < 23:
time.sleep(5)
if pending:
sys.exit("Published revision did not match on: " + ", ".join(pending))
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
summary.write(f"Published commit `{expected}` to [rea.tools](https://rea.tools/) "
f"and [GitHub Pages]({pages_url}).\n")
PY

1
.gitignore vendored
View File

@ -49,6 +49,7 @@ Thumbs.db
# Generated website download
/website/public/examples/notes-example.zip
/website/public/deployment.json
# Local Cloudflare development state
/website/.wrangler/

View File

@ -19,5 +19,6 @@ for asset notes and the source of case-study claims.
- Check both local root and `/rea/` paths, mobile layouts, expanded details,
copying and downloads. Run `scripts/prepare-website.py` followed by
`scripts/verify-website.py`.
- Keep `website-pages.yml` as the sole, manual Pages publisher. Follow the
publication procedure in the website README.
- Keep `website-pages.yml` as the sole, manual production publisher for
Cloudflare and Pages. Both hosts must receive the same verified artifact and
pass the published-commit check. Follow the website README.

View File

@ -185,7 +185,7 @@ Wrangler version without adding it to REA's package dependencies.
Cloudflare documents static-asset requests as free and unlimited, with no
additional storage cost. See [billing and limitations](https://developers.cloudflare.com/workers/static-assets/billing-and-limitations/).
### Preview and deploy
### Local and hosted previews
Run these commands from the repository root. `--cwd website` makes the build
paths consistent with the website configuration. Preview locally first:
@ -230,13 +230,7 @@ See Cloudflare's [nameserver setup procedure](https://developers.cloudflare.com/
for the complete migration steps. A CNAME to the preview's `workers.dev` hostname
at the current DNS provider does not configure a Workers Custom Domain.
Publish to the production Worker:
```sh
npx wrangler@4.149.0 deploy --cwd website --env production
```
This creates or updates `rea-website`, attaches `rea.tools`, and lets Cloudflare
The production environment creates or updates `rea-website`, attaches `rea.tools`, and lets Cloudflare
manage its DNS record and HTTPS certificate. Deployment can succeed while the
zone is pending; verify public HTTPS after activation and certificate issuance.
An existing CNAME at `rea.tools`
@ -250,15 +244,34 @@ Verify <https://rea.tools/>, <https://rea.tools/guides/javascript/>,
resolve correctly. The root README and browser guide use `https://rea.tools/`
as the public website URL.
Deployments are manual. The existing GitHub Pages publisher remains available
as a separate host; it does not publish to Cloudflare.
Production publication uses the synchronized GitHub Actions workflow below.
Local Wrangler commands above publish only the separate preview Worker.
## GitHub Pages
## Synchronized production publishing
`.github/workflows/website-pages.yml` prepares and deploys only `website/public`.
It is the sole Pages publisher, manually triggered and restricted to `main`;
`.github/workflows/website-pages.yml` prepares `website/public` once and deploys
the same artifact to Cloudflare (`rea.tools`) and GitHub Pages. It is the sole
production publisher, manually triggered and restricted to `main`;
ordinary pushes and pull requests do not publish the site.
Configure these repository **Actions secrets** before publishing:
- `CLOUDFLARE_API_TOKEN`: a Cloudflare token using the **Edit Cloudflare Workers**
template, scoped to the website's account and zone.
- `CLOUDFLARE_ACCOUNT_ID`: that Cloudflare account's ID.
See Cloudflare's [GitHub Actions authentication instructions](https://developers.cloudflare.com/workers/ci-cd/external-cicd/github-actions/).
The workflow checks that both secrets are present before preparing a release.
Cloudflare deploys first; GitHub Pages deploys after that step succeeds.
Preparation adds `deployment.json` with the workflow's commit SHA. Cloudflare
receives the extracted Pages artifact through Wrangler's explicit assets path;
the local custom-build output is not the uploaded directory. The workflow then
checks both public version markers and reports success only when they match
that commit. A failed publication remains failed; fix its cause and rerun the
workflow to complete both deployments. The two hosts can update at different
times while a run is in progress.
Website checks run only for pull requests that change `website/`, the verification
script or workflow definitions. They check local links, HTML fragments, SVG XML
and the single Pages publisher without installing npm dependencies. The same
@ -277,8 +290,9 @@ the old site.
After the site is approved and merged, select **GitHub Actions** under the
repository's **Settings → Pages → Build and deployment**. Then run **Publish REA
website** from the Actions tab on `main`. The workflow uses the `github-pages`
environment and the official Pages actions.
website** from the Actions tab on `main`. This publishes both production hosts.
The workflow uses the `github-pages` environment, the official Pages actions and
Cloudflare's Wrangler action.
The equivalent CLI command is:

View File

@ -233,6 +233,8 @@ New analysis or behavior claims need the real evidence that supports them; a moc
or a page-loading check cannot establish those claims.
Follow the PR review and CI process, then publish using the existing manual
`website-pages.yml` workflow on `main`. Ordinary commits should not publish the
site. After deployment, check the live page and confirm it matches the reviewed
version. The legacy VitePress build remains separate from the Pages publisher.
`website-pages.yml` workflow on `main`. It publishes the same verified artifact
to Cloudflare and GitHub Pages and checks both published commit markers.
Ordinary commits should not publish the site. After deployment, check both live
pages and confirm they match the reviewed version. The legacy VitePress build
remains separate from the Pages publisher.