5

My GitHub Actions workflow "runs-on" windows-latest. I want a custom Action which executes a PowerShell (core or legacy is fine) script. I have a parallel action that runs on Linux and MacOS. So, my .github/actions/install-tf-win directory contains this action.yml

name: install_tf_win
description: installs Terraform/TerraGrunt for Windows
runs:
  using: "composite"
  steps:
    - run: install-tf-win\install-tf-win.ps1
      shell: pwsh

The directory also contains install-tf-win.ps1. I have tried all sorts of variations on that run statement. Starting with "&" and without, variations in paths used with forwards and backwards slashes. I originally started with $GITHUB_ACTION_PATH/install-tf-win.ps1 (works for Linux/MacOS), however it seemed that GITHUB_ACTION_PATH was getting evaluated to be an empty string and then there were complaints about /install-tf-win.ps1 not being found. I tried both pwsh and powershell for the shell key value.

The form shown above results in this error:

Run ./.github/actions/install-tf-win
install-tf-win\install-tf-win.ps1: D:\a\_temp\c1d3d7fa-074b-4f90-ade0-799dcebd84ec.ps1:2
Line |
   2 |  install-tf-win\install-tf-win.ps1
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The module 'install-tf-win' could not be loaded. For more information, run 'Import-Module
     | install-tf-win'.

I can obviously code my way around this by just putting the PowerShell statements in a step. But, the documentation suggests this should work. https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/metadata-syntax-for-github-actions#runsstepsshell .

So, how do I get this to work, please?

Kevin Buchs
  • 2,520
  • 4
  • 36
  • 55

1 Answers1

7

The error message implies that you cannot refer to your script with a relative path:

The command is passed to pwsh, PowerShell [Core]'s CLI, via its -Command (-c) parameter, which interprets the install-tf-win part of
install-tf-win\install-tf-win.ps1 as a module name
rather than as a subdirectory in the absence of an actual subdirectory by that name on Windows[1], so the implication is that such a path doesn't exist.

The linked documentation, suggests that you need an absolute path, based on the GITHUB_ACTION_PATH environment variable, which in PowerShell must be referenced as $env:GITHUB_ACTION_PATH (untested):

# ...
    - run: '& $env:GITHUB_ACTION_PATH/install-tf-win/install-tf-win.ps1'
      shell: pwsh

Note:

  • The need to use &, the call operator, which is a syntactic necessity due to the script path containing a(n environment-)variable reference; the same would apply if the path were quoted - see this answer for background information.

  • Since & is a metacharacter in YAML when used unquoted at the start of a value, the entire string is quoted. Single-quoting ('...') is employed in this case, so that YAML doesn't interpret the contents of the string up front.

    • As an aside: The implication of '...'-quoting working (as confirmed by Kevin, the OP) is that when pwsh -c is ultimately called on Windows, the string's content is (properly) double-quoted ("..."), because PowerShell's CLI only recognizes " as having syntactic function for command-line argument parsing. By contrast, a '...'-quoted -c argument would be interpreted as a verbatim string rather than as a command, causing its content to be simply echoed.

[1] How PowerShell interprets a path such as foo\bar.ps1 when executed as a command, as of PowerShell 7.1

Interpreted as a command - both inside a PowerShell session and via the -Command / -c parameter of the PowerShell CLI, as used by GitHub Actions - the form foo\bar.ps1 is ambiguous:

  • It could refer to a module named foo, and its bar.ps1 command (even though module commands don't have .ps1 name extensions).

  • It could refer to a subdirectory foo and file bar.ps1 in it.

    • This applies to Windows only:

      • PowerShell first interprets the path as a file-system path.
      • If the path doesn't exist, it falls back to interpreting it as a module-command reference.
    • On Unix-like platforms - even though PowerShell generally allows you to use \ and / interchangeably as the path separator on all supported platforms - this particular path form is always interpreted as a module reference; in other words: you cannot use this syntax form to invoke a script this way (see below).

    • This surprising inconsistency is discussed in GitHub issue #14307.

There are two ways to avoid this ambiguity, which work on all supported platforms; the following cause PowerShell to unequivocally treat the path as file-system script path:

  • Use / instead of \: foo/bar.ps1

  • Prefix the path with .\ (or ./): .\foo\bar.ps1

(Of course, you could also use a full path.)

Note that when the -File parameter of PowerShell's is used (rather than -Command / -c), this ambiguity doesn't arise, as the argument is then always considered a (potentially relative) file-system path, even on Unix-like platforms, irrespective of whether you use \ or /.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    The form - run: '& $env:GITHUB_ACTION_PATH/install-tf-win/install-tf-win.ps1' shell: pwsh worked. It is important to note that the additional path segment "/install-tf-win/" is not required for the equivalent for Linux or MacOS. – Kevin Buchs Dec 04 '20 at 13:58