From 8331d5a5507a9858d436416e0194129e0cdca369 Mon Sep 17 00:00:00 2001 From: August Cayzer Date: Tue, 20 Oct 2026 23:42:50 +0100 Subject: [PATCH] ci: build every pull request and deploy main to withmarfa.com --- .github/dependabot.yml | 24 +++++++++++ .github/workflows/ci.yml | 86 ++++++++++++++++++++++++++++++++++++++++ AGENTS.md | 10 +++-- 3 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b4b2ce4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "monthly" + groups: + all-npm: + patterns: + - "*" + open-pull-requests-limit: 1 + labels: + - "dependencies" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + all-actions: + patterns: + - "*" + open-pull-requests-limit: 1 + labels: + - "dependencies" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2c0eea9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,86 @@ +# Builds every pull request and every push to main, and deploys main. +# +# The build job's name is the required check in the branch ruleset, so renaming +# it silently drops the gate. +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +concurrency: + # A pull request's run is superseded by its next push; a run on main is never + # cancelled, so a deploy is never cut off halfway. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + build: + name: Build and check + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v7 + with: + node-version-file: .node-version + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm run check + - run: pnpm run build + env: + PUBLIC_POSTHOG_KEY: ${{ vars.POSTHOG_PROJECT_KEY_SITES }} + - uses: actions/upload-artifact@v7 + with: + name: dist + path: dist/ + include-hidden-files: true + retention-days: 7 + + deploy: + name: Deploy to withmarfa.com + needs: build + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 10 + # One deploy at a time, in order, across runs. + concurrency: + group: deploy-withmarfa-com + cancel-in-progress: false + steps: + - uses: actions/checkout@v7 + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v7 + with: + node-version-file: .node-version + cache: pnpm + - run: pnpm install --frozen-lockfile + # Deploys exactly what the build job checked. + - uses: actions/download-artifact@v8 + with: + name: dist + path: dist/ + - run: pnpm exec wrangler deploy + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} + # A green run means the site serves this build, not only that the upload + # succeeded: the page must carry this build's script. + - name: Check the live site + run: | + script="$(grep -o '/_astro/[^"]*\.js' dist/index.html | head -1)" + for attempt in 1 2 3 4 5 6; do + if curl -fsS https://withmarfa.com/ | grep -qF "Introducing Marfa" \ + && { [ -z "$script" ] || curl -fsS https://withmarfa.com/ | grep -qF "$script"; }; then + echo "withmarfa.com serves this build"; exit 0 + fi + sleep 10 + done + echo "::error::withmarfa.com does not serve this build"; exit 1 diff --git a/AGENTS.md b/AGENTS.md index 0308f7e..419f441 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # WithMarfa website -The source of the public site at `withmarfa.com`: Astro, built to static files and served as a Cloudflare Worker with static assets only. Cloudflare Workers Builds builds every branch and deploys `main`. +The source of the public site at `withmarfa.com`: Astro, built to static files and served as a Cloudflare Worker with static assets only. GitHub Actions builds every pull request and deploys `main`. ## Conventions @@ -33,10 +33,11 @@ pnpm preview # build, then serve dist/ with Wrangler as Cloudflare will ## Deploying -- **Merging to `main` deploys.** Workers Builds runs `pnpm run check && pnpm run build`, then `npx wrangler deploy`. A pull request gets a preview URL and a check on GitHub, and the check is required. -- The Worker is `withmarfa-website`; its name in `wrangler.jsonc` must match the one Workers Builds is connected to. +- **Merging to `main` deploys.** The `CI` workflow's `Build and check` job runs `pnpm run check` and `pnpm run build` on every pull request and push; it is the required check. On `main`, the `Deploy to withmarfa.com` job deploys exactly the files that job built with `wrangler deploy`, then fails unless withmarfa.com serves them. +- The workflow uses the `CLOUDFLARE_API_TOKEN` secret, a token that can only deploy Workers in the WithMarfa account and manage Worker routes on withmarfa.com, and the `CLOUDFLARE_ACCOUNT_ID` and `POSTHOG_PROJECT_KEY_SITES` variables. Never commit any of them. - `withmarfa.com` is the Worker's custom domain (`wrangler.jsonc`). Redirects from `www.withmarfa.com`, `marfa.so` and `www.marfa.so` are zone redirect rules in Cloudflare, not in this repository, because `_redirects` cannot change the host. -- `PUBLIC_POSTHOG_KEY` is a build variable in Workers Builds. A build without it sends nothing, which is what a fork or a local build gets. +- A build without `PUBLIC_POSTHOG_KEY` sends nothing, which is what a fork or a local build gets. +- The fallback when the workflow cannot deploy is `pnpm run build && pnpm exec wrangler deploy` from a shell authenticated to the WithMarfa account. ## Analytics @@ -49,6 +50,7 @@ pnpm preview # build, then serve dist/ with Wrangler as Cloudflare will Report only a problem you can show: name the page, input or step, and what goes wrong. - **Untrue about Marfa.** Flag a claim about what Marfa does, where it runs or what it costs that withmarfa/marfa or docs.marfa.so contradicts. Never say Marfa Cloud is free. +- **Deploy weakened.** Flag a `paths` filter on `ci.yml`, a renamed `Build and check` job, or a deploy that no longer checks the live site. - **Analytics grown.** Flag an option in `src/lib/analytics.ts` that turns on autocapture, replay, surveys, flags, cookies or storage, or a static import of `posthog-js`. - **Policy broken.** Flag an inline script or style, or a new third-party origin, that `public/_headers` does not allow. - **Private detail committed.** Flag a token, key, account ID, internal hostname or personal path in a tracked file.