Add a shared static-site verification command, focused GitHub check, Pages security headers, and the exact dashboard release configuration. Silence the runner-only ShellCheck false positive in the dynamic activation test with a documented scoped annotation.
- commit
- 92e1737503c57b1345eb65c40bc4b921956a9eb4
- parent
- fba67e5351ebed8971f833dd5fc959496bfaedaf
- author
- Dami <dami.etoile@gmail.com>
| A | .github/ | 59 | ++++++++++++++++++++++++++++++ |
| M | README.md | 4 | ++ |
| A | docs/ | 58 | +++++++++++++++++++++++++++++ |
| M | scripts/ | 4 | ++- |
| A | scripts/ | 32 | ++++++++++++++++ |
| A | website/ | 6 | +++ |
@@ -0,0 +1,59 @@
1+name: Website2+3+on:4+ pull_request:5+ paths:6+ - website/**7+ - scripts/verify-website.sh8+ - .github/workflows/website.yml9+ push:10+ branches: [main]11+ paths:12+ - website/**13+ - scripts/verify-website.sh14+ - .github/workflows/website.yml15+16+permissions:17+ contents: read18+19+concurrency:20+ group: website-${{ github.workflow }}-${{ github.ref }}21+ cancel-in-progress: true22+23+jobs:24+ verify:25+ name: Static site26+ runs-on: ubuntu-24.0427+ timeout-minutes: 528+ steps:29+ - name: Check out repository30+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.131+32+ - name: Verify deployable website33+ run: ./scripts/verify-website.sh34+35+ deploy:36+ name: Cloudflare Pages37+ if: github.event_name == 'push'38+ needs: verify39+ runs-on: ubuntu-24.0440+ timeout-minutes: 1041+ environment:42+ name: main43+ url: ${{ steps.deploy.outputs.deployment-url }}44+ permissions:45+ contents: read46+ deployments: write47+ steps:48+ - name: Check out repository49+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.150+51+ - name: Deploy production site52+ id: deploy53+ uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v354+ with:55+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}56+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}57+ wranglerVersion: 4.112.058+ command: pages deploy website --project-name=goq-sh --branch=main59+ gitHubToken: ${{ secrets.GITHUB_TOKEN }}@@ -197,6 +197,10 @@ On Linux with NVIDIA:
197197 WEBKIT_DISABLE_DMABUF_RENDERER=1 cargo tauri dev198198 ```199199 200+The static `website/` directory is published through Cloudflare Pages after a201+merge to `main`. See the [Cloudflare Pages release setup](docs/cloudflare-pages.md)202+for the one-time project and GitHub environment configuration.203+200204 ## License201205 202206 MIT@@ -0,0 +1,58 @@
1+# Publish goq.sh with Cloudflare Pages2+3+The `website/` directory is a complete static site. GitHub Actions validates it4+without a package install or framework build, then deploys it to Cloudflare5+Pages with Wrangler. In this release process, a push to `main` that changes the6+site or its workflow is the production release.7+8+## One-time dashboard setup9+10+In the Cloudflare dashboard, open **Workers & Pages**, create a Pages project,11+and choose **Direct Upload**. Name the project `goq-sh`. The project must exist12+before the first production workflow runs; use the current `website/` directory13+for the dashboard's required initial upload. GitHub Actions replaces that14+deployment after the first merge.15+16+Do not also connect the Cloudflare Git integration: the repository workflow is17+the sole publisher, which prevents two independent systems from racing to18+deploy the same commit.19+20+In the GitHub repository, the `main` environment must contain these environment21+secrets:22+23+- `CLOUDFLARE_ACCOUNT_ID`24+- `CLOUDFLARE_API_TOKEN`25+26+The API token needs **Account / Cloudflare Pages / Edit** permission for the27+account that owns the project. These secret names are already present in the28+repository's `main` environment.29+30+The **Website / Static site** job validates JavaScript, required assets,31+internal asset references, and security-header configuration on pull requests.32+On a push to `main`, the **Website / Cloudflare Pages** job waits for that check,33+enters the `main` environment, and deploys `website/` to the production branch34+of the `goq-sh` Pages project.35+36+## Domain and release gate37+38+After the first successful deployment, open the Pages project's **Custom39+domains** screen and add `goq.sh`. Complete the DNS prompts in the account that40+owns the zone. Add `www.goq.sh` separately only if it should also serve or41+redirect to the site.42+43+Protect `main` in GitHub and require these checks before merge:44+45+- **CI / Complete demo gate**46+- **Website / Static site** when it is present47+48+The site release path is then:49+50+1. Open a pull request with website changes.51+2. Merge only after the repository checks pass.52+3. The production job deploys the merged `main` commit through the protected53+ GitHub environment.54+4. Cloudflare publishes the resulting immutable Pages deployment to `goq.sh`.55+56+This workflow does not publish pull-request previews because environment secrets57+are deliberately unavailable to untrusted PR code. Preview deployments can be58+added later with a separate least-privilege project and credential if needed.@@ -1,7 +1,9 @@
11 22 # The test sources production helpers dynamically, so ShellCheck cannot see33 # their references to the fixture overrides and shared state variables.4-# shellcheck disable=SC2034,SC23294+# SC2317 is the equivalent diagnostic emitted by the ShellCheck release used5+# on GitHub's Ubuntu runner for these indirectly invoked fixture functions.6+# shellcheck disable=SC2034,SC2317,SC232957 68 set -euo pipefail79 @@ -0,0 +1,32 @@
1+2+set -euo pipefail3+4+script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"5+repo_dir="$(CDPATH='' cd -- "$script_dir/.." && pwd -P)"6+site_dir="$repo_dir/website"7+8+for command_name in find grep node; do9+ if ! command -v "$command_name" >/dev/null 2>&1; then10+ printf 'required command is missing: %s\n' "$command_name" >&211+ exit 112+ fi13+done14+15+for required_file in index.html styles.css dither.js _headers; do16+ if [[ ! -f "$site_dir/$required_file" ]]; then17+ printf 'website asset is missing: %s\n' "$required_file" >&218+ exit 119+ fi20+done21+22+if find "$site_dir" -type l -print -quit | grep -q .; then23+ echo 'website output must not contain symbolic links' >&224+ exit 125+fi26+27+node --check "$site_dir/dither.js"28+grep -Fq 'href="./styles.css"' "$site_dir/index.html"29+grep -Fq 'src="./dither.js"' "$site_dir/index.html"30+grep -Fq 'Content-Security-Policy:' "$site_dir/_headers"31+32+echo 'website_static_gate=ok'@@ -0,0 +1,6 @@
1+/*2+ Content-Security-Policy: default-src 'self'; base-uri 'none'; connect-src 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self' data:; object-src 'none'; script-src 'self'; style-src 'self'; upgrade-insecure-requests3+ Permissions-Policy: camera=(), geolocation=(), microphone=()4+ Referrer-Policy: strict-origin-when-cross-origin5+ X-Content-Type-Options: nosniff6+ X-Frame-Options: DENY