Files
paseo/scripts/fix-lockfile.mjs
José Albornoz 6b0ff42748 feat: add Nix flake with package and NixOS module
Add a Nix flake that builds the Paseo daemon (server + CLI) and
provides a NixOS module for declarative deployment.

Package (nix/package.nix):
- Builds relay, server, and CLI workspaces
- Skips onnxruntime-node install script (sandbox-incompatible)
- Rebuilds only node-pty for native terminal support
- Source filter excludes app/website/desktop workspaces

NixOS module (nix/module.nix):
- Systemd service with configurable user, port, listen address
- allowedHosts for DNS rebinding protection
- relay.enable to toggle remote access via app.paseo.sh
- inheritUserEnvironment to expose user tools (git, ssh) to agents
- openFirewall and extra environment variables

ci: add Nix hash maintenance scripts and workflows

scripts/fix-lockfile.mjs:
  Adds missing resolved/integrity fields to package-lock.json for
  workspace-local overrides. Idempotent, uses `npm view`.

scripts/update-nix.sh:
  Runs fix-lockfile.mjs, prefetches deps, computes NAR hash, and
  updates npmDepsHash in nix/package.nix. Supports --check for CI.

.github/workflows/nix-build.yml:
  Builds the Nix package on push/PR and verifies the lockfile and
  hash are up to date.

.github/workflows/fix-nix-hash.yml:
  Auto-fixes lockfile signatures and Nix hash on dependabot PRs.

fix: update npmDepsHash after upstream sync

nix: allowlist workspace symlinks instead of blocklist

Prevents build failures when upstream adds new workspace packages.
2026-03-25 22:52:43 +07:00

67 lines
2.0 KiB
JavaScript

#!/usr/bin/env node
// Ensure all workspace-local node_modules entries in package-lock.json have
// `resolved` and `integrity` fields. npm omits these for workspace-hoisted
// overrides, but tools like Nix's offline `npm ci` require them.
//
// This script is idempotent — running it on an already-complete lockfile
// is a no-op. Run it after `npm install` when the lockfile changes.
//
// Usage:
// node scripts/fix-lockfile.mjs
// node scripts/fix-lockfile.mjs path/to/package-lock.json
import fs from "fs";
import { execSync } from "child_process";
const lockPath = process.argv[2] || "package-lock.json";
const lock = JSON.parse(fs.readFileSync(lockPath, "utf8"));
// Collect workspace package roots (local packages, not from npm)
const workspaceRoots = new Set();
for (const [key, val] of Object.entries(lock.packages || {})) {
if (val.link) {
workspaceRoots.add(val.resolved || key);
}
}
let fixed = 0;
for (const [key, val] of Object.entries(lock.packages || {})) {
if (
!key || // root package
key.startsWith("node_modules/") || // top-level (already has resolved)
val.link || // workspace link entry
(val.resolved && val.integrity) || // already complete
!val.version || // no version to look up
workspaceRoots.has(key) // workspace package root (local, not on npm)
)
continue;
const pkgName = key.replace(/.*node_modules\//, "");
const version = val.version;
try {
const info = JSON.parse(
execSync(`npm view ${pkgName}@${version} --json dist`, {
encoding: "utf8",
stdio: ["pipe", "pipe", "pipe"],
})
);
if (info.tarball && info.integrity) {
val.resolved = info.tarball;
val.integrity = info.integrity;
fixed++;
}
} catch {
console.error(`Warning: could not fetch info for ${pkgName}@${version}`);
}
}
fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n");
if (fixed > 0) {
console.log(`Fixed ${fixed} lockfile entries with missing resolved/integrity`);
} else {
console.log("Lockfile is already complete");
}