Skip to content

fix(macos-vm): always run ephemerd fresh runner, not a stale baked copy#96

Merged
ephpm-claude[bot] merged 1 commit into
mainfrom
fix-macos-vm-stale-runner
Jul 11, 2026
Merged

fix(macos-vm): always run ephemerd fresh runner, not a stale baked copy#96
ephpm-claude[bot] merged 1 commit into
mainfrom
fix-macos-vm-stale-runner

Conversation

@ephpm-claude

@ephpm-claude ephpm-claude Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

macOS VM jobs would register a runner and report "ready", but GitHub never assigned them their job — the VM booted, ran ~90s, exited 0, and the job sat queued forever, re-provisioning a 4GB VM every ~10 min. Native macOS jobs were unaffected — the tell that pointed straight at the VM runner.

Root cause

The macOS base image bakes a GHA runner into /Users/admin/actions-runner at provisioning time. ephemerd separately copies the current runner into /Library/ephemerd/runner over SSH, but the per-job setup script only copied it into the run dir when no run.sh already existed — so the baked runner always won. Once the pinned runner version was bumped (GitHub deprecated the old one; its broker returns 403 and refuses the JIT runner), every VM job silently ran the stale, deprecated runner → registered but never dispatched → looped.

This is the same deprecation that PR #93 fixed for the host/native path — it was still hiding in the base image because we never rebuilt it.

Fix

The setup script now always overwrites /Users/admin/actions-runner from the freshly-copied /Library/ephemerd/runner and kills any already-running runner first (e.g. one auto-started from a baked LaunchDaemon), so the fresh runner owns the job's JIT config. The overwrite is gated on the fresh source existing, so the baked fallback is never deleted when ephemerd provided nothing.

Extracted the inline setup script to a testable const with a regression guard (TestMacOSRunnerSetupScript_*) asserting the always-refresh + kill behavior and that the old skip-if-exists guard never returns.

Verification

Diagnosed live: caught the looping php-sdk VM job (#168, current HEAD, not a zombie), confirmed the VM runner path via SSH into the guest, and traced it to the setup script's skip-if-exists guard. Deploying the fix to the runner host and confirming the job completes end-to-end (update to follow).

🤖 Generated with Claude Code

…copy

macOS VM jobs would register a runner, report "ready", but never get
assigned their job — the VM booted, ran ~90s, exited 0, and the job sat
queued forever, re-provisioning a 4GB VM every ~10 min. Native macOS jobs
were unaffected, which was the tell.

Root cause: the base image bakes a GHA runner into /Users/admin/actions-
runner at provisioning time. ephemerd separately copies the *current*
runner into /Library/ephemerd/runner over SSH, but the per-job setup
script only copied it into the run dir when no run.sh already existed —
so the baked runner always won. Once the pinned runner version was bumped
(GitHub deprecated the old one; its broker returns 403 and refuses the
JIT runner), every VM job silently ran the stale, deprecated runner.

Fix: the setup script now always overwrites /Users/admin/actions-runner
from the freshly-copied /Library/ephemerd/runner and kills any already-
running runner (e.g. one auto-started from a baked LaunchDaemon) first,
so the fresh runner owns the job's JIT config. The overwrite is gated on
the fresh source existing so the baked fallback is never deleted when
ephemerd provided nothing.

Extracted the inline setup script to a testable const with a regression
guard (TestMacOSRunnerSetupScript_*) asserting the always-refresh + kill
behavior and that the old skip-if-exists guard never returns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ephpm-claude ephpm-claude Bot merged commit 3a72c98 into main Jul 11, 2026
10 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants