viteBuild takes WEBAWESOME_NPM_TOKEN from the Mill daemon, so a daemon started without it breaks dashboard builds in a fresh worktree #544

Closed
opened 2026-09-11 18:18:30 +00:00 by mph · 3 comments
Owner

frontend.viteBuild runs Yarn with the Mill daemon's environment, not the environment of the shell that asked for the build. When the daemon was started without WEBAWESOME_NPM_TOKEN, every dashboard build in a fresh worktree fails, and it keeps failing after you fix your shell, until the daemon is shut down.

Where

build.mill, frontend.viteBuild:

// Run yarn install + yarn build with inherited env (WEBAWESOME_NPM_TOKEN).
os.proc("yarn", "install", "--immutable").call(cwd = srcDir, stdout = os.Inherit, stderr = os.Inherit)
os.proc("yarn", "build", "--outDir", Task.dest.toString).call(cwd = srcDir, stdout = os.Inherit, stderr = os.Inherit)

"Inherited" means inherited from the daemon process. dashboard/frontend/.yarnrc.yml needs the token, so Yarn stops with:

Usage Error: Environment variable not found (WEBAWESOME_NPM_TOKEN) in …/dashboard/frontend/.yarnrc.yml
Error: Mill failed to resolve dashboard.assembly

Reproduction (2026-09-11)

  1. In a fresh worktree (no dashboard/frontend/node_modules, no built assembly), the first ./mill call came from a shell without the token, so the daemon started without it.
  2. ./iw ./test then failed dashboard.test, dashboard.itest.testForked, dashboard-dev-gate.bats, dashboard-dev-mode.bats and dispatcher-mill-gate.bats: 17 failing cases, all on the error above. No test assertion failed.
  3. Rerunning the same five from a shell that does export the token changed nothing: the old daemon was reused. /proc/<daemon pid>/environ had no WEBAWESOME_NPM_TOKEN.
  4. After ./mill shutdown and a rerun from that shell, the new daemon had the token, and all five passed (18/18 BATS cases).

The main checkout rarely shows this, because its frontend is already built and cached, so Yarn never runs there.

Why it is costly

The error names a missing environment variable, and the reader checks their shell: the variable is there, exported. The actual cause — a long-lived process that kept the environment it first started with — is invisible. #526 records the same trap with sbtn's server, and #494 is another case of the Mill daemon's environment causing a confusing failure.

Possible fixes

  • Pass the requesting client's environment to Yarn explicitly (Mill exposes it to tasks; check the exact API for the pinned Mill version), so the daemon's own environment no longer matters.
  • Or, at the least, have the build fail with a message that names the daemon: "WEBAWESOME_NPM_TOKEN is not in the Mill daemon's environment; run ./mill shutdown and retry from a shell that exports it."

Type: bug
Found while: running the full gate for branch wip/zg-index-setup

`frontend.viteBuild` runs Yarn with the **Mill daemon's** environment, not the environment of the shell that asked for the build. When the daemon was started without `WEBAWESOME_NPM_TOKEN`, every dashboard build in a fresh worktree fails, and it keeps failing after you fix your shell, until the daemon is shut down. ## Where `build.mill`, `frontend.viteBuild`: ```scala // Run yarn install + yarn build with inherited env (WEBAWESOME_NPM_TOKEN). os.proc("yarn", "install", "--immutable").call(cwd = srcDir, stdout = os.Inherit, stderr = os.Inherit) os.proc("yarn", "build", "--outDir", Task.dest.toString).call(cwd = srcDir, stdout = os.Inherit, stderr = os.Inherit) ``` "Inherited" means inherited from the daemon process. `dashboard/frontend/.yarnrc.yml` needs the token, so Yarn stops with: ``` Usage Error: Environment variable not found (WEBAWESOME_NPM_TOKEN) in …/dashboard/frontend/.yarnrc.yml Error: Mill failed to resolve dashboard.assembly ``` ## Reproduction (2026-09-11) 1. In a fresh worktree (no `dashboard/frontend/node_modules`, no built assembly), the first `./mill` call came from a shell without the token, so the daemon started without it. 2. `./iw ./test` then failed `dashboard.test`, `dashboard.itest.testForked`, `dashboard-dev-gate.bats`, `dashboard-dev-mode.bats` and `dispatcher-mill-gate.bats`: **17 failing cases**, all on the error above. No test assertion failed. 3. Rerunning the same five from a shell that *does* export the token changed nothing: the old daemon was reused. `/proc/<daemon pid>/environ` had no `WEBAWESOME_NPM_TOKEN`. 4. After `./mill shutdown` and a rerun from that shell, the new daemon had the token, and all five passed (18/18 BATS cases). The main checkout rarely shows this, because its frontend is already built and cached, so Yarn never runs there. ## Why it is costly The error names a missing environment variable, and the reader checks their shell: the variable is there, exported. The actual cause — a long-lived process that kept the environment it first started with — is invisible. #526 records the same trap with sbtn's server, and #494 is another case of the Mill daemon's environment causing a confusing failure. ## Possible fixes - Pass the requesting client's environment to Yarn explicitly (Mill exposes it to tasks; check the exact API for the pinned Mill version), so the daemon's own environment no longer matters. - Or, at the least, have the build fail with a message that names the daemon: "WEBAWESOME_NPM_TOKEN is not in the Mill daemon's environment; run `./mill shutdown` and retry from a shell that exports it." --- **Type:** bug **Found while:** running the full gate for branch `wip/zg-index-setup`
Member

Intake: needs-human
Workflow: diagnostic
Reason: G1 — a CLARIFY marker stands, but review found it spurious (a fake-yarn test under MILL_OUTPUT_DIR needs no licensed token, so no test-skip authorisation is needed); review also failed G7 unsound on a false no-cache-invalidation claim
Analysis: project-management/issues/IW-544/analysis.md on branch IW-544

Intake: needs-human Workflow: diagnostic Reason: G1 — a CLARIFY marker stands, but review found it spurious (a fake-yarn test under MILL_OUTPUT_DIR needs no licensed token, so no test-skip authorisation is needed); review also failed G7 unsound on a false no-cache-invalidation claim Analysis: project-management/issues/IW-544/analysis.md on branch IW-544
Member

Intake: needs-input
Workflow: diagnostic
Reason: G7 (2 blocking findings of the analysis review); the G1 marker is settled by the code
Analysis: project-management/issues/IW-544/analysis.md on branch IW-544

  1. [G7] The analysis review found two errors in the analysis (analysis.md:338-356 and :276). First: the CLARIFY "Automated test cover for a build-file fix" asks you to authorise a fix with no automated red-then-green test (its Option A), because its Option B — a fake yarn first on PATH, driving the real frontend.viteBuild — would cache an empty out/frontend/viteBuild.dest. The reviewer showed that MILL_OUTPUT_DIR (supported by the pinned Mill 1.1.5, and read by the repository's own launcher at mill:139) moves the whole out tree, the daemon included, into a scratch directory, so Option B needs no licensed token, cannot poison the real cache, and needs no authorisation. Second: the analysis says the fix "does not alter task inputs, so no cache is invalidated"; the reviewer showed that a changed viteBuild body re-runs viteBuild, so every developer's next dashboard build after the fix runs yarn install + yarn build and needs WEBAWESOME_NPM_TOKEN. What should intake do with the analysis? Options: a) correct both: take Option B under MILL_OUTPUT_DIR as the automated test, and state the one-time rebuild (and its token need) as a cost of the fix, then review the analysis again b) correct the cache claim only, and keep Option A with your authorisation to ship without an automated red-then-green test c) leave the analysis as it is and send the issue to a session Recommended: a)

Answer in a comment on this issue; intake reads it on its next iteration.

Intake: needs-input Workflow: diagnostic Reason: G7 (2 blocking findings of the analysis review); the G1 marker is settled by the code Analysis: project-management/issues/IW-544/analysis.md on branch IW-544 1. [G7] The analysis review found two errors in the analysis (analysis.md:338-356 and :276). First: the CLARIFY "Automated test cover for a build-file fix" asks you to authorise a fix with no automated red-then-green test (its Option A), because its Option B — a fake `yarn` first on `PATH`, driving the real `frontend.viteBuild` — would cache an empty `out/frontend/viteBuild.dest`. The reviewer showed that `MILL_OUTPUT_DIR` (supported by the pinned Mill 1.1.5, and read by the repository's own launcher at `mill:139`) moves the whole `out` tree, the daemon included, into a scratch directory, so Option B needs no licensed token, cannot poison the real cache, and needs no authorisation. Second: the analysis says the fix "does not alter task inputs, so no cache is invalidated"; the reviewer showed that a changed `viteBuild` body re-runs `viteBuild`, so every developer's next dashboard build after the fix runs `yarn install` + `yarn build` and needs `WEBAWESOME_NPM_TOKEN`. What should intake do with the analysis? Options: a) correct both: take Option B under `MILL_OUTPUT_DIR` as the automated test, and state the one-time rebuild (and its token need) as a cost of the fix, then review the analysis again b) correct the cache claim only, and keep Option A with your authorisation to ship without an automated red-then-green test c) leave the analysis as it is and send the issue to a session Recommended: a) Answer in a comment on this issue; intake reads it on its next iteration.
Author
Owner

Drop it now.

Drop it now.
mph closed this issue 2026-10-10 20:11:14 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
iterative-works/iw-cli#544
No description provided.