From f52ef5aa07e29f5b8384c94737320e38d5bbd44b Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Fri, 24 Apr 2026 16:05:29 -0400 Subject: [PATCH] feat(dev): port SPA asset tooling --- .github/workflows/release.yml | 3 +- .github/workflows/typescript.yml | 6 +- AGENTS.md | 4 +- ...-24-001-refactor-adopt-uv-patterns-plan.md | 2 +- .../src/commands/check_spa_budgets.rs | 137 ++++++++++ lib/crates/fabro-dev/src/commands/mod.rs | 25 +- .../fabro-dev/src/commands/refresh_spa.rs | 111 ++++++++ lib/crates/fabro-dev/src/commands/release.rs | 2 +- lib/crates/fabro-dev/tests/it/main.rs | 1 + lib/crates/fabro-dev/tests/it/spa.rs | 239 ++++++++++++++++++ scripts/check-fabro-spa-budgets.sh | 35 --- scripts/refresh-fabro-spa.sh | 17 -- 12 files changed, 501 insertions(+), 81 deletions(-) create mode 100644 lib/crates/fabro-dev/src/commands/check_spa_budgets.rs create mode 100644 lib/crates/fabro-dev/src/commands/refresh_spa.rs create mode 100644 lib/crates/fabro-dev/tests/it/spa.rs delete mode 100755 scripts/check-fabro-spa-budgets.sh delete mode 100755 scripts/refresh-fabro-spa.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83196b8aa..3d3758d53 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,8 @@ jobs: with: no-cache: true - run: bun install - - run: scripts/refresh-fabro-spa.sh + - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable + - run: cargo dev refresh-spa - run: git diff --exit-code -- lib/crates/fabro-spa/assets compile: diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml index 9c5b914cc..5ec713e7d 100644 --- a/.github/workflows/typescript.yml +++ b/.github/workflows/typescript.yml @@ -71,9 +71,9 @@ jobs: persist-credentials: false - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - run: bun install --frozen-lockfile - - run: scripts/refresh-fabro-spa.sh - - run: git diff --exit-code -- lib/crates/fabro-spa/assets - - run: scripts/check-fabro-spa-budgets.sh - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable + - run: cargo dev refresh-spa + - run: git diff --exit-code -- lib/crates/fabro-spa/assets + - run: cargo dev check-spa-budgets - run: cargo build -p fabro-cli --release - run: wc -c < target/release/fabro diff --git a/AGENTS.md b/AGENTS.md index a87a387c4..f46aaf2d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,11 +22,11 @@ macOS note: if `cargo nextest run` fails with `Too many open files (os error 24) - `cd apps/fabro-web && bun test` — run tests - `cd apps/fabro-web && bun run typecheck` — type check - `cd apps/fabro-web && bun run build` — production build (writes to `apps/fabro-web/dist/` only; does NOT update the bundled SPA that ships in the Rust binary) -- `scripts/refresh-fabro-spa.sh` — **run this before committing any TypeScript change in `apps/fabro-web/` or `lib/packages/fabro-api-client/`**. It runs the production build and then copies `dist/` into `lib/crates/fabro-spa/assets/` (which is tracked in git). CI's TypeScript `Build` job reruns this script and then `git diff --exit-code -- lib/crates/fabro-spa/assets` — if the committed bundle drifts from source (e.g. content-hashed filenames like `entry-.js` change), the check fails. `bun run build` on its own is not enough. +- `cargo dev refresh-spa` — **run this before committing any TypeScript change in `apps/fabro-web/` or `lib/packages/fabro-api-client/`**. It runs the production build and then copies `dist/` into `lib/crates/fabro-spa/assets/` (which is tracked in git). CI's TypeScript `Build` job reruns this command and then `git diff --exit-code -- lib/crates/fabro-spa/assets` — if the committed bundle drifts from source (e.g. content-hashed filenames like `entry-.js` change), the check fails. `bun run build` on its own is not enough. ### Docker image - `cargo dev docker-build` — builds the local Docker image from the current tree using the release pipeline's cargo-zigbuild approach. Honors `--arch amd64|arm64`, `--tag ` (default `fabro`), `--compile-only` (stages `docker-context//fabro` without `docker build`), and `--dry-run` (prints the Docker commands without running them). Prefer this over writing a throwaway Dockerfile; the release pipeline, `Dockerfile`, and this command share the same binary layout. -- Refresh the embedded SPA before rebuilding the image after any `apps/fabro-web` change: `scripts/refresh-fabro-spa.sh` runs the bun build and copies `dist/` into `lib/crates/fabro-spa/assets/`. Skipping this step produces a Docker image whose Rust binary embeds a stale SPA bundle. +- Refresh the embedded SPA before rebuilding the image after any `apps/fabro-web` change: `cargo dev refresh-spa` runs the bun build and copies `dist/` into `lib/crates/fabro-spa/assets/`. Skipping this step produces a Docker image whose Rust binary embeds a stale SPA bundle. ### Release automation - `cargo dev release [nightly]` — creates the next stable release or nightly prerelease tag. Use `--dry-run` to print planned commands without mutating git or running Cargo, `--skip-tests` only after running the release-mode smoke yourself, and `--release-date YYYY-MM-DD` or `FABRO_RELEASE_DATE` for deterministic version computation. diff --git a/docs/plans/2026-04-24-001-refactor-adopt-uv-patterns-plan.md b/docs/plans/2026-04-24-001-refactor-adopt-uv-patterns-plan.md index dfa8f1513..0b2ac1da9 100644 --- a/docs/plans/2026-04-24-001-refactor-adopt-uv-patterns-plan.md +++ b/docs/plans/2026-04-24-001-refactor-adopt-uv-patterns-plan.md @@ -702,7 +702,7 @@ Solid arrows are real dependencies. Dashed lines show phases that are independen --- -- [ ] **Unit 5.5: Port `refresh-fabro-spa.sh` and `check-fabro-spa-budgets.sh` to `fabro-dev`** +- [x] **Unit 5.5: Port `refresh-fabro-spa.sh` and `check-fabro-spa-budgets.sh` to `fabro-dev`** **Goal:** Replace the two SPA-related scripts with `cargo dev refresh-spa` and `cargo dev check-spa-budgets`. diff --git a/lib/crates/fabro-dev/src/commands/check_spa_budgets.rs b/lib/crates/fabro-dev/src/commands/check_spa_budgets.rs new file mode 100644 index 000000000..53f3b4183 --- /dev/null +++ b/lib/crates/fabro-dev/src/commands/check_spa_budgets.rs @@ -0,0 +1,137 @@ +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; + +use anyhow::{Context, Result, bail}; +use clap::Args; +use walkdir::WalkDir; + +const DEFAULT_ASSET_BUDGET_BYTES: u64 = 15 * 1024 * 1024; +const DEFAULT_PAYLOAD_BUDGET_BYTES: u64 = 5 * 1024 * 1024; + +#[derive(Debug, Args)] +pub(crate) struct CheckSpaBudgetsArgs { + /// Repository root containing lib/crates/fabro-spa/assets. + #[arg(long, hide = true)] + root: Option, + /// Override the raw asset budget. + #[arg(long, hide = true, default_value_t = DEFAULT_ASSET_BUDGET_BYTES)] + asset_budget_bytes: u64, + /// Override the estimated gzip payload budget. + #[arg(long, hide = true, default_value_t = DEFAULT_PAYLOAD_BUDGET_BYTES)] + payload_budget_bytes: u64, +} + +#[expect( + clippy::print_stdout, + reason = "dev check-spa-budgets command reports measured budgets directly" +)] +pub(crate) fn check_spa_budgets(args: CheckSpaBudgetsArgs) -> Result<()> { + let root = args.root.unwrap_or_else(workspace_root); + let asset_dir = root.join("lib/crates/fabro-spa/assets"); + let report = budget_report(&asset_dir)?; + + println!("fabro-spa asset bytes: {}", report.asset_bytes); + println!( + "fabro-spa estimated compressed payload bytes: {}", + report.compressed_payload_bytes + ); + + if report.asset_bytes > args.asset_budget_bytes { + bail!( + "fabro-spa committed assets exceed budget: {} > {}", + report.asset_bytes, + args.asset_budget_bytes + ); + } + + if report.compressed_payload_bytes > args.payload_budget_bytes { + bail!( + "fabro-spa compressed payload exceeds budget: {} > {}", + report.compressed_payload_bytes, + args.payload_budget_bytes + ); + } + + Ok(()) +} + +struct BudgetReport { + asset_bytes: u64, + compressed_payload_bytes: u64, +} + +fn budget_report(asset_dir: &Path) -> Result { + if !asset_dir.is_dir() { + bail!( + "fabro-spa assets directory is missing: {}", + asset_dir.display() + ); + } + + let mut files = Vec::new(); + for entry in WalkDir::new(asset_dir) { + let entry = entry.context("walking fabro-spa assets")?; + if !entry.file_type().is_file() { + continue; + } + + let path = entry.path().to_path_buf(); + if path.extension().and_then(|ext| ext.to_str()) == Some("map") { + bail!( + "source map files are not allowed in fabro-spa assets: {}", + path.display() + ); + } + files.push(path); + } + files.sort(); + + let mut asset_bytes = 0; + let mut compressed_payload_bytes = 0; + for file in files { + asset_bytes += file + .metadata() + .with_context(|| format!("reading metadata for {}", file.display()))? + .len(); + compressed_payload_bytes += gzip_size(&file)?; + } + + Ok(BudgetReport { + asset_bytes, + compressed_payload_bytes, + }) +} + +#[expect( + clippy::disallowed_methods, + reason = "dev check-spa-budgets intentionally shells out to gzip to match the legacy script" +)] +fn gzip_size(file: &Path) -> Result { + let output = Command::new("gzip") + .args(["-9", "-n", "-c"]) + .arg(file) + .output() + .with_context(|| format!("compressing {}", file.display()))?; + ensure_gzip_success(file, &output) +} + +fn ensure_gzip_success(file: &Path, output: &Output) -> Result { + if !output.status.success() { + bail!( + "gzip failed for {} with {}: {}", + file.display(), + output.status, + String::from_utf8_lossy(&output.stderr) + ); + } + + Ok(output.stdout.len() as u64) +} + +fn workspace_root() -> PathBuf { + let mut root = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf(); + root.pop(); + root.pop(); + root.pop(); + root +} diff --git a/lib/crates/fabro-dev/src/commands/mod.rs b/lib/crates/fabro-dev/src/commands/mod.rs index 37ac97026..0ce875ba4 100644 --- a/lib/crates/fabro-dev/src/commands/mod.rs +++ b/lib/crates/fabro-dev/src/commands/mod.rs @@ -1,28 +1,11 @@ -use anyhow::{Result, bail}; -use clap::Args; - mod check_boundary; +mod check_spa_budgets; mod docker_build; +mod refresh_spa; mod release; pub(crate) use check_boundary::{CheckBoundaryArgs, check_boundary}; +pub(crate) use check_spa_budgets::{CheckSpaBudgetsArgs, check_spa_budgets}; pub(crate) use docker_build::{DockerBuildArgs, docker_build}; +pub(crate) use refresh_spa::{RefreshSpaArgs, refresh_spa}; pub(crate) use release::{ReleaseArgs, release}; - -#[derive(Debug, Args)] -pub(crate) struct RefreshSpaArgs; - -#[derive(Debug, Args)] -pub(crate) struct CheckSpaBudgetsArgs; - -pub(crate) fn refresh_spa(_args: RefreshSpaArgs) -> Result<()> { - not_yet_implemented("refresh-spa") -} - -pub(crate) fn check_spa_budgets(_args: CheckSpaBudgetsArgs) -> Result<()> { - not_yet_implemented("check-spa-budgets") -} - -fn not_yet_implemented(command: &str) -> Result<()> { - bail!("{command} is not yet implemented") -} diff --git a/lib/crates/fabro-dev/src/commands/refresh_spa.rs b/lib/crates/fabro-dev/src/commands/refresh_spa.rs new file mode 100644 index 000000000..5b8eec041 --- /dev/null +++ b/lib/crates/fabro-dev/src/commands/refresh_spa.rs @@ -0,0 +1,111 @@ +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{Context, Result, bail}; +use clap::Args; +use walkdir::WalkDir; + +#[derive(Debug, Args)] +pub(crate) struct RefreshSpaArgs { + /// Repository root containing apps/fabro-web and lib/crates/fabro-spa. + #[arg(long, hide = true)] + root: Option, + /// Skip bun run build and only mirror an existing dist directory. + #[arg(long, hide = true)] + skip_build: bool, +} + +#[expect( + clippy::print_stdout, + reason = "dev refresh-spa command reports progress directly" +)] +pub(crate) fn refresh_spa(args: RefreshSpaArgs) -> Result<()> { + let root = args.root.unwrap_or_else(workspace_root); + let web_dir = root.join("apps/fabro-web"); + let dist_dir = web_dir.join("dist"); + let asset_dir = root.join("lib/crates/fabro-spa/assets"); + + if !args.skip_build { + println!("Running bun run build in apps/fabro-web..."); + run_bun_build(&web_dir)?; + } + + mirror_dist(&dist_dir, &asset_dir)?; + println!("Refreshed lib/crates/fabro-spa/assets"); + + Ok(()) +} + +#[expect( + clippy::disallowed_methods, + reason = "dev refresh-spa intentionally runs a synchronous Bun subprocess" +)] +fn run_bun_build(web_dir: &Path) -> Result<()> { + let status = Command::new("bun") + .args(["run", "build"]) + .current_dir(web_dir) + .status() + .with_context(|| format!("running bun run build in {}", web_dir.display()))?; + if !status.success() { + bail!("bun run build failed with {status}"); + } + + Ok(()) +} + +#[expect( + clippy::disallowed_methods, + reason = "dev refresh-spa mirrors build output with synchronous filesystem operations" +)] +fn mirror_dist(dist_dir: &Path, asset_dir: &Path) -> Result<()> { + if !dist_dir.is_dir() { + bail!("apps/fabro-web/dist is missing; run `bun run build` before mirroring SPA assets"); + } + + if asset_dir.exists() { + std::fs::remove_dir_all(asset_dir) + .with_context(|| format!("removing {}", asset_dir.display()))?; + } + std::fs::create_dir_all(asset_dir) + .with_context(|| format!("creating {}", asset_dir.display()))?; + + for entry in WalkDir::new(dist_dir) { + let entry = entry.context("walking apps/fabro-web/dist")?; + let source = entry.path(); + let relative = source + .strip_prefix(dist_dir) + .with_context(|| format!("{} is not under {}", source.display(), dist_dir.display()))?; + if relative.as_os_str().is_empty() { + continue; + } + + let destination = asset_dir.join(relative); + if entry.file_type().is_dir() { + std::fs::create_dir_all(&destination) + .with_context(|| format!("creating {}", destination.display()))?; + continue; + } + + if source.extension().and_then(|ext| ext.to_str()) == Some("map") { + continue; + } + + if let Some(parent) = destination.parent() { + std::fs::create_dir_all(parent) + .with_context(|| format!("creating {}", parent.display()))?; + } + std::fs::copy(source, &destination).with_context(|| { + format!("copying {} to {}", source.display(), destination.display()) + })?; + } + + Ok(()) +} + +fn workspace_root() -> PathBuf { + let mut root = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf(); + root.pop(); + root.pop(); + root.pop(); + root +} diff --git a/lib/crates/fabro-dev/src/commands/release.rs b/lib/crates/fabro-dev/src/commands/release.rs index 993fd5f50..c8389f0c7 100644 --- a/lib/crates/fabro-dev/src/commands/release.rs +++ b/lib/crates/fabro-dev/src/commands/release.rs @@ -254,7 +254,7 @@ impl ReleasePlan { } fn refresh_spa_command() -> PlannedCommand { - PlannedCommand::new("scripts/refresh-fabro-spa.sh") + PlannedCommand::new("cargo").arg("dev").arg("refresh-spa") } fn spa_assets_diff_command() -> PlannedCommand { diff --git a/lib/crates/fabro-dev/tests/it/main.rs b/lib/crates/fabro-dev/tests/it/main.rs index 466ae4ce7..6a675c082 100644 --- a/lib/crates/fabro-dev/tests/it/main.rs +++ b/lib/crates/fabro-dev/tests/it/main.rs @@ -4,6 +4,7 @@ use std::process::{Command, Output}; mod check_boundary; mod docker_build; mod release; +mod spa; fn fabro_dev() -> assert_cmd::Command { assert_cmd::cargo::cargo_bin_cmd!("fabro-dev") diff --git a/lib/crates/fabro-dev/tests/it/spa.rs b/lib/crates/fabro-dev/tests/it/spa.rs new file mode 100644 index 000000000..bed328285 --- /dev/null +++ b/lib/crates/fabro-dev/tests/it/spa.rs @@ -0,0 +1,239 @@ +use std::path::Path; + +fn fabro_dev() -> assert_cmd::Command { + assert_cmd::cargo::cargo_bin_cmd!("fabro-dev") +} + +fn output_text(bytes: &[u8]) -> String { + String::from_utf8(bytes.to_vec()).expect("command output should be valid utf-8") +} + +#[expect( + clippy::disallowed_methods, + reason = "integration tests stage temporary SPA fixture files with sync std::fs::write" +)] +fn write_file(root: &Path, path: &str, contents: &[u8]) { + let path = root.join(path); + std::fs::create_dir_all(path.parent().expect("fixture path should have parent")) + .expect("creating fixture parent directory"); + std::fs::write(path, contents).expect("writing fixture file"); +} + +#[test] +fn refresh_spa_mirrors_dist_and_removes_source_maps() { + let fixture = tempfile::tempdir().expect("creating fixture"); + write_file(fixture.path(), "apps/fabro-web/dist/index.html", b"index"); + write_file(fixture.path(), "apps/fabro-web/dist/assets/app.js", b"app"); + write_file( + fixture.path(), + "apps/fabro-web/dist/assets/app.js.map", + b"map", + ); + write_file( + fixture.path(), + "lib/crates/fabro-spa/assets/stale.txt", + b"stale", + ); + + let output = fabro_dev() + .args([ + "refresh-spa", + "--root", + fixture + .path() + .to_str() + .expect("fixture path should be utf-8"), + "--skip-build", + ]) + .assert() + .success() + .get_output() + .clone(); + let stdout = output_text(&output.stdout); + + assert!( + stdout.contains("Refreshed lib/crates/fabro-spa/assets"), + "refresh-spa should report refreshed assets:\n{stdout}" + ); + assert!( + fixture + .path() + .join("lib/crates/fabro-spa/assets/index.html") + .is_file() + ); + assert!( + fixture + .path() + .join("lib/crates/fabro-spa/assets/assets/app.js") + .is_file() + ); + assert!( + !fixture + .path() + .join("lib/crates/fabro-spa/assets/assets/app.js.map") + .exists() + ); + assert!( + !fixture + .path() + .join("lib/crates/fabro-spa/assets/stale.txt") + .exists() + ); +} + +#[test] +fn refresh_spa_missing_dist_errors_cleanly() { + let fixture = tempfile::tempdir().expect("creating fixture"); + + let output = fabro_dev() + .args([ + "refresh-spa", + "--root", + fixture + .path() + .to_str() + .expect("fixture path should be utf-8"), + "--skip-build", + ]) + .assert() + .failure() + .code(1) + .get_output() + .clone(); + let stderr = output_text(&output.stderr); + + assert!( + stderr.contains("apps/fabro-web/dist is missing; run `bun run build`"), + "missing dist should explain how to recover:\n{stderr}" + ); +} + +#[test] +fn check_spa_budgets_passes_fixture_assets() { + let fixture = tempfile::tempdir().expect("creating fixture"); + write_file( + fixture.path(), + "lib/crates/fabro-spa/assets/index.html", + b"hello", + ); + + let output = fabro_dev() + .args([ + "check-spa-budgets", + "--root", + fixture + .path() + .to_str() + .expect("fixture path should be utf-8"), + "--asset-budget-bytes", + "100", + "--payload-budget-bytes", + "100", + ]) + .assert() + .success() + .get_output() + .clone(); + let stdout = output_text(&output.stdout); + + assert!( + stdout.contains("fabro-spa asset bytes: 5"), + "budget check should print raw bytes:\n{stdout}" + ); + assert!( + stdout.contains("fabro-spa estimated compressed payload bytes:"), + "budget check should print compressed payload bytes:\n{stdout}" + ); +} + +#[test] +fn check_spa_budgets_fails_when_assets_exceed_budget() { + let fixture = tempfile::tempdir().expect("creating fixture"); + write_file( + fixture.path(), + "lib/crates/fabro-spa/assets/index.html", + b"hello", + ); + + let output = fabro_dev() + .args([ + "check-spa-budgets", + "--root", + fixture + .path() + .to_str() + .expect("fixture path should be utf-8"), + "--asset-budget-bytes", + "4", + "--payload-budget-bytes", + "100", + ]) + .assert() + .failure() + .code(1) + .get_output() + .clone(); + let stderr = output_text(&output.stderr); + + assert!( + stderr.contains("fabro-spa committed assets exceed budget: 5 > 4"), + "budget failure should report raw byte overage:\n{stderr}" + ); +} + +#[test] +fn check_spa_budgets_fails_when_source_map_is_present() { + let fixture = tempfile::tempdir().expect("creating fixture"); + write_file( + fixture.path(), + "lib/crates/fabro-spa/assets/assets/app.js.map", + b"map", + ); + + let output = fabro_dev() + .args([ + "check-spa-budgets", + "--root", + fixture + .path() + .to_str() + .expect("fixture path should be utf-8"), + ]) + .assert() + .failure() + .code(1) + .get_output() + .clone(); + let stderr = output_text(&output.stderr); + + assert!( + stderr.contains("source map files are not allowed in fabro-spa assets"), + "source maps should fail the budget check:\n{stderr}" + ); +} + +#[test] +fn check_spa_budgets_missing_assets_errors_cleanly() { + let fixture = tempfile::tempdir().expect("creating fixture"); + + let output = fabro_dev() + .args([ + "check-spa-budgets", + "--root", + fixture + .path() + .to_str() + .expect("fixture path should be utf-8"), + ]) + .assert() + .failure() + .code(1) + .get_output() + .clone(); + let stderr = output_text(&output.stderr); + + assert!( + stderr.contains("fabro-spa assets directory is missing"), + "missing assets should be reported clearly:\n{stderr}" + ); +} diff --git a/scripts/check-fabro-spa-budgets.sh b/scripts/check-fabro-spa-budgets.sh deleted file mode 100755 index 462d8fa4d..000000000 --- a/scripts/check-fabro-spa-budgets.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -asset_dir="$repo_root/lib/crates/fabro-spa/assets" -asset_budget_bytes=$((15 * 1024 * 1024)) -payload_budget_bytes=$((5 * 1024 * 1024)) - -if [[ ! -d "$asset_dir" ]]; then - echo "fabro-spa assets directory is missing: $asset_dir" >&2 - exit 1 -fi - -asset_bytes=0 -compressed_payload_bytes=0 - -while IFS= read -r -d '' file; do - file_bytes="$(wc -c < "$file" | tr -d '[:space:]')" - compressed_bytes="$(gzip -9 -n -c "$file" | wc -c | tr -d '[:space:]')" - asset_bytes=$((asset_bytes + file_bytes)) - compressed_payload_bytes=$((compressed_payload_bytes + compressed_bytes)) -done < <(find "$asset_dir" -type f -print0) - -echo "fabro-spa asset bytes: $asset_bytes" -echo "fabro-spa estimated compressed payload bytes: $compressed_payload_bytes" - -if (( asset_bytes > asset_budget_bytes )); then - echo "fabro-spa committed assets exceed budget: $asset_bytes > $asset_budget_bytes" >&2 - exit 1 -fi - -if (( compressed_payload_bytes > payload_budget_bytes )); then - echo "fabro-spa compressed payload exceeds budget: $compressed_payload_bytes > $payload_budget_bytes" >&2 - exit 1 -fi diff --git a/scripts/refresh-fabro-spa.sh b/scripts/refresh-fabro-spa.sh deleted file mode 100755 index 3028d5728..000000000 --- a/scripts/refresh-fabro-spa.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -web_dir="$repo_root/apps/fabro-web" -dist_dir="$web_dir/dist" -asset_dir="$repo_root/lib/crates/fabro-spa/assets" - -( - cd "$web_dir" - bun run build -) - -rm -rf "$asset_dir" -mkdir -p "$asset_dir" -cp -R "$dist_dir"/. "$asset_dir"/ -find "$asset_dir" -type f -name '*.map' -delete