Run every lint-class check inside Docker (closes #38)
All checks were successful
check / check (push) Successful in 1m9s
All checks were successful
check / check (push) Successful in 1m9s
Add a root Dockerfile.lint that carries the checks as build steps -- a `lint` stage running `hugo --minify --printPathWarnings` and a `fmt-check` stage running the prettier check -- and reduce script/lint and script/fmt-check to building their stage. A successful build is a clean check. There is no host path and deliberately no "am I already inside a container?" branch, which would be a host lint path in disguise. The two stages share a `base` whose first four instructions are byte-identical to the main Dockerfile's, so the expensive `RUN script/bootstrap` layer that compiles the pinned Hugo from source is a cache hit against the main image instead of a second build of the same thing. Resolve the resulting recursion by splitting the checks by where they run, not with an escape hatch. `make check` runs script/lint, so the main Dockerfile can no longer `RUN make check`: that would be docker-in-docker inside a bare Alpine with no docker client and no daemon socket, and script/cibuild is what CI runs on every push. The main Dockerfile therefore runs `make test`, the production build, and script/cibuild builds it and then calls script/lint and script/fmt-check. CI still covers the production build, lint and the format check, and it runs exactly what a developer runs. script/fmt stays on the host because it rewrites the working tree, which a container build cannot do. That makes it the authoritative copy of the prettier version, scope and flags that the fmt-check stage duplicates; both sides carry a keep-in-sync note. The duplication is forced: any `RUN script/fmt-check` inside the image is the recursion again. Caching is waived for the checks in the shape this repo already settled: `ARG CHECK_EPOCH` with no default, declared and guarded separately in each stage because ARG does not cross a FROM, with the value expanded into the checked command as well as the guard so invalidation does not rest on BuildKit's treatment of an unreferenced ARG. All four image-building entrypoints now generate and pass it -- script/cibuild, script/docker, script/lint, script/fmt-check. Verified: two consecutive script/lint runs on an unchanged tree both executed hugo for real, with script/bootstrap CACHED; a constant-epoch counterfactual restored the false green (exit 0, lint layer CACHED, no hugo output); an empty epoch failed closed on the guard; a broken template failed the lint stage and an unformatted README failed the fmt-check stage, both reverted and re-run clean; script/cibuild and `make check` are green with all three checks demonstrably executing.
This commit is contained in:
24
Dockerfile
24
Dockerfile
@@ -1,8 +1,18 @@
|
|||||||
# Hugo static-site build image. The build runs `make check` (a clean
|
# Hugo static-site build image. The build runs `make test` -- a clean
|
||||||
# `hugo --minify` production build, the `--printPathWarnings` lint
|
# `hugo --minify` production build -- so the image build fails on any
|
||||||
# build, then the read-only prettier docs check), so the image build
|
# template, content or config error.
|
||||||
# fails on any formatting or Hugo build error. This is what CI
|
#
|
||||||
# (script/cibuild) runs on every push.
|
# It deliberately does NOT run `make check`. `make check` runs
|
||||||
|
# script/lint, and script/lint is a `docker build` of Dockerfile.lint,
|
||||||
|
# so `RUN make check` here would be docker-in-docker inside a bare
|
||||||
|
# alpine with no docker client and no daemon socket. The checks are
|
||||||
|
# therefore split by where they run: the production build here, lint and
|
||||||
|
# the prettier format check in Dockerfile.lint. Do not reintroduce a
|
||||||
|
# `make check` (or a `make lint` / `make fmt-check`) line in this file.
|
||||||
|
#
|
||||||
|
# CI coverage is unaffected: script/cibuild builds this image and then
|
||||||
|
# calls script/lint and script/fmt-check, so every check in `make check`
|
||||||
|
# still runs on every push -- see script/cibuild.
|
||||||
#
|
#
|
||||||
# Build this only via script/cibuild or script/docker: both pass the
|
# Build this only via script/cibuild or script/docker: both pass the
|
||||||
# CHECK_EPOCH build argument that this file requires, and a bare
|
# CHECK_EPOCH build argument that this file requires, and a bare
|
||||||
@@ -41,5 +51,5 @@ COPY . .
|
|||||||
ARG CHECK_EPOCH
|
ARG CHECK_EPOCH
|
||||||
RUN [ -n "$CHECK_EPOCH" ] || exit 1
|
RUN [ -n "$CHECK_EPOCH" ] || exit 1
|
||||||
|
|
||||||
# Run all checks - build fails if any check fails.
|
# Run the production build - build fails if the site does not build.
|
||||||
RUN echo "check epoch: ${CHECK_EPOCH}" && make check
|
RUN echo "check epoch: ${CHECK_EPOCH}" && make test
|
||||||
|
|||||||
85
Dockerfile.lint
Normal file
85
Dockerfile.lint
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Lint image. Every lint-class check for this repo runs here and
|
||||||
|
# nowhere else: the checks are build steps, so a successful build IS a
|
||||||
|
# clean lint. There is no host lint path and no "am I already in a
|
||||||
|
# container?" bypass -- script/lint and script/fmt-check are reduced to
|
||||||
|
# building the stage below that carries their check.
|
||||||
|
#
|
||||||
|
# Build this only via script/lint or script/fmt-check: both pass the
|
||||||
|
# CHECK_EPOCH build argument that the stages here require, and a bare
|
||||||
|
# `docker build -f Dockerfile.lint .` fails by design. See the guards.
|
||||||
|
#
|
||||||
|
# Why this is a separate file from the main Dockerfile, and why that
|
||||||
|
# one no longer runs `make check`: `make check` runs script/lint, and
|
||||||
|
# script/lint is now a `docker build`. A `RUN make check` in an image
|
||||||
|
# would therefore be docker-in-docker inside a bare alpine with no
|
||||||
|
# docker client and no daemon socket. The checks are split by where
|
||||||
|
# they run instead -- the main Dockerfile runs the production build,
|
||||||
|
# this file runs lint and the format check -- so no image ever shells
|
||||||
|
# back into `make check`. script/cibuild drives all of them, so CI
|
||||||
|
# coverage is unchanged.
|
||||||
|
|
||||||
|
# alpine 3.21, 2026-02-28
|
||||||
|
FROM alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709 AS base
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# Keep these four instructions byte-identical to the main Dockerfile's,
|
||||||
|
# in the same order: Docker keys layers on the instruction chain, not on
|
||||||
|
# the file they live in, so an identical prefix means this stage is a
|
||||||
|
# cache hit against the main image's layers. script/bootstrap compiles
|
||||||
|
# the pinned Hugo from source, which is by far the most expensive step
|
||||||
|
# here, and it must not be paid twice.
|
||||||
|
COPY script/ script/
|
||||||
|
RUN script/bootstrap
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# --- lint -------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# The site's lint is a clean build that surfaces broken internal links
|
||||||
|
# and template path problems: `hugo` fails on build errors and
|
||||||
|
# --printPathWarnings reports render-target collisions.
|
||||||
|
#
|
||||||
|
# The flags are inlined rather than reached through `RUN script/lint`,
|
||||||
|
# and that is forced, not lazy: script/lint is the docker build that
|
||||||
|
# produces this stage, so calling it here is the recursion described
|
||||||
|
# above. Keep these flags in sync with script/lint's documentation.
|
||||||
|
FROM base AS lint
|
||||||
|
|
||||||
|
# CHECK_EPOCH is a per-invocation nonce supplied by script/lint. Without
|
||||||
|
# it an unchanged tree serves the check layer from cache: the lint never
|
||||||
|
# executes and the build still exits 0, which is precisely the false
|
||||||
|
# green this repo already fixed once in the main Dockerfile. Caching is
|
||||||
|
# explicitly waived for lint, so the value is expanded into the checked
|
||||||
|
# command as well as the guard -- two independent value-keyed
|
||||||
|
# invalidation points, so a cache miss never depends on BuildKit's
|
||||||
|
# treatment of an unreferenced ARG. Declared with no default: a default
|
||||||
|
# is a constant, and a constant is a stable cache key. ARG is
|
||||||
|
# stage-scoped, so the fmt-check stage below declares its own.
|
||||||
|
#
|
||||||
|
# Everything above this line still caches, so script/bootstrap is not
|
||||||
|
# rebuilt.
|
||||||
|
ARG CHECK_EPOCH
|
||||||
|
RUN [ -n "$CHECK_EPOCH" ] || exit 1
|
||||||
|
RUN echo "lint epoch: ${CHECK_EPOCH}" && hugo --minify --printPathWarnings
|
||||||
|
|
||||||
|
# --- fmt-check --------------------------------------------------------
|
||||||
|
#
|
||||||
|
# The read-only prettier check over this repo's markdown and CSS. Same
|
||||||
|
# version, same scope and same flags as script/fmt, which is the
|
||||||
|
# authoritative copy and stays on the host because it writes to the
|
||||||
|
# working tree; keep the two in sync. The exclusions live in
|
||||||
|
# .prettierignore with the reason for each.
|
||||||
|
#
|
||||||
|
# Built as a sibling of `lint` rather than stacked on top of it so that
|
||||||
|
# a --target build runs exactly one check, and so a lint failure and a
|
||||||
|
# formatting failure are reported independently.
|
||||||
|
FROM base AS fmt-check
|
||||||
|
|
||||||
|
# Same nonce, same reasoning as the lint stage above. Declared again
|
||||||
|
# because ARG does not cross a FROM.
|
||||||
|
ARG CHECK_EPOCH
|
||||||
|
RUN [ -n "$CHECK_EPOCH" ] || exit 1
|
||||||
|
RUN echo "fmt-check epoch: ${CHECK_EPOCH}" && \
|
||||||
|
npx --yes "prettier@3.4.2" --check \
|
||||||
|
'**/*.md' '**/*.css' --tab-width 4 --prose-wrap always
|
||||||
43
README.md
43
README.md
@@ -39,8 +39,12 @@ build, and the formatting check:
|
|||||||
make check
|
make check
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The lint build and the formatting check run inside Docker, so `make check` needs
|
||||||
|
a working Docker daemon; there is no host fallback.
|
||||||
|
|
||||||
`make fmt` rewrites the repo's markdown and CSS to the project's prettier
|
`make fmt` rewrites the repo's markdown and CSS to the project's prettier
|
||||||
settings; run it if `make check` fails on formatting.
|
settings; run it if `make check` fails on formatting. It runs on the host,
|
||||||
|
because it writes to your working tree.
|
||||||
|
|
||||||
To contribute to this site, contact **sneak@sneak.berlin** for git repository
|
To contribute to this site, contact **sneak@sneak.berlin** for git repository
|
||||||
access.
|
access.
|
||||||
@@ -61,22 +65,41 @@ provide:
|
|||||||
git pre-commit hook
|
git pre-commit hook
|
||||||
- `script/test` — the correctness check: a clean `hugo --minify` production
|
- `script/test` — the correctness check: a clean `hugo --minify` production
|
||||||
build
|
build
|
||||||
- `script/lint` — a clean build that surfaces broken links and path collisions
|
- `script/lint` — a clean build that surfaces broken links and path collisions,
|
||||||
|
run inside Docker: it builds the `lint` stage of `Dockerfile.lint`, where the
|
||||||
|
check is a build step, so a successful build is a clean lint
|
||||||
- `script/fmt` — format every markdown and CSS file in the repo with prettier;
|
- `script/fmt` — format every markdown and CSS file in the repo with prettier;
|
||||||
the exclusions live in `.prettierignore` with the reason for each
|
the exclusions live in `.prettierignore` with the reason for each. The one
|
||||||
- `script/fmt-check` — check that formatting (read-only)
|
prettier entrypoint that runs on the host, because it writes to your working
|
||||||
|
tree
|
||||||
|
- `script/fmt-check` — check that formatting (read-only), also inside Docker:
|
||||||
|
the `fmt-check` stage of `Dockerfile.lint`
|
||||||
- `script/check` — run `script/test`, `script/lint`, then `script/fmt-check`;
|
- `script/check` — run `script/test`, `script/lint`, then `script/fmt-check`;
|
||||||
modifies no tracked files
|
modifies no tracked files
|
||||||
- `script/docker` — build the Docker image tagged with the project name
|
- `script/docker` — build the Docker image tagged with the project name
|
||||||
- `script/cibuild` — the CI build; the Dockerfile runs `make check`
|
- `script/cibuild` — the CI build: the main image (the production build), then
|
||||||
|
`script/lint` and `script/fmt-check`
|
||||||
- `script/install-precommit` — install the git pre-commit hook that runs
|
- `script/install-precommit` — install the git pre-commit hook that runs
|
||||||
`script/check`
|
`script/check`
|
||||||
|
|
||||||
Build the image through `script/cibuild` or `script/docker` only. Both pass a
|
Every lint run for this repo happens inside a container. `script/lint` and
|
||||||
per-invocation `CHECK_EPOCH` build argument that the Dockerfile requires, so the
|
`script/fmt-check` have no host path and no "already inside a container?"
|
||||||
`make check` layer can never be served from cache — without it Docker returns a
|
branch, so what a developer runs and what CI runs are the same build.
|
||||||
green it did not earn. A bare `docker build .` fails closed on the Dockerfile's
|
|
||||||
`CHECK_EPOCH` guard rather than caching its way to a false success.
|
That is also why the main `Dockerfile` runs `make test` rather than
|
||||||
|
`make check`: `make check` calls `script/lint`, which is itself a
|
||||||
|
`docker build`, so a `make check` inside an image would be docker-in-docker in a
|
||||||
|
bare Alpine with no docker client and no daemon socket. The checks are split by
|
||||||
|
where they run — the production build in `Dockerfile`, lint and the format check
|
||||||
|
in `Dockerfile.lint` — and `script/cibuild` drives all three, so CI coverage is
|
||||||
|
unchanged.
|
||||||
|
|
||||||
|
Build any image through `script/cibuild`, `script/docker`, `script/lint` or
|
||||||
|
`script/fmt-check` only. All four pass a per-invocation `CHECK_EPOCH` build
|
||||||
|
argument that the Dockerfiles require, so a check layer can never be served from
|
||||||
|
cache — without it Docker returns a green it did not earn. A bare `docker build`
|
||||||
|
fails closed on the `CHECK_EPOCH` guard rather than caching its way to a false
|
||||||
|
success.
|
||||||
|
|
||||||
A convenience `make serve` target runs `hugo server` for local preview.
|
A convenience `make serve` target runs `hugo server` for local preview.
|
||||||
|
|
||||||
|
|||||||
101
TODO.md
101
TODO.md
@@ -20,17 +20,64 @@ wrangler CLI install, an exact version), and the Hugo that builds the published
|
|||||||
site is a deliberate pinned version rather than whatever the base image's
|
site is a deliberate pinned version rather than whatever the base image's
|
||||||
package repo serves. The site now ships a Cloudflare Pages `_headers` file, so
|
package repo serves. The site now ships a Cloudflare Pages `_headers` file, so
|
||||||
its response security headers are declared in the repo instead of being whatever
|
its response security headers are declared in the repo instead of being whatever
|
||||||
the edge defaults to — unverified in production until the next deploy.
|
the edge defaults to — unverified in production until the next deploy. Every
|
||||||
|
lint-class check now runs inside a container and nowhere else: `script/lint` and
|
||||||
|
`script/fmt-check` build stages of `Dockerfile.lint`, with no host path to fall
|
||||||
|
back to.
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Move the artifact actions in `.gitea/workflows/deploy.yml` to v4 once this Gitea
|
Add the missing `cibuild` and `precommit` shims to the `Makefile`, so that every
|
||||||
Actions instance serves the v4 artifact protocol; they are pinned on the
|
documented entrypoint has a make target and the documented "always use make
|
||||||
deprecated v3 line because v4 fails here (#20). This touches the live deploy
|
targets" rule is actually satisfiable
|
||||||
path, so it needs a real workflow run to verify rather than a local check.
|
(https://git.eeqj.de/sneak/lora.vegas/issues/34). Done when `make cibuild` and
|
||||||
|
`make precommit` exist, are declared `.PHONY`, and the README Entrypoints
|
||||||
|
section matches.
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-08-10: moved every lint-class check into Docker
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/38). A new root `Dockerfile.lint`
|
||||||
|
carries the checks as build steps — a `lint` stage running
|
||||||
|
`hugo --minify --printPathWarnings` and a `fmt-check` stage running the
|
||||||
|
prettier check — on a shared `base` stage whose first four instructions are
|
||||||
|
byte-identical to the main `Dockerfile`'s, so the expensive
|
||||||
|
`RUN script/bootstrap` layer that compiles Hugo from source is a cache hit
|
||||||
|
against the main image rather than a second build of the same thing.
|
||||||
|
`script/lint` and `script/fmt-check` are now nothing but a `docker build` of
|
||||||
|
their stage; there is no host path and deliberately no "already inside a
|
||||||
|
container?" branch, which would be a host lint path in disguise. The recursion
|
||||||
|
this creates was resolved by splitting the checks by where they run rather
|
||||||
|
than by adding an escape hatch: `make check` runs `script/lint`, so the main
|
||||||
|
`Dockerfile` can no longer `RUN make check` — that would be docker-in-docker
|
||||||
|
inside a bare Alpine with no docker client and no daemon socket, and
|
||||||
|
`script/cibuild` is what CI runs on every push. The main `Dockerfile`
|
||||||
|
therefore runs `make test`, the production build, and `script/cibuild` builds
|
||||||
|
it and then calls `script/lint` and `script/fmt-check`, so CI still covers all
|
||||||
|
three and cannot drift from what a developer runs. `script/fmt` stays on the
|
||||||
|
host because it rewrites the working tree, which makes it the authoritative
|
||||||
|
copy of the prettier version and flags that the `fmt-check` stage duplicates;
|
||||||
|
both sides carry a keep-in-sync note, and that duplication is forced, since
|
||||||
|
any `RUN script/fmt-check` inside the image is the recursion again. Caching is
|
||||||
|
waived for the checks exactly as the main `Dockerfile` already does it:
|
||||||
|
`ARG CHECK_EPOCH` with no default, declared and guarded separately in each
|
||||||
|
stage because `ARG` does not cross a `FROM`, with the value expanded into the
|
||||||
|
checked command as well as the guard. All four image-building entrypoints now
|
||||||
|
generate and pass it — `script/cibuild`, `script/docker`, `script/lint`,
|
||||||
|
`script/fmt-check` — which is the failure mode this repo already hit once, a
|
||||||
|
Dockerfile guard asserting a property one entrypoint did not supply. Verified
|
||||||
|
rather than assumed: two consecutive `script/lint` runs on an unchanged tree
|
||||||
|
both executed hugo for real (second run 2.9s wall, `RUN script/bootstrap`
|
||||||
|
`CACHED`, distinct epoch echoed, `Total in 37 ms` printed), a constant-epoch
|
||||||
|
counterfactual restored the false green (exit 0 in 0.25s, lint layer `CACHED`,
|
||||||
|
no hugo output at all), an empty epoch failed closed on the guard, a broken
|
||||||
|
template failed the lint stage with hugo's own render error, and an over-long
|
||||||
|
line appended to `README.md` failed the fmt-check stage with
|
||||||
|
`[warn] README.md`; both violations were reverted and re-run clean. Not
|
||||||
|
changed here, and still true: the lint stage fails on hugo build errors but
|
||||||
|
not on render-target collisions, which `--printPathWarnings` only prints
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/25) — containerising the run
|
||||||
|
neither fixes nor worsens that
|
||||||
- 2026-08-10: added the `LICENSE` file and made the README say what it says
|
- 2026-08-10: added the `LICENSE` file and made the README say what it says
|
||||||
(closes #10). The repo is public (`private: false` on the Gitea API, verified
|
(closes #10). The repo is public (`private: false` on the Gitea API, verified
|
||||||
rather than assumed), so the owner's standing policy — MIT on any public repo
|
rather than assumed), so the owner's standing policy — MIT on any public repo
|
||||||
@@ -226,8 +273,40 @@ path, so it needs a real workflow run to verify rather than a local check.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
|
Startable work first. Everything under "Blocked" waits on somebody or something
|
||||||
|
outside this repo, so nothing there may be picked up as the Next Step.
|
||||||
|
|
||||||
|
- Make the prettier scope's exclusion of dot-directories explicit instead of
|
||||||
|
leaning on `.gitignore` (https://git.eeqj.de/sneak/lora.vegas/issues/33)
|
||||||
|
- Fix the README's SSH-only clone URL, and add the two entrypoints the
|
||||||
|
Entrypoints section omits, `script/precommit` and `script/projectname`
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/36)
|
||||||
|
- Drop the Go toolchain and module cache from the check image's final layer;
|
||||||
|
they are needed to build hugo and dead weight afterwards
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/28)
|
||||||
|
- Add a timeout guard to `script/test` and `script/lint` so a wedged build fails
|
||||||
|
instead of hanging (https://git.eeqj.de/sneak/lora.vegas/issues/16)
|
||||||
|
- Sync the reformat of `REPO_POLICIES.md` back upstream to `prompts` so the
|
||||||
|
canonical copy is clean under the shared prettier settings and future syncs
|
||||||
|
are a straight byte copy
|
||||||
|
- Keep mesh channel and signal group listings current
|
||||||
|
|
||||||
|
## Blocked
|
||||||
|
|
||||||
|
- Decide whether `script/lint` should fail on render-target collisions rather
|
||||||
|
than only print them; `--printPathWarnings` exits 0 today, so the signal is
|
||||||
|
reported and not enforced. Owner call, since it changes what the gate rejects
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/25)
|
||||||
|
- Move the artifact actions in `.gitea/workflows/deploy.yml` to v4 once this
|
||||||
|
Gitea Actions instance serves the v4 artifact protocol; they are pinned on the
|
||||||
|
deprecated v3 line because v4 fails here
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/20). This touches the live deploy
|
||||||
|
path, so it needs a real workflow run to verify rather than a local check
|
||||||
- Move the deploy container to a pinned node 22 so the wrangler pin can advance
|
- Move the deploy container to a pinned node 22 so the wrangler pin can advance
|
||||||
past 4.86.0 (#21)
|
past 4.86.0 (https://git.eeqj.de/sneak/lora.vegas/issues/21)
|
||||||
|
- Delete the stale remote branches `feat/initial-site` and `security-audit`;
|
||||||
|
only the owner can remove them
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/15)
|
||||||
- After the next deploy, confirm the `_headers` file actually took effect, on
|
- After the next deploy, confirm the `_headers` file actually took effect, on
|
||||||
both `https://lora.vegas/` and `https://www.lora.vegas/`: `curl -sSI` against
|
both `https://lora.vegas/` and `https://www.lora.vegas/`: `curl -sSI` against
|
||||||
each must show `strict-transport-security` or `content-security-policy`.
|
each must show `strict-transport-security` or `content-security-policy`.
|
||||||
@@ -237,12 +316,10 @@ path, so it needs a real workflow run to verify rather than a local check.
|
|||||||
`includeSubDomains` rests on `www.lora.vegas` being served by this same Pages
|
`includeSubDomains` rests on `www.lora.vegas` being served by this same Pages
|
||||||
project, which was established behaviourally from identical response bodies
|
project, which was established behaviourally from identical response bodies
|
||||||
rather than from the Cloudflare dashboard. If `www` turns out not to be
|
rather than from the Cloudflare dashboard. If `www` turns out not to be
|
||||||
covered, the `includeSubDomains` decision has to be revisited (#14)
|
covered, the `includeSubDomains` decision has to be revisited
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/14)
|
||||||
- Decide the HSTS `includeSubDomains` and `preload` posture for `lora.vegas`.
|
- Decide the HSTS `includeSubDomains` and `preload` posture for `lora.vegas`.
|
||||||
Both are owner calls: neither can be walked back inside the max-age window,
|
Both are owner calls: neither can be walked back inside the max-age window,
|
||||||
and `includeSubDomains` binds hostnames this repo does not control (#14)
|
and `includeSubDomains` binds hostnames this repo does not control
|
||||||
- Sync the reformat of `REPO_POLICIES.md` back upstream to `prompts` so the
|
(https://git.eeqj.de/sneak/lora.vegas/issues/14)
|
||||||
canonical copy is clean under the shared prettier settings and future syncs
|
|
||||||
are a straight byte copy
|
|
||||||
- Verify the Cloudflare Pages deploy still works after the workflow changes
|
- Verify the Cloudflare Pages deploy still works after the workflow changes
|
||||||
- Keep mesh channel and signal group listings current
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
# scripts-to-rule-them-all. Must not modify any tracked files. Runs the
|
# scripts-to-rule-them-all. Must not modify any tracked files. Runs the
|
||||||
# canonical order: the clean production build, then the lint build that
|
# canonical order: the clean production build, then the lint build that
|
||||||
# reports path warnings, then the read-only formatting check.
|
# reports path warnings, then the read-only formatting check.
|
||||||
|
#
|
||||||
|
# The last two run inside Docker (they build stages of Dockerfile.lint),
|
||||||
|
# so this script needs a working docker daemon. That is deliberate:
|
||||||
|
# every lint run for this repo happens in a container, and there is no
|
||||||
|
# host fallback to drop back to.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
|||||||
@@ -1,17 +1,32 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# script/cibuild: run the CI build. The Dockerfile runs `make check`,
|
# script/cibuild: run the CI build. The Gitea workflow runs this on
|
||||||
# so a successful build implies all checks pass. The Gitea workflow
|
# push, and it is the single entrypoint that covers everything:
|
||||||
# runs this on push.
|
|
||||||
#
|
#
|
||||||
# That implication only holds because of CHECK_EPOCH. Docker keys the
|
# 1. the main Dockerfile, which runs the clean `hugo --minify`
|
||||||
# `RUN make check` layer on content, so on an unchanged tree it is
|
# production build (`make test`)
|
||||||
# served from cache: the checks never execute and the build still exits
|
# 2. script/lint, which builds Dockerfile.lint's `lint` stage
|
||||||
# 0. Passing a value that differs on every invocation invalidates that
|
# 3. script/fmt-check, which builds Dockerfile.lint's `fmt-check`
|
||||||
# layer and everything below it, while the script/bootstrap toolchain
|
# stage
|
||||||
# layer above it keeps caching.
|
#
|
||||||
|
# Steps 2 and 3 are delegated to the same scripts a developer runs, so
|
||||||
|
# CI cannot drift from `make check`. They are separate builds rather
|
||||||
|
# than a `RUN make check` inside the main image because script/lint is
|
||||||
|
# itself a `docker build`: shelling back into `make check` from an image
|
||||||
|
# would be docker-in-docker inside a bare alpine with no docker client
|
||||||
|
# and no daemon socket. See Dockerfile.lint for the full reasoning.
|
||||||
|
#
|
||||||
|
# The main image build below only implies a passing production build
|
||||||
|
# because of CHECK_EPOCH. Docker keys the `RUN make test` layer on
|
||||||
|
# content, so on an unchanged tree it is served from cache: the build
|
||||||
|
# never executes and the image build still exits 0. Passing a value that
|
||||||
|
# differs on every invocation invalidates that layer and everything
|
||||||
|
# below it, while the script/bootstrap toolchain layer above it keeps
|
||||||
|
# caching. script/lint and script/fmt-check each do the same for their
|
||||||
|
# own stage.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
@@ -25,6 +40,9 @@ main() {
|
|||||||
# 0, so `$$` is appended to cover that degradation.
|
# 0, so `$$` is appended to cover that degradation.
|
||||||
epoch="$(date +%s%N)$$"
|
epoch="$(date +%s%N)$$"
|
||||||
docker build --build-arg CHECK_EPOCH="$epoch" .
|
docker build --build-arg CHECK_EPOCH="$epoch" .
|
||||||
|
|
||||||
|
"$SCRIPT_DIR/lint"
|
||||||
|
"$SCRIPT_DIR/fmt-check"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
# the reason next to each entry: the Hugo layout templates, which are
|
# the reason next to each entry: the Hugo layout templates, which are
|
||||||
# Go templates and not HTML, and content/, whose reformatting was
|
# Go templates and not HTML, and content/, whose reformatting was
|
||||||
# measured to change the rendered page.
|
# measured to change the rendered page.
|
||||||
|
#
|
||||||
|
# This runs on the host, unlike the read-only check: it rewrites the
|
||||||
|
# working tree, which a container build cannot do. It is therefore the
|
||||||
|
# authoritative copy of the prettier version, scope and flags -- the
|
||||||
|
# fmt-check stage of Dockerfile.lint duplicates them and must be kept in
|
||||||
|
# sync with this file.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# script/fmt-check: check the formatting of this repo's markdown and
|
# script/fmt-check: check the formatting of this repo's markdown and
|
||||||
# CSS (read-only). Same scope and same settings as script/fmt - keep
|
# CSS (read-only). Like script/lint, it runs only in Docker: it builds
|
||||||
# the two in sync - but fails instead of writing.
|
# the `fmt-check` stage of Dockerfile.lint, where prettier runs as a
|
||||||
|
# build step. Leaving prettier to run on the host here would have left
|
||||||
|
# `make check` with a host lint path, which is the thing being removed.
|
||||||
|
#
|
||||||
|
# The version, scope and flags live in Dockerfile.lint and must stay in
|
||||||
|
# sync with script/fmt, which is the authoritative copy and stays on the
|
||||||
|
# host because it writes to the working tree.
|
||||||
|
#
|
||||||
|
# Dockerfile.lint requires the CHECK_EPOCH build argument, generated
|
||||||
|
# here exactly as script/cibuild generates it -- see that script for why
|
||||||
|
# the check layer must not be allowed to cache, and why the value is
|
||||||
|
# built in an assignment rather than inline.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
PRETTIER_VERSION="3.4.2"
|
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
npx --yes "prettier@${PRETTIER_VERSION}" --check \
|
epoch="$(date +%s%N)$$"
|
||||||
'**/*.md' '**/*.css' --tab-width 4 --prose-wrap always
|
docker build -f Dockerfile.lint --target fmt-check \
|
||||||
|
--build-arg CHECK_EPOCH="$epoch" .
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
21
script/lint
21
script/lint
@@ -1,15 +1,26 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# script/lint: this Hugo site has no dedicated linter, so the lint gate
|
# script/lint: run the lint. This Hugo site has no dedicated linter, so
|
||||||
# is a clean build that surfaces broken internal links and template
|
# the lint gate is a clean build that surfaces broken internal links and
|
||||||
# path problems. It is a real check: `hugo` fails on build errors, and
|
# template path problems -- but where it runs is not negotiable: every
|
||||||
# --printPathWarnings reports render-target collisions.
|
# lint run happens inside a Docker container, so this script does
|
||||||
|
# nothing except build the `lint` stage of Dockerfile.lint. The check is
|
||||||
|
# a build step there, so a successful build is a clean lint. There is
|
||||||
|
# deliberately no host fallback and no "already inside a container?"
|
||||||
|
# branch: either would be a host lint path wearing a disguise.
|
||||||
|
#
|
||||||
|
# Dockerfile.lint requires the CHECK_EPOCH build argument, generated
|
||||||
|
# here exactly as script/cibuild generates it -- see that script for why
|
||||||
|
# the check layer must not be allowed to cache, and why the value is
|
||||||
|
# built in an assignment rather than inline.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
hugo --minify --printPathWarnings
|
epoch="$(date +%s%N)$$"
|
||||||
|
docker build -f Dockerfile.lint --target lint \
|
||||||
|
--build-arg CHECK_EPOCH="$epoch" .
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user