All checks were successful
Run Check Script / check (pull_request) Successful in 2m17s
The image shipped empty CSS: build.rs shells out to the tailwindcss v4 CLI and silently falls back to an empty bundle when it's absent — which it was in the rust:slim builder, so /static/tailwind.css served nothing. - Dockerfile: install the pinned Tailwind v4 standalone CLI (curl) in the builder and set TAILWIND_REQUIRED=1. - build.rs: when TAILWIND_REQUIRED is set (container/prod), a missing or failing CLI is now a hard build error instead of empty CSS; dev builds keep the soft fallback for the `serve-web --css-from` workflow. The env is a rerun trigger, so the first required build regenerates rather than reusing a cache-mounted empty bundle. Verified: with the CLI on PATH the embedded bundle is ~26 KB; with TAILWIND_REQUIRED=1 and no CLI the build fails as intended.
71 lines
2.9 KiB
Rust
71 lines
2.9 KiB
Rust
//! Best-effort Tailwind CSS build for the `web-frontend` feature.
|
|
//!
|
|
//! When the standalone `tailwindcss` v4 CLI is on PATH and the
|
|
//! `web-frontend` feature is on, this script generates the production
|
|
//! CSS bundle into `$OUT_DIR/tailwind.css`. The binary embeds that file
|
|
//! via `include_bytes!`.
|
|
//!
|
|
//! If the CLI is missing or fails we write an *empty* CSS file rather
|
|
//! than failing the build — the dev workflow uses
|
|
//! `serve-web --css-from <path>` to read Tailwind output from a sidecar
|
|
//! `tailwindcss --watch` process, so the embedded copy is irrelevant
|
|
//! there. Production builds should ensure `tailwindcss` is on PATH; the
|
|
//! warning below shows up in `cargo build` output when it is not.
|
|
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
fn main() {
|
|
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
|
let output = out_dir.join("tailwind.css");
|
|
|
|
if std::env::var_os("CARGO_FEATURE_WEB_FRONTEND").is_none() {
|
|
// Feature off — emit an empty placeholder so the `include_bytes!`
|
|
// path in src/frontend/assets.rs still compiles if anything
|
|
// references it (it should be cfg-gated, but belt-and-braces).
|
|
std::fs::write(&output, b"").unwrap();
|
|
return;
|
|
}
|
|
|
|
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
|
let input = manifest_dir.join("style/input.css");
|
|
|
|
println!("cargo:rerun-if-changed=style/input.css");
|
|
println!("cargo:rerun-if-changed=src/frontend");
|
|
println!("cargo:rerun-if-changed=src/service");
|
|
// Toggling this re-runs the script, so the container build (which sets
|
|
// it) regenerates rather than reusing a cache-mounted empty bundle.
|
|
println!("cargo:rerun-if-env-changed=TAILWIND_REQUIRED");
|
|
|
|
// Container/production builds set TAILWIND_REQUIRED: a missing or
|
|
// failing CLI is a hard error there (never ship empty CSS). Dev builds
|
|
// leave it unset and fall back to empty, serving CSS via
|
|
// `serve-web --css-from <path>` against a `tailwindcss --watch` sidecar.
|
|
let required = std::env::var_os("TAILWIND_REQUIRED").is_some();
|
|
let fall_back = |reason: String| {
|
|
assert!(
|
|
!required,
|
|
"{reason}\nTAILWIND_REQUIRED is set: refusing to ship a frontend with empty CSS. \
|
|
Install the v4 standalone CLI: https://github.com/tailwindlabs/tailwindcss/releases"
|
|
);
|
|
println!(
|
|
"cargo:warning={reason}; embedded CSS will be empty \
|
|
(use `serve-web --css-from <path>` in dev)."
|
|
);
|
|
std::fs::write(&output, b"").unwrap();
|
|
};
|
|
|
|
match Command::new("tailwindcss")
|
|
.arg("--input")
|
|
.arg(&input)
|
|
.arg("--output")
|
|
.arg(&output)
|
|
.arg("--minify")
|
|
.status()
|
|
{
|
|
Ok(s) if s.success() => {}
|
|
Ok(s) => fall_back(format!("tailwindcss exited with status {s}")),
|
|
Err(e) => fall_back(format!("tailwindcss not invocable ({e})")),
|
|
}
|
|
}
|