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
25 changes: 25 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Line endings are load-bearing in this repository.
#
# support.js loads the vendored React, ReactDOM, and Babel bundles with a
# Subresource Integrity digest. SRI hashes exact bytes, so a checkout that
# rewrites LF to CRLF — the Git default on Windows — produces files whose digest
# no longer matches, the integrity check fails, React never loads, and every
# .dc.html page renders completely blank. The site is fine; only that checkout is
# broken, which makes it an unusually confusing failure to diagnose.
#
# Normalising to LF everywhere removes the whole class of problem.

* text=auto eol=lf

# Never transform the vendored bundles, whatever the platform or autocrlf setting.
vendor/** -text

# Binary assets: no conversion, no diffing as text.
*.png binary
*.jpg binary
*.jpeg binary
*.webp binary
*.ico binary
*.woff binary
*.woff2 binary
*.ttf binary
60 changes: 60 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Bug report
description: Something on the site does not work or does not look right
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for reporting. The decks scale to fit the window, so layout problems
are often specific to a viewport size — please include yours.

- type: dropdown
id: page
attributes:
label: Which page
options:
- Landing page (index.html)
- The Solar System deck
- The Moons deck
- JunoCam deck
- JunoCam explorer
- JunoCam poster
- Beta preview
- Pocket Planetarium prototype
- More than one, or not sure
validations:
required: true

- type: textarea
id: what-happened
attributes:
label: What happened
description: What you saw, and what you expected instead.
validations:
required: true

- type: textarea
id: steps
attributes:
label: How to reproduce it
placeholder: |
1. Open …
2. Press …
3. The slide …
validations:
required: true

- type: input
id: environment
attributes:
label: Browser, operating system, and window size
placeholder: Firefox 128, macOS 15, roughly 1280×800
validations:
required: true

- type: textarea
id: console
attributes:
label: Anything in the browser console
description: Optional, but a blank page almost always has an error behind it.
render: text
52 changes: 52 additions & 0 deletions .github/ISSUE_TEMPLATE/rfc-proposal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: RFC proposal
description: Propose a substantive change, or ask whether one needs an RFC
labels: [rfc]
body:
- type: markdown
attributes:
value: |
Substantive changes — new pages or features, changes to a documented
invariant, changes to the runtime contract or the release process — are
proposed as an RFC before they are built. See
[docs/SDLC.md](../blob/main/docs/SDLC.md).

Not sure whether yours qualifies? Open this anyway and ask. Deciding an
RFC is unnecessary takes a minute.

- type: textarea
id: proposal
attributes:
label: What you want to change
validations:
required: true

- type: textarea
id: motivation
attributes:
label: Why
description: What is broken, risky, or unclear today? Concrete evidence beats principle.
validations:
required: true

- type: textarea
id: requirements
attributes:
label: Obligations this would introduce
description: |
Rough is fine. What must be true afterwards that is not true now? These
become the normative requirements, so it helps to think about them early.

- type: textarea
id: alternatives
attributes:
label: Alternatives you considered
description: Including doing nothing.

- type: checkboxes
id: scope
attributes:
label: Scope
options:
- label: This changes something the specifications currently guarantee
- label: This changes how the site is built, tested, or deployed
- label: This changes the runtime contract the pages depend on
47 changes: 47 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!--
Thanks for contributing. The questions below are the ones a reviewer would
otherwise have to ask. Delete any section that genuinely does not apply.
-->

## What this changes

<!-- One or two sentences. What is different afterwards? -->

## Why

<!-- The problem, not the solution. Link the issue or RFC if there is one. -->

## Requirements touched

<!--
List the requirement IDs this change adds, alters, or discharges, and the tests
that cover them. If this is a routine change (typo, copy fix, image swap), write
"none — routine change" and move on.
-->

| Requirement | Level | Covered by |
| --- | --- | --- |
| `AREA-000` | MUST | `tests/…` |

## RFC

<!--
Substantive changes need one — see docs/SDLC.md. Link it, or state why this is
routine.
-->

## Checklist

- [ ] `npm test` passes locally
- [ ] New behaviour is specified in `docs/specs/` and annotated `@REQ` in tests
- [ ] Documentation that states facts about the build (README counts, file tables)
is still true
- [ ] Any known deviation introduced is recorded in the specification rather than
worked around silently

## Visual impact

<!--
Does this change how anything looks? If so, say which pages. Baselines are
refreshed with the "Update visual baselines" workflow, not by hand.
-->
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 2
updates:
# The site vendors its own runtime deliberately, so the only dependencies worth
# automating are the development tooling and the workflow actions.
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
commit-message:
prefix: 'ci:'

- package-ecosystem: npm
directory: /
schedule:
interval: monthly
commit-message:
prefix: 'dev:'
groups:
playwright:
patterns: ['@playwright/*', 'playwright*']
120 changes: 120 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: CI

# The single definition of "is this good enough to ship".
#
# Runs on every pull request, and is called by the deploy workflow before it
# publishes, so the pull-request gate and the pre-deploy gate cannot drift apart.
# See docs/rfcs/0002-quality-gates-and-cicd.md.

on:
pull_request:
workflow_call:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.event_name }}-${{ github.ref }}
# Supersede stale pull-request runs, but never cancel a run that a deploy is
# waiting on.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
NODE_VERSION: '22'

jobs:
validate:
name: Validation and traceability
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- run: npm ci

# Static checks first: they take about a second, and catching a broken
# link here saves spinning up a browser to discover it.
- name: Repository validation
run: npm run test:validation

- name: Requirement traceability
run: npm run test:trace

browser:
name: Functional, e2e and regression
runs-on: ubuntu-24.04
needs: validate
steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- run: npm ci

- name: Resolve Playwright version
id: playwright
run: echo "version=$(node -p "require('./package-lock.json').packages['node_modules/@playwright/test'].version")" >> "$GITHUB_OUTPUT"

- name: Cache browsers
id: browser-cache
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}

- name: Install Chromium
run: npx playwright install --with-deps chromium
if: steps.browser-cache.outputs.cache-hit != 'true'

- name: Install system dependencies
run: npx playwright install-deps chromium
if: steps.browser-cache.outputs.cache-hit == 'true'

- name: Browser tests
run: npm run test:browser

- name: Upload report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: playwright-report/
retention-days: 14

visual:
name: Visual regression
runs-on: ubuntu-24.04
needs: validate
steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- run: npm ci

- name: Install Chromium
run: npx playwright install --with-deps chromium

# Skips itself, loudly, when no baselines are committed yet. Seed them with
# the "Update visual baselines" workflow.
- name: Compare against baselines
run: npm run test:visual

- name: Upload diffs
if: failure()
uses: actions/upload-artifact@v7
with:
name: visual-diffs
path: test-results/
retention-days: 14
27 changes: 25 additions & 2 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,49 @@
name: Deploy to GitHub Pages

# Publishes the repository root to Pages — but only after the same gate that
# guards pull requests has passed. A red gate leaves the live site untouched.
#
# Rollback is `git revert <sha> && git push`, which republishes in about thirty
# seconds; the deploy always publishes the current tree, so there is no partial
# state to reconcile.

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
gates:
name: Quality gate
uses: ./.github/workflows/ci.yml
permissions:
contents: read

deploy:
name: Deploy
needs: gates
runs-on: ubuntu-24.04
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- uses: actions/configure-pages@v5

# The site is the repository: no build step, nothing generated.
- uses: actions/upload-pages-artifact@v3
with:
path: .

- id: deployment
uses: actions/deploy-pages@v4
Loading
Loading