diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e4ce2a90..ef3e4ca91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,8 @@ jobs: tools_dev_tests_required=true tools_pack_tests_required=true fi - if [[ "$file" == "package.json" || "$file" == "apps/"*/"package.json" || "$file" == "packages/"*/"package.json" || "$file" == "tools/"*/"package.json" || "$file" == "e2e/package.json" || "$file" == "pnpm-lock.yaml" || "$file" == "pnpm-workspace.yaml" || "$file" == "flake.nix" || "$file" == "flake.lock" || "$file" == "nix/"* || "$file" == ".github/workflows/ci.yml" || "$file" == ".github/workflows/nix-check.yml" ]]; then + # Keep this filter in sync with flake.nix daemonWorkspacePaths / webWorkspacePaths. + if [[ "$file" == "package.json" || "$file" == "pnpm-lock.yaml" || "$file" == "pnpm-workspace.yaml" || "$file" == "flake.nix" || "$file" == "flake.lock" || "$file" == "nix/"* || "$file" == ".github/workflows/ci.yml" || "$file" == ".github/workflows/nix-check.yml" || "$file" == ".github/workflows/nix-hash-autofix.yml" || "$file" == "apps/daemon/"* || "$file" == "apps/web/"* || "$file" == "packages/contracts/"* || "$file" == "packages/registry-protocol/"* || "$file" == "packages/agui-adapter/"* || "$file" == "packages/plugin-runtime/"* || "$file" == "packages/sidecar-proto/"* || "$file" == "packages/sidecar/"* || "$file" == "packages/platform/"* || "$file" == "packages/diagnostics/"* || "$file" == "packages/host/"* || "$file" == "assets/"* || "$file" == "plugins/"* || "$file" == "skills/"* || "$file" == "design-systems/"* || "$file" == "design-templates/"* || "$file" == "craft/"* || "$file" == "prompt-templates/"* || "$file" == "scripts/update-nix-pnpm-deps-hash.ts" ]]; then nix_validation_required=true fi case "$file" in @@ -151,9 +152,97 @@ jobs: experimental-features = nix-command flakes accept-flake-config = true + - name: Setup Node for Nix hash refresh + if: ${{ github.event_name == 'pull_request' }} + uses: actions/setup-node@v4 + with: + node-version-file: package.json + - name: nix flake check + id: flake_check + continue-on-error: true run: nix flake check --print-build-logs --keep-going + - name: Generate Nix hash refresh patch + id: hash_refresh + if: ${{ github.event_name == 'pull_request' && steps.flake_check.outcome == 'failure' }} + shell: bash + run: | + set -euo pipefail + out_dir="$RUNNER_TEMP/nix-hash-refresh" + mkdir -p "$out_dir" + + status="update-failed" + if node --experimental-strip-types ./scripts/update-nix-pnpm-deps-hash.ts >"$out_dir/update.log" 2>&1; then + if git diff --quiet --exit-code -- nix/pnpm-deps.nix; then + status="no-change" + else + git diff -- nix/pnpm-deps.nix >"$out_dir/nix-pnpm-deps.patch" + cp nix/pnpm-deps.nix "$out_dir/pnpm-deps.nix" + status="patch-generated" + fi + fi + + printf '{"status":"%s","runId":%s,"prNumber":%s,"headSha":"%s"}\n' \ + "$status" \ + '${{ github.run_id }}' \ + '${{ github.event.pull_request.number }}' \ + '${{ github.event.pull_request.head.sha }}' >"$out_dir/metadata.json" + + echo "status=$status" >> "$GITHUB_OUTPUT" + + - name: Upload Nix hash refresh artifact + if: ${{ github.event_name == 'pull_request' && steps.flake_check.outcome == 'failure' && steps.hash_refresh.outputs.status != 'no-change' }} + uses: actions/upload-artifact@v4 + with: + name: nix-hash-refresh + path: ${{ runner.temp }}/nix-hash-refresh + if-no-files-found: error + retention-days: 14 + + - name: Summarize Nix hash refresh guidance + if: ${{ github.event_name == 'pull_request' && steps.flake_check.outcome == 'failure' }} + env: + HASH_REFRESH_STATUS: ${{ steps.hash_refresh.outputs.status }} + shell: bash + run: | + set -euo pipefail + case "${HASH_REFRESH_STATUS:-not-run}" in + patch-generated) + cat >> "$GITHUB_STEP_SUMMARY" <<'EOF' + ## Generated Nix hash refresh + + CI regenerated a patch for `nix/pnpm-deps.nix` and uploaded it as the + `nix-hash-refresh` artifact for this run. + + - same-repo PRs: the follow-up `nix-hash-autofix` workflow will try to + push the hash-only patch back to the PR branch automatically; + - fork PRs: a PR comment will include the patch and artifact guidance. + EOF + ;; + no-change) + cat >> "$GITHUB_STEP_SUMMARY" <<'EOF' + ## Nix hash refresh unavailable + + `nix flake check` failed, but `nix/pnpm-deps.nix` did not change after + running the hash refresh helper. Inspect the Nix build logs for a + non-hash failure. + EOF + ;; + *) + cat >> "$GITHUB_STEP_SUMMARY" <<'EOF' + ## Nix hash refresh failed + + `nix flake check` failed and the helper could not generate a hash-only + patch. See the `nix-hash-refresh` artifact for `update.log`. + EOF + ;; + esac + + - name: Fail when Nix validation fails + if: ${{ steps.flake_check.outcome == 'failure' }} + run: exit 1 + preflight: name: Preflight needs: [change_scopes] diff --git a/.github/workflows/nix-hash-autofix.yml b/.github/workflows/nix-hash-autofix.yml new file mode 100644 index 000000000..c182a4ca7 --- /dev/null +++ b/.github/workflows/nix-hash-autofix.yml @@ -0,0 +1,205 @@ +name: nix-hash-autofix + +on: + workflow_run: + workflows: [ci] + types: [completed] + +permissions: + actions: read + contents: read + pull-requests: write + +jobs: + autofix: + if: ${{ github.repository == 'nexu-io/open-design' && github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure' }} + runs-on: ubuntu-latest + + steps: + - name: Check for Nix hash refresh artifact + id: artifact + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + RUN_ID: ${{ github.event.workflow_run.id }} + shell: bash + run: | + set -euo pipefail + artifact_id="$(gh api "repos/$REPO/actions/runs/$RUN_ID/artifacts" --jq '.artifacts[] | select(.name == "nix-hash-refresh") | .id' | head -n 1 || true)" + if [ -z "$artifact_id" ]; then + echo "present=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "present=true" >> "$GITHUB_OUTPUT" + echo "artifact_id=$artifact_id" >> "$GITHUB_OUTPUT" + + - name: Download Nix hash refresh artifact + if: ${{ steps.artifact.outputs.present == 'true' }} + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + ARTIFACT_ID: ${{ steps.artifact.outputs.artifact_id }} + shell: bash + run: | + set -euo pipefail + mkdir -p "$RUNNER_TEMP/nix-hash-refresh" + gh api \ + -H 'Accept: application/vnd.github+json' \ + "repos/$REPO/actions/artifacts/$ARTIFACT_ID/zip" > "$RUNNER_TEMP/nix-hash-refresh.zip" + unzip -q "$RUNNER_TEMP/nix-hash-refresh.zip" -d "$RUNNER_TEMP/nix-hash-refresh" + + - name: Read PR and patch metadata + if: ${{ steps.artifact.outputs.present == 'true' }} + id: meta + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + WORKFLOW_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number || '' }} + WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + shell: bash + run: | + set -euo pipefail + status="$(python3 -c 'import json, sys; data = json.load(open(sys.argv[1], "r", encoding="utf-8")); print(data.get("status", "missing"))' "$RUNNER_TEMP/nix-hash-refresh/metadata.json")" + artifact_head_sha="$(python3 -c 'import json, sys; data = json.load(open(sys.argv[1], "r", encoding="utf-8")); print(data.get("headSha", ""))' "$RUNNER_TEMP/nix-hash-refresh/metadata.json")" + + pr_number="$WORKFLOW_PR_NUMBER" + if [ -z "$pr_number" ]; then + pr_number="$(gh api "repos/$REPO/commits/$WORKFLOW_HEAD_SHA/pulls" --jq '.[0].number // empty')" + fi + if ! [[ "$pr_number" =~ ^[0-9]+$ ]]; then + echo "Unable to derive PR number for workflow head $WORKFLOW_HEAD_SHA." >&2 + exit 1 + fi + + pr_json="$(gh api "repos/$REPO/pulls/$pr_number")" + head_repo="$(printf '%s' "$pr_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["head"]["repo"]["full_name"])')" + head_ref="$(printf '%s' "$pr_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["head"]["ref"])')" + head_sha="$(printf '%s' "$pr_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["head"]["sha"])')" + { + echo "status=$status" + echo "artifact_head_sha=$artifact_head_sha" + echo "pr_number=$pr_number" + echo "head_repo=$head_repo" + echo "head_ref=$head_ref" + echo "head_sha=$head_sha" + } >> "$GITHUB_OUTPUT" + if [ -n "$artifact_head_sha" ] && [ "$artifact_head_sha" = "$head_sha" ]; then + echo "head_sha_matches=true" >> "$GITHUB_OUTPUT" + else + echo "head_sha_matches=false" >> "$GITHUB_OUTPUT" + fi + if [ "$head_repo" = "$REPO" ] && [ "$status" = "patch-generated" ] && [ "$artifact_head_sha" = "$head_sha" ]; then + echo "can_autopush=true" >> "$GITHUB_OUTPUT" + else + echo "can_autopush=false" >> "$GITHUB_OUTPUT" + fi + + - name: Detect bot credentials + if: ${{ steps.meta.outputs.can_autopush == 'true' }} + id: bot-secrets + env: + BOT_APP_ID: ${{ secrets.BOT_APP_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.BOT_APP_PRIVATE_KEY }} + shell: bash + run: | + set -euo pipefail + if [ -n "${BOT_APP_ID}" ] && [ -n "${BOT_APP_PRIVATE_KEY}" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + + - name: Generate Open Design bot token + if: ${{ steps.meta.outputs.can_autopush == 'true' && steps.bot-secrets.outputs.present == 'true' }} + id: bot-token + continue-on-error: true + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.BOT_APP_ID }} + private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + owner: nexu-io + repositories: open-design + permission-contents: write + + - name: Checkout PR branch for auto-apply + if: ${{ steps.meta.outputs.can_autopush == 'true' && steps.bot-token.outcome == 'success' }} + uses: actions/checkout@v6.0.2 + with: + repository: ${{ steps.meta.outputs.head_repo }} + ref: ${{ steps.meta.outputs.head_ref }} + token: ${{ steps.bot-token.outputs.token }} + + - name: Apply generated hash patch + if: ${{ steps.meta.outputs.can_autopush == 'true' && steps.bot-token.outcome == 'success' }} + id: apply + shell: bash + run: | + set -euo pipefail + patch_path="$RUNNER_TEMP/nix-hash-refresh/nix-pnpm-deps.patch" + if [ ! -f "$patch_path" ]; then + echo "applied=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + git apply --check "$patch_path" + git apply "$patch_path" + changed_files="$(git diff --name-only)" + if [ "$changed_files" != "nix/pnpm-deps.nix" ]; then + echo "Unexpected files changed after applying hash patch:" >&2 + printf '%s\n' "$changed_files" >&2 + exit 1 + fi + if git diff --quiet --exit-code; then + echo "applied=false" >> "$GITHUB_OUTPUT" + else + echo "applied=true" >> "$GITHUB_OUTPUT" + fi + + - name: Commit and push generated hash refresh + if: ${{ steps.meta.outputs.can_autopush == 'true' && steps.bot-token.outcome == 'success' && steps.apply.outputs.applied == 'true' }} + shell: bash + run: | + set -euo pipefail + git config user.name 'open-design-bot[bot]' + git config user.email '282769551+open-design-bot[bot]@users.noreply.github.com' + git add nix/pnpm-deps.nix + git commit -m 'chore(nix): refresh pnpm deps hash' + git push origin "HEAD:${{ steps.meta.outputs.head_ref }}" + + - name: Upsert fork/manual patch comment + if: ${{ steps.artifact.outputs.present == 'true' && steps.meta.outputs.head_sha_matches == 'true' && (steps.meta.outputs.can_autopush != 'true' || steps.bot-token.outcome != 'success' || steps.apply.outputs.applied != 'true') }} + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ steps.meta.outputs.pr_number }} + HASH_REFRESH_STATUS: ${{ steps.meta.outputs.status }} + shell: bash + run: | + set -euo pipefail + body_file="$RUNNER_TEMP/nix-hash-refresh-comment.md" + patch_file="$RUNNER_TEMP/nix-hash-refresh/nix-pnpm-deps.patch" + marker='' + + { + printf '%s\n' "$marker" + if [ "${HASH_REFRESH_STATUS}" = 'patch-generated' ]; then + printf "A generated Nix hash refresh is available for this PR.\n\n" + printf "Apply the patch from the \`nix-hash-refresh\` artifact attached to the failed \`ci\` run, or paste the diff below into \`git apply\`:\n\n" + printf '```diff\n' + if [ -f "$patch_file" ]; then + cat "$patch_file" + else + printf '(artifact did not include nix-pnpm-deps.patch)\n' + fi + printf '\n```\n' + else + printf "The failed \`ci\` run attempted to refresh \`nix/pnpm-deps.nix\`, but it could not produce a hash-only patch.\n\n" + printf "Download the \`nix-hash-refresh\` artifact from that run and inspect \`update.log\` for details.\n" + fi + } > "$body_file" + + comment_id="$(gh api --paginate "repos/$REPO/issues/$PR_NUMBER/comments" --jq ".[] | select(.body | contains(\"$marker\")) | .id" | tail -n 1 || true)" + if [ -n "$comment_id" ]; then + gh api --method PATCH "repos/$REPO/issues/comments/$comment_id" --field body="$(cat "$body_file")" >/dev/null + else + gh api --method POST "repos/$REPO/issues/$PR_NUMBER/comments" --field body="$(cat "$body_file")" >/dev/null + fi diff --git a/AGENTS.md b/AGENTS.md index d48346baf..08e752deb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -167,7 +167,7 @@ root `pnpm tools-pr` script without a new explicit maintainer decision. ## Validation strategy - After package, workspace, or command-entry changes, run `pnpm install` so workspace links and generated dist entries stay fresh. -- Treat every `pnpm-lock.yaml` change as requiring a Nix pnpm deps hash refresh check. Use `pnpm nix:update-hash` to regenerate `nix/pnpm-deps.nix`, then re-run `nix flake check --print-build-logs --keep-going` (or rely on the PR `Validate workspace` gate if Nix is unavailable locally). +- Treat every `pnpm-lock.yaml` change as requiring a Nix pnpm deps hash refresh check. `nix/pnpm-deps.nix` is a generated lock artifact; use `pnpm nix:update-hash` only when intentionally maintaining Nix packaging, then re-run `nix flake check --print-build-logs --keep-going`. Contributors without Nix can rely on the PR `Validate workspace` gate, which now uploads or auto-applies the generated hash-only fix when possible. - Before marking regular work ready, run at least `pnpm guard` and `pnpm typecheck`, plus the package-scoped tests/builds that match the files changed. Do not use or add root `pnpm test`/`pnpm build` aliases. - For local web runtime loops, prefer `pnpm tools-dev run web --daemon-port --web-port `. - On a GUI-capable machine, validate desktop by running `pnpm tools-dev`, then `pnpm tools-dev inspect desktop status`. diff --git a/flake.nix b/flake.nix index 5c256f04d..afbdc6cfe 100644 --- a/flake.nix +++ b/flake.nix @@ -40,12 +40,34 @@ perSystem = flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs {inherit system;}; nodejs = pkgs.nodejs_24; - daemonSrc = filterProjectSource [ + # Keep in sync with .github/workflows/ci.yml change_scopes + # nix_validation_required filter. + daemonWorkspacePaths = [ + "packages/contracts" + "packages/registry-protocol" + "packages/agui-adapter" + "packages/plugin-runtime" + "packages/sidecar-proto" + "packages/sidecar" + "packages/platform" + "packages/diagnostics" + "apps/daemon" + ]; + # Keep in sync with .github/workflows/ci.yml change_scopes + # nix_validation_required filter. + webWorkspacePaths = [ + "packages/contracts" + "packages/host" + "packages/platform" + "packages/sidecar" + "packages/sidecar-proto" + "apps/web" + ]; + daemonSrc = filterProjectSource ([ "package.json" "pnpm-lock.yaml" "pnpm-workspace.yaml" "tsconfig.json" - "scripts" "assets" "plugins" "skills" @@ -53,21 +75,15 @@ "design-templates" "craft" "prompt-templates" - "apps/daemon" - "packages" - ]; - webSrc = filterProjectSource [ + ] + ++ daemonWorkspacePaths); + webSrc = filterProjectSource ([ "package.json" "pnpm-lock.yaml" "pnpm-workspace.yaml" "tsconfig.json" - "apps/web" - "packages/contracts" - "packages/host" - "packages/platform" - "packages/sidecar" - "packages/sidecar-proto" - ]; + ] + ++ webWorkspacePaths); # nixpkgs ships pnpm 10.33.0; the repo's package.json declares # `engines.pnpm: ">=10.33.2 <11"` and pnpm refuses to install @@ -92,10 +108,12 @@ daemon = pkgs.callPackage ./nix/package-daemon.nix { inherit dream2nix nixpkgs system nodejs pnpm_10; src = daemonSrc; + workspacePaths = daemonWorkspacePaths; }; web = pkgs.callPackage ./nix/package-web.nix { inherit dream2nix nixpkgs system nodejs pnpm_10; src = webSrc; + workspacePaths = webWorkspacePaths; }; in { packages = { diff --git a/nix/README.md b/nix/README.md index 9e62f76a3..e9e32a695 100644 --- a/nix/README.md +++ b/nix/README.md @@ -220,27 +220,42 @@ Never inline a secret with `pkgs.writeText` or `home.file`. ## First-build hash pinning -`nix/pnpm-deps.nix` is the single source of truth for the vendored pnpm -store hash used by both `nix/package-daemon.nix` and -`nix/package-web.nix`. If `pnpm-lock.yaml` changes, run: +`nix/pnpm-deps.nix` is the generated single source of truth for the +vendored pnpm store hash used by both `nix/package-daemon.nix` and +`nix/package-web.nix`. Treat it like a lock artifact, not a hand-edited +source file. If `pnpm-lock.yaml` changes and you are intentionally +maintaining the Nix packaging, run: ```bash pnpm nix:update-hash ``` -The script temporarily swaps one consumer to `lib.fakeHash`, runs -`nix build .#web --print-build-logs`, extracts the expected hash from the -failure output, writes it back into `nix/pnpm-deps.nix`, and restores the -consumer file. +The script temporarily swaps one consumer to `lib.fakeHash`, runs the +matching `nix build .# --print-build-logs`, extracts the +expected hash from the failure output, writes it back into +`nix/pnpm-deps.nix`, and restores the consumer file. The script runs via +`node --experimental-strip-types`, so CI can invoke it without first +installing the workspace. ## CI `.github/workflows/nix-check.yml` runs `nix flake check` on pushes to `main` and can also be started manually with `workflow_dispatch`. -Pull requests that touch Nix or dependency inputs are validated earlier in +Pull requests that touch Nix inputs, daemon/web Nix build closures, or the +generated hash maintenance workflows are validated earlier in `.github/workflows/ci.yml` via the required `Validate workspace` gate. -That PR path runs `nix flake check` when `pnpm-lock.yaml`, package -manifests, `flake.*`, `nix/**`, or the Nix workflows change, so fixed- -output hash drift is caught before merge while keeping unrelated PRs off -the slower Nix path. +That PR path runs `nix flake check` for `flake.*`, `nix/**`, root lock and +workspace manifests, and files that are actually in the daemon/web Nix +closures. The flake also filters each derivation down to only the workspace +packages it actually installs, so unrelated package/tool changes stay off the +slower Nix path and do not churn the other derivation's pnpm store hash. + +When a PR run fails because `nix/pnpm-deps.nix` is stale, the CI job also +tries to regenerate a hash-only patch: + +- same-repo PRs get a bot-authored commit pushed back to the PR branch when + the generated patch only touches `nix/pnpm-deps.nix`; +- fork PRs get a PR comment plus a workflow artifact containing the patch; +- the failing run still stays red until the generated patch lands and a + fresh validation run passes. diff --git a/nix/package-daemon.nix b/nix/package-daemon.nix index 0708a4f6b..64115ecf8 100644 --- a/nix/package-daemon.nix +++ b/nix/package-daemon.nix @@ -9,6 +9,7 @@ fetchPnpmDeps, pnpmConfigHook, src, + workspacePaths, makeWrapper, python3, gnumake, @@ -40,10 +41,13 @@ let version = (lib.importJSON ../package.json).version; pnpmDepsHash = (import ./pnpm-deps.nix).daemonHash; + pnpmWorkspaceFilters = map (workspacePath: "./${workspacePath}") workspacePaths; in stdenv.mkDerivation (finalAttrs: { inherit pname version src; + pnpmWorkspaces = pnpmWorkspaceFilters; + nativeBuildInputs = [ nodejs pnpm_10 @@ -60,6 +64,7 @@ in pnpmDeps = fetchPnpmDeps { inherit (finalAttrs) pname version src; hash = pnpmDepsHash; + pnpmWorkspaces = pnpmWorkspaceFilters; fetcherVersion = 3; }; @@ -128,17 +133,7 @@ in exit 1 fi - for target in \ - packages/contracts \ - packages/registry-protocol \ - packages/agui-adapter \ - packages/plugin-runtime \ - packages/sidecar-proto \ - packages/sidecar \ - packages/platform \ - packages/diagnostics \ - apps/daemon - do + for target in ${lib.escapeShellArgs workspacePaths}; do pnpm -C "$target" run --if-present build done runHook postBuild diff --git a/nix/package-web.nix b/nix/package-web.nix index dfd2fb4f4..9de7e1691 100644 --- a/nix/package-web.nix +++ b/nix/package-web.nix @@ -9,6 +9,7 @@ fetchPnpmDeps, pnpmConfigHook, src, + workspacePaths, }: # Builds the @open-design/web Next.js static export. # @@ -27,10 +28,14 @@ let version = (lib.importJSON ../package.json).version; pnpmDepsHash = (import ./pnpm-deps.nix).webHash; + pnpmWorkspaceFilters = map (workspacePath: "./${workspacePath}") workspacePaths; + dependencyBuildPaths = lib.filter (workspacePath: workspacePath != "apps/web") workspacePaths; in stdenv.mkDerivation (finalAttrs: { inherit pname version src; + pnpmWorkspaces = pnpmWorkspaceFilters; + nativeBuildInputs = [ nodejs pnpm_10 @@ -40,6 +45,7 @@ in pnpmDeps = fetchPnpmDeps { inherit (finalAttrs) pname version src; hash = pnpmDepsHash; + pnpmWorkspaces = pnpmWorkspaceFilters; fetcherVersion = 3; }; @@ -50,13 +56,7 @@ in buildPhase = '' runHook preBuild - for target in \ - packages/contracts \ - packages/host \ - packages/sidecar-proto \ - packages/sidecar \ - packages/platform - do + for target in ${lib.escapeShellArgs dependencyBuildPaths}; do pnpm -C "$target" run --if-present build done diff --git a/nix/pnpm-deps.nix b/nix/pnpm-deps.nix index 97d3b754d..daea7e98b 100644 --- a/nix/pnpm-deps.nix +++ b/nix/pnpm-deps.nix @@ -1,5 +1,6 @@ { # Vendored pnpm store hashes for the workspace packages built by the flake. + # Generated lock artifact; do not hand-edit outside intentional Nix maintenance. # # The daemon and web derivations now build from different filtered source # trees, so each fetchPnpmDeps invocation needs its own fixed-output hash. diff --git a/package.json b/package.json index a412e4a6a..1e8834e1a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "tools-dev": "pnpm exec tools-dev", "tools-pack": "pnpm exec tools-pack", "tools-serve": "pnpm exec tools-serve", - "nix:update-hash": "tsx ./scripts/update-nix-pnpm-deps-hash.ts", + "nix:update-hash": "node --experimental-strip-types ./scripts/update-nix-pnpm-deps-hash.ts", "guard": "tsx ./scripts/guard.ts && node --import tsx --test scripts/style-policy.test.ts scripts/approve-fork-pr-workflows.test.ts", "i18n:check": "tsx ./scripts/i18n-check.ts", "i18n:coverage": "tsx ./scripts/i18n-coverage-report.ts",