tailwindcss is unpinned, so the committed static/css/tailwind.css is not reproducible from the repo's own tooling #231

Open
opened 2026-08-20 07:16:52 +02:00 by clawbot · 0 comments
Collaborator

Found while reviewing #219, which had to regenerate static/css/tailwind.css.

Makefile:53 invokes a bare tailwindcss from PATH. There is no package.json, no version pin, and no hash pin anywhere. So the committed generated artefact depends entirely on whichever tailwind binary happens to be installed on the machine that ran make css.

Consequences, in order of how much they bite:

  • The CSS is a COMMITTED, SERVED artefact. Nothing in Dockerfile or script/ regenerates it, so whatever is in the tree is what users get. A regeneration on a different tailwind version silently changes the shipped stylesheet.
  • Two contributors running make css can produce different output from identical input, and neither can tell which is correct.
  • This is what already went wrong: #219 added ten classes to a template and the CSS was not regenerated, so the chevron <svg> rendered at the 300x150 CSS default on every delivery row. The gate could not catch it because nothing verifies the artefact matches the templates.
  • The tailwind CLI is not present on this host at all, so the regeneration had to be done in an ad-hoc container. It produced correct output, but by a route the repo does not define.

It also sits against the repo policy of hash-pinning external references, which the golangci-lint image digest and the script/bootstrap release-archive sha256 pins already follow.

Definition of done:

  • tailwindcss is pinned to an exact version, hash-pinned consistent with how the lint image and bootstrap archives are pinned
  • make css runs it in a container the same way linting does, rather than depending on a host binary
  • a check fails when static/css/tailwind.css does not match what the pinned tailwind produces from the current templates and input.css — that is the part that would have caught #219's missing classes
  • regenerating on the pinned version produces no diff against the committed artefact, or the one-time reflow is committed in the same change
Found while reviewing https://git.eeqj.de/sneak/webhooker/pulls/219, which had to regenerate `static/css/tailwind.css`. `Makefile:53` invokes a bare `tailwindcss` from `PATH`. There is no `package.json`, no version pin, and no hash pin anywhere. So the committed generated artefact depends entirely on whichever tailwind binary happens to be installed on the machine that ran `make css`. Consequences, in order of how much they bite: - The CSS is a COMMITTED, SERVED artefact. Nothing in `Dockerfile` or `script/` regenerates it, so whatever is in the tree is what users get. A regeneration on a different tailwind version silently changes the shipped stylesheet. - Two contributors running `make css` can produce different output from identical input, and neither can tell which is correct. - This is what already went wrong: https://git.eeqj.de/sneak/webhooker/pulls/219 added ten classes to a template and the CSS was not regenerated, so the chevron `<svg>` rendered at the 300x150 CSS default on every delivery row. The gate could not catch it because nothing verifies the artefact matches the templates. - The tailwind CLI is not present on this host at all, so the regeneration had to be done in an ad-hoc container. It produced correct output, but by a route the repo does not define. It also sits against the repo policy of hash-pinning external references, which the `golangci-lint` image digest and the `script/bootstrap` release-archive sha256 pins already follow. Definition of done: - `tailwindcss` is pinned to an exact version, hash-pinned consistent with how the lint image and bootstrap archives are pinned - `make css` runs it in a container the same way linting does, rather than depending on a host binary - a check fails when `static/css/tailwind.css` does not match what the pinned tailwind produces from the current templates and `input.css` — that is the part that would have caught https://git.eeqj.de/sneak/webhooker/pulls/219's missing classes - regenerating on the pinned version produces no diff against the committed artefact, or the one-time reflow is committed in the same change
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#231