Skip to content

Action fails on runners where $HOME differs between composite steps #337

Description

@mre

Summary

lychee-action fails with lychee: command not found on self-hosted runners (and any environment) where $HOME resolves to a different path between composite-action steps. The action ends up adding one directory to $GITHUB_PATH and installing the binary into a different one.

Where it goes wrong

In action.yml, $HOME is evaluated independently in two separate bash shells:

- name: Set up environment
  run: |
    echo "$HOME/.local/bin" >> "$GITHUB_PATH"   # ← $HOME #1
    mkdir -p "$HOME/.local/bin"
  shell: bash

# ...later...

- name: Install lychee
  run: |
    install -t "$HOME/.local/bin" -D "${{ steps.lychee-setup.outputs.temp_dir }}/lychee"   # ← $HOME #2
  shell: bash

On ubuntu-latest both $HOMEs resolve to /home/runner, so it works. But on runners where each step gets a fresh ephemeral $HOME (we hit this on an internal self-hosted runner; actions/checkout itself logs Temporarily overriding HOME='/workspace/_temp/<uuid>' post-step), the two paths diverge. PATH points at the old $HOME, install lands in the new one, and entrypoint.sh errors with:

entrypoint.sh: line 49: lychee: command not found
##[error]Process completed with exit code 127.

Repro

Any runner that resets/overrides $HOME between composite-action steps. The clearest sign in logs is:

  • Step "Install lychee" reports success
  • Step "Run Lychee" immediately errors with lychee: command not found
  • Earlier setup step also added a different $HOME/.local/bin to $GITHUB_PATH

Suggested fix

Install into a path that does not depend on $HOME. The natural choice is $RUNNER_TEMP — guaranteed-writable, stable across steps within a job, and exactly what it's for:

- name: Set up environment
  run: |
    echo "$RUNNER_TEMP/lychee/bin" >> "$GITHUB_PATH"
    mkdir -p "$RUNNER_TEMP/lychee/bin"
  shell: bash

…and use $RUNNER_TEMP/lychee/bin in the install step too. This also makes the action robust in containers where $HOME may be unset or read-only.

For comparison, jdx/mise-action is a JS action and uses the Actions toolkit's core.addPath() from inside Node — same intent, but it sidesteps the "$HOME evaluated twice in two shells" pitfall entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions