1

I have two steps in my github actions that only one of them executes based on an if condition

      - name: step-stable
        uses: some-action@some-version
        if: ${{ env.BRANCH_NAME != 'stable' }}
        with:
          A=1
          B=1
          C=1
          D=1
          E=1
          F=1
          G=1
          H=1
          I=1

      - name: step-other
        uses: some-action@some-version
        if: ${{ env.BRANCH_NAME == 'stable' }}
        with:
          A=2
          B=2
          C=1
          D=1
          E=1
          F=1
          G=1
          H=1
          I=1

This is a simplified version of my real problem.

As you see the below are the same in both cases.

          D=1
          E=1
          F=1
          G=1
          H=1
          I=1

I dont want to repeat them.

I am looking for a way to implement something similar to this to make it look nicer and easier to maintain

This is a pseudo-code and I am trying to communicate what I need

      - name: simplified
        uses: some-action@some-version
        with:
          A=1 if ${{ env.BRANCH_NAME != 'stable' }} else 2
          B=1 if ${{ env.BRANCH_NAME != 'stable' }} else 2
          C=1
          D=1
          E=1
          F=1
          G=1
          H=1
          I=1

How can I do this?

Amin Ba
  • 1,603
  • 1
  • 13
  • 38
  • Your pseudo code might oversimplify this, but you could have a separate step that does all the common things, and then the conditional steps with the things specific to the env variable value. Are there dependencies between the commands? – Benjamin W. Apr 01 '23 at 02:40
  • Oh, nvm, they're inputs to an action. – Benjamin W. Apr 01 '23 at 02:41
  • they are inputs to actions. the dependencies are exactly like above. it is just simplified – Amin Ba Apr 01 '23 at 02:42
  • It's unfortunate, but I don't think there's a way to make a simplified compound action without needing to completely re-implement both actions into a single new action. I have a lot of projects that use the same several actions, following the same patterns, it would be much nicer if I could just specify a single action with minimal inputs. What I end up doing is just implementing the 'simplified' action myself from scratch :/ – sytech Apr 01 '23 at 03:12
  • @sytech Ah, that's a great idea: a composite action that takes input for both cases, and separates them internally to call the action, i.e., a wrapper? – Benjamin W. Apr 01 '23 at 03:52
  • 1
    @BenjaminW. Pretty much, yeah. For example, if the user inputs the name of the workload they want to deploy (like "my-service") I can determine several inputs for other actions, such as the IAM role name to use with `aws-actions/configure-aws-credentials` action because the convention is something like "iam_role_name={workload_name}-deployment-role" -- and so on, for a number of predefined and custom actions that are always used in the same way together. But with the lack of composite actions, all the YAML files are unnecessarily verbose, unless I implement my own version of everything in one. – sytech Apr 01 '23 at 15:30

2 Answers2

2

I don't think expressions let you do this inline, but here's an option: in a separate run step,

  • set all the values like they would be for when BRANCH_NAME isn't stable
  • if BRANCH_NAME is stable, override the values that are different
  • set all values as outputs and reference them when calling the action

Something like this:

- name: Set parameters
  id: params
  run: |
    A=1 B=1 C=1 D=1 E=1 F=1 G=1 H=1 I=1

    if [[ $BRANCH_NAME == 'stable' ]]; then
        A=2
        B=2
    fi

    printf '%s=%s\n' \
        "A=$A" "B=$B" "C=$C" "D=$D" "E=$E "F=$F" "G=$G" "H=$H" "I=$I" \
        >> "GITHUB_OUTPUT"

- name: Call action
  uses: some-action@some-version
  with:
    A: ${{ steps.params.outputs.A }}
    B: ${{ steps.params.outputs.B }}
    C: ${{ steps.params.outputs.C }}
    D: ${{ steps.params.outputs.D }}
    E: ${{ steps.params.outputs.E }}
    F: ${{ steps.params.outputs.F }}
    G: ${{ steps.params.outputs.G }}
    H: ${{ steps.params.outputs.H }}
    I: ${{ steps.params.outputs.I }}
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • thanks - great solution - it helps with maintaining the code but more complex than the original one and still as many lines of code as the original one, even more than the original – Amin Ba Apr 01 '23 at 03:10
  • do you have any solution for this? https://stackoverflow.com/questions/75844035/github-action-how-to-reference-env-variable-in-the-job-and-step-if-condition – Amin Ba Apr 01 '23 at 03:22
2

You can simply use AND && and OR || operators like this:

A: ${{ env.BRANCH == 'stable' && 1 || 2 }}
B: ${{ env.BRANCH == 'stable' && 1 || 2 }}

Here's another similar thread but for inputs: GitHub Actions condition based on inputs

Azeem
  • 11,148
  • 4
  • 27
  • 40
  • This is of course way more elegant and exactly what the question was looking for. – Benjamin W. Apr 01 '23 at 19:31
  • I'm surprised that this works (and it does, I tried it ;)), because coercion rules would imply that you get a boolean value back from an expression like this. Do you know if this is mentioned in the docs somewhere? – Benjamin W. Apr 01 '23 at 19:33
  • what if 1 and 2 are env variables: like `env.one` and `env.two` given that `env.one=1` and `env.two=2` , defined on the workflow level – Amin Ba Apr 02 '23 at 03:16
  • I am wondering what does this mean `${{ env.BRANCH == 'stable' && 1 || 2 }}` Does it mean that `set A as 1 if env.BRANCH == 'stable', otherwise set A as 2`? – Amin Ba Apr 02 '23 at 03:18
  • @BenjaminW.: Yes, it works! No, I haven't seen docs mentioning it like that. It's similar to Bash i.e. `[[ condition ]] && true || false`. The end result should evaluate to valid YAML. – Azeem Apr 02 '23 at 04:02
  • @AminBa: Yes, that should work with the `env` context e.g. `${{ github.ref_name == 'stable' && env.VAR1 || env.VAR2 }}`. For any other context, you should always refer to the [context availability](https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability) first, similar to your [other thread](https://stackoverflow.com/questions/75844035). – Azeem Apr 02 '23 at 04:07
  • 1
    FWIW, I've opened an [issue in the GitHub Docs repo](https://github.com/github/docs/issues/24822) to get this behaviour documented. – Benjamin W. Apr 02 '23 at 19:17
  • @BenjaminW.: Coercion is applied to equality operations i.e. "**GitHub performs loose equality comparisons.**". I didn't get the part where you mix the result of the equality operation itself and then the part after `&&`. Within the context of this question, i.e. comparison between `env.BRANCH` and `stable` shouldn't even involve any kind of coercion as both are strings. – Azeem Apr 03 '23 at 08:07
  • as far as I tested, it is not working. the workflow is not valid `A: ${{ env.BRANCH_NAME == 'stable' && "$HELM_CHART_NAME" || "canary-$HELM_CHART_NAME" }}` `B: ${{ env.BRANCH_NAME == 'stable' && "env-override-values/dev-stable.yaml" || "env-override-values/dev-canary.yaml" }}` – Amin Ba Apr 03 '23 at 13:23
  • @AminBa: What is `HELM_CHART_NAME`? – Azeem Apr 03 '23 at 14:39
  • `env: HELM_CHART_NAME: ${{ github.event.repository.name }} ` It is defined on the workflow level – Amin Ba Apr 03 '23 at 14:47
  • @AminBa: Your issue is the quotes. See [Literals](https://docs.github.com/en/actions/learn-github-actions/expressions#literals) i.e. `string`: **You don't need to enclose strings in `${{` and `}}`. However, if you do, you must use single quotes (`'`) around the string. To use a literal single quote, escape the literal single quote using an additional single quote (`''`). Wrapping with double quotes (`"`) will throw an error.**". – Azeem Apr 03 '23 at 14:47
  • note that github is complaining about `B: ${{ env.BRANCH_NAME == 'stable' && "env-override-values/dev-stable.yaml" || "env-override-values/dev-canary.yaml" }}` as well – Amin Ba Apr 03 '23 at 14:47
  • I first tried without quotes – Amin Ba Apr 03 '23 at 14:48
  • @AminBa: You cannot use `HELM_CHART_NAME` in non-shell context. You need to use `env.HELM_CHART_NAME`. – Azeem Apr 03 '23 at 14:48
  • I first tried `A: ${{ env.BRANCH_NAME == 'stable' && $HELM_CHART_NAME || canary-$HELM_CHART_NAME }}` `B: ${{ env.BRANCH_NAME == 'stable' && env-override-values/dev-stable.yaml || env-override-values/dev-canary.yaml }}` – Amin Ba Apr 03 '23 at 14:49
  • @AminBa: Here's a [sample valid workflow](https://rhysd.github.io/actionlint/#eJx1kFFrwjAUhd/7Kw6lmKe494BCK2UdTB+c28uQkmqcbjWVJK2I+t+XNJ1Ihw8h3Jzv3nNuJN8LhoXQJggqyXCs1M+mrI75eqcP3Ky2QfBdFZoFgLGQuwFVS00dXRe1NDUtuWkHOE0bcdAeAyhkO/9NGCTzeDbJIGSDhqsOgKvZrUBHMZDofMbXzmzrYqjEJndzcL2SG2ozMIjVtkLoe0aRv0OMx4ieXxbZe5Kns4+gl6Xd9YF7zOB87euwizsagWjDi1IQDAatkqWv03ySxfNFPounKS4XkBWXXJ3oX3MfscHtuV+zb+S5nhuxOq0aodRuLWjDy1rop7VoqGeGJ74vSev/CPS5OvAuQft7l7tA/ift/p9RvAz/CYkTkmX4C2WunEM=) with your use case. Please use https://rhysd.github.io/actionlint/ and share the workflow which is not working for you. – Azeem Apr 03 '23 at 14:50
  • all env variables should go after `on: workflow_dispatch` Will try and report here – Amin Ba Apr 03 '23 at 14:53
  • @Azeem Yes, comparing two strings should return a boolean, so the question is what something like `true && 'abc'` should be. Looks like it evaluates to `abc`, but I believe the documentation doesn't reflect that. – Benjamin W. Apr 03 '23 at 15:27
  • `B: ${{ env.BRANCH_NAME == 'stable' && env.HELM_CHART_NAME || format('canary-{0}', env.HELM_CHART_NAME) }}` is not complaining in linting now but it complains about the other one – Amin Ba Apr 03 '23 at 17:52
  • got unexpected character / while lexing expression – Amin Ba Apr 03 '23 at 17:54
  • 1
    it worked. `A: ${{ env.BRANCH_NAME == 'stable' && 'env-override-values/dev-stable.yaml' || 'env-override-values/dev-canary.yaml' }}` `B: ${{ env.BRANCH_NAME == 'stable' && env.HELM_CHART_NAME || format('canary-{0}', env.HELM_CHART_NAME) }}` – Amin Ba Apr 03 '23 at 18:01
  • I have a smilar question but with some variations https://stackoverflow.com/questions/76066748/github-actions-how-to-conditionaly-set-input-variable-to-an-action – Amin Ba Apr 20 '23 at 17:24