Skip to content

Avoid exporting PATH through GITHUB_ENV #935

Description

@kjanat

setupPath() currently removes entries containing ruby by exporting a complete
replacement PATH/Path through GITHUB_ENV, then adds the selected Ruby paths
separately through GITHUB_PATH:

setup-ruby/common.js

Lines 371 to 393 in 95ef2b0

export function setupPath(newPathEntries) {
const originalPath = process.env[PATH_ENV_VAR].split(path.delimiter)
let cleanPath = originalPath.filter(entry => !/\bruby\b/i.test(entry))
core.group(`Modifying ${PATH_ENV_VAR}`, async () => {
// First remove the conflicting path entries
if (cleanPath.length !== originalPath.length) {
console.log(`Entries removed from ${PATH_ENV_VAR} to avoid conflicts with default Ruby:`)
for (const entry of originalPath) {
if (!cleanPath.includes(entry)) {
console.log(` ${entry}`)
}
}
core.exportVariable(PATH_ENV_VAR, cleanPath.join(path.delimiter))
}
console.log(`Entries added to ${PATH_ENV_VAR} to use selected Ruby:`)
for (const entry of newPathEntries) {
console.log(` ${entry}`)
}
})
core.addPath(newPathEntries.join(path.delimiter))

This appears to work at process-launch time, but it creates two independently
managed sources of PATH state in the Actions runner. In particular, downstream
action log headers show the replacement value from GITHUB_ENV without the Ruby
paths managed through GITHUB_PATH.

Example public job:

https://github.com/kjanat/actions-shells/actions/runs/33935875402/job/101223659801

On macOS, setup-ruby reports:

Entries removed from PATH to avoid conflicts with default Ruby:
  /opt/homebrew/lib/ruby/gems/3.4.0/bin
  /opt/homebrew/opt/ruby@3.4/bin
Entries added to PATH to use selected Ruby:
  /Users/runner/hostedtoolcache/Ruby/3.4.10/arm64/bin

The immediately following action's logged environment contains the cleaned
PATH, but not the selected Ruby directory. Every later logged PATH has the
same shape. Nevertheless, the effective process environment is correct:

runtime:            ruby
command:            /Users/runner/hostedtoolcache/Ruby/3.4.10/arm64/bin/ruby
version:            ruby 3.4.10 ...

Windows exhibits the same split representation: the logged Path omits the
new Ruby/MSYS2 entries while later processes resolve Ruby 3.4.10 correctly.
Linux does not export a replacement PATH in this job because there were no
Ruby-named entries to remove, so it does not exhibit the confusing split.

There is no runtime failure demonstrated here. The concern is that exporting a
snapshot of the complete shared PATH from a reusable setup action is fragile:

  • it mixes a literal job environment value with the runner's separately tracked
    prepend-path state;
  • downstream action logs do not show the effective process PATH;
  • removal from the exported snapshot does not obviously remove matching entries
    already retained by the runner's prepend-path state;
  • behavior depends on runner merge/order and Windows environment-key handling.

Could setup-ruby avoid exporting the complete PATH through GITHUB_ENV and
only prepend the selected Ruby through GITHUB_PATH? Since the selected Ruby is
prepended, it should win command lookup without removing unrelated PATH state.
If removal is required for a case that prepending does not handle, it would be
helpful to document why this split mutation is safe and add an integration test
covering the effective PATH across subsequent action and shell steps on macOS
and Windows.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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