Commit Graph

460 Commits

Author SHA1 Message Date
github-actions[bot] e19997e37a chore: version packages (#251)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.7 dtu-github-actions@0.10.7
2026-04-15 10:54:37 +02:00
Peter Pistorius e482875a95 fix: setup-node "Bad credentials" + stale-toolcache hint (#252)
* fix: setup-node "Bad credentials" + stale-toolcache hint (#171, #249)

Route setup-node's versions-manifest fetch through the DTU so it no
longer 401s against real api.github.com, and surface an actionable
hint when a stale /opt/hostedtoolcache bind mount blocks tar extract.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: unit + smoke coverage for setup-node manifest mock

Adds unit tests for rewriteSetupNodeTarball and for the cached tree/blob
route paths, plus a nested-agent-ci smoke workflow that fails if
setup-node logs "Bad credentials" (the symptom of the manifest mock
breaking and falling back to nodejs.org).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:46:11 +02:00
Simon 2114d67739 fix: permission errors in direct-container mode on Arch Linux (#248) 2026-04-15 08:26:24 +02:00
Peter Pistorius bcd5f0e356 docs: make packages/cli/README the main README (symlinked from root) (#247)
* docs: promote packages/cli/README as the main README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: symlink root README to packages/cli/README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: reframe Why section — remote CI is the gatekeeper, agent-ci is the pre-flight check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: note Docker Desktop is also supported on Linux

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:56:51 +02:00
github-actions[bot] 95b748c344 chore: version packages (#246)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.6 dtu-github-actions@0.10.6
2026-04-13 21:57:16 +02:00
Peter Pistorius 64d654d768 fix: auto-pull runner image with progress on first run (#242) (#245)
First-time users saw a frozen spinner or "No such image" error because
the runner image pull was silent and failures were only debug-logged.

Now the CLI detects a missing image, shows download/extraction progress,
and surfaces pull errors visibly.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:55:53 +02:00
github-actions[bot] ccf77636f9 chore: version packages (#244)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.5 dtu-github-actions@0.10.5
2026-04-13 19:58:05 +02:00
Peter Pistorius 9f7bc6feeb fix: include default_branch in event.repository payload (#241) (#243)
Actions like dorny/paths-filter read github.event.repository.default_branch
to determine the base branch. The DTU was already passing this value in
Resources.Repositories but omitting it from the event context.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:10:18 +02:00
Peter Pistorius 2e2bd5e124 fix: always show workflows and jobs in --all mode (#240)
* fix: always show workflows and jobs in --all mode, fix duplicate matrix jobs

Two problems fixed:

1. Jobs would disappear. When running --all, jobs under a workflow
   (like install-a, install-b, install-c) would only show up while
   the workflow was running. Before it started or after it finished,
   they'd vanish. Now they always stay visible.

2. Matrix jobs showed up twice. When a workflow had matrix shards
   (like 3 copies of the same job), the display would show 5 entries
   instead of 3. This happened because each job got two different
   names — one when we reserved its spot, and a different one when
   it actually started running. The second name didn't match the
   first, so the system thought they were different jobs. Now both
   use the same name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: reduce spinner flicker in --all mode with line-diff renderer

Replace log-update's full erase-and-rewrite approach with a line-level
diff renderer that only sends ANSI escape codes for changed lines. For a
30-line tree where only spinner characters change, this skips ~90% of
terminal I/O per frame.

Also make job cleanup non-blocking: convert fs.rmSync to fs.promises.rm
and store.save() to async fs operations so completing runners don't
freeze the event loop (and stall all other spinners).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: unblock event loop during workspace prep to keep spinners smooth

Convert initFakeGitRepo (7 sequential execSync calls) and the
git-archive path in prepareWorkspace to use promisified exec. Each
await yields to the event loop between git process spawns, letting
the render timer fire instead of freezing all spinners for ~200ms.

Also convert the chmod -R 777 call to async exec, and wrap diff-renderer
updates in DEC synchronized output markers so terminals that support it
batch the entire frame into a single repaint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: return Promise from save() and update tests to await it

The async save() was fire-and-forget, so tests checking the file
immediately after save() failed. Return the Promise so callers
(including tests) can await completion when needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 17:59:05 +02:00
github-actions[bot] c61f27d773 chore: version packages (#239)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.4 dtu-github-actions@0.10.4
2026-04-13 13:22:55 +02:00
Peter Pistorius 25c1c5decd fix: Cypress install fails with EACCES on /home/runner/.cache (#238)
* test: add smoke test reproducing Cypress cache permission failure (#234)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: make /home/runner/.cache world-writable so Cypress can install (#234)

The Playwright bind mount causes Docker to create /home/runner/.cache as
root, so Cypress postinstall fails with EACCES when it tries to mkdir
/home/runner/.cache/Cypress. chmod 1777 in the entrypoint fixes it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:21:47 +02:00
github-actions[bot] e8b638f620 chore: version packages (#237)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.3 dtu-github-actions@0.10.3
2026-04-13 12:50:14 +02:00
Peter Pistorius ef2ee43835 Catch any missing tool in runner image hint (#231)
* fix: skip orphan cleanup when running inside a Docker container

When agent-ci runs nested inside a container (e.g. smoke-bun-setup step 8),
it shares the host's Docker socket. killOrphanedContainers() checks
agent-ci.pid labels using process.kill(pid, 0), but host PIDs don't
exist in the container's PID namespace — so every container looks like
an orphan. This caused the nested agent-ci to force-remove its own
parent container (exit 137).

Both pruneOrphanedDockerResources() and killOrphanedContainers() now
bail out when /.dockerenv exists.

Fixes #228

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: ignore inherited AGENT_CI_DTU_HOST inside containers

When agent-ci runs nested inside a container (e.g. smoke-bun-setup),
the outer container's AGENT_CI_DTU_HOST=host.docker.internal is
inherited. The nested agent-ci used this to tell the inner container
where the DTU is, but the nested DTU is inside the outer container,
not on the host. The inner container got "Connection refused" trying
to reach host.docker.internal:PORT.

Now when /.dockerenv exists, AGENT_CI_DTU_HOST is ignored and the
container's own IP (via hostname -I) is used instead, so sibling
containers can reach the DTU.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move /.dockerenv guard to call site so tests still work

The /.dockerenv check inside killOrphanedContainers() broke the
shutdown integration tests — they run inside a Docker container (the
test runner) and need the function to work. Moved the guard to the
session bootstrap in cli.ts where the nested agent-ci problem occurs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add global concurrency limiter across all workflows

In --all mode, each workflow created its own per-wave limiter, allowing
20+ containers to run simultaneously and exhaust available memory (OOM
kills with exit 137). Now a single global limiter (CPU/2) is created in
the orchestrator and shared by all handleWorkflow calls, including
single-job workflows and cold-cache first jobs.

Closes #225

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: factor Docker VM memory into concurrency limit

CPU-based limit alone (cpuCount/2 = 7) still causes OOM kills. Now
reads MemAvailable from /proc/meminfo inside the Docker VM and caps
at floor(available / 3GB per container). Falls back to docker info
with a 4GB reserve if busybox isn't available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: guard orphan cleanup in the function, not just the call site

The /.dockerenv guard was only in cli.ts, but killOrphanedContainers
is also called directly by the Docker integration tests (via a spawned
tsx subprocess). Inside a container, the test's subprocess would see
the parent container's PID label, misidentify it as an orphan, and
kill the container running the tests.

Put the guard back in both functions AND keep the cli.ts guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: increase per-container memory estimate to 4 GB

3 GB per container wasn't enough — tests.yml (vitest + Docker
integration tests) and smoke-binary.yml (full build) both got killed
at 3 concurrent containers. 4 GB gives 2 concurrent containers on a
12 GB Docker VM, with ~5.4 GB each.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: mock fs.existsSync in shutdown unit tests for /.dockerenv guard

The unit tests mock execSync but not fs. Inside a runner container,
the real fs.existsSync("/.dockerenv") returns true, causing
killOrphanedContainers to bail before reaching the mocked execSync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip Docker integration tests for orphan cleanup inside containers

The integration tests spawn a real killOrphanedContainers() subprocess.
Inside a container, the function bails due to /.dockerenv (PID namespace
mismatch), so the tests can't exercise it. Skip them with insideDocker().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add --jobs flag and document concurrency

Adds -j/--jobs <n> to override the auto-detected concurrency limit.
Adds a Concurrency section to the README explaining how the limit is
calculated and how to override it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: catch any missing tool in runner image hint, add Nix recipe and docs

Closes #229

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert "feat: catch any missing tool in runner image hint, add Nix recipe and docs"

This reverts commit ca261639e9.

* feat: generic missing-tool detection with Nix/xz recipe

Replace the hardcoded per-tool pattern list with a single generic
"command not found" regex that catches any missing tool. Add detection
for the Nix installer's "you do not have 'xz' installed" message.
Update hint to point to runner-image.md docs instead of inlining a
Dockerfile snippet. Add Nix recipe to runner-image.md and a runner
image section to the README.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:48:26 +02:00
Peter Pistorius ca4610fb95 fix: evaluate boolean operators in workflow expressions (#236)
* fix: support boolean operators and built-in functions in workflow expressions (#224)

The workflow expression evaluator only understood simple variable lookups
and hashFiles(). Expressions like `!github.event.pull_request && format(...)  || 'plan'`
would silently fail, producing wrong values.

Add support for `&&`, `||`, `!`, parentheses, `format()`, `toJSON()`,
and string literals so these expressions evaluate correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert: drop MAYBE_SUDO from docker socket chmod

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add contains, startsWith, endsWith, join, comparison operators, and type literals

Expand the expression evaluator to cover the remaining GitHub Actions
expression features: string search functions, comparison operators
(==, !=, <, >, <=, >=), status functions, and literal types
(true, false, null, numbers).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: update compatibility table for new expression features

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix formatting in compatibility.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:09:17 +02:00
Peter Pistorius b31fc80ff7 docs: simplify usage to npx @redwoodjs/agent-ci everywhere (#233)
No install step needed. Just run with npx.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@redwoodjs/agent-ci@0.10.2 dtu-github-actions@0.10.2
2026-04-13 11:11:01 +02:00
github-actions[bot] 6f1389732a chore: version packages (#235)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-13 11:10:17 +02:00
Peter Pistorius 37d6125aa0 chore: add missing changeset for #226
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:03:57 +02:00
Peter Pistorius b7363bb7e1 fix: nested agent-ci crashes and global concurrency limiter (#226)
* fix: skip orphan cleanup when running inside a Docker container

When agent-ci runs nested inside a container (e.g. smoke-bun-setup step 8),
it shares the host's Docker socket. killOrphanedContainers() checks
agent-ci.pid labels using process.kill(pid, 0), but host PIDs don't
exist in the container's PID namespace — so every container looks like
an orphan. This caused the nested agent-ci to force-remove its own
parent container (exit 137).

Both pruneOrphanedDockerResources() and killOrphanedContainers() now
bail out when /.dockerenv exists.

Fixes #228

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: ignore inherited AGENT_CI_DTU_HOST inside containers

When agent-ci runs nested inside a container (e.g. smoke-bun-setup),
the outer container's AGENT_CI_DTU_HOST=host.docker.internal is
inherited. The nested agent-ci used this to tell the inner container
where the DTU is, but the nested DTU is inside the outer container,
not on the host. The inner container got "Connection refused" trying
to reach host.docker.internal:PORT.

Now when /.dockerenv exists, AGENT_CI_DTU_HOST is ignored and the
container's own IP (via hostname -I) is used instead, so sibling
containers can reach the DTU.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move /.dockerenv guard to call site so tests still work

The /.dockerenv check inside killOrphanedContainers() broke the
shutdown integration tests — they run inside a Docker container (the
test runner) and need the function to work. Moved the guard to the
session bootstrap in cli.ts where the nested agent-ci problem occurs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add global concurrency limiter across all workflows

In --all mode, each workflow created its own per-wave limiter, allowing
20+ containers to run simultaneously and exhaust available memory (OOM
kills with exit 137). Now a single global limiter (CPU/2) is created in
the orchestrator and shared by all handleWorkflow calls, including
single-job workflows and cold-cache first jobs.

Closes #225

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: factor Docker VM memory into concurrency limit

CPU-based limit alone (cpuCount/2 = 7) still causes OOM kills. Now
reads MemAvailable from /proc/meminfo inside the Docker VM and caps
at floor(available / 3GB per container). Falls back to docker info
with a 4GB reserve if busybox isn't available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: guard orphan cleanup in the function, not just the call site

The /.dockerenv guard was only in cli.ts, but killOrphanedContainers
is also called directly by the Docker integration tests (via a spawned
tsx subprocess). Inside a container, the test's subprocess would see
the parent container's PID label, misidentify it as an orphan, and
kill the container running the tests.

Put the guard back in both functions AND keep the cli.ts guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: increase per-container memory estimate to 4 GB

3 GB per container wasn't enough — tests.yml (vitest + Docker
integration tests) and smoke-binary.yml (full build) both got killed
at 3 concurrent containers. 4 GB gives 2 concurrent containers on a
12 GB Docker VM, with ~5.4 GB each.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: mock fs.existsSync in shutdown unit tests for /.dockerenv guard

The unit tests mock execSync but not fs. Inside a runner container,
the real fs.existsSync("/.dockerenv") returns true, causing
killOrphanedContainers to bail before reaching the mocked execSync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip Docker integration tests for orphan cleanup inside containers

The integration tests spawn a real killOrphanedContainers() subprocess.
Inside a container, the function bails due to /.dockerenv (PID namespace
mismatch), so the tests can't exercise it. Skip them with insideDocker().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add --jobs flag and document concurrency

Adds -j/--jobs <n> to override the auto-detected concurrency limit.
Adds a Concurrency section to the README explaining how the limit is
calculated and how to override it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(website): include install steps in copy agent instructions

The "Copy agent instructions" button and Terminal preview now include
setup steps: installing the package (with package manager detection for
Node.js projects, or npx for non-Node projects) and installing the
agent skill.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 10:23:51 +02:00
Peter Pistorius 3f964c00db docs: unify agent instructions across repo, README, and website (#220)
* docs: unify agent instructions across repo, README, and website

Remove the .agents/ hook system and Machinen-specific CLAUDE.md instructions.
Public-facing docs (README, website) now show generic commands that work with
any agent harness. CLAUDE.md uses the /validate skill directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: mention /validate skill on website

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add Agent Skills spec skill and update docs with `npx skills add`

Ship an agentskills.io-compatible skill at skills/agent-ci/SKILL.md so
users can install with `npx skills add redwoodjs/agent-ci --skill agent-ci`.
Works with Claude Code, Cursor, Codex, and 40+ other agents.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add penberg testimonial, update bebraw quote

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: simplify agent instructions to reference the skill

Instead of repeating raw commands, tell agents to run the `agent-ci` skill
that was installed via `npx skills add`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add AI disclosure to blog posts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: move AI disclosure to top of blog posts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: clarify runner image vs VM image in blog post

Explain that agent-ci uses the ~400 MB official runner binary, not
GitHub's ~30 GB VM image.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: show nav links on mobile across all pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: wrap nav links to second row on mobile

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: show Star us button on mobile

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 18:35:33 +02:00
github-actions[bot] c82f51a3b8 chore: version packages (#223)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.10.1 dtu-github-actions@0.10.1
2026-04-12 15:44:12 +02:00
Peter Pistorius 71a3ebbfb5 fix: exclude test files from published dist (#222)
Test files (*.test.ts) were being compiled into dist/ and included in
the npm package. Add tsconfig exclude to keep them out of the build.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:43:01 +02:00
Peter Pistorius 9d1c0f4031 fix: format markdown files and auto-format after changeset version (#221)
The release workflow failed because changeset-generated CHANGELOGs weren't
formatted. Fix the existing files and run oxfmt after `changeset version`
so future Version Packages PRs include formatted markdown.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@redwoodjs/agent-ci@0.10.0 dtu-github-actions@0.10.0
2026-04-12 15:35:23 +02:00
Peter Pistorius c8c2b6cbcd docs: update compatibility.md to reflect current behavior (#199)
* docs: update compatibility.md to reflect current behavior

- GITHUB_TOKEN: no longer auto-injected, requires --github-token or AGENT_CI_GITHUB_TOKEN
- github.sha: now uses real commit SHA from HEAD, not hardcoded zeros
- secrets: documents env var fallback and --github-token auto-population

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update github.sha to reflect dirty-tree SHA support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 12:32:52 +02:00
github-actions[bot] be2d64e734 chore: version packages (#205)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-12 12:32:26 +02:00
Peter Pistorius 93f0417098 fix: service containers leak on unclean shutdown (#217)
* fix: service containers leak on unclean shutdown

Problem: Service containers (e.g. `agent-ci-*-svc-cache-db`) were created
without the `agent-ci.pid` label, making them invisible to the bootstrap
orphan cleanup (`killOrphanedContainers`). When the parent process died
without hitting its signal handler (crash, SIGKILL, Bash tool timeout),
these containers leaked permanently. Additionally, SIGHUP was not handled,
so closing a tmux pane or SSH disconnect also leaked runner containers.
Containers stuck in `created` state were also missed by the prune logic.

Solution: Label service containers with `agent-ci.pid` at creation time.
Update `killOrphanedContainers` to clean up unlabeled containers (handles
pre-fix containers) and derive the runner name from svc container names
(strip `-svc-*` suffix) with deduplication. Add SIGHUP to signal handlers
in both `cli.ts` and `local-job.ts`. Extend `pruneOrphanedDockerResources`
to also prune `status=created` containers.

Fixes #216

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add Docker integration smoke tests for orphan cleanup

Spawn real Docker containers (unlabeled, dead-PID-labeled, alive-PID-labeled)
and verify killOrphanedContainers handles each case correctly against the
real Docker daemon. Tests are skipped when Docker is unavailable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct import path in orphan cleanup smoke test

The test spawns a subprocess via `npx tsx -e` which imports
shutdown.ts. The path was relative to the repo root but vitest
runs with cwd=packages/cli/, causing MODULE_NOT_FOUND in CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:22:01 +02:00
Peter Pistorius 7a06aec813 fix: setup-bun cache collision (#219)
* test: smoke repro for setup-bun cache collision (#212)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: setup-bun fails in bun projects (#212)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:21:49 +02:00
Peter Pistorius 38994c80cf fix: use XDG cache on Linux Docker Desktop so bind mounts work (#218)
* fix: service containers leak on unclean shutdown

Problem: Service containers (e.g. `agent-ci-*-svc-cache-db`) were created
without the `agent-ci.pid` label, making them invisible to the bootstrap
orphan cleanup (`killOrphanedContainers`). When the parent process died
without hitting its signal handler (crash, SIGKILL, Bash tool timeout),
these containers leaked permanently. Additionally, SIGHUP was not handled,
so closing a tmux pane or SSH disconnect also leaked runner containers.
Containers stuck in `created` state were also missed by the prune logic.

Solution: Label service containers with `agent-ci.pid` at creation time.
Update `killOrphanedContainers` to clean up unlabeled containers (handles
pre-fix containers) and derive the runner name from svc container names
(strip `-svc-*` suffix) with deduplication. Add SIGHUP to signal handlers
in both `cli.ts` and `local-job.ts`. Extend `pruneOrphanedDockerResources`
to also prune `status=created` containers.

Fixes #216

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use XDG cache on Linux Docker Desktop so bind mounts work (#215)

`/tmp` is not in Docker Desktop's default shared-folder list on Linux,
so bind mounts from `/tmp` silently fail. Detect Linux Docker Desktop
via `~/.docker/desktop/docker.sock` and fall back to `$XDG_CACHE_HOME`
(or `~/.cache`). Also update the validate command to run agent-ci in
the background with Monitor for proper pause-on-failure support, and
note Docker Desktop as a supported Linux provider in the README.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove stop hook that ran agent-ci synchronously

The stop hook can't support pause-on-failure since it runs after the
conversation ends. Use `/validate` instead, which runs agent-ci in the
background with Monitor support for fixing and retrying failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:02:35 +02:00
Peter Pistorius 1c7e663327 fix: use /var/run/docker.sock as bind mount when user is not in docker group (#209)
Fixes #209 — on Linux with Docker Desktop active, agent-ci failed at
runner container creation with "mounts denied" when the user isn't in
the docker group.

Splits socketPath (API client, needs R/W) and bindMountPath (container
mount source, only needs path recognition by Docker's VM) as
independent decisions. Whenever /var/run/docker.sock exists on the
host, use it as bindMountPath regardless of our process's R/W access.
Collapses #197 (macOS Desktop symlink) and #209 (Linux Desktop
non-docker-group) into one rule.

The working-directory half of the original #210 report is tracked
separately in #215.

Closes #209

Co-Authored-By: Bastiaan Steinmeier <bastiaan.steinmeier@broekhuis.nl>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:51:36 +02:00
Peter Pistorius cf185857df perf: hoist docker cleanup and image prefetch to session bootstrap (#213)
Global Docker/workspace cleanup and runner-image prefetch now run once
per session instead of once per workflow. Also dedup concurrent
ensureImagePulled calls so parallel workflows share a single in-flight
docker pull. Together these eliminate the N× daemon storm that was
serializing `agent-ci run --all`.

Per-job ensureImagePulled calls remain as a safety net and take the
inspect() fast path since images are already warm.

Fixes #211

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:58:40 +02:00
Peter Pistorius 46c4d1457c fix: drop markdown from lint-staged format check
Auto-generated CHANGELOGs from changesets don't match oxfmt's
markdown formatting, which breaks the release workflow when
changesets/action tries to commit version bumps through husky.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 20:27:56 +02:00
Peter Pistorius 66ac2a4b6b test: smoke repro for missing cc in runner image (#207)
* test: add smoke repro for missing cc in runner image

Minimal smoke workflow that calls `command -v cc` and compiles a
trivial C program. Reproduces a cargo/Rust workflow failure where
`linker 'cc' not found` breaks builds under agent-ci-dev because
ghcr.io/actions/actions-runner:latest does not ship build-essential.

CI-only change (no packages/ code modified) — no changeset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: pluggable runner image via .github/agent-ci.Dockerfile (#208)

agent-ci now discovers a user-provided Dockerfile at
.github/agent-ci.Dockerfile (or .github/agent-ci/Dockerfile for builds
with a COPY context), hashes its contents, builds it locally via
`docker build`, and uses the resulting agent-ci-runner:<hash> tag as
the default runner image. Edits trigger automatic rebuilds; identical
contents reuse the cached image.

This closes the gap where the minimal ghcr.io/actions/actions-runner
image lacks build-essential, python3, and other toolchains that
GitHub's hosted ubuntu-latest VM ships preinstalled. Workflows green
on GitHub but failing locally with `linker 'cc' not found` can now
opt into a richer image by dropping a 5-line Dockerfile into .github/.

Resolution order (highest wins):
  1. per-job `container:` (unchanged)
  2. AGENT_CI_RUNNER_IMAGE env var
  3. .github/agent-ci/Dockerfile (directory form, supports COPY)
  4. .github/agent-ci.Dockerfile (simple form, empty context)
  5. ghcr.io/actions/actions-runner:latest (default)

Also adds an error-hint heuristic: when a step fails with "command not
found" for cc/gcc/make/python3/pkg-config and the user is still on the
default image, the failure summary includes a paste-ready Dockerfile
pointing at the fix. Docs at packages/cli/runner-image.md.

Dogfoods the feature by shipping .github/agent-ci.Dockerfile for this
repo, which makes smoke-cc-compiler.yml (the issue #208 reproduction)
pass end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: announce first-run runner image build

Print a one-time stderr notice before `docker build` fires so users
understand why the first run pauses for ~60s. The message lives in
stderr so log-update can't clobber it and stays in scrollback after.

Docs updated to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: clarify that container: runs verbatim, no Dockerfile layering

Spell out explicitly that a job-level container: directive bypasses
.github/agent-ci.Dockerfile entirely — agent-ci does not layer the
Dockerfile on top. This preserves the "same YAML runs locally and on
GitHub" parity promise.

Also document the two recommended ways to combine a custom base
image with agent-ci's extras.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 20:12:21 +02:00
Peter Pistorius 1a92bbd74d fix: preserve symlink path for Docker socket bind mount on macOS (#197) (#202)
* docs: simplify testing instructions in CLAUDE.md

Use single `npx agent-ci run --all` command instead of separate unit/e2e steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: preserve symlink path for Docker socket bind mount on macOS (#197)

When /var/run/docker.sock is a symlink (Docker Desktop), the resolved
real path was used as the container bind-mount source, causing
"error while creating mount source path" in Docker's VM.

Adds `bindMountPath` to `DockerSocket` (the pre-symlink path) for bind
mounts, while `socketPath` (resolved) continues to be used for the
Docker API client. Adds a regression test for the specific failure case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: suppress passing step output in quiet mode

In --quiet mode, only emit failures and pause messages. Completed
and running step lines (✓/▸) and "Starting runner" headers were
noise on a passing run -- the final printSummary covers everything.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add repro-197 integration test for Docker Desktop socket bind mount

Tests the full chain from filesystem state → socket resolution → container
bind string. Verifies that /var/run/docker.sock (the symlink) is used as
the bind-mount source, never the resolved ~/.docker/run/docker.sock that
causes Docker Desktop's VM to fail with "error while creating mount source path".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 13:41:23 +02:00
github-actions[bot] 33351243cd chore: version packages (#195)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.9.0 dtu-github-actions@0.9.0
2026-04-11 13:10:00 +02:00
Peter Pistorius 1e2714b101 fix: pull runner image before createContainer (#203) (#204)
* docs: simplify testing instructions in CLAUDE.md

Use single `npx agent-ci run --all` command instead of separate unit/e2e steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: pull runner image before createContainer to fix first-run failure

Fixes #203

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 13:08:14 +02:00
dependabot[bot] e20712c39c chore(deps): bump the npm_and_yarn group across 3 directories with 7 updates (#200)
Bumps the npm_and_yarn group with 3 updates in the / directory: [react-server-dom-webpack](https://github.com/facebook/react/tree/HEAD/packages/react-server-dom-webpack), [rwsdk](https://github.com/redwoodjs/sdk/tree/HEAD/sdk) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).
Bumps the npm_and_yarn group with 3 updates in the /apps/website directory: [react-server-dom-webpack](https://github.com/facebook/react/tree/HEAD/packages/react-server-dom-webpack), [rwsdk](https://github.com/redwoodjs/sdk/tree/HEAD/sdk) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).
Bumps the npm_and_yarn group with 3 updates in the /experiments/pkg-cache-bench directory: [react-server-dom-webpack](https://github.com/facebook/react/tree/HEAD/packages/react-server-dom-webpack), [rwsdk](https://github.com/redwoodjs/sdk/tree/HEAD/sdk) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `react-server-dom-webpack` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-server-dom-webpack)

Updates `rwsdk` from 1.0.4 to 1.0.6
- [Release notes](https://github.com/redwoodjs/sdk/releases)
- [Commits](https://github.com/redwoodjs/sdk/commits/v1.0.6/sdk)

Updates `vite` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

Updates `basic-ftp` from 5.2.0 to 5.2.2
- [Release notes](https://github.com/patrickjuchli/basic-ftp/releases)
- [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/patrickjuchli/basic-ftp/compare/v5.2.0...v5.2.2)

Updates `lodash` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `rollup` from 4.57.1 to 4.60.1
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.57.1...v4.60.1)

Updates `react-server-dom-webpack` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-server-dom-webpack)

Updates `rwsdk` from 1.0.4 to 1.0.6
- [Release notes](https://github.com/redwoodjs/sdk/releases)
- [Commits](https://github.com/redwoodjs/sdk/commits/v1.0.6/sdk)

Updates `vite` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

Updates `react-server-dom-webpack` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-server-dom-webpack)

Updates `rwsdk` from 1.0.0-beta.57 to 1.0.6
- [Release notes](https://github.com/redwoodjs/sdk/releases)
- [Commits](https://github.com/redwoodjs/sdk/commits/v1.0.6/sdk)

Updates `vite` from 7.3.1 to 7.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

---
updated-dependencies:
- dependency-name: react-server-dom-webpack
  dependency-version: 19.2.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: rwsdk
  dependency-version: 1.0.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: basic-ftp
  dependency-version: 5.2.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: rollup
  dependency-version: 4.60.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: react-server-dom-webpack
  dependency-version: 19.2.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: rwsdk
  dependency-version: 1.0.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: react-server-dom-webpack
  dependency-version: 19.2.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: rwsdk
  dependency-version: 1.0.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 21:58:22 +02:00
Peter Pistorius b93ecdf303 feat: compute dirty SHA for uncommitted worktrees (#198) (#201)
When no --sha flag is given and the working tree is dirty, compute an
ephemeral commit SHA via git write-tree / commit-tree that represents
the actual code being executed. No refs are moved and history is
untouched.

Closes #198

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 19:52:32 +02:00
Peter Pistorius 68b1d14772 fix: show failure output for paused jobs in multi-job workflows (#196)
The last output lines and retry/abort hints were only displayed in
single-job mode. Multi-job workflows (e.g. build + test) would show
"Step failed attempt #1" with no error context or instructions.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 17:15:44 +02:00
Peter Pistorius 9ff07107ca fix: deduplicate identical failure errors in output summary (#194)
When multiple matrix jobs fail with the same error (e.g. missing secrets),
the FAILURES section now groups them and shows the error once instead of
repeating it for each job. Streaming error messages are also deduplicated.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:59:13 +02:00
Peter Pistorius 2cf4034821 feat: resolve workflow secrets from environment variables (#193)
* feat: resolve workflow secrets from shell environment variables

Shell env vars now act as a fallback for .env.agent-ci when resolving
${{ secrets.FOO }} references. Also auto-populates secrets.GITHUB_TOKEN
from --github-token.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add loadMachineSecrets env fallback tests, update README

Add 7 tests covering process.env fallback behavior in loadMachineSecrets:
file precedence, env-only resolution, key scoping, and merge behavior.
Add a Secrets section to the README documenting resolution order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:54:38 +02:00
github-actions[bot] e958072dfe chore: version packages (#188)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.8.2 dtu-github-actions@0.8.2
2026-04-10 14:21:21 +02:00
Peter Pistorius e42f4a9efe fix: skip inaccessible docker sockets during resolution (#192)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 14:18:24 +02:00
Peter Pistorius cd24a04107 fix: support actions/checkout@v6 by using real HEAD SHA (#189)
Checkout v6 added post-fetch validation that the checked-out ref matches
github.sha. The git shim was returning a fake placeholder SHA (aaaa...)
in dirty workspace mode while github.sha used the real HEAD SHA, causing
the validation to fail. Use realHeadSha consistently everywhere and
remove the now-unnecessary computeFakeSha helper.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:13:42 +02:00
Peter Pistorius 02741dcf0a fix: mount warm node_modules directly at workspace path (#190)
The previous approach symlinked workspace/node_modules → /tmp/node_modules,
which broke esbuild path resolution in pnpm monorepos. Tools like rwsdk's
directive scanner resolved paths through the symlink incorrectly, producing
paths like apps/web/tmp/node_modules/.pnpm/... instead of /tmp/node_modules/.pnpm/...

Mount the warm cache as a Docker bind mount directly at the workspace
node_modules path. This makes the filesystem layout identical to production
GitHub Actions. The original EBUSY concern is already mitigated by the git
shim (blocks git clean) and checkout patching (clean: false).

Adds a smoke test exercising esbuild resolution through pnpm workspace
symlinks to prevent regression.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:13:16 +02:00
Peter Pistorius f7e42f0fb4 fix: clean up runner directory on SIGINT/SIGTERM (#187)
* fix: clean up runner directory on SIGINT/SIGTERM

The signal handler was missing `hostRunnerDir` from its cleanup list,
causing runner directories to leak when the process was interrupted
via Ctrl+C. Moved variable declarations above the handler so it can
reference `hostRunnerDir`, and added it to the cleanup loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: detect and kill orphaned containers via parent-PID liveness

Label every container with `agent-ci.pid=<PID>`. On startup, scan
running agent-ci-* containers and check if their parent PID is still
alive. Dead PID → orphan → kill it along with its sidecars and network.

Also wires up the existing but unused `pruneStaleWorkspaces()` to
remove run directories older than 24 hours.

Closes #186

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:09:01 +02:00
github-actions[bot] 7731f9cbdc chore: version packages (#185)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@redwoodjs/agent-ci@0.8.1 dtu-github-actions@0.8.1
2026-04-10 11:58:18 +02:00
Peter Pistorius 1f24fecc14 fix: make GitHub auth opt-in for remote workflow fetching (#183)
* fix: make GitHub auth opt-in for remote reusable workflow fetching

Remove implicit credential resolution (gh auth token / GITHUB_TOKEN fallback)
that could silently send user credentials. Auth is now opt-in via
--github-token CLI flag or AGENT_CI_GITHUB_TOKEN env var.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add changeset for opt-in GitHub auth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add --github-token flag and AGENT_CI_GITHUB_TOKEN to docs

Update CLI reference tables, environment variables section, and
compatibility matrix to reflect opt-in GitHub auth for remote
reusable workflows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make commit status posting opt-in via --github-token

Gate postCommitStatus() on the githubToken parameter. When no token
is provided, print a warning instead of silently using implicit
gh CLI credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add --commit-status flag to opt in to posting GitHub commit statuses

Commit status posting now requires both --commit-status and
--github-token. Without --commit-status, no warning is printed
and no status is posted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add tests for 403 error hint and public repo fetch without auth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 11:55:26 +02:00
Peter Pistorius d25354e3ca fix: disable ts-runner npm publishing (#182)
Mark @redwoodjs/ts-runner as private and add it to changeset ignore
list. The package was being published to npm but version 0.7.0 is
returning 404 errors, breaking installs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 11:13:43 +02:00
github-actions[bot] b90191b477 chore: version packages (#181)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 10:53:40 +02:00
Peter Pistorius 2820b5a09f fix: remove test script from ts-runner to unblock release workflow (#180)
The ts-runner package had a "test": "vitest run" script but no test
files. Vitest exits with code 1 when it finds nothing to run, which
has been failing the Release workflow since the package was added,
preventing changesets/action from creating release PRs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 10:49:00 +02:00
Peter Pistorius 912ed83465 fix: preserve git-tracked symlinks in workspace snapshot (#179)
* fix: mount warm-modules at /tmp/node_modules for pnpm TypeScript compat

TypeScript resolves @types/* by walking up from realpath(). With pnpm's
.pnpm virtual store symlinked to /tmp/warm-modules, the upward walk never
finds a node_modules directory. Mounting at /tmp/node_modules instead means
TypeScript's walk from /tmp/node_modules/.pnpm/... naturally finds
/tmp/node_modules/@types/.

Closes #176

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: preserve git-tracked symlinks in workspace snapshot

copyWorkspace was using cp/fs.cpSync which dereference symlinks, causing
git mode 120000 entries to be dropped from the workspace commit. Now we
check lstat() before copying and recreate symlinks via symlinkSync.

Closes #178

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add pnpm symlink smoke test reproducing #176 and #178

Adds a committed fixture at .github/fixtures/pnpm-symlinks/ with a
git-tracked symlink (mode 120000) and a TSX file that requires
@types/react resolution through pnpm's .pnpm virtual store.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 10:30:28 +02:00