Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
danielhanchen
3a9be04108 Guard the frontend freshness find against missing src/public dirs
Under set -euo pipefail, find over a missing directory exits non-zero, and the
command substitution propagates that so setup aborts on a slim dist-only layout
(a PyPI wheel that ships frontend/dist without src/ or public/). Pass only the
directories that exist to find, so the cache-hit path completes instead of aborting.
2026-07-06 10:32:33 +00:00
Daniel Han
21e549c069
Merge branch 'main' into fix/cache-frontend-build 2026-03-18 00:22:36 -07:00
Daniel Han
21de1c5399 Fix broken frontend freshness detection in setup scripts
- setup.sh: Replace broken `find | xargs find -newer` pipeline with
  single `find ... -newer` call. The old pipeline produced "paths must
  precede expression" errors (silently suppressed by 2>/dev/null),
  causing top-level config changes to never trigger a rebuild.
- setup.sh: Add `command -v npm` guard to oxc-validator block so it
  does not fail when Node was not installed (build-skip path).
- setup.ps1: Replace `Get-ChildItem -Include` (unreliable without
  -Recurse on PS 5.1) with explicit directory paths for src/ and
  public/ scanning.
- Both: Add *.html to tracked file patterns so index.html (Vite
  entry point) changes trigger a rebuild.
- Both: Use -print -quit instead of piping to head -1 for efficiency.
2026-03-18 07:20:04 +00:00
Daniel Han
153c203a89 Fix frontend rebuild detection and decouple oxc-validator install
Address review feedback:

- Check entire frontend/ directory for changes, not just src/.
  The build also depends on package.json, vite.config.ts,
  tailwind.config.ts, public/, and other config files. A change
  to any of these now triggers a rebuild.
- Move oxc-validator npm install outside the frontend build gate
  in setup.sh so it always runs on setup, matching setup.ps1
  which already had it outside the gate.
2026-03-18 05:59:42 +00:00
Daniel Han
5e36f09664 Rebuild frontend when source files are newer than dist/
Instead of only checking if dist/ exists, compare source file timestamps
against the dist/ directory. If any file in frontend/src/ is newer than
dist/, trigger a rebuild. This handles the case where a developer pulls
new frontend changes and re-runs setup -- stale assets get rebuilt
automatically.
2026-03-18 05:44:16 +00:00
Daniel Han
1f075a932b Cache frontend build across setup runs
Skip the frontend npm install + build if frontend/dist already exists.
Previously setup.ps1 nuked node_modules and package-lock.json on every
run, and both scripts always rebuilt even when dist/ was already present.

On a git clone editable install, the first setup run still builds the
frontend as before. Subsequent runs skip it, saving several minutes.
To force a rebuild, delete frontend/dist and re-run setup.
2026-03-18 05:14:37 +00:00
2 changed files with 66 additions and 14 deletions

View file

@ -748,9 +748,39 @@ Write-Host ""
# ==========================================================================
# PHASE 2: Frontend build (skip if pip-installed -- already bundled)
# ==========================================================================
$DistDir = Join-Path $FrontendDir "dist"
# Skip build if dist/ exists and no tracked input is newer than dist/.
# Checks src/, public/, package.json, config files -- not just src/.
$NeedFrontendBuild = $true
if ($IsPipInstall) {
$NeedFrontendBuild = $false
Write-Host "[OK] Running from pip install - frontend already bundled, skipping build" -ForegroundColor Green
} else {
} elseif (Test-Path $DistDir) {
$DistTime = (Get-Item $DistDir).LastWriteTime
# Check src/ and public/ recursively using explicit paths
$TrackedDirs = @("src", "public") |
ForEach-Object { Join-Path $FrontendDir $_ } |
Where-Object { Test-Path $_ }
$NewerFile = $null
if ($TrackedDirs.Count -gt 0) {
$NewerFile = $TrackedDirs |
ForEach-Object { Get-ChildItem -Path $_ -Recurse -File -ErrorAction SilentlyContinue } |
Where-Object { $_.LastWriteTime -gt $DistTime } | Select-Object -First 1
}
# Also check top-level config and entry files (package.json, vite.config.ts, index.html, etc.)
if (-not $NewerFile) {
$NewerFile = Get-ChildItem -Path $FrontendDir -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '\.(json|ts|js|mjs|html)$' -and $_.LastWriteTime -gt $DistTime } |
Select-Object -First 1
}
if (-not $NewerFile) {
$NeedFrontendBuild = $false
Write-Host "[OK] Frontend already built and up to date -- skipping build" -ForegroundColor Green
} else {
Write-Host "[INFO] Frontend source changed since last build -- rebuilding..." -ForegroundColor Yellow
}
}
if ($NeedFrontendBuild -and -not $IsPipInstall) {
Write-Host ""
Write-Host "Building frontend..." -ForegroundColor Cyan
# npm writes warnings to stderr; lower ErrorActionPreference so PS doesn't
@ -758,9 +788,6 @@ if ($IsPipInstall) {
$prevEAP_npm = $ErrorActionPreference
$ErrorActionPreference = "Continue"
Push-Location $FrontendDir
# Remove stale node_modules and package-lock.json to avoid version conflicts
if (Test-Path "node_modules") { Remove-Item -Recurse -Force "node_modules" }
if (Test-Path "package-lock.json") { Remove-Item -Force "package-lock.json" }
npm install 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Pop-Location

View file

@ -41,13 +41,33 @@ if [[ "$keynames" == *$'\nCOLAB_'* ]]; then
fi
# ── Detect whether frontend needs building ──
# Only skip when BOTH conditions are true:
# 1. We're inside site-packages (PyPI / pip install, not editable)
# 2. dist/ already exists (pre-built in the wheel)
# Otherwise always (re)build — handles upgrades, editable installs, and
# pip-from-source where dist/ was never built.
if [[ "$SCRIPT_DIR" == */site-packages/* ]] && [ -d "$SCRIPT_DIR/frontend/dist" ]; then
echo "✅ Frontend pre-built (PyPI) — skipping Node/npm check."
# Skip if dist/ exists AND no tracked input is newer than dist/.
# Checks top-level config/entry files and src/, public/ recursively.
# This handles: PyPI installs (dist/ bundled), repeat runs (no changes),
# and upgrades/pulls (source newer than dist/ triggers rebuild).
_NEED_FRONTEND_BUILD=true
if [ -d "$SCRIPT_DIR/frontend/dist" ]; then
# Check top-level config and entry files (package.json, vite.config.ts, index.html, etc.)
_changed=$(find "$SCRIPT_DIR/frontend" -maxdepth 1 \
\( -name "*.json" -o -name "*.ts" -o -name "*.js" -o -name "*.mjs" -o -name "*.html" \) \
-newer "$SCRIPT_DIR/frontend/dist" -print -quit 2>/dev/null)
# Check src/ and public/ recursively (only dirs that exist; a slimmed
# layout that ships dist/ without src/ or public/ must not abort under set -e)
if [ -z "$_changed" ]; then
_src_dirs=()
[ -d "$SCRIPT_DIR/frontend/src" ] && _src_dirs+=("$SCRIPT_DIR/frontend/src")
[ -d "$SCRIPT_DIR/frontend/public" ] && _src_dirs+=("$SCRIPT_DIR/frontend/public")
if [ "${#_src_dirs[@]}" -gt 0 ]; then
_changed=$(find "${_src_dirs[@]}" \
-type f -newer "$SCRIPT_DIR/frontend/dist" -print -quit 2>/dev/null)
fi
fi
if [ -z "$_changed" ]; then
_NEED_FRONTEND_BUILD=false
fi
fi
if [ "$_NEED_FRONTEND_BUILD" = false ]; then
echo "✅ Frontend already built and up to date -- skipping Node/npm check."
else
NEED_NODE=true
if command -v node &>/dev/null && command -v npm &>/dev/null; then
@ -146,12 +166,17 @@ run_quiet "npm run build" npm run build
_restore_gitignores
trap - EXIT
cd "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator"
run_quiet "npm install (oxc validator runtime)" npm install
cd "$SCRIPT_DIR"
echo "✅ Frontend built to frontend/dist"
fi # end frontend dist check
fi # end frontend build check
# ── oxc-validator runtime (always install, independent of frontend build) ──
if [ -d "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator" ] && command -v npm &>/dev/null; then
cd "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator"
run_quiet "npm install (oxc validator runtime)" npm install
cd "$SCRIPT_DIR"
fi
# ── 6. Python venv + deps ──