Instant MicroVMs/Unikernels for macOS and Linux
linux unikernel unikraft osv freebsd netbsd microvm libkrun

ci: a real GitHub Actions runner — uses: steps execute, not translate master

Any JavaScript or composite action now runs. At plan time the action's action.yml is fetched (host-side, cached, fetcher injectable for offline tests) to learn what the action is; at run time the guest clones it at its ref and executes it under the genuine protocol: INPUT_* from with: and defaults (dashes preserved, as the real runner sets them), GITHUB_ENV / GITHUB_PATH / GITHUB_OUTPUT / GITHUB_STATE command files — heredoc spelling included — whose effects persist into every later step through a durable env file each step sources, run: steps included, because that is GitHub's semantics. steps.<id>.outputs resolve in the guest at run time, where the outputs live. A node runtime and the tool baseline actions assume (unzip, xz — GitHub's runners preship them) provision once per job. Container actions and pre/post hooks are refused visibly; composite if: conditions are not evaluated (stated limits, not silent ones). Three lessons the live run taught, each now a test: - ${{ github.token }} in an env value must never leave a literal or an empty-but-present bearer behind — both were 401s. Values get strict interpolation (unknown expressions vanish; setup-bun's token default is a && || expression), empties resolve to unset, and an injected --secret GITHUB_TOKEN backs github.token when authentication is wanted. - Interpolate's keep-visible fallback re-emitted the marker it scanned for — an infinite loop; replacements now go through a cursor that never rescans output. - @actions/tool-cache shells out to unzip, which slim images lack. Verified on arm64/macOS: the actual oven-sh/setup-bun@v2 executes end to end — bun downloaded and installed by the action's own JavaScript, GITHUB_PATH written, and the next plain run: step finds bun on PATH. Unit tests cover ref parsing, JS and composite translation, input layering, expression semantics in value vs command position, protocol wrapping, and every refusal reason. e2e gains the example, continue- on-error until x86_64 rules.


+189 -18
13 changed files