Files
runicgateway.com/.gitea/workflows/pr-checks.yml
wtclaude a2faf07104
All checks were successful
PR checks / checks (pull_request) Successful in 55s
feat(legal): phase 6 — the privacy policy and the terms
PLAN.md §9. Builds /privacy and /terms, links them from the footer on every page,
and generates the Play Data Safety notes from the same inventory the policy renders.

Four decisions taken by the org lead before either page was written, recorded in
§9 under "How phase 6 built the legal pages":

  D30  DNS-only records, so the reverse proxy on the host keeps the only access
       log. Described qualitatively — the retention belongs to the proxy, and a
       policy that quotes a number the deployment does not enforce is worse than
       one that does not.
  D31  Eighteen or older. Above the children's-consent threshold everywhere in the
       EEA, so consent works with no parental-consent machinery this form could not
       honestly operate. Four surfaces render it from src/data/legal.mjs, and every
       one says plainly that nothing verifies it.
  D32  No governing-law clause. Nothing of value is contracted for here.
  D33  PLAY_DATA_SAFETY.md is generated from src/data/collection.mjs and checked in
       CI, so the published policy and the answers given to Google cannot drift.

/privacy is three separately-scoped sections because "we" means three different
parties: this site (one form, no cookies, no third-party requests), the Android app
(we operate no server it talks to — the rows are what the DEVICE holds), and a
self-hosted deployment (the operator is the controller, not us). Every row names the
file it was read out of, because a policy is the document most likely to be written
from a template and least likely to be re-read against the software.

/terms governs only what we run: this site, the beta list, and the APK we publish.
The software is governed by its licence, and a community's deployment by that
community — a terms page claiming authority over every install of a GPL program is
the thing a generated template gets wrong.

Also here:
  - the age clause changed CONSENT_TEXT, so CONSENT_VERSION gained a suffix; rows
    written from now on carry the new sentence and older rows keep theirs
  - PLANNED_ROUTES is now empty — these were its last two entries, and its reverse
    check is what forced the deletion; the list stays for phases 7 and 8
  - test/legal.test.mjs asserts the structural promises no build check can see,
    including that every mapped Play row still answers "not collected, not shared"
  - --check normalises line endings: the repo has no .gitattributes and Windows
    checkouts are CRLF, so a byte comparison would fail for every Windows developer
    while passing in CI

Verified: npm run verify green end to end (tokens, brand, data safety, astro check,
36 tests, build, 214 links, 19 facts), both pages walked in a browser, and neither
overflows at 390px. One defect the checks could not see and a look could: the
retention line was being pushed to the foot of the tallest card in its row, opening
a void in the middle of the short ones.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 04:21:47 -05:00

102 lines
4.3 KiB
YAML

name: PR checks
# Gitea Actions caution, learned elsewhere in this org: never leave an empty
# template expression anywhere in a `run:` script, not even inside a comment.
# The runner silently SKIPS the whole step without failing the job, and the
# problem is invisible in the workflow list.
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
- name: Install
run: npm ci
- name: Design tokens
# PLAN.md §7 — no colour literal outside src/styles/tokens.css.
run: npm run check:tokens
- name: Branding pipeline
# PLAN.md §7 — brand-default is complete, every /brand/* URL the source asks for
# resolves against the route's own allowlist, and every brand string the boot
# rewrite replaces is distinctive enough to replace blindly.
run: npm run check:brand
- name: Play Data Safety declaration
# PLAN.md §9 / D33 — PLAY_DATA_SAFETY.md is generated from the same
# src/data/collection.mjs rows that /privacy section 2 renders, so the published
# policy and the answers given to Google cannot drift apart. This re-runs the
# generator and fails if the committed copy differs.
#
# It runs before the build because it needs neither one: it is the cheapest check
# here and the one whose failure is easiest to act on.
run: npm run check:datasafety
- name: Types
run: npm run check
- name: Unit tests
# PLAN.md §8 — the beta signup's decision path: honeypot, form token, timing, rate
# limit, cap, validation, duplicate, removal.
#
# The first thing in this repository that the other checks cannot see. They all read
# the built output, and none of this appears there: a honeypot that has stopped
# working produces a build that is identical in every way to one where it works.
#
# The test file is NAMED rather than the directory passed. `node --test test/` fails
# on Node 22 with MODULE_NOT_FOUND — directory mode is not portable across the
# versions this org runs, and this workflow pins 22 while developers are on 24, so
# the shorter form would pass locally and break only here.
run: npm test
- name: Production build
run: npm run build
- name: Links
# PLAN.md §12 — every internal link resolves, and every outbound link into a
# RunicGateway repository points at a branch path rather than a commit permalink.
#
# It runs AFTER the build, and that ordering is the design rather than a
# convenience: it reads the built HTML, so links assembled from data files and
# template literals are checked as the strings they actually become. A source scan
# would see an expression and skip most of what phase 4 added.
#
# No network: the outbound rule is about the shape of a URL, and a build that
# fails because some other host is slow is a check people learn to ignore.
run: npm run check:links
- name: Platform facts
# PLAN.md §12 — every version, protocol number and bundle tag is re-read from
# its authority over the Gitea API and must agree with src/data/platform.json.
#
# This needs a token that can read the OTHER repositories in the org: link,
# servuo-plugins, website and installer. The automatic per-run token is scoped
# to this repository alone and 404s on all four, so the job uses the org-level
# REGISTRY_TOKEN, which already exists and already carries the right scope.
#
# The secret is named for the registry; the script reads GITEA_TOKEN. Mapping it
# here rather than renaming either side keeps the script's interface honest — it
# wants a Gitea token, not this org's particular secret.
#
# It runs last, and it is the only step that touches the network, so a Gitea
# outage cannot mask a real failure in the build.
env:
GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: npm run check:facts