chore(nix): streamline pnpm deps hash maintenance (#2919)

* chore(nix): streamline pnpm deps hash maintenance

Generated-By: looper 0.9.0 (runner=worker, agent=opencode)

* fix(ci): satisfy actionlint in nix hash autofix

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* fix(ci): allow nix hash autofix on fork PRs

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* fix(ci): follow up nix hash review

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* fix(ci): tolerate nix hash bot token failures

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)
This commit is contained in:
Marc Chan 2026-05-26 15:35:38 +08:00 committed by GitHub
parent fb1e0c819f
commit d5659d82d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 369 additions and 46 deletions

View file

@ -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]

205
.github/workflows/nix-hash-autofix.yml vendored Normal file
View file

@ -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='<!-- nix-hash-refresh -->'
{
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

View file

@ -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 <port> --web-port <port>`.
- On a GUI-capable machine, validate desktop by running `pnpm tools-dev`, then `pnpm tools-dev inspect desktop status`.

View file

@ -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 = {

View file

@ -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 .#<consumer> --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.

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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",