Deploy #96
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy | |
| # FR-1/FR-12 — one workflow owns every production deploy: | |
| # - push to main → production deploy (runs when a PR is merged to main) | |
| # - hourly schedule → fresh build (news/release/docs refresh) + production deploy | |
| # - workflow_dispatch → "right now" button | |
| # pull_request is deliberately NOT a trigger: PRs are validated by CI (ci.yml), | |
| # which builds and uploads a `site-preview` artifact — no per-PR preview deploys. | |
| # | |
| # One-time setup (repo Settings, no Cloudflare dashboard clicks needed): | |
| # Secrets: CLOUDFLARE_API_TOKEN (Cloudflare Pages: Edit), CLOUDFLARE_ACCOUNT_ID | |
| # Variable: PAGES_PROJECT (e.g. keeltrading-com) | |
| # Until those exist, every run builds and then skips the deploy gracefully. | |
| on: | |
| push: | |
| branches: [main] | |
| schedule: | |
| # Hourly at :07, off the top of the hour. | |
| - cron: "7 * * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: deploy-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| deploy: | |
| name: Build & deploy to Cloudflare Pages | |
| runs-on: ubuntu-latest | |
| env: | |
| PAGES_PROJECT: ${{ vars.PAGES_PROJECT }} | |
| # Cookieless Web Analytics beacon (FR-11) — omitted from the build when unset. | |
| PUBLIC_CF_ANALYTICS_TOKEN: ${{ vars.PUBLIC_CF_ANALYTICS_TOKEN }} | |
| HAS_DEPLOY_SECRETS: ${{ secrets.CLOUDFLARE_API_TOKEN != '' && secrets.CLOUDFLARE_ACCOUNT_ID != '' && 'true' || 'false' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci --ignore-scripts | |
| # FR-4: `npm run build` chains the fetch scripts before astro build — a | |
| # moved engine doc fails here and blocks the deploy by design. (Never | |
| # call `astro build` directly in CI: the fetched content collection is | |
| # gitignored and would silently render a site without the docs pages.) | |
| - name: Build site | |
| run: npm run build | |
| env: | |
| # The fetch scripts hit api.github.com; unauthenticated calls from | |
| # shared Actions runner IPs are rate-limited (60/hr per IP), which | |
| # silently degraded the Install and News pages to their fallbacks. | |
| # The workflow's built-in token removes the limit — no new secrets. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Skip deploy (Cloudflare not configured yet) | |
| if: env.PAGES_PROJECT == '' || env.HAS_DEPLOY_SECRETS != 'true' | |
| run: | | |
| echo "PAGES_PROJECT or Cloudflare secrets not configured — deploy skipped." | |
| echo "Setup: repo secrets CLOUDFLARE_API_TOKEN (Pages:Edit) + CLOUDFLARE_ACCOUNT_ID," | |
| echo "and variable PAGES_PROJECT. See docs/DEPLOYMENT.md." | |
| - name: Deploy | |
| if: env.PAGES_PROJECT != '' && env.HAS_DEPLOY_SECRETS == 'true' | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| BRANCH: ${{ github.head_ref || github.ref_name }} | |
| run: | | |
| # Create the project on first run; "already exists" is fine. | |
| ./node_modules/.bin/wrangler pages project create "$PAGES_PROJECT" --production-branch main || \ | |
| echo "project exists (or deploy will surface the real error)" | |
| ./node_modules/.bin/wrangler pages deploy dist \ | |
| --project-name="$PAGES_PROJECT" \ | |
| --branch="$BRANCH" \ | |
| --commit-hash="$GITHUB_SHA" |