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
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Keeps the actions pinned in .github/workflows current. They are pinned to
# full commit SHAs, with the version in a comment, so a moved tag cannot
# change what runs; Dependabot updates the SHA and the comment together.
#
# npm dependencies are not listed here: no entry existed before, and adding
# one is a separate decision about how many pull requests this repo wants.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
# A release has a week to be pulled or found bad before a pin moves to it.
cooldown:
default-days: 7
groups:
actions:
patterns: ["*"]
commit-message:
prefix: ci
48 changes: 28 additions & 20 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ on:
push:
branches: [main, staging]

permissions:
contents: read
# Nothing at the workflow level: each job asks for what it uses, and no job
# here pushes, tags or comments, so each reads the repository and no more.
permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -15,11 +16,17 @@ concurrency:
jobs:
check:
name: astro check + tests + build
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
# Nothing here pushes, so the token is not left in .git/config for
# every later step (and every npm install script) to read.
persist-credentials: false

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22"
cache: "npm"
Expand All @@ -30,20 +37,26 @@ jobs:
- name: Type-check
run: npm run check

# `npm test` runs vitest, builds the site, and then verifies that every
# inline script in the build is covered by the CSP hash allowlist in
# vercel.json — so an edit to the theme bootstrap cannot ship a page the
# browser will refuse to run.
# `npm test` builds the site, runs the tests (vp test), and then verifies
# that every inline script in the build is covered by the CSP hash
# allowlist in vercel.json — so an edit to an inline script cannot ship
# a page the browser will refuse to run.
- name: Tests, build, and CSP check
run: npm test

audit:
name: npm audit
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
# Nothing here pushes, so the token is not left in .git/config for
# every later step (and every npm install script) to read.
persist-credentials: false

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22"
cache: "npm"
Expand All @@ -62,16 +75,11 @@ jobs:
- name: Audit what ships
run: node scripts/audit.mjs

# Build and test tooling, reported but not blocking.
#
# vitest currently carries an advisory with no installable fix: every
# published version up to 4.1.10 is affected, and 4.1.11 — the fix —
# cannot be installed, because it trips a resolver bug in npm 10.9
# ("Cannot read properties of null (reading 'edgesOut')") that aborts
# `npm install` outright. Blocking on it would pin this job red until
# npm ships a fix, which trains everyone to ignore it. So it is surfaced
# in the log instead, and this step goes back to blocking the moment
# vitest 4.1.11 installs cleanly.
# Build and test tooling, reported but not blocking. None of it ships:
# the site is static HTML. Blocking on an advisory in a build tool with
# no non-breaking fix would pin this job red and train everyone to
# ignore it, so it is surfaced in the log instead. Today that is
# postcss-selector-parser, through @tailwindcss/typography.
- name: Report advisories in build and test tooling
continue-on-error: true
run: npm audit --audit-level=moderate
5 changes: 3 additions & 2 deletions .github/workflows/main-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ on:
options: [minor, major]
default: minor

permissions:
contents: read
# Nothing at the workflow level: the one job below asks for the write access
# that tagging needs, and only it.
permissions: {}

jobs:
version:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/staging-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ on:
options: [patch, minor, major]
default: patch

permissions:
contents: read
# Nothing at the workflow level: the one job below asks for the write access
# that tagging needs, and only it.
permissions: {}

jobs:
version:
Expand Down
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,54 @@ numbers below were the site's earlier, hand-kept numbering and are retired; the
from 0.8.2 stays, as history: flags it hid stay concealed, and versions
before 0.9.0 used the storage key `gbx-hide-flags` and asked the licence
host `licences.nyuchi.dev`.
- **Google Analytics loads only after consent (#83).** Google's tag is no
longer on the page behind Consent Mode set to denied, which still sent
Google a cookieless ping; it is not requested at all until the visitor
agrees. `Analytics.astro` queues the denied defaults and holds the only
loader, which runs on a stored yes from the current banner version or when
`CookieBanner.astro` records one. A record from an older version, the first
banner's `analytics-consent` key, or anything malformed counts as no answer,
and the banner asks again; `SupportWidget.astro` applies the same check
through `parseConsent` in `src/data/consent.ts`. The privacy and cookie
policies now say that Google Analytics contacts Google only after you agree
(Google Fonts is still named separately), dated 13 October 2026, as is
llms.txt. The CSP has one inline-script hash fewer. `tests/consent.test.ts`
runs the built script against current, old, legacy and malformed records,
and `npm run consent:probe` now fails on any Google Analytics request
before consent.
- **CI asks for less (#84).** Every workflow grants nothing at the top level;
each job asks for its own permissions (`contents: read` for the build and
audit jobs, `contents: write` only for the two tagging jobs). The build
workflow's checkouts set `persist-credentials: false`, since nothing there
pushes. `actions/checkout` and `actions/setup-node` are pinned to full
commit SHAs (v4.4.0 each), with the version in a comment, and the new
`.github/dependabot.yml` keeps those pins current, a week after each
release (`cooldown`). The reusable workflows
from `nyuchi/.github` were already pinned to a commit.
- **Docs match the site as it is (#86).** README, CONTRIBUTING and
`docs/toolchain.md` describe the Astro site's pages, the staging → main
release flow, Vite+ as adopted (no longer "cannot be adopted"), the inline
scripts the CSP hashes, and the current audit; the dead Lint badge, the
vitest pin, `master` and "two pages" are gone. `react` and `react-dom` are
no longer dev dependencies: nothing imports them, and `@bundu/ui` declares
no peer dependencies. Prettier and `vp fmt` both stay, because each is run
by a required org gate; `tests/toolchain.test.ts` fails if their settings
differ.

### Fixed

- **Dependency advisories.** `http-cache-semantics` takes its patched
release (GHSA-ch52-4w7c-c8xp), so its dated exception in
`scripts/audit.mjs` is gone. `postcss-selector-parser` is held at 7.1.6 or
later through an npm `overrides` entry (GHSA-rj75-hqrm-r3gf): the
typography plugin pins 6.0.10, and the built CSS is byte-for-byte the same
with the newer version.
- **Anchors for the extension's test builds 0.9.6, 0.9.7 and 0.9.8 (#85)**
on the Releases page, under 0.10.0 beside 0.9.1 to 0.9.5, so the links from
those builds' menus land on the release they lead to. 0.10.0 is still
"Coming soon to the Chrome Web Store". `taggedTestBuilds` in
`src/data/releases.ts` lists every tagged test build, and a test fails the
build if one has no anchor, or more than one.

## 0.1.0 — 2026-10-07

Expand Down
53 changes: 34 additions & 19 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ three predictable places.

## What gets changed where

| Change | File |
| --------------------------------------------------------- | ----------------------------------------------------- |
| Nav links, footer columns, legal links, the wordmark | `src/data/site.ts` |
| Links to the extension (repo, releases, deployment guide) | `src/data/site.ts` (`toddleExtension`) |
| The three destination cards | the `surfaces` array in `src/pages/index.astro` |
| Home page copy | `src/pages/index.astro` |
| Extension page copy | `src/pages/toddle-enhancement-extension.astro` |
| Header / footer structure | `src/components/SiteHeader.astro`, `SiteFooter.astro` |
| `<head>`, metadata, theme bootstrap | `src/layouts/BaseLayout.astro` |
| Change | File |
| ------------------------------------------------------- | ----------------------------------------------------- |
| Nav links, footer columns, legal links, the wordmark | `src/data/site.ts` |
| Links to the extension (store listing, releases, guide) | `src/data/site.ts` (`toddleExtension`) |
| The extension's release notes | `src/data/releases.ts` |
| Facts on the legal pages (versions, prices, contacts) | `src/data/legal.ts` |
| Consent categories and the banner's version | `src/data/consent.ts` |
| The three destination cards | the `surfaces` array in `src/pages/index.astro` |
| Home page copy | `src/pages/index.astro` |
| Extension page copy | `src/pages/toddle-enhancement-extension.astro` |
| Header / footer structure | `src/components/SiteHeader.astro`, `SiteFooter.astro` |
| `<head>`, metadata, theme bootstrap | `src/layouts/BaseLayout.astro` |

## What must not be changed here

Expand All @@ -38,9 +41,11 @@ lowercase spelling anywhere in `src/` fails that test too.
npm install
npm run dev # http://localhost:4321 — hot reload
npm run check # astro check
npm test # vitest, then build, then the CSP hash check
npm test # build, then the tests (vp test), then the CSP hash check
npm run lint # vp lint
npm run format # prettier --write
npm run format:check # the org lint gate runs this
npm run fmt # vp fmt; the org vite-plus gate runs `vp fmt --check`
```

## Keeping the clone light
Expand All @@ -62,11 +67,12 @@ clones (`toddle-enhancement-extension`, `learning`, `nyuchi-licence-server`)
the pull request belongs to. The same script is in all three; only its list
of generated paths differs.

## If you touch the theme bootstrap
## If you touch an inline script

The one inline script in `BaseLayout.astro` is allowed by a hash in the
Content-Security-Policy in `vercel.json`. Change the script and the hash no
longer matches, so:
The inline scripts (the theme bootstrap in `BaseLayout.astro`, the consent
check in `Analytics.astro`, and the small modules Astro inlines) are allowed by
hashes in the Content-Security-Policy in `vercel.json`. Change one and its hash
no longer matches, so:

```sh
npm run build
Expand All @@ -75,8 +81,12 @@ npm run csp:fix # rewrite vercel.json with the new hashes

Then review the diff — a changed hash should correspond to a change you meant
to make. `npm test` fails if you skip this, which is the point: a stale hash
would mean the browser refuses to run the script and the theme silently breaks
in production.
would mean the browser refuses to run the script and it silently breaks in
production.

If you touch the consent banner or analytics, run `npm run consent:probe` too:
Google's tag must not be requested before the visitor agrees, and the privacy
policy says so.

## Adding a page

Expand All @@ -87,9 +97,14 @@ in production.

## Pull requests

CI runs `astro check`, the tests, the build and the CSP check, plus the org
lint gate (actionlint, JSON validity, Prettier, markdownlint, yamllint). All of
it has to be green. Merging to `master` deploys to Vercel.
Branch from `staging` and open the pull request against `staging`. CI runs
`astro check`, the build, the tests and the CSP check, plus the org gates: the
lint gate (actionlint, JSON validity, Prettier, markdownlint, yamllint) and
`vite-plus / check` (`astro check`, `vp fmt --check`). All of it has to be
green. Each merge into `staging` is tagged as the next patch. A release is a
pull request from `staging` to `main`: production deploys from `main`, which
is tagged as the next minor. Add a line under "Unreleased" in CHANGELOG.md
for anything a reader of the site, or of this repo, would notice.

Do not add a link to the extension's source repository — it is private, and a
test fails the build on any `github.com` link for that reason.
Expand Down
Loading
Loading