Skip to content

fix(ui): rebuild stale Tailwind bundle — CI now fails on a stale one - #137

Merged
MrChengLen merged 1 commit into
mainfrom
pr-tailwind-rebuild
Sep 25, 2026
Merged

MrChengLen merged 1 commit into
mainfrom
pr-tailwind-rebuild

Conversation

@MrChengLen

Copy link
Copy Markdown
Owner

What

  • Rebuilds the stale Tailwind bundle (tailwind.625748cf.css → tailwind.8be3407b.css). The committed bundle dated from May; 46 utility classes that templates and scripts use never reached the browser.
  • New CI step Tailwind bundle freshness gate in lint-and-test: rebuilds the bundle and fails if app/static/css/ changes. On failure it uploads the CI-built bundle as the tailwind-bundle-ci-built artifact.
  • scripts/build-tailwind.sh:
    • SHA-256 pins for the v3.4.17 CLI, taken from the release's sha256sums.txt.
    • A download is verified before it replaces the binary or becomes executable.
    • A mismatching binary in .tools/ is re-downloaded.
    • curl -fsSL --retry 3.
  • Small fixes:
    • Removes the #cookie-notice positioning shim from style.css; its classes are now in the bundle.
    • Fixes the /pricing heading class typo (text-h-section → text-h-sect).
    • Corrects two stale comments (cookie_notice.html, app/main.py CachingStaticFiles).

Why

Visible on the live site until now:

  • Footer: the "Popular conversions" grid stops at 3 columns and has no gaps.
  • Homepage: the self-hosting checklist is single-column.
  • Lists: the "How it works" lists on /compress, the PDF tools and /redact have no numbers.
  • Notice boxes: the converter's notice boxes have no tint and show a light-grey default border on the dark UI.
  • /pricing: the "Compare plans" heading renders as body text.

Root cause: docs/tailwind-build-setup.md always described this CI gate, but ci.yml never had it.

Verification

  • ruff check, ruff format --check, check_template_classes.py and i18n drift-check are clean. Full suite 1177 passed, 60 skipped.
  • The rebuild is idempotent: every run produces the same hash.
  • All three script paths were tested offline with a curl stand-in: valid binary (no download), corrupt download (exit 1, nothing left in .tools/), missing binary (verified install, then build).
  • Computed styles were compared on two local servers, before (old bundle) and after, at 1280 px and 375 px:
Element (1280 px) Before After
Footer "Popular conversions" 3 cols, no gap 4 cols, 24/6 px gap
Self-hosting checklist 1 col 2 cols
/formats groups 1 col 2 cols, 32 px gap
"How it works" lists list-style: none decimal
Converter notice boxes transparent, light-grey border sky tint, sky border
Cookie notice fixed bottom (via shim) fixed bottom (via classes)

No horizontal overflow at either width.

  • Reviews:
    • security-auditor: PASS with notes. Both Low findings are fixed: verify-before-install, and git status on its own line.
    • code-reviewer: ready with nits. Suggestions applied.
  • /pricing and /enterprise are disabled locally. They get checked on filemorph.io after deploy.

Notes for the maintainer

  • Ruleset: protect-main has "Require branches to be up to date before merging" off. Two PRs that each rebuild the bundle merge without a conflict: git sees a delete plus an add, not a rename. That would leave two bundles on main. Right now no other open PR touches the bundle.
  • docker.yml: .github/workflows/docker.yml on main was committed with CRLF line endings (PR ci(docker): allow a manual rebuild via workflow_dispatch #129). Every checkout therefore shows it as modified. This PR does not touch it.

🤖 Generated with Claude Code

The committed bundle (tailwind.625748cf.css) was last rebuilt in May, so every
utility class a template or script started using afterwards never reached the
browser: 46 of them. On the live site that meant a three-column footer grid
without gaps, a single-column self-hosting checklist, un-numbered "How it
works" steps on /compress, the PDF tools and /redact, notice boxes without
their tint (a light-grey default border on the dark UI instead), and a cookie
notice that only stayed at the bottom thanks to a positioning shim in
style.css. The rebuild (tailwind.8be3407b.css) emits all of them; the shim is
removed. The one class dropped, grid-cols-3, is used nowhere.

Root cause: docs/tailwind-build-setup.md always described a CI step that
rebuilds the bundle and fails on a difference, but ci.yml never had it. It is
now the "Tailwind bundle freshness gate". `git status --porcelain` over
app/static/css/ catches a rotated hash (tracked deletion plus untracked file)
as well as any content drift; it runs on its own line so a git failure aborts
the step instead of passing it. A failing run uploads the CI-built bundle as
an artifact, since Tailwind also scans untracked local files CI never sees.

Because CI now downloads and executes the Tailwind CLI on every run,
build-tailwind.sh verifies each download against SHA-256 pins taken from the
v3.4.17 release's sha256sums.txt before it replaces the binary or becomes
executable. A mismatching binary in .tools/ (truncated download, old VERSION)
is re-downloaded, a mismatching download is deleted and the script exits 1,
and curl -f turns an HTTP error into a failure instead of a saved error page.

Also: the "Compare plans" heading on /pricing used text-h-section, which does
not exist (the token is text-h-sect), so it rendered as body text. The
CachingStaticFiles comment in app/main.py claimed no static file is
content-hashed; the Tailwind bundle is, and takes the immutable branch.

Considered and rejected: a pytest that greps template classes against the
bundle — heuristic and blind to classes built in JS, whereas rebuilding the
real bundle is exact; caching the CLI with actions/cache — saves seconds but
adds another pinned third-party action for a fast in-GitHub download.

Verified: rebuild is idempotent (same hash on every run); the script's three
paths (valid binary, corrupt download, missing binary) exercised offline with
a curl stand-in; before/after computed-style comparison on two local servers
at 1280 px and 375 px (footer 3 -> 4 columns with 24/6 px gaps, checklist
1 -> 2 columns, /formats grids 1 -> 2, "How it works" lists numbered, notice
boxes tinted, cookie notice still fixed at the bottom, no horizontal
overflow). Reviewed by security-auditor (pass) and code-reviewer.

Full suite 1177 green (60 skipped); ruff + format + template-class gate +
i18n-drift clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MrChengLen
MrChengLen merged commit 393a31b into main Sep 25, 2026
5 checks passed
@MrChengLen
MrChengLen deleted the pr-tailwind-rebuild branch September 25, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant