Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Clarify how to pass an environmental variable from the command line in a cross-platform manor #1282

Open
NiceGuyIT opened this issue Jul 26, 2023 · 1 comment
Labels
state: needs triage Waiting to be triaged by a maintainer.

Comments

@NiceGuyIT
Copy link

It would be nice if the documentation provided a clearer picture of how to pass environmental variables to Task for all platforms. Linux/macOS can use ENV_VAR='something' task task-name while Windows has to use task task-name VAR='something'.

The docs about variables state the following. This implies that environmental variables can be set from the command line. (See conclusion at the end.)

Since some shells do not support the above syntax to set environment variables (Windows) tasks also accept a similar style when not at the beginning of the command.

Given this taskfile, I would expect ENV_VAR to be set when called on the command line.

tasks:
  get-var:
    vars:
      INTERNAL_VAR:
        sh: |
          echo $ENV_VAR
    cmds:
      - cmd: |
          echo "ENV_VAR='$ENV_VAR'"
          echo "INTERNAL_VAR='{{.INTERNAL_VAR}}'"

If ENV_VAR is set as explained in the docs, the environmental variable is not set.

$ task get-var ENV_VAR="My var"
task: [get-var] echo "ENV_VAR='$ENV_VAR'"
echo "INTERNAL_VAR=''"

ENV_VAR=''
INTERNAL_VAR=''

If we set the environmental variable before calling task, it's set. This doesn't work on Windows and my goal is to support all platforms.

$ ENV_VAR="My var" task get-var
task: [get-var] echo "ENV_VAR='$ENV_VAR'"
echo "INTERNAL_VAR=''"

ENV_VAR='My var'
INTERNAL_VAR=''

The docs state the env: property can set custom environmental variables. Since we need to set the environmental variable ENV_VAR to something, let's set it to the variable ENV_VAR that task gets from the command line. Here's the new task with a new line showing the value of the .ENV_VAR variable.

tasks:
  get-var:
    env:
      ENV_VAR: '{{.ENV_VAR}}'
    vars:
      INTERNAL_VAR:
        sh: |
          echo $ENV_VAR
    cmds:
      - cmd: |
          echo "ENV_VAR='$ENV_VAR'"
          echo "(variable) ENV_VAR='{{.ENV_VAR}}'"
          echo "INTERNAL_VAR='{{.INTERNAL_VAR}}'"

If we set the value on the command line, the ENV_VAR variable is set, which in turn sets the environmental variable ENV_VAR, but that doesn't trickle down into the INTERNAL_VAR variable. After a lot of research, I found this is because variables are expanded before environmental variables.

$ task get-var ENV_VAR="My var"
task: [get-var] echo "ENV_VAR='$ENV_VAR'"
echo "(variable) ENV_VAR='My var'"
echo "INTERNAL_VAR=''"

ENV_VAR='My var'
(variable) ENV_VAR='My var'
INTERNAL_VAR=''

The documentation on environmental variables state the following.

env supports expansion and retrieving output from a shell command just like variables, as you can see in the Variables section.

Using this information we can expand ENV_VAR to the $ENV_VAR environmental variable. That doesn't work because it's not an environmental variable; it's a variable. Let's use the {{.ENV_VAR}} variable to set the $ENV_VAR environmental variable.

tasks:
  get-var:
    env:
      ENV_VAR:
        sh: |
          echo {{.ENV_VAR}}
    vars:
      INTERNAL_VAR:
        sh: |
          echo $ENV_VAR
    cmds:
      - cmd: |
          echo "ENV_VAR='$ENV_VAR'"
          echo "(variable) ENV_VAR='{{.ENV_VAR}}'"
          echo "INTERNAL_VAR='{{.INTERNAL_VAR}}'"

This still doesn't work because it seems variables are expanded before environmental variables. This makes it hard to set an environmental variable from the command line on Windows.

task get-var ENV_VAR="My var"
task: [get-var] echo "ENV_VAR='$ENV_VAR'"
echo "(variable) ENV_VAR='My var'"
echo "INTERNAL_VAR=''"

ENV_VAR='My var'
(variable) ENV_VAR='My var'
INTERNAL_VAR=''

After some more research, I realized the best option is to check for both the environmental variable $ENV_VAR and the Task variable {{.ENV_VAR}} to set the internal variable INTERNAL_VAR.

tasks:
  get-var:
    env:
      ENV_VAR:
        sh: |
          echo {{.ENV_VAR}}
    vars:
      INTERNAL_VAR:
        sh: |
          if [ ! -z "$ENV_VAR" ]; then
            echo "${ENV_VAR}"
          elif [ ! -z "{{.ENV_VAR}}" ]; then
            echo "{{.ENV_VAR}}"
          else
            echo "ENV_VAR is not set"
          fi
    cmds:
      - cmd: |
          echo "ENV_VAR='$ENV_VAR'"
          echo "(variable) ENV_VAR='{{.ENV_VAR}}'"
          echo "INTERNAL_VAR='{{.INTERNAL_VAR}}'"

Can you add the following to the docs?

To consistently get the environmental variable on all systems, the environmental variable needs to be expanded, either by variable expansion or shell expansion.

tasks:
  get-var:
    env:
      # Variable expansion
      ENV_VAR1: '{{.ENV_VAR1}}'
      # Shell expansion
      ENV_VAR2:
        sh: |
          echo {{.ENV_VAR2}}
    cmds:
      - cmd: |
          echo "ENV_VAR1='$ENV_VAR1'"
          echo "ENV_VAR2='$ENV_VAR2'"

To use an environmental variable in a regular variable, check for both the environmental variable and regular variable, returning the approprate value. (Prioritize as necessary.)

tasks:
  get-var:
    vars:
      INTERNAL_VAR:
        sh: |
          if [ ! -z "$ENV_VAR" ]; then
            # ENV_VAR="some var" task get-var
            echo "${ENV_VAR}"
          elif [ ! -z "{{.ENV_VAR}}" ]; then
            # task get-var ENV_VAR="some var"
            echo "{{.ENV_VAR}}"
          else
            echo "ENV_VAR is not set"
          fi
    cmds:
      - cmd: |
          echo "(variable) ENV_VAR='{{.ENV_VAR}}'"
          echo "INTERNAL_VAR='{{.INTERNAL_VAR}}'"

Both tasks produce the same output regardless if task is called with the environmental variables set (Linux/macOS):
ENV_VAR="some var" task get-var

or if task sets the "environmental" variables (Windows):
task get-var ENV_VAR="some var"


"environmental" is in quotes because the snippet at the beginning is in the Variables section implying that task task-name VAR='something' only sets variables, not environmental variables, even though it talks about setting environmental variables. Hence this write up to improve the docs.

Note: Similar to issue #483: Inconsistent behavior when passing vars from CLI in different ways.

@task-bot task-bot added the state: needs triage Waiting to be triaged by a maintainer. label Jul 26, 2023
@NiceGuyIT
Copy link
Author

I discovered the above only works for task (local) variables, not global variables. I found the order of expansion in the docs for version 3:

  1. Environment variables
  2. Global + CLI variables
  3. Call variables
  4. Task variables

This will not work as expected.

---
version: "3"

env:
  GLOBAL_ENV_VAR: '{{.GLOBAL_ENV_VAR}}'

vars:
  GLOBAL_VAR:
    sh: "echo global variable: {{.GLOBAL_ENV_VAR}}"

tasks:
  dynamic-var:
    desc: Test environmental variable expansion in dynamic variables
    env:
      LOCAL_ENV_VAR: '{{.LOCAL_ENV_VAR}}'
    vars:
      LOCAL_VAR:
        sh: "echo local variable: {{.LOCAL_ENV_VAR}}"
    cmds:
      - cmd: |
          echo "LOCAL_VAR value is: '{{.LOCAL_VAR}}'"
          echo "GLOBAL_VAR value is: '{{.GLOBAL_VAR}}'"

I would have expected GLOBAL_VAR to contain EXTERNAL global var just like the LOCAL_VAR.

$ task --verbose GLOBAL_ENV_VAR="EXTERNAL global var" LOCAL_ENV_VAR="EXTERNAL local var" dynamic-var
task: dynamic variable: "echo global variable: " result: "global variable:"
task: dynamic variable: "echo local variable: EXTERNAL local var" result: "local variable: EXTERNAL local var"
task: "dynamic-var" started
task: [dynamic-var] echo "LOCAL_VAR value is: 'local variable: EXTERNAL local var'"
echo "GLOBAL_VAR value is: 'global variable:'"

LOCAL_VAR value is: 'local variable: EXTERNAL local var'
GLOBAL_VAR value is: 'global variable:'
task: "dynamic-var" finished

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state: needs triage Waiting to be triaged by a maintainer.
Projects
None yet
Development

No branches or pull requests

2 participants