Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,70 @@
name: CI

# Pull requests only: a merge to main ships commits these checks already passed on their PR, so
# main does not run them again (run it by hand from the Actions tab if needed). Every PR runs this
# workflow so "CI passed", the check the main ruleset requires, always reports; `changes` decides
# which jobs a PR needs, and a job skipped by its `if` counts as passed.
on:
pull_request:
push:
branches: [main]
workflow_dispatch:

# A new push to a PR cancels the run for the commit it replaces.
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

# Every job only checks out and runs checks; nothing writes to the repo or calls the API.
# The one outside call is the coverage upload to Codecov, which is best effort.
permissions:
contents: read

jobs:
# code: anything but Markdown, docs/ (except openapi.yaml, which openapi.test.ts reads), the docs
# site, the issue templates and the other workflows; a new non-code file a test reads must count
# as code here. docs: the docs site. A renamed file counts under both its names. A manual run
# runs everything.
changes:
name: Changed files
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
code: ${{ steps.classify.outputs.code }}
docs: ${{ steps.classify.outputs.docs }}
steps:
- id: classify
env:
GH_TOKEN: ${{ github.token }}
EVENT: ${{ github.event_name }}
PR: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
if [ "$EVENT" != pull_request ]; then
echo "code=true" >> "$GITHUB_OUTPUT"
echo "docs=true" >> "$GITHUB_OUTPUT"
exit 0
fi
files=$(gh api "repos/$REPO/pulls/$PR/files" --paginate --jq '.[] | .filename, (.previous_filename // empty)')
code=false
docs=false
while IFS= read -r f; do
case "$f" in
.github/workflows/ci.yml) code=true; docs=true ;;
docs-site/*) docs=true ;;
docs/openapi.yaml) code=true ;;
*.md | docs/* | .github/ISSUE_TEMPLATE/* | .github/workflows/claude.yml | \
.github/workflows/claude-code-review.yml | .github/workflows/deploy-docs.yml) ;;
*) code=true ;;
esac
done <<< "$files"
echo "code=$code docs=$docs"
echo "code=$code" >> "$GITHUB_OUTPUT"
echo "docs=$docs" >> "$GITHUB_OUTPUT"

deno:
name: Deno checks
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -34,6 +86,8 @@ jobs:

deno-eslint:
name: Deno ESLint
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -53,6 +107,8 @@ jobs:

migrations:
name: Migrations on fresh Postgres
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -82,6 +138,8 @@ jobs:

web:
name: Web app
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
defaults:
run:
Expand Down Expand Up @@ -110,8 +168,11 @@ jobs:
SUPABASE_URL: https://example.supabase.co
SUPABASE_ANON_KEY: placeholder


docs:
name: Docs site
needs: changes
if: needs.changes.outputs.docs == 'true'
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -127,3 +188,14 @@ jobs:
- run: npm ci
# Only builds; deploy-docs.yml is the only thing that publishes the site.
- run: npm run build

# The one check the main ruleset requires. It fails when any job above failed or was cancelled;
# skipped jobs pass.
ci-passed:
name: CI passed
if: always()
needs: [changes, deno, deno-eslint, migrations, web, docs]
runs-on: ubuntu-latest
steps:
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
13 changes: 8 additions & 5 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ on:
# - "src/**/*.js"
# - "src/**/*.jsx"

# A new push to a PR cancels the review of the commit it replaces, so only the latest is reviewed.
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
claude-review:
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
# Not on drafts (marking one ready reviews it), and not on Dependabot's PRs, whose runs get
# no repository secrets, so the action could only fail.
if: ${{ !github.event.pull_request.draft && github.actor != 'dependabot[bot]' }}

runs-on: ubuntu-latest
permissions:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
# Using a different branch name? Replace `main` with your branch's name
push:
branches: [ main ]
# Long Lines: only when the site changes (everything else here is the stock workflow).
paths:
- "docs-site/**"
- ".github/workflows/deploy-docs.yml"
# Allows you to run this workflow manually from the Actions tab on GitHub.
workflow_dispatch:

Expand Down
16 changes: 11 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,15 @@ Local invocation: `curl -X POST http://127.0.0.1:54321/functions/v1/ingest-pota

## CI

`.github/workflows/ci.yml` runs on every PR and push to main: Deno fmt/lint/check/test, ESLint
and typecheck (`npm run lint`, `npm run typecheck`), then
`supabase db start` + `supabase db lint` + `scripts/smoke-test.sql` on a fresh Postgres.
`.github/workflows/ci.yml` runs on PRs (and by hand), never on pushes to main, whose commits
passed it on their PR: Deno fmt/lint/check/test, ESLint and typecheck (`npm run lint`,
`npm run typecheck`), then `supabase db start` + `supabase db lint` + `scripts/smoke-test.sql` on
a fresh Postgres, and the docs site build. Its `changes` job sorts the PR's files: the app jobs
skip a PR that changes only Markdown, `docs/` (except `docs/openapi.yaml`, which a test reads),
`docs-site/` or the other workflows, and the docs job runs only for `docs-site/`; a new non-code
file a test reads must count as code there. `CI passed` (`ci-passed`) always runs and is the one
check to require: a job skipped by its `if` passes it, so never move the filter back to a
workflow-level `paths`, which leaves a required check waiting forever.
The Deno and web jobs upload their lcov to Codecov (`CODECOV_TOKEN`, flags `deno` and `web`,
`codecov.yml`); its statuses are informational and the 80% gates in CI stay the check.
Migrations already applied to the hosted project must not be edited; add a new migration instead
Expand Down Expand Up @@ -549,8 +555,8 @@ editor.
- The user documentation site lives in `docs-site/` (Starlight), with its own `package.json` and
lockfile, outside every Deno and ESLint check.
- Only `.github/workflows/deploy-docs.yml` deploys it: Astro's stock GitHub Pages workflow, kept
stock, publishing to GitHub Pages at docs.longlines.io on pushes to main. `ci.yml`'s `docs` job
builds it as a PR check.
stock except for its `paths` filter, publishing to GitHub Pages at docs.longlines.io on pushes
to main that change `docs-site/`. `ci.yml`'s `docs` job builds it on PRs that change it.
- `docs/` stays the internal docs (architecture, operations, specs) and is not the site.
- The site is for end users of Long Lines and Long Lines Relay and imports nothing from the app.
- There is no landing page: Getting started is `index.md`, a plain doc page served at `/`. Never
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ and lockfile, and nothing in it imports app code.
cd docs-site
npm ci
npm run dev # the site on http://localhost:4321
npm run build # what CI builds on every PR
npm run build # what CI builds on every PR that changes the site
```

Every push to main deploys it to GitHub Pages at https://docs.longlines.io
(`.github/workflows/deploy-docs.yml`).
Every push to main that changes `docs-site/` deploys it to GitHub Pages at
https://docs.longlines.io (`.github/workflows/deploy-docs.yml`).

## Linting the Deno code

Expand Down
2 changes: 1 addition & 1 deletion docs-site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ npm run dev # http://localhost:4321
npm run build # static site in dist/
```

Pushes to main deploy it to https://docs.longlines.io through
Pushes to main that change `docs-site/` deploy it to https://docs.longlines.io through
`.github/workflows/deploy-docs.yml`.
7 changes: 4 additions & 3 deletions docs/OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,9 +609,10 @@ and reset links will not land in the app.
## Documentation site on GitHub Pages

The user documentation site is `docs-site/`, built with Starlight. `.github/workflows/deploy-docs.yml`
(Astro's stock GitHub Pages workflow) deploys it on every push to main: `withastro/action` builds
and uploads it, and `actions/deploy-pages` publishes it. It can also be run by hand from the
Actions tab. `ci.yml`'s `docs` job builds it as a PR check and publishes nothing.
(Astro's stock GitHub Pages workflow, plus a `paths` filter) deploys it on every push to main that
changes `docs-site/`: `withastro/action` builds and uploads it, and `actions/deploy-pages`
publishes it. It can also be run by hand from the Actions tab, which is how to redeploy without a
change. `ci.yml`'s `docs` job builds it on PRs that change it and publishes nothing.

Set it up once, in this order, so the first deploy lands on the domain:

Expand Down
Loading