Skip to content

Deployment

Rod Christiansen edited this page Oct 8, 2026 · 3 revisions

Deployment

The site is published by one GitHub Actions workflow, .github/workflows/deploy.yml, named "Deploy to Cloudflare Pages". It is the only deploy path; the openapi-sync workflow (below) starts it after updating the API spec.

Trigger

Event Behaviour
push to main Builds and deploys
workflow_dispatch Can be started manually, but the job is skipped unless the ref is main. openapi-sync dispatches it this way

The job carries if: github.ref == 'refs/heads/main'. That guard exists because GitHub offers no branch filter on workflow_dispatch: anyone with write access could otherwise run the workflow from an arbitrary branch and reach the production secrets. A manual run from a feature branch will appear in the Actions tab with the job skipped — that is the guard working, not a failure.

The workflow declares permissions: contents: read.

Steps

The job runs on ubuntu-latest and does four things:

  1. actions/checkout — checkout, pinned to a commit SHA.
  2. actions/setup-node — Node.js 20 with npm caching, pinned to a commit SHA.
  3. npm ci then npm run build — a clean install against package-lock.json, then the static Astro build into dist/.
  4. cloudflare/wrangler-action — pinned to a commit SHA, running the Pages deploy.

Every third-party action is pinned by full commit SHA with the tag in a trailing comment. Keep that convention when bumping a version: change both the SHA and the comment.

The publish command

The wrangler action runs:

pages deploy dist --project-name=reportmate-website --branch=main

So the built dist/ directory is uploaded to a Cloudflare Pages project named reportmate-website, on the main branch — which for a Pages project is the production deployment.

Keeping the API spec current

.github/workflows/openapi-sync.yml keeps public/openapi.json, which /docs renders, in step with the API.

Setting Value
Trigger Daily at 05:23 UTC, and workflow_dispatch
Guard The job runs only on main
Permissions contents: write, actions: write

It checks out reportmate-api without credentials, installs its requirements, runs scripts/export_openapi.py, checks that the output parses as JSON and copies it over public/openapi.json. When the file changed, it commits Sync openapi.json with reportmate-api at <sha> as github-actions[bot], pushes to main, and runs gh workflow run deploy.yml --ref main — a push made with the workflow token does not trigger other workflows on its own. When nothing changed it exits without a commit.

Secrets

Two repository secrets are required. Neither is in the repo.

Secret Used for
CLOUDFLARE_API_TOKEN Authenticates the Pages deploy
CLOUDFLARE_ACCOUNT_ID Identifies the Cloudflare account holding the project

A fork will not have these, so the deploy step fails there. That is expected; forks should rely on npm run build locally.

Domain

astro.config.mjs sets site: 'https://reportmate.app', which is what canonical links and Open Graph image URLs are built from. The custom domain itself is attached to the Cloudflare Pages project in the Cloudflare dashboard, not in this repository — nothing here configures DNS.

Note on the README

README.md says the site is "Hosted on AWS (S3 + CloudFront)" and that dist/ should be deployed to S3. The workflow deploys to Cloudflare Pages. Treat the workflow as authoritative and the README line as stale.

Deploying by hand

Not normally necessary — merging to main is the supported path. If you do need to publish out of band, build first and then run the same wrangler command locally with an API token in your environment:

npm ci
npm run build
npx wrangler pages deploy dist --project-name=reportmate-website --branch=main

The next push to main re-runs the workflow and overwrites whatever you pushed, so make sure the change is also committed.