static.go embeds the js directory, so a missing fetched asset silently produces a binary with no Alpine #166

Closed
opened 2026-08-17 23:12:31 +02:00 by clawbot · 2 comments
Collaborator

Found by the independent review of #164, which introduced the build-time fetch. Not a release blocker and deliberately NOT milestoned 1.0.0 — see below.

static/static.go embeds by directory:

//go:embed css js
var Static embed.FS

A directory pattern matches whatever is present, so a missing static/js/alpine.min.js is not a compile error. The reviewer ran make build on a tree where the asset had not been fetched: exit 0, binary produced, admin UI silently has no Alpine. Same for make run, make dev and a bare go build.

Why this is not a 1.0.0 blocker: every gated path fails loudly. The Dockerfile runs script/fetch-assets with sha256 verification, make check runs static/vendor_test.go which re-hashes the embedded bytes, and CI runs both. So the release artifact cannot ship without the verified asset. This is local-development ergonomics — a developer who skips the fetch gets a confusing half-working UI rather than an error.

Definition of done

  • static/static.go names the files it requires, e.g. //go:embed css js/app.js js/alpine.min.js, so absence is a compile error on every build path rather than only on gated ones.
  • make build on a tree with no static/js/alpine.min.js FAILS, and the failure names the missing file.
  • make check still passes with the asset present.
  • Check whether css should be named explicitly too, and say what you concluded. Note the same review observed that static/css/style.css is embedded but no template loads it, so the css side may want its own decision rather than a mechanical change.

Implementation requirements

  • Branch from next, PR based on next, single commit, title ending (closes #N).
  • Do not modify TODO.md (see #112).
  • Gate on make check plus the Docker lint path with the cache defeated. All linting runs in Docker, never on the host.
Found by the independent review of https://git.eeqj.de/sneak/webhooker/pulls/164, which introduced the build-time fetch. Not a release blocker and deliberately NOT milestoned 1.0.0 — see below. `static/static.go` embeds by directory: ```go //go:embed css js var Static embed.FS ``` A directory pattern matches whatever is present, so a missing `static/js/alpine.min.js` is not a compile error. The reviewer ran `make build` on a tree where the asset had not been fetched: exit 0, binary produced, admin UI silently has no Alpine. Same for `make run`, `make dev` and a bare `go build`. Why this is not a 1.0.0 blocker: every gated path fails loudly. The `Dockerfile` runs `script/fetch-assets` with sha256 verification, `make check` runs `static/vendor_test.go` which re-hashes the embedded bytes, and CI runs both. So the release artifact cannot ship without the verified asset. This is local-development ergonomics — a developer who skips the fetch gets a confusing half-working UI rather than an error. ## Definition of done - `static/static.go` names the files it requires, e.g. `//go:embed css js/app.js js/alpine.min.js`, so absence is a compile error on every build path rather than only on gated ones. - `make build` on a tree with no `static/js/alpine.min.js` FAILS, and the failure names the missing file. - `make check` still passes with the asset present. - Check whether `css` should be named explicitly too, and say what you concluded. Note the same review observed that `static/css/style.css` is embedded but no template loads it, so the css side may want its own decision rather than a mechanical change. ## Implementation requirements - Branch from `next`, PR based on `next`, single commit, title ending ` (closes #N)`. - Do not modify `TODO.md` (see https://git.eeqj.de/sneak/webhooker/issues/112). - Gate on `make check` plus the Docker lint path with the cache defeated. All linting runs in Docker, never on the host.
clawbot self-assigned this 2026-08-17 23:12:31 +02:00
Author
Collaborator

Plan. Check against next first: Alpine now comes from the committed tarball in 3p/ (#345), extracted by script/assets, and is its CSP build (#371), and static/css/style.css is now loaded by the layout (#375).

  • static/static.go names each file the pages need in its embed line, so a build without the extracted Alpine file fails to compile, naming it.
  • The CSS files are named the same way, now that every one of them is loaded.
  • The PR says what make build prints on a tree without the Alpine file.

Model: opus-5-5

Plan. Check against `next` first: Alpine now comes from the committed tarball in `3p/` (https://git.eeqj.de/sneak/webhooker/issues/345), extracted by `script/assets`, and is its CSP build (https://git.eeqj.de/sneak/webhooker/issues/371), and `static/css/style.css` is now loaded by the layout (https://git.eeqj.de/sneak/webhooker/issues/375). - `static/static.go` names each file the pages need in its embed line, so a build without the extracted Alpine file fails to compile, naming it. - The CSS files are named the same way, now that every one of them is loaded. - The PR says what `make build` prints on a tree without the Alpine file. Model: opus-5-5
Author
Collaborator

Done in #467. static/static.go now names the four files the pages load instead of embedding the css and js directories, so a build without the extracted Alpine.js file fails, naming js/alpine.min.js. Both lint stages extract Alpine.js before linting, since the static package no longer compiles without it.

  • Judgement call: make build extracts Alpine.js first, so on a tree without the file it recreates it and succeeds. The failure shows when that step is skipped (make -o assets build, or a bare go build).
  • Judgement call: static/css/input.css, the Tailwind source that no page loads, is no longer embedded, so /s/css/input.css is no longer served.

Model: opus-5-5

Done in https://git.eeqj.de/sneak/webhooker/pulls/467. `static/static.go` now names the four files the pages load instead of embedding the `css` and `js` directories, so a build without the extracted Alpine.js file fails, naming `js/alpine.min.js`. Both lint stages extract Alpine.js before linting, since the `static` package no longer compiles without it. - Judgement call: `make build` extracts Alpine.js first, so on a tree without the file it recreates it and succeeds. The failure shows when that step is skipped (`make -o assets build`, or a bare `go build`). - Judgement call: `static/css/input.css`, the Tailwind source that no page loads, is no longer embedded, so `/s/css/input.css` is no longer served. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#166