0

I have a step where I check if there are changes in a specific folder since the last commit (I set isOutput=true because I need this in another stage later as well):

 bash: |
    git checkout origin/$(System.PullRequest.SourceBranch)
    DIFF_VALUE=$(git diff --name-only HEAD..HEAD~1 -- functions/$(directoryName) | wc -l)
    echo Looking for changes in directory: $(directoryName)
    if [[ $DIFF_VALUE -eq 0 ]]
    then
      echo "There are no changes since the previous commit."
      echo "##vso[task.setvariable variable=gitDiff;isOutput=true]false"
    else
      echo "There are changes since the previous commit."
      echo "##vso[task.setvariable variable=gitDiff;isOutput=true]true"
    fi
  name: check_changes
  displayName: Check Changes

Now I want to include some steps that are in file called function.yml, but I only want them to run if there were no changes in that folder since the last commit! Since it is not possible to add condition to a template step, I add the template step with the parameter gitdiffbool (which is actually a string, but that is okay):

- template: ../steps/ci/function.yml
  parameters:
    directoryName: $(directoryName)
    gitdiffbool: $(check_changes.gitDiff)

My function.yml looks like this:

parameters:
  - name: directoryName
    type: string
  - name: gitdiffbool
    type: string

steps:
  - bash: echo "Diff value is ${{ parameters.gitdiffbool }}!"
  - ${{ if eq(parameters.gitdiffbool, 'true') }}:
      - bash: echo "This step and others after this never run!!!!"

When I run this, the first bash inside the function.yml will always run and display the parameter value correctly: Diff value is false! or Diff value is true! according to if I had changes in that particular directory or not. But it can't resolve the same parameter inside the ${{ if ... }}. No matter how I try, it is never able to compare that if it is 'true' or 'false'. (I tried true, false, True, False, 'True', 'False', but no luck)

Why is it able to resolve ${{ parameters.gitdiffbool }} correctly, but not the one ${{ if eq(parameters.gitdiffbool, 'true') }}? If things inside ${{ }} get resolved during compile time, then why is the first bash able to output the correct string? What am I missing here? How can I solve this without needing to add condition to all the tasks in the function.yml file?

1 Answers1

0

Because this only gets a value as the pipeline runs through:

gitdiffbool: $(check_changes.gitDiff)

But this is being checked at compile-time i.e. before the pipeline runs:

- ${{ if eq(parameters.gitdiffbool, 'true') }}:

You can only use the ${{ }} syntax for values which are known before the pipeline starts running. That means parameters and hard-coded variables, not variables which are created/populated by tasks as the pipeline runs.

Vince Bowdren
  • 8,326
  • 3
  • 31
  • 56
  • 1
    But `${{ parameters.gitdiffbool }}` works as intended, which is also inside curly braces, and it is right above the line `${{ if eq(parameters.gitdiffbool, 'true') }}`. What am I missing here? – András Czinege Oct 13 '22 at 21:42
  • 1
    The difference is that at compile-time the overall structure of the pipeline is fixed i.e. which tasks are included and which are left out, which is what the `{{ if }}` is doing. By contrast, the details of task inputs (e.g. the script to be run in that first bash task) don't need to be evaluated until the task is about to be run, by which time the runtime variable exists. – Vince Bowdren Oct 14 '22 at 07:58
  • 1
    I posted an answer on a similar issue here: https://stackoverflow.com/a/72530649/174843. In that case it's about the `{{ each }}` construct rather than `{{ if }}`, but the principle of compile-time evaluation applies to both. I hope it helps. – Vince Bowdren Oct 14 '22 at 08:00
  • 1
    Thank you for your time that you spend answering my question. Does this also mean that the official Microsoft documentation (https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops) does not tell the truth about template expressions then? Because it seems that not all the `${{ }}` expressions are evaluated at compile time, only the ones that specify which tasks to run and maybe the ones where you use it to define variables based on the value inside. – András Czinege Oct 14 '22 at 08:27
  • 1
    Not quite; they are all evaluated at runtime, but the effect of the evaluation is different. In one case it means that the pipeline omits a task; in the other case the evaluation results in a changes from `"Diff value is ${{ parameters.gitdiffbool }}!"` to `"Diff value is $(check_changes.gitDiff)!"` – Vince Bowdren Oct 14 '22 at 09:49
  • 1
    So it means that before the pipeline starts, it replaces `parameters.gitdiffbool` everywhere with `$(check_changes.gitDiff)`, and since `$(check_changes.gitDiff)` as a string in the curly braces with the `if` condition will never be equal to 'true' (or the variable does not exist at that time), the pipeline always omits the task? If not, then can you please walk me through what happens exactly? – András Czinege Oct 14 '22 at 11:27
  • 1
    Yes, that's right – Vince Bowdren Oct 14 '22 at 11:40
  • 1
    One thing that I don't really understand from your comments: What did you mean when your wrote: "they are all evaluated at runtime"? If this was true, then the `if` condition was able to find out whether the things inside `eq()` are equal or not, since `$(check_changes.gitDiff)` is available by the time it gets to that line. But I think I really misinterpret this sentence. – András Czinege Oct 14 '22 at 11:51
  • Sorry, typo. I mean to say they are all evaluated at compile-time. – Vince Bowdren Oct 14 '22 at 12:40
  • Okay then, all clear. Thanks for saving the day for me, as this just drove me crazy. I wish I could upvote, but since this is my first question and only registered recently, I can't. – András Czinege Oct 14 '22 at 13:05