Files
homarr/apps/nextjs/scripts/standalone-artifact.mjs
T
Thomas 229bd4b3ed chore: use Bun package management and Turbo remote caching (#6978)
* feat: migrate tooling and runtimes to bun

* fix: serialize act installs and adapt backup checks

* fix: finish bun scripts and rootless act setup

* fix: support bun images in runtime benchmarks

* fix: keep a single bun toolchain configuration

* fix: include docs mascot sources in workshop build

* docs: use bun in remaining tool commands

* fix: complete bun sqlite development tooling

* docs: explain sqlite development adapter

* fix: align remaining commands and local ci validation

* fix: use renovate regex fallback with bun

* fix: finalize bun sqlite connections during cleanup

* fix: preserve sqlite foreign key enforcement

* fix: align docker build frontends

* fix: prepare release versions with bun

* Add Bun benchmarks and preserve SQLite connection defaults

* Benchmark Bun installs, conflicts and production workloads

* Force Bun workspace tools and HTTP/1.1 upstream streaming

* Exclude benchmark evidence from Docker contexts

* Expose branch benchmarks through CI

* Verify hydrated widget data in production benchmarks

* Measure image archives and document benchmark protocol

* Audit dependency sharing across every workspace

* Pin pnpm for nested baseline benchmark scripts

* Preserve nested manager pinning on benchmark resume

* Include application and runtime image footprints

* Fail benchmark gates on candidate command errors

* Report complete build overhead and sampled memory bounds

* Validate migration comparisons across pinned revisions

* Record configurable runtime memory budgets

* Retain out-of-memory kills in runtime budget probes

* Reject OOM events consistently across runtime metrics

* Record complete Bun migration benchmarks

* Record memory diagnosis and correct Turbo caching

* Use Bun package management while preserving Node runtimes

* Keep native build fallbacks and exclude development artifacts

* Fix Node ESM metadata and isolate local CI configuration

* Measure development performance and isolate act environment

* Support isolated environment files in rootless act

* Validate the full migration range with act

* Preserve native SQLite binding for bundled migrations

* Use documented dev defaults and report benchmark scope

* Validate dev defaults and isolate restart measurements

* Record package-manager and development benchmarks

* Keep benchmark measurements and link full command artifacts

* Clean up temporary act environment files

* Validate Node runtimes in package-manager comparisons

* Validate every workspace in manual CI runs

* Retain patched nested brace expansion versions

* Complete package-manager and Node performance report

* Normalize archived benchmark logs

* Record final hosted validation

* Cache both Next builds with platform isolation

* Prime cache with write-only mode

* Split Next artifacts below Cloudflare upload limit

* Assemble coherent standalone cache outputs

* Preserve traced middleware entrypoints

* Assemble traced directory links before files

* Separate cached dependencies from assembled output

* Record signed Turbo cache benchmarks

* Remove archived benchmark captures

Full benchmark evidence is preserved at 8f7d3bcd14109bfb13fa664d31710b88d820ad66.

* Remove migration benchmark harness

* fix: correct migration launch and local Act prerequisites

* Consolidate Turbo caches and production artifacts

* Exclude generated files from Docker cache inputs

* fix: address Bun migration review findings

* fix: check restored compiler bundle manifests
2026-10-07 16:38:14 +02:00

83 lines
3.6 KiB
JavaScript

import { constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
import { dirname, join, relative, resolve, sep } from "node:path";
export const compiled = resolve(".next");
export const root = resolve("../..");
export const appDirectory = relative(root, process.cwd());
export const dependencies = resolve(".output/dependencies");
export const standalone = resolve(".output/standalone");
function isOutside(path) {
return path === ".." || path.startsWith(`..${sep}`);
}
// The compiled artifact owns every generated file, including Next's launcher.
// The dependency artifact contains only traced files outside the build tree.
export function collectFiles() {
const files = new Set([join(compiled, "BUILD_ID"), join(compiled, "required-server-files.json")]);
const manifest = JSON.parse(readFileSync(join(compiled, "required-server-files.json"), "utf8"));
for (const file of manifest.files) files.add(resolve(file));
files.add(resolve("package.json"));
for (const file of [".env", ".env.production"]) {
if (existsSync(file)) files.add(resolve(file));
}
function collectTraces(directory) {
for (const entry of readdirSync(directory, { withFileTypes: true })) {
if (directory === compiled && ["cache", "dev", "standalone"].includes(entry.name)) continue;
const path = join(directory, entry.name);
if (entry.isDirectory()) {
collectTraces(path);
continue;
}
if (!entry.name.endsWith(".nft.json")) continue;
// NFT lists dependencies, excluding the entrypoint itself (including proxy).
const entrypoint = path.slice(0, -".nft.json".length);
if (existsSync(entrypoint)) files.add(entrypoint);
const trace = JSON.parse(readFileSync(path, "utf8"));
for (const file of trace.files) {
const target = resolve(dirname(path), file);
if (isOutside(relative(root, target))) throw new Error(`Trace escapes the repository: ${target}`);
const generatedPath = relative(compiled, target);
if (generatedPath.startsWith(`standalone${sep}`)) continue;
// Keep native alias links, without copying through them into dependencies.
if (generatedPath.startsWith(`node_modules${sep}`) && !lstatSync(target).isSymbolicLink()) continue;
files.add(target);
}
}
}
collectTraces(compiled);
// Edge middleware/functions can have assets which are not covered by NFT.
const middleware = JSON.parse(readFileSync(join(compiled, "server/middleware-manifest.json"), "utf8"));
for (const entry of [...Object.values(middleware.middleware), ...Object.values(middleware.functions)]) {
for (const file of entry.files ?? []) files.add(join(compiled, file));
for (const file of [...(entry.wasm ?? []), ...(entry.assets ?? [])]) files.add(join(compiled, file.filePath));
}
return [...files];
}
export function isCompiledFile(file) {
return !isOutside(relative(compiled, file));
}
export function copyFiles(files, destinationRoot) {
const ordered = files.map((file) => ({ file, symbolicLink: lstatSync(file).isSymbolicLink() }));
ordered.sort((left, right) => {
if (left.symbolicLink !== right.symbolicLink) {
if (left.symbolicLink) return -1;
return 1;
}
return left.file.localeCompare(right.file);
});
for (const { file, symbolicLink } of ordered) {
const destination = join(destinationRoot, relative(root, file));
mkdirSync(dirname(destination), { recursive: true });
if (symbolicLink) {
cpSync(file, destination, { recursive: true, verbatimSymlinks: true });
} else {
copyFileSync(file, destination, constants.COPYFILE_FICLONE);
}
}
}