Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
266 commits
Select commit Hold shift + click to select a range
9bba9ef
[rush-lib] Workspace fingerprint: ignore build state of projects nest…
TheLarkInn Sep 28, 2026
593dbcf
[rush-daemon] Warm set: read status once per maintenance pass, not on…
TheLarkInn Sep 28, 2026
cd7a2a5
[rush-lib] Keep RUSH_DAEMON* variables out of lifecycle script enviro…
TheLarkInn Sep 28, 2026
ba3ff35
[rush-lib] Let daemon engines serve builds with declared daemon-compa…
TheLarkInn Sep 28, 2026
78b29c2
[rush-daemon] Abort a shared iteration's leftover work once no remain…
TheLarkInn Sep 28, 2026
cf3ff01
[rush-lib] Run the initial script for every command outside watch mode
TheLarkInn Sep 28, 2026
889b2b4
[rush-daemon] Share input captures between concurrent requests
TheLarkInn Sep 28, 2026
d5ccd5b
[rush-lib] Skip cache writes when input files change while the inputs…
TheLarkInn Sep 28, 2026
ef51bf7
[rush-daemon] don't let a request received during a batch's reconcile…
TheLarkInn Sep 28, 2026
103bd5e
[rush-daemon] Another request's graph load doesn't spend any finite w…
TheLarkInn Sep 28, 2026
86780e6
[rush-lib] Keep cache trust for unselected retained results and re-ve…
TheLarkInn Sep 28, 2026
f3cc6af
[rush-cli-client] Show engine and plugin warnings in agent output; ke…
TheLarkInn Sep 28, 2026
6a05b01
[node-core-library] LockFile ties, TZ/locale rule, /proc start times …
TheLarkInn Sep 28, 2026
bc5e6c9
[rush-daemon] A request can share an input capture that started after…
TheLarkInn Sep 28, 2026
14fbc54
[rush-lib] Hand requests to in-process Rush when a project's configur…
TheLarkInn Sep 28, 2026
bd7bc49
[rush-lib] With cache writes off, a result whose inputs changed durin…
TheLarkInn Sep 28, 2026
afcb545
[rush-lib] A failed pnpm install releases the package-manager lock
TheLarkInn Sep 28, 2026
ab2fb77
[rush-daemon-transport] rush-lib path handoff, startup reservation, l…
TheLarkInn Sep 28, 2026
c515810
[rush-client-core] Wait for a daemon a live process is still starting…
TheLarkInn Sep 28, 2026
adf57d5
[rush-lib] Reuse workspace input digests and project configurations w…
TheLarkInn Sep 28, 2026
9000351
[rush-cli-client] The daemon's admission and restart-drain reasons in…
TheLarkInn Sep 28, 2026
96a7e49
[rush-cli-client] Report failed operations as they fail in agent output
TheLarkInn Sep 28, 2026
52e592c
[rush-lib] Keep results that a plugin found up to date in long-lived …
TheLarkInn Sep 28, 2026
d2dc437
[rush-lib] Pass getOperationEnvironment to the before/after iteration…
TheLarkInn Sep 28, 2026
9b5b7ea
[rush-lib] Let the daemon run :incremental scripts when a guard allow…
TheLarkInn Sep 28, 2026
060e7ee
[rush-lib] Test that a configuration is read again while only its rig…
TheLarkInn Sep 28, 2026
e671434
[rush-lib] The incremental guard detects an in-place rewrite of an ou…
TheLarkInn Sep 28, 2026
851e3f2
[rush-lib] Tests for the incremental guard's configuration inputs, co…
TheLarkInn Sep 28, 2026
53f3b7e
[rush-daemon] A served rushx script releases the lifecycle gate once …
TheLarkInn Sep 28, 2026
8f52b68
[rush-daemon] A warm-set pass takes the repository lock only when it …
TheLarkInn Sep 28, 2026
3821da0
[node-core-library] A leftover LockFile whose pid now belongs to an o…
TheLarkInn Sep 29, 2026
1d1a217
[credential-cache] A credentials.json that isn't valid JSON no longer…
TheLarkInn Sep 29, 2026
1840241
[rush-daemon] A failed agent-output build returns as soon as nothing …
TheLarkInn Sep 29, 2026
7945a0e
[rush-daemon] A request that meets a removed or changed installation …
TheLarkInn Sep 29, 2026
1f2c904
[rush-daemon] A daemon-served build logs one native telemetry entry p…
TheLarkInn Sep 29, 2026
c97c702
[rush-daemon] A stuck request no longer keeps a stopped daemon alive
TheLarkInn Sep 29, 2026
ce12b8c
[rush-daemon] A hot request reuses the latest input capture that star…
TheLarkInn Sep 29, 2026
d5b3f82
[rush-azure-storage-build-cache-plugin] The Azure build cache picks u…
TheLarkInn Sep 29, 2026
02e9125
[rush-lib] build-cache.json accepts azureBlobStorageConfiguration.sto…
TheLarkInn Sep 29, 2026
3c0d3d9
[rush-daemon] A lingering daemon logs its PID and stop time, without …
TheLarkInn Sep 29, 2026
7c93aea
[rush-cli-client] The client prints a cancelling line at once, agent …
TheLarkInn Sep 29, 2026
1474733
[rush-daemon] Tests for a cancel while continuing work, and for the e…
TheLarkInn Sep 29, 2026
e178316
[rush-client-core] The client relaunches the daemon after a startup h…
TheLarkInn Sep 29, 2026
2ace16d
[rush-client-core] After a daemon crash the client reaps its orphaned…
TheLarkInn Sep 29, 2026
62fc563
[rush-daemon] Test that an expired protected project takes no warm-se…
TheLarkInn Sep 29, 2026
0b97837
[package-deps-hash] A hot request with no file change since the previ…
TheLarkInn Sep 29, 2026
071dc65
[rush-daemon] An invalid build command line fails with native Rush's …
TheLarkInn Sep 29, 2026
4388b98
[rush-daemon] Coalesced requests run with each requester's environmen…
TheLarkInn Sep 29, 2026
0f2d00c
[rush-lib] A build-cache hit no longer restores an earlier failing ru…
TheLarkInn Sep 29, 2026
08ad2ac
[rush-lib] An iteration checks an operation's build-cache inputs only…
TheLarkInn Sep 29, 2026
a3d3e65
[rush-lib] A public beta API lets a plugin runner run :incremental un…
TheLarkInn Sep 29, 2026
8e8621d
[rush-daemon] Answer a usage error only from a session that a build b…
TheLarkInn Sep 29, 2026
f529007
[node-core-library] Test the edges of LockFile's time zone check
TheLarkInn Sep 29, 2026
ef98cd2
[rush-daemon-protocol] Name the variables when a command's environmen…
TheLarkInn Sep 29, 2026
ec6d7b8
[rush-daemon] A restart drain's waiter rechecks its tier
TheLarkInn Sep 29, 2026
1b71530
[rush-daemon] ODSP_TELEMETRY_TAG no longer restarts the daemon
TheLarkInn Sep 29, 2026
c40f21d
[rush-daemon] The daemon's failure summary names the error, not the f…
TheLarkInn Sep 29, 2026
4484f46
[rush-daemon] Check the installation again after a restart's waits
TheLarkInn Sep 29, 2026
fe18240
[rush-lib] A parameter whose short name the tool also defines can be …
TheLarkInn Sep 29, 2026
2f235bc
[rush-azure-storage-build-cache-plugin] Cache Azure credentials per s…
TheLarkInn Sep 29, 2026
c8e43c1
[rush-cli-client] Test the cancellation notice when the client never …
TheLarkInn Sep 29, 2026
4602af1
[rush-client-core] Test that a takeover keeps a reservation that repl…
TheLarkInn Sep 29, 2026
a4b4900
[rush-lib] Type PhasedCommandEngineUsageError's options without Error…
TheLarkInn Sep 29, 2026
6121a82
[rush-cli-client] A request that waits for a daemon restart says why
TheLarkInn Sep 29, 2026
23c3e88
[rush-lib] A warm daemon request parses its command line once
TheLarkInn Sep 29, 2026
810f077
[package-deps-hash] Keep the untracked cache and fsmonitor token when…
TheLarkInn Sep 29, 2026
7396853
[rush-daemon] Test that a usage error goes to in-process Rush while a…
TheLarkInn Sep 29, 2026
d719e2d
[package-deps-hash] Test more RepoStateCache changes
TheLarkInn Sep 29, 2026
26f85ba
[rush-lib] The daemon can keep warm heft run-watch workers for increm…
TheLarkInn Sep 29, 2026
5a1996b
[rush-daemon] The daemon logs each shutdown with its reason, and exit…
TheLarkInn Sep 29, 2026
2f7679e
[rush-client-core] Daemon stop during a startup, and daemon status wi…
TheLarkInn Sep 29, 2026
475fc57
[rush-lib] afterExecuteRequestAsync runs once for each request, with …
TheLarkInn Sep 29, 2026
659f897
[rush-cli-client] Report the output behind warnings given after an op…
TheLarkInn Sep 29, 2026
69cb52b
[rush-daemon] Keep the warm daemon across Claude Code's per-command v…
TheLarkInn Sep 29, 2026
edd2366
[rush-cli-client] Say when a failed operation ran its incremental com…
TheLarkInn Sep 29, 2026
42b35f2
[rush-daemon] Detect in-place output edits and nested output deletes
TheLarkInn Sep 29, 2026
e5c75f1
[rush-cli-client] Say why a command runs in-process, keep the user's …
TheLarkInn Sep 29, 2026
b240ad5
[rush-lib] Task 97: stop printing "An unknown error occurred." for en…
TheLarkInn Sep 29, 2026
e36dd1b
[rush-daemon] After a client leaves a shared iteration, unstarted wor…
TheLarkInn Sep 29, 2026
af084ae
[rush-lib] Test legacy skip records for rebuild and disabled cache
TheLarkInn Sep 29, 2026
c63d36c
[rush-lib] Reuse the state of unchanged projects when taking a new in…
TheLarkInn Sep 29, 2026
24e812e
[rush-daemon] Say which batches write every result before logging
TheLarkInn Sep 29, 2026
526ae83
[rush-daemon-transport] Keep the socket's error when the lockfile's a…
TheLarkInn Sep 29, 2026
18c2d24
[rush-daemon-transport] Settle a backpressured frame send when its so…
TheLarkInn Sep 29, 2026
2ab333f
[rush-client-core] Test how rush-client-core waits for a daemon that …
TheLarkInn Sep 29, 2026
a784605
[rush-cli-client] Say which process rush-client waits for in its agen…
TheLarkInn Sep 29, 2026
b178b3d
[rush-daemon] Escape control characters in restart variable names (ta…
TheLarkInn Sep 29, 2026
26eed2a
[rush-daemon] Test the default shutdown deadline of a daemon process
TheLarkInn Sep 29, 2026
3d631f4
[rush-daemon] Test that a request that needs a restart for its enviro…
TheLarkInn Sep 29, 2026
3f1511d
[rush-azure-storage-build-cache-plugin] Test the storageEndpoint patt…
TheLarkInn Sep 29, 2026
b159b4b
[rush-daemon][rush-cli-client] Test the restart reason's file cap, a …
TheLarkInn Sep 29, 2026
8b63634
[package-deps-hash] Test that a new copy of the Git index keeps no ca…
TheLarkInn Sep 29, 2026
cfefdb2
[rush-cli-client] Test the fallback line for a multi-line reason from…
TheLarkInn Sep 29, 2026
98c5d18
[heft-typescript-plugin] Use the system's own watchFile when TypeScri…
TheLarkInn Sep 29, 2026
5fc8b91
[rush-daemon-transport] Report a reclaim's stopped operations to a ca…
TheLarkInn Sep 29, 2026
cc14840
[rush-client-core] Name a daemon that exited without shutting down af…
TheLarkInn Sep 29, 2026
8123f8c
[rush-client-core] Start a client's launcher-log line on a line of it…
TheLarkInn Sep 29, 2026
6ea793f
[rush-daemon] Keep rushd running, and stopping cleanly, when nothing …
TheLarkInn Sep 29, 2026
8e84350
[rush-daemon] Test that an early result's iteration measure ends at t…
TheLarkInn Sep 29, 2026
1534a71
[rush-lib] Allow "disableBuildCache" on phased commands in command-li…
TheLarkInn Sep 29, 2026
f6f5b4e
[rush-lib] Drain stdout and stderr before exiting on failure
TheLarkInn Sep 29, 2026
5c91d89
[package-deps-hash] Compare the content of Git configuration files in…
TheLarkInn Sep 29, 2026
10ce365
[rush-daemon] Take the inputs snapshot while validating the graph inputs
TheLarkInn Sep 29, 2026
7869af9
[rush-lib] Test that a warm worker survives an iteration that skips i…
TheLarkInn Sep 29, 2026
885e33b
[heft] Never exit with code 0 after an error
TheLarkInn Sep 29, 2026
63365bc
[rush-lib] Fix rush-lib critical path lengths that depended on visit …
TheLarkInn Sep 29, 2026
ed08a0e
[rush-lib] Don't reinstall the package manager because of a stale loc…
TheLarkInn Sep 29, 2026
2fe1ce9
[rush-daemon-transport] Test a send on a connection that already closed
TheLarkInn Sep 29, 2026
8445b52
[rush-lib] Reuse unchanged operation state hashes across iterations
TheLarkInn Sep 29, 2026
35b782a
[rush-cli-client] Skip the restart notice when a wait line already ga…
TheLarkInn Sep 29, 2026
d3faa08
[rush-cli-client] Test that the early daemon check reads "enabled" on…
TheLarkInn Sep 29, 2026
e0eb3c3
[rush-client-core, rush-cli-client] Test the startup cases that n133'…
TheLarkInn Sep 29, 2026
ea74314
[rush-daemon-transport] Stat sockets with { bigint: true }
TheLarkInn Sep 29, 2026
f550183
[rush-lib] Pin total memory and the CPU model in the telemetry test
TheLarkInn Sep 29, 2026
48b1d13
[rush-daemon] Test that a reconciliation uses the inputs snapshot it …
TheLarkInn Sep 29, 2026
8725660
[heft] Test that Heft writes an unreported error before it exits
TheLarkInn Sep 29, 2026
b2aa065
[rush-lib] Give the FlagFile and LastInstallFlag tests separate temp …
TheLarkInn Sep 29, 2026
ac3c53b
[rush-lib] Test that a result restored from the build cache passes an…
TheLarkInn Sep 29, 2026
f2ec4b9
[rush-daemon-transport] Probe a recorded group with kill() before sca…
TheLarkInn Sep 29, 2026
e8a455e
[rush-client-core] Say that another client may be resetting the daemo…
TheLarkInn Sep 29, 2026
aabe56a
[rush-daemon] Return a request's freed memory to the OS once the daem…
TheLarkInn Sep 29, 2026
d462a1e
[rush-daemon] Keep test fixture commits from starting Git maintenance
TheLarkInn Sep 29, 2026
847f530
[heft] Rename the heft change file for the watcher fix
TheLarkInn Sep 29, 2026
3d6848d
[rush-lib] Test that the incremental guard skips files Git ignores
TheLarkInn Sep 29, 2026
01027c3
[rush-daemon-transport] Make OrphanReapOnReclaim.test's operation chi…
TheLarkInn Sep 29, 2026
f0a23b6
[rush-cli-client] Test that pongs keep a long request from counting a…
TheLarkInn Sep 29, 2026
5e396c3
[rush-lib] Queue a watch iteration when an IPC runner requests a run
TheLarkInn Sep 29, 2026
3e7936c
[rush-client-core] Move task 214's new tests so that they merge with …
TheLarkInn Sep 29, 2026
a2d945a
[rush-daemon] Let clients that are still connecting join a shared bui…
TheLarkInn Sep 29, 2026
22e0c0b
[heft-lint-plugin] Fix the ESLint cache for files that have suppresse…
TheLarkInn Sep 29, 2026
636b70c
[rush-client-core] Say what a startup test's reservation pins
TheLarkInn Sep 29, 2026
605ae9f
[rush-daemon] End held test fixture scripts after 2 minutes
TheLarkInn Sep 29, 2026
857fcb2
[rush-daemon] Test unreadable outputs, entry size and identity, and s…
TheLarkInn Sep 29, 2026
363cffc
[rush-cli-client] Test that a wait line for other variables doesn't s…
TheLarkInn Sep 29, 2026
be85a49
[rush-daemon] Serve the phased commands of command-line.json (task 35…
TheLarkInn Sep 29, 2026
01215d6
[rush-lib] Delete consumer skip records after an unverifiable rebuild
TheLarkInn Sep 29, 2026
2abdb18
[rush-cli-client] Cancel a request whose output reader exits
TheLarkInn Sep 29, 2026
268c866
[rush-daemon] A native install or update whose waits span an installa…
TheLarkInn Sep 29, 2026
66eaac4
[rush-client-core] Leave the warm set out of the reply to the livenes…
TheLarkInn Sep 29, 2026
e6e6a92
[rush-daemon] Hash each output folder's file entries in one update (a…
TheLarkInn Sep 29, 2026
f73d9eb
[rush-daemon] Reject a command that the daemon never serves before it…
TheLarkInn Sep 29, 2026
fc21c8d
[rush-client-core] Test that a held daemon keeps its ownership when a…
TheLarkInn Sep 29, 2026
96779ba
[heft-webpack5-plugin] Wait for pending watch events before a flush
TheLarkInn Sep 29, 2026
a873b3c
[rush-lib] Queue a watch iteration for edits made during an iteration
TheLarkInn Sep 29, 2026
c9a97fd
[rush-lib] Check input files for skip detection when the build cache …
TheLarkInn Sep 29, 2026
f00f81f
[rush-cli-client] Tolerate a late daemon answer in the RushX daemon t…
TheLarkInn Sep 29, 2026
4621a2d
[heft-webpack5-plugin] Don't delete assets outside the output folder
TheLarkInn Sep 29, 2026
1fed617
[rush-daemon] Log one line for a reply to a client that went away
TheLarkInn Sep 29, 2026
5c07f6b
[rush-daemon-transport] rush-daemon-transport tests: remove what test…
TheLarkInn Sep 29, 2026
2d57d64
[rush-daemon] Test that a batched iteration announces a shared operat…
TheLarkInn Sep 29, 2026
919fb7a
[rush-lib] Stop a CLI command whose output reader exits, and exit 141
TheLarkInn Sep 29, 2026
5bf3072
[rush-daemon] Log the operations that a starting daemon stops as one …
TheLarkInn Sep 29, 2026
7be5591
[rush-cli-client] Test that the daemon liveness lines go to stderr
TheLarkInn Sep 29, 2026
5a508b0
[package-deps-hash] Detect changes to hidden .gitattributes files in …
TheLarkInn Sep 29, 2026
4734cdc
Update the warm worker notes on input checks and TypeScript
TheLarkInn Sep 29, 2026
b79ba27
[heft-webpack5-plugin] Explain when a relative path is absolute
TheLarkInn Sep 29, 2026
88dbabb
[rush-lib] Test that watch mode finds edits made during an iteration
TheLarkInn Sep 29, 2026
30abc40
[heft-webpack5-plugin] Tests hold the thread pool until the flush waits
TheLarkInn Sep 29, 2026
dbda68d
[heft-rspack-plugin] Wait for pending watch events in the Rspack and …
TheLarkInn Sep 29, 2026
3009e26
[node-core-library] Don't run ps for the lockfile of a process that e…
TheLarkInn Sep 29, 2026
ff92858
[rush-client-core] Run Rush in-process when auto-start cannot open th…
TheLarkInn Sep 29, 2026
2b97550
[rush-daemon] Record each operation's outputs when Rush reports its r…
TheLarkInn Sep 29, 2026
8eceed9
[rush-daemon][rush-lib] Test four behaviors of task 140 that no test …
TheLarkInn Sep 29, 2026
5b89b0c
[rush-daemon] Wait for another Rush process's repository lock within …
TheLarkInn Sep 29, 2026
577bcc1
[rush-sdk] Update the snapshot of rush-lib's exported names
TheLarkInn Sep 29, 2026
0cbc5e3
[rush-client-core] Connect as soon as the startup helper exits
TheLarkInn Sep 29, 2026
3b1e034
[rush-client-core] Connect as soon as another client's startup reserv…
TheLarkInn Sep 29, 2026
ac86d34
[rush-client-core] Poll for daemon readiness every 50 ms at startup
TheLarkInn Sep 29, 2026
cc562a6
[rush-daemon] rush-daemon tests: stop successor daemons after each te…
TheLarkInn Sep 29, 2026
660c20e
[rush-daemon] Test that the resolver rejects a command it never serve…
TheLarkInn Sep 29, 2026
b2cd1ad
[package-deps-hash] Note the sparse checkout cost of hidden attributes
TheLarkInn Sep 29, 2026
b9cdacd
[heft-rspack-plugin] Hold the thread pool in the Rspack and Webpack 4…
TheLarkInn Sep 29, 2026
9f06990
[rush-lib] Add a beforeLogRequest hook for every telemetry entry
TheLarkInn Sep 29, 2026
910736d
[heft] Don't start an extra watch run for a change a run already read
TheLarkInn Sep 29, 2026
52f68f1
[rush-daemon] Say that the client-gone check remembers the error
TheLarkInn Sep 29, 2026
cfca39a
[heft-webpack5-plugin] Name the Node version in a test comment
TheLarkInn Sep 29, 2026
0d930c9
[rush-client-core] Expect "did not answer" in the rush-daemon test of…
TheLarkInn Sep 29, 2026
1ef258f
[rush-lib] Fail a lock-wait test that never stops trying instead of h…
TheLarkInn Sep 29, 2026
56a45d7
[rush-client-core] Stat the socket with bigint in the reset's reclaim
TheLarkInn Sep 29, 2026
170f38d
[rush-daemon-transport] Tie a leaderless operation group to its daemon
TheLarkInn Sep 29, 2026
ce90b75
[rush-lib] [rush-daemon] Say which command sets each differing parameter
TheLarkInn Sep 29, 2026
0164704
[rush-lib] Hash inputs saved near the snapshot in process before Git
TheLarkInn Sep 29, 2026
ea2c828
[rush-daemon] Add tests for the record stage, lock retry delay and re…
TheLarkInn Sep 29, 2026
d861e19
[rush-client-core] Make a startup helper test deterministic
TheLarkInn Sep 29, 2026
8363b87
[heft] Add watch tests for an older mtime and a folder a run didn't read
TheLarkInn Sep 29, 2026
741c33e
[rush-daemon] Start a served script while a reload waits for a build
TheLarkInn Sep 29, 2026
bf39f11
[rush-daemon] Expect no lock holder PID where the platform cannot tel…
TheLarkInn Sep 29, 2026
1adb165
[rush-lib] Run the initial command after a success with warnings that…
TheLarkInn Sep 29, 2026
e151755
[rush-daemon] Test the verdict's client key and installer failures
TheLarkInn Sep 29, 2026
2aed82d
[rush-daemon] Expect only the live-owner message in NativeMutationCle…
TheLarkInn Sep 29, 2026
ba5524b
[rush-cli-client] Give the parser's lock-wait tests their own timeout…
TheLarkInn Sep 29, 2026
f22fb17
[rush-client-core] Wait 1 s, not 5, for an exited daemon to be reaped…
TheLarkInn Sep 29, 2026
76e2417
[rush-lib] Test a blocked verified skip in a later iteration
TheLarkInn Sep 29, 2026
6521def
[rush-lib] Test that a build cache receipt checks the archived files …
TheLarkInn Sep 29, 2026
2e98204
[rush-daemon] Keep the lock holder's command when it exits
TheLarkInn Sep 29, 2026
6ee8041
[rush-client-core] Fix the note on a restarted daemon's descriptors
TheLarkInn Sep 29, 2026
2a34bd5
[rush-daemon-protocol] Send a queued request to a new daemon once if …
TheLarkInn Sep 29, 2026
9f5b485
[rush-lib] Capture input file states only for operations that execute…
TheLarkInn Sep 29, 2026
535f71e
[rush-daemon] Tell a request queued at daemon shutdown that it did no…
TheLarkInn Sep 29, 2026
e6e7743
[rush-client-core] Say which operations a reclaim stopped in its laun…
TheLarkInn Sep 29, 2026
efb5462
[rush-daemon] Keep the daemon after a served install or update that f…
TheLarkInn Sep 29, 2026
0f57874
[rush-lib] Restore build cache outputs through a staging folder
TheLarkInn Sep 29, 2026
fdc4845
[rush-daemon] Name the lock holder to requests behind a reload
TheLarkInn Sep 29, 2026
7ea7e3b
[rush-lib] Let a long-lived operation graph extend its current iterat…
TheLarkInn Sep 29, 2026
1dcaf1e
[rush-daemon] Test that an exclusive request does not join an iteration
TheLarkInn Sep 29, 2026
2cd7cd6
[rush-daemon] Move the request selection helpers into PhasedRequestSe…
TheLarkInn Sep 30, 2026
68f7856
[rush-cli-client] Say resubmitted, not the lock wait, after a restart
TheLarkInn Sep 30, 2026
09cbde9
[rush-daemon] Print the usage of an invalid command line in legacy ou…
TheLarkInn Sep 30, 2026
99b0d90
[rush-daemon] Test that the lock holder's command is sent once it can…
TheLarkInn Sep 30, 2026
c31a081
[rush-daemon-transport] Resend a queued request only when every daemo…
TheLarkInn Sep 30, 2026
258b5d6
[rush-daemon-protocol] Tell a request queued behind a script wait wha…
TheLarkInn Sep 30, 2026
6068efb
[rush-daemon] Test which lock holder a reload names to the requests b…
TheLarkInn Sep 30, 2026
9714ded
[rush-daemon] Test that a flag written again in place counts as a change
TheLarkInn Sep 30, 2026
4f21845
[rush-lib] Add tests for build cache receipt edge cases
TheLarkInn Sep 30, 2026
16c4d2f
[rush-lib] Add tests for build cache restores through a staging folder
TheLarkInn Sep 30, 2026
20ce9f4
[rush-lib] Test that a join leaves an executing operation with the sn…
TheLarkInn Sep 30, 2026
dcdd264
[rush-lib] Test that a join captures input files from the start of th…
TheLarkInn Sep 30, 2026
fd77bcb
[rush-daemon] Stop the output digest pool's idle workers
TheLarkInn Sep 30, 2026
4893044
[rush-lib] Write no receipt while an older write is pending
TheLarkInn Sep 30, 2026
510426d
[rush-lib] Abort an engine creation at its next step
TheLarkInn Sep 30, 2026
e8ec259
[operation-graph] Report VmRSS from /proc/self/status after each warm…
TheLarkInn Sep 30, 2026
0291f9d
[rush-cli-client] Test more ways a lock wait precedes a restart in ag…
TheLarkInn Sep 30, 2026
eabe7eb
[rush-daemon] Test that a request's plugin names must match the warm …
TheLarkInn Sep 30, 2026
cc06a2b
[rush-daemon] Test validateEngineShape with two phases and two plugins
TheLarkInn Sep 30, 2026
4b8a947
[rush-daemon-transport] Record a detached child before it can be orph…
TheLarkInn Sep 30, 2026
016aa46
[rush-daemon] Measure the daemon's resident memory from VmRSS
TheLarkInn Sep 30, 2026
c5cc1e6
[rush-daemon-transport] Test closedAfterReadingAll after abort() and …
TheLarkInn Sep 30, 2026
6b70123
[rush-daemon] Test the digest pool's default idle timeout and dispose
TheLarkInn Sep 30, 2026
8ab4ede
[rush-daemon] Let a request with an explicit wait timeout wait for an…
TheLarkInn Sep 30, 2026
4442188
[rush-daemon] Test unknown names and a warm engine with no plugins
TheLarkInn Sep 30, 2026
d8caa68
[rush-daemon] Test the idle timeout that a digester gives its pool by…
TheLarkInn Sep 30, 2026
5c4db66
[rush-daemon] Test the lifetime defaults of a daemon that owns its pr…
TheLarkInn Sep 30, 2026
4dea9b0
[rush-daemon-transport] Test the spawn mark and own group reads witho…
TheLarkInn Sep 30, 2026
cb17927
[rush-lib] Test two cases of deferred build cache writes
TheLarkInn Sep 30, 2026
0d03ae7
[rush-cli-client] Document the silent stop by a served reload, rebuil…
TheLarkInn Sep 30, 2026
34df84c
[rush-cli-client] Say which operations rushd stops for a served command
TheLarkInn Sep 30, 2026
40ed1fc
[rig-package] Name the package in the missing "-rig" suffix error
TheLarkInn Sep 30, 2026
0e7f4ce
[rush-lib] Quote custom parameter values for the shell that runs a pr…
TheLarkInn Sep 30, 2026
17e7252
[rush-lib] Read a warm worker's input folders before its command starts
TheLarkInn Sep 30, 2026
8da94f7
[rush-daemon] Name the operations that rushd stops to a request that …
TheLarkInn Sep 30, 2026
4502875
[rush-bridge-cache-plugin] Act only on the operations that the comman…
TheLarkInn Sep 30, 2026
bcec363
[rush-daemon] Prepare the next engine in the background
TheLarkInn Sep 30, 2026
b201b2a
[rush-daemon] Count the capture's calls in BackgroundPrepare's tests
TheLarkInn Sep 30, 2026
581c8f1
[rush-daemon] Test that a preparation failure before the reload stops…
TheLarkInn Sep 30, 2026
b55d11a
[rush-daemon] Drop internal references from test names and comments
TheLarkInn Sep 30, 2026
44910f0
[rush-daemon] Keep rush-daemon test socket paths short however long T…
TheLarkInn Sep 30, 2026
d23ccd5
[rush-cli-client] Name `rush-client daemon status` when rushd has not…
TheLarkInn Sep 30, 2026
eea0048
[package-deps-hash] Hash the paths that "git hash-object" can't read
TheLarkInn Sep 30, 2026
832fe12
[package-deps-hash] Resolve a relative RepoStateCache temporary folde…
TheLarkInn Sep 30, 2026
5ff84ef
[rush-daemon] Keep the environment out of failed test assertions (and…
TheLarkInn Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
140 changes: 140 additions & 0 deletions apps/heft/src/cli/HeftCommandLineParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import * as childProcess from 'node:child_process';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';

jest.setTimeout(30_000);

const HEFT_START_PATH: string = path.resolve(__dirname, '../start.js');

// A project with one task. The task sets process.exitCode to FIXTURE_EXIT_CODE in the Heft process, the
// way a test that Jest runs in band can, and then fails if FIXTURE_FAIL is set.
const FIXTURE_FILES: Record<string, string> = {
'package.json': JSON.stringify({ name: 'heft-exit-code-fixture', version: '1.0.0', private: true }),
'heft-plugin.json': JSON.stringify({
taskPlugins: [{ pluginName: 'exit-code-fixture-plugin', entryPoint: './plugin.js' }]
}),
'config/heft.json': JSON.stringify({
phasesByName: {
test: { tasksByName: { fixture: { taskPlugin: { pluginPackage: 'heft-exit-code-fixture' } } } }
}
}),
'plugin.js': `module.exports = class {
apply(taskSession) {
taskSession.hooks.run.tapPromise('exit-code-fixture-plugin', async () => {
process.exitCode = Number(process.env.FIXTURE_EXIT_CODE);
if (process.env.FIXTURE_FAIL) {
throw new Error('The fixture task failed on purpose');
}
});
}
};
`
};

// The same project, but its heft.json declares an alias with the name of the "test" phase's action, so Heft fails
// before any task runs, with an error that nothing has reported yet.
const ALIAS_CLASH_FIXTURE_FILES: Record<string, string> = {
...FIXTURE_FILES,
'config/heft.json': JSON.stringify({
...JSON.parse(FIXTURE_FILES['config/heft.json']),
aliasesByName: { test: { actionName: 'test' } }
})
};

function createFixtureFolder(files: Record<string, string>): string {
const fixtureFolderPath: string = fs.mkdtempSync(path.join(os.tmpdir(), 'heft-exit-code-'));
for (const [relativePath, contents] of Object.entries(files)) {
const filePath: string = path.join(fixtureFolderPath, relativePath);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, contents);
}
return fixtureFolderPath;
}

interface IHeftResult {
exitCode: number | undefined;
output: string;
}

describe('HeftCommandLineParser', () => {
let fixtureFolderPath: string;
let aliasClashFixtureFolderPath: string;

beforeAll(() => {
fixtureFolderPath = createFixtureFolder(FIXTURE_FILES);
aliasClashFixtureFolderPath = createFixtureFolder(ALIAS_CLASH_FIXTURE_FILES);
});

afterAll(() => {
fs.rmSync(fixtureFolderPath, { recursive: true, force: true });
fs.rmSync(aliasClashFixtureFolderPath, { recursive: true, force: true });
});

async function runHeftTestAsync(
taskExitCode: number,
taskFails: boolean,
folderPath: string = fixtureFolderPath
): Promise<IHeftResult> {
const env: NodeJS.ProcessEnv = { ...process.env, FIXTURE_EXIT_CODE: String(taskExitCode) };
delete env.FIXTURE_FAIL;
delete env._RUSH_REPORTER_CHILD_FD;
delete env._RUSH_REPORTER_CHILD_ACK_FD;
if (taskFails) {
env.FIXTURE_FAIL = '1';
}

const child: childProcess.ChildProcess = childProcess.spawn(process.execPath, [HEFT_START_PATH, 'test'], {
cwd: folderPath,
env,
stdio: ['ignore', 'pipe', 'pipe']
});
let output: string = '';
const appendOutput: (chunk: string) => void = (chunk: string) => {
output += chunk;
};
child.stdout?.setEncoding('utf8').on('data', appendOutput);
child.stderr?.setEncoding('utf8').on('data', appendOutput);
const exitCode: number | null = await new Promise((resolve, reject) => {
child.once('error', reject);
child.once('close', resolve);
});

return { exitCode: exitCode ?? undefined, output };
}

it('exits with 1 when a task fails after code in the Heft process set process.exitCode to 0', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, true);
expect(output).toContain('The fixture task failed on purpose');
expect(exitCode).toBe(1);
});

it('exits with 1 when a task fails after code in the Heft process set a negative process.exitCode', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(-1, true);
expect(output).toContain('The fixture task failed on purpose');
expect(exitCode).toBe(1);
});

it('keeps a positive process.exitCode when a task fails', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(3, true);
expect(output).toContain('The fixture task failed on purpose');
expect(exitCode).toBe(3);
});

it('exits with 0 when the task succeeds after code in the Heft process set process.exitCode to 0', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, false);
expect(output).not.toContain('The fixture task failed on purpose');
expect(exitCode).toBe(0);
});

it('writes an error that nothing reported before it exits, and exits with 1', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, false, aliasClashFixtureFolderPath);
expect(output).toContain(
'The alias "test" specified in heft.json cannot be used because an action with that name already exists.'
);
expect(exitCode).toBe(1);
});
});
9 changes: 2 additions & 7 deletions apps/heft/src/cli/HeftCommandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { PhaseAction } from './actions/PhaseAction';
import { RunAction } from './actions/RunAction';
import type { IHeftActionOptions } from './actions/IHeftAction';
import { AliasAction } from './actions/AliasAction';
import { getToolParameterNamesFromArgs } from '../utilities/CliUtilities';
import { getErrorExitCode, getToolParameterNamesFromArgs } from '../utilities/CliUtilities';
import { Constants } from '../utilities/Constants';
import { HeftChildReporter } from '../pluginFramework/logging/HeftChildReporter';

Expand Down Expand Up @@ -271,11 +271,6 @@ export class HeftCommandLineParser extends CommandLineParser {
this.globalTerminal.writeErrorLine(error.stack!);
}

const exitCode: string | number | undefined = process.exitCode;
if (!exitCode || typeof exitCode !== 'number' || exitCode > 0) {
process.exit(exitCode);
} else {
process.exit(1);
}
process.exit(getErrorExitCode(process.exitCode));
}
}
12 changes: 12 additions & 0 deletions apps/heft/src/utilities/CliUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ export function getToolParameterNamesFromArgs(argv: string[] = process.argv): Se
}
return toolParameters;
}

/**
* Get the exit code for a Heft process that is exiting because of an error. This is `exitCode` if it
* is a positive integer, and 1 otherwise, so it is never 0.
*
* @param exitCode - The current value of `process.exitCode`. Code that ran in the Heft process, such as
* a test that Jest ran in band, may have set it to 0.
*/
export function getErrorExitCode(exitCode: string | number | undefined): number {
const numericExitCode: number = Number(exitCode);
return Number.isInteger(numericExitCode) && numericExitCode > 0 ? numericExitCode : 1;
}
171 changes: 164 additions & 7 deletions apps/heft/src/utilities/WatchFileSystemAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,126 @@ interface ITimeEntry {
safeTime: number;
}

/**
* What a run read. The run's watcher checks the changes that it reports against this.
*/
interface IRunInputs {
/**
* When the run started. The watcher reports files whose times are close to this time, or later.
*/
baseline: number | undefined;
/**
* The time of each file that the run read, as the run recorded it
*/
files: ReadonlyMap<string, number>;
/**
* The folders that the run read
*/
contexts: ReadonlyMap<string, number>;
/**
* The times that the previous watcher had when the run started
*/
times: ReadonlyMap<string, ITimeEntry> | undefined;
}

/**
* The fields of watchpack's internal `DirectoryWatcher` that {@link refreshPendingTimes} reads.
* They aren't part of watchpack's public API, so they are all optional.
*/
interface IDirectoryWatcherInternals {
path?: string;
/**
* The names of the files that have an OS event whose `fs.lstat()` hasn't finished yet. The change is
* recorded only when the `fs.lstat()` finishes.
*/
_activeEvents?: Map<string, boolean>;
}

/**
* A watchpack instance with the internal state that {@link refreshPendingTimes} reads
*/
interface IWatchpackWithInternals extends Watchpack {
watcherManager?: {
directoryWatchers?: Map<string, IDirectoryWatcherInternals>;
};
}

const OUTDATED_ON_ATTACH_EXPLANATION: string = 'watch (outdated on attach)';
// A new watcher's first scan reports each file whose mtime is later than the watcher's start time, less the file
// system's accuracy. A watcher that attaches to the watcher of a folder that has already scanned reports the
// folder in the same way. Watchpack's other explanations are for events from the file system.
const SCAN_EXPLANATIONS: ReadonlySet<string> = new Set(['scan (file)', OUTDATED_ON_ATTACH_EXPLANATION]);

function tryLstatSync(filePath: string): fs.Stats | undefined {
try {
return fs.lstatSync(filePath, { throwIfNoEntry: false });
} catch {
return undefined;
}
}

function getTimestamp(stats: fs.Stats): number {
return stats.mtime.getTime() || stats.ctime.getTime() || Date.now();
}

/**
* Watchpack records a file's new time only when its `fs.lstat()` after the file system's event finishes. If a
* run starts before then, the previous watcher's time for the file is from before the change. Read the time
* again, so that the run sees the change, and so that the run's watcher knows that the run saw it.
*/
function refreshPendingTimes(watcher: Watchpack, times: Map<string, ITimeEntry>): void {
const directoryWatchers: Map<string, IDirectoryWatcherInternals> | undefined = (
watcher as IWatchpackWithInternals
).watcherManager?.directoryWatchers;
if (!(directoryWatchers instanceof Map)) {
return;
}

for (const { path: folderPath, _activeEvents: pendingNames } of directoryWatchers.values()) {
if (typeof folderPath !== 'string' || !(pendingNames instanceof Map)) {
continue;
}

for (const name of pendingNames.keys()) {
const filePath: string = path.join(folderPath, name);
const stats: fs.Stats | undefined = tryLstatSync(filePath);
if (!stats) {
times.delete(filePath);
} else if (!stats.isDirectory()) {
const timestamp: number = getTimestamp(stats);
times.set(filePath, { timestamp, safeTime: timestamp });
}
}
}
}

/**
* Returns false if the watcher reported the path only because the path's time is close to the run's start, and
* the run has already read the path as it is now.
*/
function isReportedChangeNew(filePath: string, explanation: string, inputs: IRunInputs): boolean {
if (!SCAN_EXPLANATIONS.has(explanation) || inputs.baseline === undefined) {
return true;
}

const stats: fs.Stats | undefined = tryLstatSync(filePath);
if (!stats) {
return true;
}

if (stats.isDirectory()) {
// The watcher of a folder that the run read reports the folder's files one by one
return explanation !== OUTDATED_ON_ATTACH_EXPLANATION || !inputs.contexts.has(filePath);
}

if (stats.ctimeMs >= inputs.baseline) {
return true;
}

const readTime: number | undefined = inputs.files.get(filePath) ?? inputs.times?.get(filePath)?.timestamp;
return readTime !== getTimestamp(stats);
}

/**
* A filesystem adapter for use with the "fast-glob" package. This adapter tracks file system accesses
* to initialize `watchpack`.
Expand Down Expand Up @@ -261,11 +381,21 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter {
public setBaseline(): void {
this.#lastQueryTime = Date.now();

if (this.#watcher) {
const times: Map<string, { timestamp: number; safeTime: number }> = new Map();
this.#watcher.pause();
this.#watcher.collectTimeInfoEntries(times, times);
const watcher: Watchpack | undefined = this.#watcher;
if (watcher) {
this.#watcher = undefined;
const times: Map<string, ITimeEntry> = new Map();
watcher.collectTimeInfoEntries(times, times);
refreshPendingTimes(watcher, times);
// Close the previous watcher instead of only pausing it. A paused watcher keeps its directory watchers,
// so every run would leak another set of them. A kept watcher also keeps its OS watch on a folder that
// was deleted and recreated, and later watchers on that folder would share the dead watch, so edits in
// the recreated folder would be reported one run late.
watcher.close();
this.#times = times;
} else {
// Nothing is watching, so times collected for an earlier run may be out of date.
this.#times = undefined;
}
}

Expand All @@ -277,11 +407,39 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter {
return;
}

const inputs: IRunInputs = {
baseline: this.#lastQueryTime,
files: this.#files,
contexts: this.#contexts,
times: this.#times
};

const watcher: Watchpack = new Watchpack({
aggregateTimeout: 0,
followSymlinks: false
});

// A file that changed just before the run started is reported again when the watcher starts, although the
// run has read the change. Call onChange only for a change that the run may not have seen.
let hasNewChange: boolean = false;
const onReportedChange = (filePath: string, modifiedTime: number, explanation: string): void => {
hasNewChange ||= isReportedChangeNew(filePath, explanation, inputs);
};
const onReportedRemove = (): void => {
hasNewChange = true;
};
const onAggregated = (): void => {
if (hasNewChange) {
watcher.off('change', onReportedChange);
watcher.off('remove', onReportedRemove);
watcher.off('aggregated', onAggregated);
onChange();
}
};
watcher.on('change', onReportedChange);
watcher.on('remove', onReportedRemove);
watcher.on('aggregated', onAggregated);

this.#watcher = watcher;
watcher.watch({
files: this.#files.keys(),
Expand All @@ -290,12 +448,11 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter {
startTime: this.#lastQueryTime
});

// The watcher checks its reports against `inputs`, so the next run records into new maps
this.#lastFiles = this.#files;
this.#files = new Map();
this.#contexts.clear();
this.#contexts = new Map();
this.#missing.clear();

watcher.once('aggregated', onChange);
}

/**
Expand Down
Loading