fd3cd4c18c413aa753f8194af85bf6680032a230
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fd3cd4c18c |
Add Makefile shims for cibuild and precommit (closes #34)
check / check (push) Successful in 16s
script/cibuild and script/precommit both existed and were already the documented CI and pre-commit entrypoints, but neither had a Makefile target, so the standing rule to drive the repo through make targets rather than the underlying tool could not be followed for either. It matters most for the build. A bare `docker build .` fails closed on the CHECK_EPOCH guard by design, so script/cibuild is one of only three supported ways to build an image here, and it was the only one of the three without a target while `make docker` had one. The two targets are thin shims in the same style as every other target and change nothing about what the scripts do. .PHONY was already complete for the targets that existed and now lists both new ones. README.md's Entrypoints section gains the script-to-target mapping so the two documents agree, including the two names that do not match: script/install-precommit is `make hooks`, and script/precommit is `make precommit`. It also notes that `make cibuild` is the slowest target, because it is the only one that runs two container builds -- while still taking seconds once the shared script/bootstrap layer is cached, since Dockerfile.lint's first four instructions are byte-identical to the main Dockerfile's and the pinned-Hugo compile is therefore paid once per machine rather than twice. Two accuracy fixes to text the same section already carried. The script/install-precommit bullet said the installed hook runs script/check; the script writes script/precommit into .git/hooks/pre-commit, and its own header comment says so. And the Makefile is described as listing the operations you are expected to run rather than as the authoritative list of everything the repo can do, which is not literally true: script/projectname is an internal helper that script/docker calls to compute a tag, and it has no target deliberately -- a target for it would be noise in the `make<tab>` listing this change exists to make useful. TODO.md also loses the stale Future Step asking someone to confirm the static/_headers file took effect in production. That was confirmed live on both hostnames on 2026-08-10 and recorded at #14 , so the item is work already done. The Status paragraph's matching "unverified in production until the next deploy" clause is corrected for the same reason: a commit that edits TODO.md should not leave a known-false statement in it. |
||
|
|
25b6c0a9de |
Run the lint inside Docker via Dockerfile.lint (closes #38)
check / check (push) Successful in 1m23s
Add a root Dockerfile.lint that runs `hugo --minify --printPathWarnings`
as a build step, so a successful build IS a clean lint, and reduce
script/lint to building that file. There is no host lint path and
deliberately no "am I already inside a container?" branch, which would
be a host lint path in disguise.
The containerisation boundary is lint only, per the owner ruling on the
issue: formatting is not a lint, so script/fmt and script/fmt-check stay
on the host, unchanged in version, scope and flags. That also removes
the forced duplication of prettier's settings between a script and a
Dockerfile, and with it the keep-in-sync notes that duplication needed.
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 carry no ordering edge, so a second
stage beside the lint would be silently skipped by exactly the
invocation the canonical org-wide script/lint uses -- a green that
linted nothing, which the per-stage CHECK_EPOCH guard cannot catch
because the stage that did run satisfies it. With one stage there is
nothing to skip and script/lint needs no --target. A comment in the file
says that any second check added here must be chained or carry an
explicit ordering edge, never left as a sibling.
Its first four instructions are byte-identical to the main Dockerfile's
and in the same order, so the expensive `RUN script/bootstrap` layer
that compiles the pinned Hugo from source is shared between the two
images rather than paid twice.
Resolve the recursion by direction, not detection. `make check` calls
script/lint, and script/lint is now a `docker build`, so `RUN make
check` in an image would attempt a docker build inside a build step
where there is no daemon. The main Dockerfile therefore runs the
individual non-lint checks -- script/test and script/fmt-check, as
separate RUN lines under the CHECK_EPOCH guard -- matching the canonical
shape, and only the lint is absent from it. script/cibuild runs
script/lint first, for fail-fast feedback: on a runner with no cached
bootstrap layer a lint failure should not wait behind a Hugo build from
source. CI coverage is therefore unchanged, and it runs the same scripts
a developer runs.
Caching is waived for the lint in the shape this repo already settled:
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 treatment of an unreferenced ARG. Every image-building
entrypoint generates and passes it -- script/cibuild, script/docker,
script/lint -- each as a whole assignment rather than inline, for the
`set -e` reason script/cibuild documents.
script/lint builds with `--output type=cacheonly`: the build is run for
its exit status, not for an image, and because the lint layer is
cache-busted on every invocation an exporting build leaves one dangling
image per lint run. On a host shared with other work that accumulates.
The build cache is unaffected, so script/bootstrap still hits, and
failures still propagate.
Two divergences from REPO_POLICIES.md, stated rather than buried:
- REPO_POLICIES.md:92, "all Dockerfiles must run `make check`". That
rule and "every lint run happens in Docker" cannot both hold once
`make check` contains the lint.
- REPO_POLICIES.md:102-168, which requires a separate lint stage whose
result the build stage depends on through
`COPY --from=lint /src/go.sum /dev/null`, on the stated grounds that
without the edge "the build stage would not wait for lint to finish
and a lint failure might not fail the overall build". No such edge
exists here: the lint is its own file and its own build, sequenced
by script/cibuild rather than by BuildKit. Both sections are
superseded upstream by 12e8db8 in sneak/prompts, which deletes the
Go multistage lint stage and its ordering trick for the same reason
-- that stage ran `make lint`, which is now a docker build.
Verified: two consecutive script/lint runs on an unchanged tree both
executed hugo for real, distinct epochs echoed, script/bootstrap CACHED,
second run 0.85s; a whole-file `docker build -f Dockerfile.lint .` with
the argument and no --target ran the lint for real; 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 non-zero
in 0.6s with the main image build never starting; a planted over-long
line failed the host script/fmt-check; both reverted and re-run clean;
`make check`, script/docker and script/cibuild all green with every
check layer observed executing rather than served from cache, and the
bootstrap layer CACHED in both images. The deploy path is byte-identical
to main: .gitea/, script/bootstrap, script/test and .dockerignore are
untouched.
|
||
|
|
407b0a0d79 |
Add the MIT LICENSE and state it in the README (closes #10)
check / check (push) Successful in 11s
The repo had no LICENSE, which REPO_POLICIES.md lists as a mandatory
minimum file, and the README's License section said "Content is provided
as-is for community use." That granted nothing explicitly and matched no
committed file.
The repo is public, verified on the Gitea API rather than assumed, so
the standing policy applies: MIT on any public repo lacking a license.
LICENSE is byte-identical to the canonical sneak/homoicon copy (same git
blob,
|
||
|
|
5f998c6e70 |
Add a Cloudflare Pages _headers file with security headers (closes #14)
check / check (push) Successful in 9s
Hugo copies static/ verbatim into public/, so static/_headers lands at the deploy output root, which is where Pages reads it from. This is the first root-level static/ in the repo; Hugo unions it with the theme's static/ per path rather than shadowing it, and the built tree confirms that: public/css/style.css and public/index.html are byte-identical to the previous build and the static file count goes from 1 to 2. The live "before" was measured rather than assumed. Cloudflare already sends X-Content-Type-Options and Referrer-Policy by default, so those two lines are restatements; the substance is Strict-Transport-Security, Content-Security-Policy, X-Frame-Options and Permissions-Policy, none of which the site sends today. Every value is checked against the built page, which loads nothing: no script, img, link, iframe, form or media element, no style= and no on*= attribute. It has exactly one inline <style> block, filled by readFile in baseof.html. So default-src 'none' with style-src 'unsafe-inline' is both achievable and tight, and 'unsafe-inline' is required by, and only by, that deliberate inlining. There is no script-src allowance because there are no scripts. X-Frame-Options: DENY and frame-ancestors 'none' agree. HSTS carries neither preload nor includeSubDomains. www.lora.vegas is the only other name in DNS and it is served by this same Pages project, so this file sets HSTS on its responses directly; includeSubDomains would instead bind every future subdomain for a year, with no way to walk it back inside the max-age window without also dropping the apex protection. Verified in a headless Chrome against a local server that parses the committed _headers and applies it as real response headers: zero CSP violations, the inlined stylesheet parses to 17 rules with the computed body padding, tagline colour and link colour all coming from the theme CSS, framing from another origin refused by frame-ancestors, and all five named outbound links still navigating with status 200. Whether Pages actually parses the file cannot be verified from here. Pages silently ignores a malformed _headers, so the green build proves nothing about it; that check belongs after the next deploy and must be made on Strict-Transport-Security or Content-Security-Policy, since X-Content-Type-Options would pass either way. It has to be run against both lora.vegas and www.lora.vegas: dropping includeSubDomains rests on www being served by this same Pages project, which was established from identical response bodies rather than from the Cloudflare dashboard. |
||
|
|
bcb90e74b4 |
Run script/lint from script/check (closes #9)
check / check (push) Successful in 14s
script/check ran only fmt-check then test, so script/lint was never invoked anywhere in the gate: make check shims to script/check, the Dockerfile runs make check, script/cibuild builds the Dockerfile, and the pre-commit hook calls script/check. The script was dead code that the README advertised as part of the gate. It now runs test, lint, fmt-check in the canonical order. script/lint is hugo --minify --printPathWarnings, which reports render-target collisions that the plain hugo --minify in script/test does not; that signal was being discarded. The gate still modifies no tracked files. script/test and script/lint both write to public/, which is gitignored and was already written by script/test before this change. Corrects the two documents that enumerated the old two-step gate: the README Entrypoints line for script/check, and the Dockerfile header comment above the RUN make check that executes it. |