Are Contexts always able to be evaluated in a runner script?

This is likely because of the rapid evolution of GitHub Actions/Workflows, but when referencing many examples/tutorials (and some GitHub Actions docs) on the web, many note that when using environment variables you have to handle expansion using the appropriate syntax for your given shell

For example:

job:
  print-stuff:
    runs-on: *windows/linux, depending on shell usage*
    env:
      some_var: Hello World
    steps:
    - shell: pwsh
      run: echo $Env:some_var
    - shell: cmd
      run: echo %some_var%
    - shell: bash
      run: echo $some_var

This makes sense since the runner is actually processing these lines directly, especially for variables you set within the runner itself and not in the workflow file.

But then, in a couple places, including the official docs, I’ve seen syntax like this:

job:
  print-stuff:
    runs-on: *whatever*
    env:
      some_var: Hello World
    steps:
      run: echo ${{ env.some_var }}

Until I found this in the documentation I was confused as I thought this wasn’t allowed, given all of the statements regarding the necessity of per-shell variable expansion syntax. Perhaps this is a newer feature, since the few examples I’ve found of it are newer?

So basically my question is, is it always OK to resolve environment (or other context) variables at the “workflow level” in a ‘run’ statement using expressions like this? Are there any gotchyas/things to watch out for?

Obviously the context has to be available in a given situation and variables in the ‘env’ context are only there if defined in the workflow or written back using the environment file.

It seems like the answer is yes, but again I’m just a bit unsure now given all of the talk about shell specific variable expansion.

This just seems much easier to do when availability permits since its shell agonistic.

The main thing to keep in mind is that ${{ ... }} is Actions syntax: It has absolutely nothing to do with the shell, and the shell never knows about it. It’s templating, basically: The Actions runner substitutes the ${{ ... }} with whatever the result of the expression between the double curly brackets is to make a script file. That script file is then passed to the shell for the run step. So if the result of the expression contains any characters the shell treats specially (e.g. spaces, quotes, semicolons, …), you may get odd to dangerous results.

When you use environment variables with shell syntax you get whatever processing/escaping your shell of choice does.

Of course you need to be careful with untrusted input either way, and validate or escape it as appropriate.

1 Like

Ok, I understood that the expressions are evaluated at the “Actions level”, being passed to the shell with all substitutions already performed, but thanks for the notes on what to watch for when approaching variables this way as I do see how it could be a bit easier to miss special characters being passed in without the proper escapes.

I’m also just curious about the history of this use given all of the instances of guides implying variable substitution must be applied by the shell in a run action, whereas it sounds like, at least today, its more of a recommendation than a requirement.

Wasn’t sure if it used to be the only option, or is just the way that most prefer, and therefore is spoke about in a way as if it was the method.

The templating syntax has been available since public availability of Actions, I don’t know if there was anything different during private beta.

I guess using shell variables is more common because it’s natural to do it that way if you’re familiar with shell scripting. Just write like you would elsewhere, without switching between shell and Actions template. Also passing variables via env makes it nicely obvious what gets passed in, including in the log.

In the end I think it’s a matter of choosing the tool that best fits your needs. :slightly_smiling_face:

Ah, thanks for the clarification.

Perhaps I was exaggerating how strict the references I’m referring to sounded, but that was still my overall initial impression.

With the “luxury” of being someone just getting started with this, I will be trying to use the env context as much as possible, so that’s nice to hear the logs will be a little more helpful in that case. Though, I’m certainly a bit irked by the fact you can’t set variables in the env path using other env context variables, and instead need to do so using the shell. Hopefully in the the workflow script parser becomes powerful enough to allow for that :sweat_smile:.

1 Like

One addition: The article linked below is a good description of what can go wrong with untrusted input. And yes, it’s easier to do wrong with templating. :sweat_smile:

1 Like

I appreciate the reference.

This does explain why some just avoid using the GitHub expressions when writing run sections altogether. Otherwise I will need to pay close attention to which context variables are used, and where, with what events I trigger a given workflow, and who exactly is interacting with the repository.

I’ll definitely be more careful and not just blindly use ${{ X }} all over the place.

1 Like