next: accumulated work for the current cycle #39
25
Dockerfile
25
Dockerfile
@@ -1,8 +1,16 @@
|
|||||||
# Hugo static-site build image. The build runs `make check` (a clean
|
# Hugo static-site build image. The build runs the individual non-lint
|
||||||
# `hugo --minify` production build, the `--printPathWarnings` lint
|
# checks -- script/test, a clean `hugo --minify` production build, and
|
||||||
# build, then the read-only prettier docs check), so the image build
|
# script/fmt-check, the read-only prettier check -- so the image build
|
||||||
# fails on any formatting or Hugo build error. This is what CI
|
# fails on any template, content, config or formatting error.
|
||||||
# (script/cibuild) runs on every push.
|
#
|
||||||
|
# It deliberately does NOT run `make check`, and only the lint is
|
||||||
|
# missing from what it does run. `make check` calls script/lint, and
|
||||||
|
# script/lint is a `docker build` of Dockerfile.lint, so `RUN make
|
||||||
|
# check` here would attempt a docker build inside a build step, in a
|
||||||
|
# bare alpine with no docker client and no daemon socket. Putting
|
||||||
|
# `make check` (or a `make lint`) back reintroduces exactly that
|
||||||
|
# recursion. The lint is not skipped: script/cibuild runs script/lint
|
||||||
|
# first, in its own container, before this build starts.
|
||||||
#
|
#
|
||||||
# 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 +49,8 @@ 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.
|
# The individual non-lint checks - build fails if either fails. Invoked
|
||||||
RUN echo "check epoch: ${CHECK_EPOCH}" && make check
|
# as script/ entrypoints rather than `make check` for the reason in the
|
||||||
|
# header comment above.
|
||||||
|
RUN echo "check epoch: ${CHECK_EPOCH}" && script/test
|
||||||
|
RUN script/fmt-check
|
||||||
|
|||||||
61
Dockerfile.lint
Normal file
61
Dockerfile.lint
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Lint-only image. `script/lint` builds this file and nothing else: the
|
||||||
|
# lint runs as a build step, so a successful build IS a clean lint.
|
||||||
|
#
|
||||||
|
# One stage, deliberately. A whole-file `docker build -f Dockerfile.lint .`
|
||||||
|
# builds only the file's LAST stage, and sibling stages off a shared base
|
||||||
|
# have no ordering edge between them, so a second stage sitting beside
|
||||||
|
# this one would be silently skipped by exactly the invocation the
|
||||||
|
# canonical org-wide `script/lint` uses -- a green that linted nothing,
|
||||||
|
# which is the failure mode this file exists to prevent. With a single
|
||||||
|
# stage there is nothing to skip and `script/lint` needs no `--target`.
|
||||||
|
# If a second check is ever added here it must be chained (`FROM lint AS
|
||||||
|
# ...`) or carry an explicit ordering edge, never left as a sibling.
|
||||||
|
#
|
||||||
|
# Only linting is containerised (owner ruling, 2026-08-10: "fmt and fmt
|
||||||
|
# check arent docker, just linting"). script/fmt and script/fmt-check run
|
||||||
|
# on the host, and the main Dockerfile runs the production build and the
|
||||||
|
# format check directly -- see the comment there.
|
||||||
|
#
|
||||||
|
# The lint is invoked directly below rather than through `make lint` or
|
||||||
|
# `script/lint`. That is not a style choice: `script/lint` IS this build,
|
||||||
|
# so calling it from inside would recurse into a docker build with no
|
||||||
|
# daemon.
|
||||||
|
#
|
||||||
|
# This repo's lint is a clean Hugo build that surfaces broken internal
|
||||||
|
# links and template path problems: `hugo` fails on build errors and
|
||||||
|
# --printPathWarnings reports render-target collisions.
|
||||||
|
|
||||||
|
# alpine 3.21, 2026-02-28
|
||||||
|
FROM alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
|
||||||
|
|
||||||
|
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 build 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. The dependency layer is also
|
||||||
|
# deliberately above the ARG below, so it stays cached and only the lint
|
||||||
|
# step re-runs on every invocation.
|
||||||
|
COPY script/ script/
|
||||||
|
RUN script/bootstrap
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# CHECK_EPOCH is a per-invocation nonce supplied by script/lint. Without
|
||||||
|
# it an unchanged tree serves the lint 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 linted
|
||||||
|
# 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, and the epoch is visible in the build
|
||||||
|
# log. Declared with no default: a default is a constant, and a constant
|
||||||
|
# is a stable cache key. The guard makes a bare
|
||||||
|
# `docker build -f Dockerfile.lint .` fail loudly instead of silently
|
||||||
|
# reusing the empty (and therefore stable) cache key. Keep both
|
||||||
|
# references.
|
||||||
|
ARG CHECK_EPOCH
|
||||||
|
RUN [ -n "$CHECK_EPOCH" ] || exit 1
|
||||||
|
RUN echo "lint epoch: ${CHECK_EPOCH}" && hugo --minify --printPathWarnings
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Jeffrey Paul <sneak@sneak.berlin>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
43
README.md
43
README.md
@@ -1,7 +1,8 @@
|
|||||||
# lora.vegas
|
# lora.vegas
|
||||||
|
|
||||||
`lora.vegas` is the website of the Las Vegas Meshtastic and LoRa community: a
|
`lora.vegas` is the website of the Las Vegas Meshtastic and LoRa community: an
|
||||||
single-page static site, built with Hugo, by [@sneak](https://sneak.berlin).
|
MIT-licensed single-page static site, built with Hugo, by
|
||||||
|
[@sneak](https://sneak.berlin).
|
||||||
|
|
||||||
It publishes what the local mesh needs in one linkable place:
|
It publishes what the local mesh needs in one linkable place:
|
||||||
|
|
||||||
@@ -38,6 +39,11 @@ build, and the formatting check:
|
|||||||
make check
|
make check
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The lint runs inside Docker, so `make check` needs a working Docker daemon;
|
||||||
|
there is no host fallback. On a machine that has never built the image, the
|
||||||
|
first run compiles the pinned Hugo from source, which takes minutes; later runs
|
||||||
|
reuse that cached layer.
|
||||||
|
|
||||||
`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.
|
||||||
|
|
||||||
@@ -60,22 +66,37 @@ 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 `Dockerfile.lint`, where the lint 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
|
||||||
- `script/fmt-check` — check that formatting (read-only)
|
- `script/fmt-check` — check that formatting (read-only)
|
||||||
- `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: `script/lint` first, for fail-fast feedback,
|
||||||
|
then the main image, which runs the non-lint checks
|
||||||
- `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, and only the lint does.
|
||||||
per-invocation `CHECK_EPOCH` build argument that the Dockerfile requires, so the
|
`script/lint` has no host path and no "already inside a container?" branch, so
|
||||||
`make check` layer can never be served from cache — without it Docker returns a
|
what a developer runs and what CI runs are the same build. `script/fmt` and
|
||||||
green it did not earn. A bare `docker build .` fails closed on the Dockerfile's
|
`script/fmt-check` run on the host: a formatting check is not a lint.
|
||||||
`CHECK_EPOCH` guard rather than caching its way to a false success.
|
|
||||||
|
That is also why the main `Dockerfile` runs `script/test` and `script/fmt-check`
|
||||||
|
rather than `make check`. `make check` calls `script/lint`, which is itself a
|
||||||
|
`docker build`, so a `make check` inside an image would attempt a docker build
|
||||||
|
in a bare Alpine with no docker client and no daemon socket. The lint is not
|
||||||
|
skipped — `script/cibuild` runs it first, in its own container, before the main
|
||||||
|
image build starts.
|
||||||
|
|
||||||
|
Build any image through `script/cibuild`, `script/docker` or `script/lint` only.
|
||||||
|
All three 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.
|
||||||
|
|
||||||
@@ -109,7 +130,9 @@ The live task list is in [TODO.md](TODO.md).
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Content is provided as-is for community use.
|
MIT. See [LICENSE](LICENSE). This covers everything in the repository — the Hugo
|
||||||
|
configuration, the `script/` entrypoints, the vendored `themes/loravega/`
|
||||||
|
templates and CSS, and the site content in `content/`.
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
|
|||||||
149
TODO.md
149
TODO.md
@@ -13,23 +13,103 @@
|
|||||||
pre-1.0
|
pre-1.0
|
||||||
|
|
||||||
No git tags. The site is live and now has the scripts-to-rule-them-all scaffold
|
No git tags. The site is live and now has the scripts-to-rule-them-all scaffold
|
||||||
(`Makefile`, `script/`, `Dockerfile`, `check.yml`) and the canonical policy
|
(`Makefile`, `script/`, `Dockerfile`, `check.yml`), the canonical policy
|
||||||
dotfiles; `LICENSE` is the only mandated file still missing. Every external
|
dotfiles and `LICENSE`, so the mandated minimum file list is complete. Every
|
||||||
reference in the repo is now pinned by cryptographic hash (or, for the wrangler
|
external reference in the repo is now pinned by cryptographic hash (or, for the
|
||||||
CLI install, an exact version), and the Hugo that builds the published site is a
|
wrangler CLI install, an exact version), and the Hugo that builds the published
|
||||||
deliberate pinned version rather than whatever the base image's package repo
|
site is a deliberate pinned version rather than whatever the base image's
|
||||||
serves. The site now ships a Cloudflare Pages `_headers` file, so its response
|
package repo serves. The site now ships a Cloudflare Pages `_headers` file, so
|
||||||
security headers are declared in the repo instead of being whatever the edge
|
its response security headers are declared in the repo instead of being whatever
|
||||||
defaults to — unverified in production until the next deploy.
|
the edge defaults to — unverified in production until the next deploy. The lint
|
||||||
|
now runs inside a container and nowhere else: `script/lint` is a build of
|
||||||
|
`Dockerfile.lint`, with no host path to fall back to. Formatting is not a lint
|
||||||
|
and stays on the host.
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Add `LICENSE` (#10) and replace the README's "content is provided as-is" note
|
Add the missing `cibuild` and `precommit` shims to the `Makefile`, so that every
|
||||||
with the committed license. Blocked on the owner's choice of license — the
|
documented entrypoint has a make target and the documented "always use make
|
||||||
remaining policy scaffold is otherwise complete.
|
targets" rule is actually satisfiable
|
||||||
|
(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 the lint into Docker
|
||||||
|
(https://git.eeqj.de/sneak/lora.vegas/issues/38). A new root `Dockerfile.lint`
|
||||||
|
runs `hugo --minify --printPathWarnings` as a build step, so a successful
|
||||||
|
build is a clean lint, and `script/lint` is nothing but a build of that file —
|
||||||
|
no host path and deliberately no "already inside a container?" branch, which
|
||||||
|
would be a host lint path in disguise. The containerisation boundary is lint
|
||||||
|
only, per the owner ruling of the same day: formatting is not a lint, so
|
||||||
|
`script/fmt` and `script/fmt-check` stay on the host. `Dockerfile.lint` has
|
||||||
|
exactly one stage on purpose. A whole-file `docker build -f Dockerfile.lint .`
|
||||||
|
builds only the file's last stage, and sibling stages off a shared base have
|
||||||
|
no ordering edge, so any second stage beside the lint would be silently
|
||||||
|
skipped by the invocation the canonical org-wide `script/lint` uses — a green
|
||||||
|
that linted nothing. With one stage there is nothing to skip and `script/lint`
|
||||||
|
needs no `--target`. Its first four instructions are byte-identical to the
|
||||||
|
main `Dockerfile`'s, so the expensive `RUN script/bootstrap` layer that
|
||||||
|
compiles Hugo from source is shared between the two images rather than paid
|
||||||
|
twice. The recursion this creates was resolved by direction, not detection:
|
||||||
|
`make check` calls `script/lint`, so the main `Dockerfile` can no longer
|
||||||
|
`RUN make check` — that would attempt a docker build inside a build step, in a
|
||||||
|
bare Alpine with no docker client and no daemon socket. It runs the individual
|
||||||
|
non-lint checks instead, `script/test` and `script/fmt-check`, matching the
|
||||||
|
canonical shape upstream, and `script/cibuild` runs `script/lint` first for
|
||||||
|
fail-fast feedback before the main image build starts. So CI still covers all
|
||||||
|
three checks and cannot drift from what a developer runs. Caching is waived
|
||||||
|
for the lint exactly as the main `Dockerfile` already does it:
|
||||||
|
`ARG CHECK_EPOCH` with no default, guarded with
|
||||||
|
`[ -n "$CHECK_EPOCH" ] || exit 1`, and the value expanded into the linted
|
||||||
|
command as well as the guard, so invalidation never rests on BuildKit's
|
||||||
|
handling of an unreferenced `ARG`. Every image-building entrypoint generates
|
||||||
|
and passes it — `script/cibuild`, `script/docker`, `script/lint` — which is
|
||||||
|
the failure mode this repo already hit once, a Dockerfile guard asserting a
|
||||||
|
property one entrypoint did not supply. `script/lint` builds with
|
||||||
|
`--output type=cacheonly`: the build is run for its exit status, not for an
|
||||||
|
image, and since the lint layer is cache-busted every run an exporting build
|
||||||
|
would leave one dangling image per lint on a host shared with other work.
|
||||||
|
Verified rather than assumed: two consecutive `script/lint` runs on an
|
||||||
|
unchanged tree both executed hugo for real (second run 0.85s wall,
|
||||||
|
`RUN script/bootstrap` `CACHED`, distinct epochs echoed, real build tables
|
||||||
|
printed); a whole-file `docker build -f Dockerfile.lint .` with the argument
|
||||||
|
and no `--target` ran the lint for real, which is the regression test for the
|
||||||
|
skipped-sibling hazard; a bare build with no argument failed closed on the
|
||||||
|
guard; a planted template error failed the lint with hugo's own render error
|
||||||
|
and made `script/cibuild` exit in 0.6s without the main image build starting
|
||||||
|
at all; a planted over-long line failed the host `script/fmt-check` with
|
||||||
|
`[warn] README.md`; both violations were reverted and re-run clean. Not
|
||||||
|
changed here, and still true: the lint 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
|
||||||
|
(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
|
||||||
|
lacking a license — applies. `LICENSE` is byte-identical to the canonical
|
||||||
|
`sneak/homoicon` copy, confirmed by git blob hash rather than by eye
|
||||||
|
(`3274443`), and its body is word-for-word the SPDX MIT text with only the
|
||||||
|
line wrapping differing. The README's "Content is provided as-is for community
|
||||||
|
use." — which granted nothing and matched no committed file — is replaced by
|
||||||
|
`MIT. See [LICENSE](LICENSE).` plus an explicit statement that the licence
|
||||||
|
covers the content in `content/` as well as the code, since this repo carries
|
||||||
|
both and MIT names only "the Software". The Description first line now carries
|
||||||
|
the licence, which `REPO_POLICIES.md` requires and which was the one field it
|
||||||
|
was missing. Nothing published contradicts the choice: the built `public/`
|
||||||
|
tree carries no copyright, all-rights-reserved or terms-of-use string
|
||||||
|
anywhere, in `index.html`, `css/style.css`, `index.xml` or `sitemap.xml` — the
|
||||||
|
footer `baseof.html` renders names `@sneak` and links the repo but asserts no
|
||||||
|
reservation of rights, and the content is factual mesh channel data with no
|
||||||
|
licence claim of its own. The fmt gate cannot reach `LICENSE` and needed no
|
||||||
|
`.prettierignore` entry: `script/fmt` passes prettier the explicit globs
|
||||||
|
`'**/*.md'` and `'**/*.css'`, and an extensionless root file matches neither.
|
||||||
|
Measured, not assumed — a `script/fmt` run leaves the file's hash unchanged,
|
||||||
|
and a counterfactual `LICENSE.md` copy was reflowed by the same run, which is
|
||||||
|
the direct evidence that it is the extension and not an ignore rule doing the
|
||||||
|
excluding. Deliberately not done, per the issue: per-file licence headers and
|
||||||
|
SPDX identifiers, which no org standard mandates
|
||||||
- 2026-08-09: added `static/_headers` so Cloudflare Pages serves baseline
|
- 2026-08-09: added `static/_headers` so Cloudflare Pages serves baseline
|
||||||
response security headers (closes #14). Hugo copies `static/` verbatim into
|
response security headers (closes #14). Hugo copies `static/` verbatim into
|
||||||
`public/`, which is the deploy root Pages reads the file from; this is the
|
`public/`, which is the deploy root Pages reads the file from; this is the
|
||||||
@@ -200,11 +280,40 @@ remaining policy scaffold is otherwise complete.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
- Move the artifact actions to v4 once this Gitea Actions instance serves the v4
|
Startable work first. Everything under "Blocked" waits on somebody or something
|
||||||
artifact protocol; they are pinned on the deprecated v3 line because v4 fails
|
outside this repo, so nothing there may be picked up as the Next Step.
|
||||||
here (#20)
|
|
||||||
|
- 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`.
|
||||||
@@ -214,12 +323,10 @@ remaining policy scaffold is otherwise complete.
|
|||||||
`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,14 @@
|
|||||||
# 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 lint - and only the lint - runs inside Docker: it is a build of
|
||||||
|
# Dockerfile.lint, so this script needs a working docker daemon and has
|
||||||
|
# no host fallback to drop back to. Budget for the cold case: the first
|
||||||
|
# lint on a machine with no cached script/bootstrap layer compiles the
|
||||||
|
# pinned Hugo from source, which takes minutes. That cost falls on the
|
||||||
|
# pre-commit hook too, since it runs this script. Every later run reuses
|
||||||
|
# that layer and only the lint step re-executes.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
|||||||
@@ -1,20 +1,39 @@
|
|||||||
#!/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. Two
|
||||||
# runs this on push.
|
# container builds, in order:
|
||||||
#
|
#
|
||||||
# That implication only holds because of CHECK_EPOCH. Docker keys the
|
# 1. script/lint, which builds Dockerfile.lint -- the lint runs as a
|
||||||
# `RUN make check` layer on content, so on an unchanged tree it is
|
# build step there
|
||||||
# served from cache: the checks never execute and the build still exits
|
# 2. the main Dockerfile, which runs the non-lint checks: the clean
|
||||||
# 0. Passing a value that differs on every invocation invalidates that
|
# `hugo --minify` production build (script/test) and the read-only
|
||||||
# layer and everything below it, while the script/bootstrap toolchain
|
# prettier check (script/fmt-check)
|
||||||
# layer above it keeps caching.
|
#
|
||||||
|
# Lint goes first, for fail-fast feedback: on a runner with no cached
|
||||||
|
# script/bootstrap layer the main image compiles Hugo from source, and a
|
||||||
|
# lint failure should not wait behind that. It is a separate build
|
||||||
|
# rather than a step inside the main image because script/lint is itself
|
||||||
|
# a `docker build`, and a docker build cannot run a docker build. See
|
||||||
|
# Dockerfile.lint for the full reasoning.
|
||||||
|
#
|
||||||
|
# The lint is delegated to the same script a developer runs, so CI
|
||||||
|
# cannot drift from `make check`.
|
||||||
|
#
|
||||||
|
# Neither build implies a passing check without CHECK_EPOCH. Docker keys
|
||||||
|
# the check layers on content, so on an unchanged tree they are served
|
||||||
|
# from cache: nothing executes and the build still exits 0. Passing a
|
||||||
|
# value that differs on every invocation invalidates those layers and
|
||||||
|
# everything below them, while the script/bootstrap toolchain layer
|
||||||
|
# above keeps caching. script/lint does the same for its own build.
|
||||||
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"
|
||||||
|
"$SCRIPT_DIR/lint"
|
||||||
|
|
||||||
# Assigned to a variable rather than substituted inline in the
|
# Assigned to a variable rather than substituted inline in the
|
||||||
# argument list: a command substitution that fails inside an
|
# argument list: a command substitution that fails inside an
|
||||||
# argument does not trip `set -e`, so the inline form would quietly
|
# argument does not trip `set -e`, so the inline form would quietly
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
# 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.
|
||||||
|
#
|
||||||
|
# Both prettier entrypoints run on the host: only linting is
|
||||||
|
# containerised (owner ruling, 2026-08-10), and formatting is not a
|
||||||
|
# lint. Keep the version, scope and flags here in sync with
|
||||||
|
# script/fmt-check.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
# 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). Same scope and same settings as script/fmt - keep
|
||||||
# the two in sync - but fails instead of writing.
|
# the two in sync - but fails instead of writing.
|
||||||
|
#
|
||||||
|
# This runs on the host, not in a container: only linting is
|
||||||
|
# containerised (owner ruling, 2026-08-10), and a formatting check is
|
||||||
|
# not a lint. Running here also keeps it usable offline, since npx
|
||||||
|
# reuses ~/.npm/_npx after the first run.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|||||||
35
script/lint
35
script/lint
@@ -1,15 +1,40 @@
|
|||||||
#!/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 Dockerfile.lint. The lint 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.
|
||||||
|
#
|
||||||
|
# No --target: Dockerfile.lint has exactly one stage, so the whole-file
|
||||||
|
# build IS the lint. See that file for why a second, sibling stage would
|
||||||
|
# be a silent skip.
|
||||||
|
#
|
||||||
|
# Dockerfile.lint requires the CHECK_EPOCH build argument, generated
|
||||||
|
# here exactly as script/cibuild generates it -- see that script for why
|
||||||
|
# the lint 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)$$"
|
||||||
|
# --output type=cacheonly: this build is run for its exit status,
|
||||||
|
# not for an image. Because the lint layer is cache-busted on every
|
||||||
|
# invocation the result is a new image every time, and an untagged
|
||||||
|
# build would leave one dangling image per lint run on a host shared
|
||||||
|
# with other work. cacheonly keeps the build cache (so
|
||||||
|
# script/bootstrap still hits) and exports nothing. Failures still
|
||||||
|
# propagate: an empty CHECK_EPOCH or a failing lint exits non-zero.
|
||||||
|
docker build \
|
||||||
|
--build-arg CHECK_EPOCH="$epoch" \
|
||||||
|
--output type=cacheonly \
|
||||||
|
-f Dockerfile.lint \
|
||||||
|
.
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user