Last updated 2026-05-07

Marketplace Actions

The runner can run any marketplace action: composite, Node.js, or Docker. Common setup actions are intercepted with built-in shims so you don't need cloud GitHub Actions infrastructure to use them.

Resolution flow

When a step has uses: owner/repo@ref:

  1. Check the built-in shim table for owner/repo. If present, generate a local composite action.
  2. Check the local cache at ~/.local/share/local-runner/actions-cache/owner/repo/ref/.
  3. If the cache is fresh (< TTL), use it directly.
  4. Otherwise git clone --depth 1 --branch ref from GitHub.
  5. Falls back to git clone --no-checkout --filter=blob:none + git checkout ref for SHA refs.
  6. Removes .git/, writes a freshness marker, returns the path.

Subpath actions (owner/repo/subpath@ref) are supported — the runner clones the repo and returns the subpath within it.

Cache settings

SettingDefaultEnv var
Actions cache directory~/.local/share/local-runner/actions-cache/RUNNER_ACTIONS_CACHE
Cache TTL24 hRUNNER_CACHE_TTL_MS (ms)
Skip cache entirelyoffRUNNER_NO_CACHE=1

Built-in shims

These actions are intercepted and replaced with local equivalents:

ActionShim behaviour
actions/checkoutNo-op. Workspace is already mounted. Supports ref for branch switching.
actions/upload-artifactCopies files to $WORKSPACE/.artifacts/<name>/.
actions/download-artifactCopies files from $WORKSPACE/.artifacts/<name>/.
actions/cacheFilesystem cache at ~/.local/share/local-runner/cache/. Supports key, restore-keys, prefix matching.
actions/setup-pythonDetects Python via pyenv or PATH. Outputs python-version, python-path.
actions/setup-java/usr/libexec/java_home, SDKMAN, Homebrew, system paths, or JAVA_HOME. Sets JAVA_HOME, PATH.
actions/setup-ruby / ruby/setup-rubyrbenv, rvm, or system. Optionally bundle install.
actions/setup-gogoenv, PATH, Homebrew. Sets GOPATH.
actions/setup-dotnetPATH or common locations. Disables telemetry.
actions/setup-gradle / gradle/actions/setup-gradleGradle wrapper, PATH, Homebrew, SDKMAN. Soft-fail.
actions/setup-xcode / maxim-lobanov/setup-xcodexcode-select; supports version selection from /Applications/Xcode_*.app.
subosito/flutter-actionPATH, common locations, or fvm.

Action types

Composite

Recursively executed step-by-step in the same shell environment.

action.yml
runs:
using: composite
steps:
- run: echo "hello"
shell: bash

Node.js

The runner spawns node with the action's main, pre, and post scripts and populates the @actions/core environment (INPUT_*, $GITHUB_OUTPUT, $GITHUB_ENV, $GITHUB_PATH).

runs:
using: node20
main: dist/index.js
pre: dist/pre.js
post: dist/post.js

Docker

The runner builds (or pulls) the image and runs it as a container with the same env contract.

runs:
using: docker
image: docker://alpine:3.19
args: ["echo", "hello"]

Private repos

Set GITHUB_TOKEN or GH_TOKEN in the runner's environment. The runner injects it into clone URLs as x-access-token.

Force re-download

RUNNER_NO_CACHE=1 npm run dev
# or
RUNNER_NO_CACHE=1 stax runner start

Adding your own shim

For a language/tool setup, add to src/actions/shim-definitions.ts:

'owner/action-name': {
name: 'My Tool (local shim)',
description: 'Local runner shim for my-tool',
inputs: {
'version': { description: 'Tool version', default: '' },
},
outputs: {
'version': { description: 'Installed version' },
},
detect: [
{
name: 'PATH',
detect: [
'if command -v my-tool &>/dev/null; then',
' TOOL_FOUND="path"',
'fi',
].join('\n'),
},
],
setup: [
'MY_VER=$(my-tool --version)',
'echo "version=$MY_VER" >> $GITHUB_OUTPUT',
].join('\n'),
notFoundMessage: 'my-tool not found. Install with: brew install my-tool',
},

Then register it in src/actions/resolver.ts:

'owner/action-name': langShim('owner/action-name'),