Releases: redwoodjs/local-ci
Release list
dtu-github-actions@0.17.1
Patch Changes
@redwoodjs/agent-ci@0.17.1
Patch Changes
- 9ec9d45: Refs #378. Give every job private writable
node_modules, keep npm installs private while sharing npm's download cache, and atomically publish completed pnpm, Yarn, and Bun dependency trees as lockfile-keyed snapshots that are copied into each job with copy-on-write when available. - Updated dependencies [9ec9d45]
- dtu-github-actions@0.17.1
dtu-github-actions@0.17.0
Patch Changes
- 37a094e: Harden ephemeral DTU startup and cleanup for nested local runs, including safer host/network resolution and test coverage for loopback routing through the local runner.
- 06fa6e6: Harden the ephemeral DTU control plane: require a cryptographically secure, in-process control token for seed/start-runner/dump endpoints (including trailing-slash routes), fail closed when secure randomness is unavailable, and reject runner log paths that escape the run log root through symlinks. Also remove shell execution from compare handling and update vulnerable dependencies, including the unpatched
decompresstransitive dependency.
@redwoodjs/agent-ci@0.17.0
Minor Changes
-
7a349fd: Add the Rust Agent CI runner implementation for source-checkout parity testing while keeping the published npm package on the TypeScript runner path. From a repository checkout,
AGENT_CI_FORCE_RUST=1 pnpm agent-ci-dev ...builds and runs the Rust binary; published npm installs do not include a native runner yet. The Rust runner now honors--jobsfor concurrent dependency-wave execution, macOS VM execution honorsAGENT_CI_MACOS_VM_CONCURRENCY, nested local runs avoid container-name collisions, smoke benchmarks compare TypeScript and Rust orchestration overhead, shared TypeScript/Rust fixture contracts cover scheduler, event, run-result, Docker socket, and default job-limit parity, pure workflow planning plus reusable workflow expansion and event/result contracts now live inagent-ci-core, the generic job-wave pool and execution-plan adapters live inagent-ci-runtime,--allhas Rust smoke coverage and workflow fan-out, and the Rust implementation is split into core and runtime crates with focused run, DTU, expression, Docker, runner, and macOS VM modules.Native npm platform-package publishing and npm-launcher native opt-in are intentionally deferred until the release workflow builds, stages, and verifies real target binaries in the same artifact-staging style used by
redwoodjs/machinen.
Patch Changes
-
928fb44: Refs #370. Add
agent-ci run --prewarm-through <workflow:job:step-id>andAGENT_CI_PREWARM_THROUGHso a disposable job can warm sharednode_modulesthrough an explicit workflow step before parallel jobs begin. Agent CI now warns with an actionable prewarm command when cold parallel install jobs look likely, including a structureddiagnosticevent in--jsonmode. -
06fa6e6: Harden the ephemeral DTU control plane: require a cryptographically secure, in-process control token for seed/start-runner/dump endpoints (including trailing-slash routes), fail closed when secure randomness is unavailable, and reject runner log paths that escape the run log root through symlinks. Also remove shell execution from compare handling and update vulnerable dependencies, including the unpatched
decompresstransitive dependency. -
c331673: Harden the opt-in Rust runner orchestration for matrix needs, reusable workflow expansion and outputs, partial wave failures, cyclic dependency planning errors, pull request branch filters, detached pause handling, nested ephemeral DTU host/network resolution, cleanup of nested containers attached to Rust job networks, and the expanded Rust smoke parity gate with per-workflow diagnostics, heartbeats, status ledgers, and timeout cleanup.
Refs #367.
-
Updated dependencies [37a094e]
-
Updated dependencies [06fa6e6]
- dtu-github-actions@0.17.0
dtu-github-actions@0.16.2
@redwoodjs/agent-ci@0.16.2
Patch Changes
-
b619fc7: Avoid reusing runner numbers while stable log directories still exist, and clear stale per-run timeline/log artifacts when a runner name is reused, so old
timeline.jsonrecords cannot be merged into a fresh run and reported as a false failure.Refs #341.
-
Updated dependencies [b619fc7]
- dtu-github-actions@0.16.2
dtu-github-actions@0.16.1
dtu-github-actions@0.16.0
Minor Changes
-
c8da13a: Add
agent-ci run --var-file <path|->for loading workflow variables from JSON files or GitHub CLIgh variable list --json name,valueoutput piped on stdin. Explicit--var KEY=VALUEflags override file-provided values.Refs #358.
-
ab075d9: chore: require Node 24 and drop
tsxNode 24 ships native TypeScript stripping as a stable feature, so we no
longer need thetsxruntime to execute.tsfiles. Everytsx foo.ts
invocation in package scripts becomesnode foo.ts.tsxis removed
fromdevDependenciesin every workspace.To make this work with the codebase's existing import convention,
TypeScript is configured to emit.jspaths in built output while
allowing source files to use real.tsextensions:allowImportingTsExtensions: truerewriteRelativeImportExtensions: true
All 72 source files have been mechanically updated: every relative
import that previously saidfrom "./foo.js"now says
from "./foo.ts". The compileddist/output still emits the.js
extension, so consumers see no change.Breaking change: the published packages now declare
engines.node: ">=24". Node 22 is no longer supported.CI: the
tests.ymlworkflow bumps from Node 22 to Node 24. Smoke
workflows that setnode-version: 22are left alone — they are
fixtures exercising specific Node versions viaactions/setup-node,
not our project's runtime.
Patch Changes
-
c20a05b: perf(cli): parallelize the startup git calls
The first thing
agent-ci rundoes is ask git for several pieces of
information: the current branch, the head commit SHA, the changed
files, the remote slug, and (when the tree is dirty) an ephemeral
commit that captures the working-tree state. Each call shelled out to
execSync, blocking the event loop for ~50–200 ms.This change converts each of those helpers to use
execFilevia
promisify, so they return promises.handleWorkflowthen runs them
concurrently withPromise.allinstead of one at a time.Functions converted:
getFirstRemoteUrlandresolveRepoSluginconfig.tscomputeDirtyShainrunner/dirty-sha.tsgetChangedFilesinworkflow/workflow-parser.tsresolveHeadSha,resolveBaseSha, andpersistRunResultin
commands/run.ts
Switching from
execSync(command-string)toexecFile("git", [args])
also removes a shell escaping step on every call — args are passed as
an array, not a single string.Refs #334.
-
50933a6: chore: remove unused runtime dependencies
Three runtime dependencies were declared in
package.jsonfiles but
never imported by any source file in the package:log-updatefrom@redwoodjs/agent-ci(the diff-renderer module
replaced it long ago; only stale code comments remain).jsonc-parserfromdtu-github-actions.yamlfrom@redwoodjs/ts-runner(theclipackage still depends
onyaml; this only drops the unused declaration ints-runner).
Smaller
node_modules, smaller published packages, and one fewer
thing to keep up to date when the upstream releases a new version.
No runtime behaviour change. -
4b07e75: refactor(workflow-parser): split the GitHub Actions expression evaluator
Collapses the eight-parameter context that
resolveExprAtom/
evaluateExprValuewere threading through every recursive call into a single
ExprContextobject, extracts each built-in function (hashFiles,fromJSON,
toJSON,format,contains,startsWith,endsWith,join) into its own
handler, and moves context-variable lookups (runner.*,github.*,matrix.*,
secrets.*,vars.*,inputs.*,steps.*,needs.*,env.*) into
resolveContextRef.expandExpressions's public positional signature is
unchanged.No behavioral changes.
-
24387c7: perf(cli): lazy-load command modules so light commands skip the heavy dependency graph
Extracts the
run,retry/abort, andcleancommands into separate modules
loaded via dynamicimport()fromcli.ts. The dispatcher now only loads what
the invoked command actually needs.Measured impact on
agent-ci --help:- Cold start: 240 ms → 20 ms
- Peak RSS: 88 MB → 42 MB
--helpand unknown commands no longer load dockerode, @grpc/grpc-js,
protobufjs, ssh2, the runner graph, or the workflow parser. Behavior of every
command is unchanged;--help/-hnow exits 0 (previously 1, which was a
quirk of falling through the dispatch chain).Refs #334.
-
d0a8495: refactor(local-job): extract three helpers out of
executeLocalJobexecuteLocalJobhad grown to ~860 lines and scored 73 on the
cognitive-complexity metric — the highest in theclipackage after
the previous round of refactors. Three self-contained blocks of code
inside it have been moved into module-scope helpers:pullContainerImageWithProgress(docker, image, store, containerName)
— the ~100-line Docker pull with per-layer download / extract
progress reporting (direct-container mode).seedRunnerBinaryToHost(docker, hostRunnerSeedDir)— the one-time
extraction of the actions-runner binary from the seed image
(direct-container mode).waitForContainerExit(container, waitPromise, timeoutMs)— the
promise-race that force-stops the container if the runner does not
exit within the timeout.
executeLocalJobis now ~715 lines, cognitive 56. No behaviour
change; the full local smoke suite passes. -
2f2af0d: refactor(local-job): lift the timeline-sync closure to module scope
executeLocalJobhad a ~190-lineupdateStoreFromTimelineclosure
that readtimeline.jsonplus the paused-signal file every 100ms and
updated the RunStateStore. The closure captured six mutablelet
variables defined just above it; fallow's previous report flagged it
as the biggest remaining complexity hotspot (cognitive 70).This change pulls the closure to module scope as two helpers:
syncTimelineToStore(state, ctx)— drives one poll tick. Cognitive
score 22.buildStepsFromTimeline(steps, state)— folds the raw timeline
records into theStepState[]shape the renderer expects. Cognitive
score 45.
Both take an explicit
TimelineSyncStateobject that the polling loop
mutates between ticks, plus a read-onlyTimelineSyncContextwith the
paths, store reference, andonNewPausecallback.Also drops
padW/totalStepsfrom the old closure — they were
computed but never used (legacy padding logic).No behaviour change; the full local smoke suite passes 45/45.
-
6b7802b: chore: relax published
engines.nodeback to>=22#351 bumped the published packages'
engines.nodeto>=24along
with the development-side switch to Node's native TypeScript support.
End users never run our source files, only the compiled
dist/cli.js. That compiled output targets ES2020 and only uses APIs
available on Node 22 (the long-term support release), so the
published requirement was stricter than it needed to be.This change:
- Sets
engines.nodeto>=22in@redwoodjs/agent-ciand
dtu-github-actions. End users on Node 22 stop seeing the
"unsupported engine" warning. - Adds
engines.node: ">=24"to the repo-rootpackage.jsonso
contributors keep getting an explicit signal that the development
scripts (which run.tsfiles directly through Node's native
type-stripping) need Node 24.
No code change.
- Sets
-
8d92c73: refactor(cli/run): split
runCmdandhandleWorkflowinto focused helperspackages/cli/src/commands/run.tshoused two very long orchestrator
functions. Static analysis (fallow health) scored them as the two
highest-complexity functions in the cli package:runCmd— cognitive 91, ~228 lineshandleWorkflow— cognitive 136, ~717 lines
They mixed argument parsing, workflow discovery, matrix expansion,
resource classification, scheduling, wave execution, and final reporting
in a single body, which made each one hard to follow and hard to change.This change pulls clearly bounded steps out into top-level helpers
without changing any observable behaviour:parseRunArgs,parseJobsFlag,parseVarFlag,resolveGithubTokenFlag,
discoverRelevantWorkflows,resolveWorkflowArgPath,finalizeRun—
carved out ofrunCmd.expandJobs,classifyJobsResources,runWaveJobs— carved out of
handleWorkflow. TheExpandedJobtype is lifted to module scope so
the new helpers can take it.
New scores (fallow health):
runCmd: cognitive 9 (was 91)handleWorkflow: cognitive 61 (was 136)parseRunArgs: cognitive 26 (new, replaces the inline arg loop)
No runtime behaviour change; full smoke suite passes.
-
a0d3bb0: chore: unexport helpers that were never imported externally
log-prune.tsandgenerators.tshad six identifiers markedexport
that no other file actually imported:DEFAULT_RETAIN_DAYS,DEFAULT_RETAIN_RUNS,DEFAULT_THROTTLE_MS
(used only insidelog-prune.ts)toContextData,toTemplateTokenMapping
(used only insidegenerators.ts)toContainerTemplateToken
(not used anywhere — wholly dead, removed)
Tightens the public surface so callers can't accidentally rely on
internal helpers, and gets a step closer to a clean dead-code report.
No runtime behaviour change.
@redwoodjs/agent-ci@0.16.1
@redwoodjs/agent-ci@0.16.0
Minor Changes
-
c8da13a: Add
agent-ci run --var-file <path|->for loading workflow variables from JSON files or GitHub CLIgh variable list --json name,valueoutput piped on stdin. Explicit--var KEY=VALUEflags override file-provided values.Refs #358.
-
ab075d9: chore: require Node 24 and drop
tsxNode 24 ships native TypeScript stripping as a stable feature, so we no
longer need thetsxruntime to execute.tsfiles. Everytsx foo.ts
invocation in package scripts becomesnode foo.ts.tsxis removed
fromdevDependenciesin every workspace.To make this work with the codebase's existing import convention,
TypeScript is configured to emit.jspaths in built output while
allowing source files to use real.tsextensions:allowImportingTsExtensions: truerewriteRelativeImportExtensions: true
All 72 source files have been mechanically updated: every relative
import that previously saidfrom "./foo.js"now says
from "./foo.ts". The compileddist/output still emits the.js
extension, so consumers see no change.Breaking change: the published packages now declare
engines.node: ">=24". Node 22 is no longer supported.CI: the
tests.ymlworkflow bumps from Node 22 to Node 24. Smoke
workflows that setnode-version: 22are left alone — they are
fixtures exercising specific Node versions viaactions/setup-node,
not our project's runtime.
Patch Changes
-
c20a05b: perf(cli): parallelize the startup git calls
The first thing
agent-ci rundoes is ask git for several pieces of
information: the current branch, the head commit SHA, the changed
files, the remote slug, and (when the tree is dirty) an ephemeral
commit that captures the working-tree state. Each call shelled out to
execSync, blocking the event loop for ~50–200 ms.This change converts each of those helpers to use
execFilevia
promisify, so they return promises.handleWorkflowthen runs them
concurrently withPromise.allinstead of one at a time.Functions converted:
getFirstRemoteUrlandresolveRepoSluginconfig.tscomputeDirtyShainrunner/dirty-sha.tsgetChangedFilesinworkflow/workflow-parser.tsresolveHeadSha,resolveBaseSha, andpersistRunResultin
commands/run.ts
Switching from
execSync(command-string)toexecFile("git", [args])
also removes a shell escaping step on every call — args are passed as
an array, not a single string.Refs #334.
-
50933a6: chore: remove unused runtime dependencies
Three runtime dependencies were declared in
package.jsonfiles but
never imported by any source file in the package:log-updatefrom@redwoodjs/agent-ci(the diff-renderer module
replaced it long ago; only stale code comments remain).jsonc-parserfromdtu-github-actions.yamlfrom@redwoodjs/ts-runner(theclipackage still depends
onyaml; this only drops the unused declaration ints-runner).
Smaller
node_modules, smaller published packages, and one fewer
thing to keep up to date when the upstream releases a new version.
No runtime behaviour change. -
4b07e75: refactor(workflow-parser): split the GitHub Actions expression evaluator
Collapses the eight-parameter context that
resolveExprAtom/
evaluateExprValuewere threading through every recursive call into a single
ExprContextobject, extracts each built-in function (hashFiles,fromJSON,
toJSON,format,contains,startsWith,endsWith,join) into its own
handler, and moves context-variable lookups (runner.*,github.*,matrix.*,
secrets.*,vars.*,inputs.*,steps.*,needs.*,env.*) into
resolveContextRef.expandExpressions's public positional signature is
unchanged.No behavioral changes.
-
24387c7: perf(cli): lazy-load command modules so light commands skip the heavy dependency graph
Extracts the
run,retry/abort, andcleancommands into separate modules
loaded via dynamicimport()fromcli.ts. The dispatcher now only loads what
the invoked command actually needs.Measured impact on
agent-ci --help:- Cold start: 240 ms → 20 ms
- Peak RSS: 88 MB → 42 MB
--helpand unknown commands no longer load dockerode, @grpc/grpc-js,
protobufjs, ssh2, the runner graph, or the workflow parser. Behavior of every
command is unchanged;--help/-hnow exits 0 (previously 1, which was a
quirk of falling through the dispatch chain).Refs #334.
-
d0a8495: refactor(local-job): extract three helpers out of
executeLocalJobexecuteLocalJobhad grown to ~860 lines and scored 73 on the
cognitive-complexity metric — the highest in theclipackage after
the previous round of refactors. Three self-contained blocks of code
inside it have been moved into module-scope helpers:pullContainerImageWithProgress(docker, image, store, containerName)
— the ~100-line Docker pull with per-layer download / extract
progress reporting (direct-container mode).seedRunnerBinaryToHost(docker, hostRunnerSeedDir)— the one-time
extraction of the actions-runner binary from the seed image
(direct-container mode).waitForContainerExit(container, waitPromise, timeoutMs)— the
promise-race that force-stops the container if the runner does not
exit within the timeout.
executeLocalJobis now ~715 lines, cognitive 56. No behaviour
change; the full local smoke suite passes. -
2f2af0d: refactor(local-job): lift the timeline-sync closure to module scope
executeLocalJobhad a ~190-lineupdateStoreFromTimelineclosure
that readtimeline.jsonplus the paused-signal file every 100ms and
updated the RunStateStore. The closure captured six mutablelet
variables defined just above it; fallow's previous report flagged it
as the biggest remaining complexity hotspot (cognitive 70).This change pulls the closure to module scope as two helpers:
syncTimelineToStore(state, ctx)— drives one poll tick. Cognitive
score 22.buildStepsFromTimeline(steps, state)— folds the raw timeline
records into theStepState[]shape the renderer expects. Cognitive
score 45.
Both take an explicit
TimelineSyncStateobject that the polling loop
mutates between ticks, plus a read-onlyTimelineSyncContextwith the
paths, store reference, andonNewPausecallback.Also drops
padW/totalStepsfrom the old closure — they were
computed but never used (legacy padding logic).No behaviour change; the full local smoke suite passes 45/45.
-
6b7802b: chore: relax published
engines.nodeback to>=22#351 bumped the published packages'
engines.nodeto>=24along
with the development-side switch to Node's native TypeScript support.
End users never run our source files, only the compiled
dist/cli.js. That compiled output targets ES2020 and only uses APIs
available on Node 22 (the long-term support release), so the
published requirement was stricter than it needed to be.This change:
- Sets
engines.nodeto>=22in@redwoodjs/agent-ciand
dtu-github-actions. End users on Node 22 stop seeing the
"unsupported engine" warning. - Adds
engines.node: ">=24"to the repo-rootpackage.jsonso
contributors keep getting an explicit signal that the development
scripts (which run.tsfiles directly through Node's native
type-stripping) need Node 24.
No code change.
- Sets
-
8d92c73: refactor(cli/run): split
runCmdandhandleWorkflowinto focused helperspackages/cli/src/commands/run.tshoused two very long orchestrator
functions. Static analysis (fallow health) scored them as the two
highest-complexity functions in the cli package:runCmd— cognitive 91, ~228 lineshandleWorkflow— cognitive 136, ~717 lines
They mixed argument parsing, workflow discovery, matrix expansion,
resource classification, scheduling, wave execution, and final reporting
in a single body, which made each one hard to follow and hard to change.This change pulls clearly bounded steps out into top-level helpers
without changing any observable behaviour:parseRunArgs,parseJobsFlag,parseVarFlag,resolveGithubTokenFlag,
discoverRelevantWorkflows,resolveWorkflowArgPath,finalizeRun—
carved out ofrunCmd.expandJobs,classifyJobsResources,runWaveJobs— carved out of
handleWorkflow. TheExpandedJobtype is lifted to module scope so
the new helpers can take it.
New scores (fallow health):
runCmd: cognitive 9 (was 91)handleWorkflow: cognitive 61 (was 136)parseRunArgs: cognitive 26 (new, replaces the inline arg loop)
No runtime behaviour change; full smoke suite passes.
-
a0d3bb0: chore: unexport helpers that were never imported externally
log-prune.tsandgenerators.tshad six identifiers markedexport
that no other file actually imported:DEFAULT_RETAIN_DAYS,DEFAULT_RETAIN_RUNS,DEFAULT_THROTTLE_MS
(used only insidelog-prune.ts)toContextData,toTemplateTokenMapping
(used only insidegenerators.ts)toContainerTemplateToken
(not used anywhere — wholly dead, removed)
Tightens the public surface so callers can't accidentally rely on
internal helpers, and gets a step closer to a clean dead-code report.
No runtime behaviour change. -
Updated dependencies [c20a05b]
-
Updated dependencies [50933a6]
-
Updated dependencies [4b...