Speed up deploy preparation and add opt-in frontend build reuse
Repeated deploys redo frontend dependency and archive work, and unchanged production frontends always rerun Vite. This PR reuses matching installed framework pins, avoids unnecessary manifest rewrites and sidecar recompression, and adds opt-in reuse of deterministic production frontend builds.
Changes
- Retain equivalent package manifests without rewriting them, preserving main's deterministic dependency-only install fingerprint.
- Reuse matching framework pins after a successful persisted-lock install. Verify npm caret declarations against both lock metadata and installed package versions, use
--save-dev, and include development dependencies underNODE_ENV=production. - Cache archive exclusion identities and avoid recompressing frontend gzip/Brotli/Zstandard sidecars. Keep normal frontend/backend files deflated.
- Preserve main's per-package 24-hour version-check cache and one-hour failure cooldown; handle legacy/local and UTC timestamps, reject failed HTTP responses, and skip provider lookup when no provider prompt is needed. Required hosting compatibility checks remain active.
- Add
REFLEX_FRONTEND_BUILD_CACHE=true, disabled by default. Cache pristine Vite output and replay post-build plugins, fallback handling, compression, and frontend-path processing. Validate inputs/output, invalidate changed inputs, fall back on unsupported/corrupt snapshots, and publish only after a successful complete build.
Current rebase validation (87ea648d)
Rebased onto main at 77cfe61b on September 8, 2026. The rebase preserves main's lazy CLI startup, dependency-only install fingerprint, per-package version checks, and failure cooldown.
- Production workspace locking now also works on Windows, even with build reuse disabled. It waits through contention, propagates other acquisition failures, supports nested same-thread calls, and releases after failed builds. Opt-in build reuse itself remains macOS/Linux only.
- Build fingerprints ignore the new per-package version-check and attempt timestamps, avoiding needless cache misses after routine metadata updates.
- Regression failures were captured before fixing Windows lock bypass and timestamp-driven cache invalidation. Separate spawned-process locking tests run on all CI platforms with caching both enabled and disabled.
- The final
87ea648dfollow-up only consolidates documentation and adds duration comments. Its Python AST is identical to the fully testedb9230726revision. - Full final local unit suite: 8,479 passed, 18 skipped; 76.45% coverage on Python 3.14.5. An earlier run alongside other checks hit a Redis lock-expiry timing failure; the isolated final suite passed without changes to the Redis implementation or that test.
- Focused rebase/cache/deploy suite: 341 passed; final cache/prerequisites check after the platform-guard adjustment: 204 passed.
- Real production build with caching enabled: 9 passed, covering gzip/Brotli/Zstandard sidecars and HTTP content negotiation; 9 development-mode cases intentionally skipped.
- All repository pre-commit hooks passed: Ruff, codespell, stub generation, Pyright, ty, and Biome.
Historical local deploy measurements (93c8683, before rebase)
These measurements predate this rebase. Main now includes independent startup and dependency-cache improvements, so these numbers are not a performance comparison against current main.
Same nine-page dashboard, two warmups and three fresh-process samples per variant, rotating run order. Times include imports, preparation, compilation/dependency handling, build or restore, postprocessing/compression, and both ZIPs. They exclude cloud authentication/provider APIs, upload, remote builds/rollout, optional version requests, telemetry delivery, interpreter teardown, and artifact verification.
| Unchanged frontend | Original | New default | Build cache enabled |
|---|---|---|---|
| npm | 5.792 s | 2.533 s (56.3% less time) | 1.248 s (78.4% less time) |
| Bun | 2.551 s | 2.464 s (3.4% less time) | 1.244 s (51.2% less time) |
All unchanged candidate runs skipped dependency-manager commands; enabled-cache runs also skipped Vite. Python hash seeds vary between interpreters while the child build environment remains constant.
| Changed API/deploy URLs | Original | New default | Build cache enabled, miss |
|---|---|---|---|
| npm | 5.787 s | 3.390 s | 4.188 s |
| Bun | 2.541 s | 2.510 s | 3.299 s |
Cache misses add about 0.8 seconds over the new default. The cache is for repeat deterministic builds on local macOS/Linux filesystems with reliable file timestamps; Windows bypasses it. External/clock/remote build inputs and excluded generated caches require a fresh build. Set REFLEX_FRONTEND_BUILD_CACHE=false to discard the snapshot and rebuild. Source/snapshot contents are hashed; installed dependencies use identity, size, mtime, and ctime. Documentation covers ignored private telemetry timestamps and symlink handling.
Measured on macOS arm64, Python 3.14.5, Node 26.0.0, npm 11.12.1, Bun 1.4.0 against base c49a85df9c41ddffbe9fc2ac37d495d363f4e313. No competing builds/tests ran during timing. These are warm local results, not total cloud deploy latency. Small differences can be noise. The original npm changed-URL samples were 8.489, 5.787, and 5.776 seconds; all are retained in the median. Avoiding sidecar recompression increased the earlier dashboard ZIP by 0.063%; upload impact was not measured.
Earlier validation before rebase
- Full unit suite: 8,441 passed, 18 skipped, 76.38% coverage (72% required).
- Production integration: 14 passed each with Bun and npm, with caching enabled; real JS/CSS compression and HTTP negotiation plus Chromium chart rendering/state updates. Each manager skipped 9 development cases and deselected 5 development browser cases.
- Fixed-clock real exports cover unchanged repeats, URL/Python/CSS/SVG mutations, and dependency addition/removal. 96 cross-variant archive comparisons plus 48 reference checks verify raw extracted payloads, ordered names, counts, modes, current assets/content, and actual cache hit/miss commands. npm comparisons permit dependency-section moves and
dev/devOptionalflags while requiring other manifest/lock metadata and resolved versions to agree. - The archive matrix preceded the last symlink-redirect invalidation fix; its new failing-before/passing-after regression and the final full unit/production suites cover that correction. Earlier validation also covered clean
.webrestoration and npmNODE_ENV=production. - Ruff, formatting,
pyright reflex tests, whitespace checks, and patch application against the base pass. All applicable pre-commit hooks pass, including codespell, stub generation, Pyright, and ty. Independent review identified and resolved serializer and symlink edge cases. Regression tests, documentation, and news fragments for each touched package are included.
Full cloud deployment and arbitrary concurrent workspace writers (initialization/dev hot reload/external tools) were not exercised. The new concurrency regressions use separate processes with controlled compiler/build/ZIP test doubles; production behavior is also checked by the Bun/npm integration tests.
Earlier review follow-up (4da5154, before rebase)
- Serialize initialized production exports from environment/URL configuration through compilation, setup, build/cache operations, and ZIP creation. Production/preview startup uses the same reentrant POSIX lock through frontend build and releases it before serving; direct builds and cache-disabled calls participate too. Forked children drop inherited lock ownership safely. Lock acquisition failures stop the operation. Initialization, dev hot reload, and unrelated workspace writers remain outside this scope.
- Bump the cache schema to discard snapshots created before locking.
- Expire successful version checks after 24 hours; expired, malformed, and future timestamps refresh. Preserve legacy local timestamps, write UTC for new checks, and keep network/HTTP/parse failures retryable.
- Added regressions for process overlap, distinct export URLs, ZIP interleaving, mixed cache settings, reentrancy/fork cleanup, unsafe/failed locks, expiry boundaries, timezone handling, and retries. 8,441 unit tests, 28 production integration checks, and all applicable pre-commit hooks pass.
Candidate-only follow-up on the same warmed unchanged-frontend fixture (two warmups, three fresh-process runs per variant, alternating order):
| Manager | Default | Build reuse enabled |
|---|---|---|
| npm | 2.601 s | 1.253 s |
| bun | 2.527 s | 1.268 s |
All measured repeats skipped dependency-manager commands; enabled-cache repeats skipped Vite and reran compression. The original baseline and changed-URL cases were not rerun in this follow-up; the earlier table remains the initial revision's comparison. Timing still covers local preparation through ZIP creation, excluding cloud and interpreter teardown.