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

YAML - Support conditions for templates #1749

Closed
CoolDadTx opened this issue Aug 7, 2018 · 36 comments
Closed

YAML - Support conditions for templates #1749

CoolDadTx opened this issue Aug 7, 2018 · 36 comments

Comments

@CoolDadTx
Copy link

Have you tried trouble shooting?

Queuing a build generates an error. Unexpected value 'condition'.

Agent Version and Platform

Version of your agent? 2.102.0/2.100.1/...

OS of the machine running the agent? Windows

VSTS Type and Version

VSTS

If VisualStudio.com, what is your account name? https://fsmb.visualstudio.com

What's not working?

I am trying to set up a YAML build. We have a lot of builds and our existing definitions use task groups. I am moving the task group logic into a template so they can be shared across build definitions. The templates are stored in a separate repository. All this is working correctly.

There is one step that shouldn't execute when it is a PR build so I tried to do this.

- template: sharedstep.yml@templates
  parameters:
     value: true
  condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))

The build won't queue because of the error - Unexpected value 'condition'.

It would be nice if templates could be conditional like tasks are. Right now the workaround would be to either use a parameter or specify the condition inside the template but the template may not always know the correct condition to use.

Agent and Worker's Diagnostic Logs

It is not queuing so there is no log.

Related Repositories

Please ensure you are logging issues to the correct repository in order to get the best support.

  • Tasks Repository - contains all of the inbox tasks we ship with VSTS/TFS. If you are having issues with tasks in Build/Release jobs (e.g. unreasonable task failure) please log an issue here.
  • Hosted Agent Image Repository - contains the VM image used in the VSTS Hosted Agent Pool. If you are having Build/Release failures that seems like they are related to software installed on the Hosted Agent (e.g. the DotnetSDK is missing or the AzureSDK is not on the latest version) please log an issue here.

If you are hitting a generic issue about VSTS/TFS, please report it to the Developer Community

@ericsciple
Copy link
Contributor

ericsciple commented Aug 10, 2018

this is supported:

- ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
  - template: sharedstep.yml@templates
    parameters:
       value: true

@CoolDadTx
Copy link
Author

Yes I agree a template would work but the enhancement request is that I be able to use a condition like I can for every other item I can put into a step. Conditions are far easier to write and read then template expressions and the overly complex syntax you have to use to avoid errors.

@carct
Copy link

carct commented Sep 26, 2018

i seem to be experiencing some issues with this kind of conditions in yaml`s

child yaml:

  • ${{ if or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(parameters.updateArmTemplate, 'true')) }}:
    • powershell: |
      blabla-my-ps-code

parent yaml:

  • template: "Pipelines\.vsts-arm-pipeline-template.yml"
    parameters:
    updateArmTemplate: $(updateArmTemplate)

$(updateArmTemplate) is a variable settable at queue time, with default value 'true' and around the yaml i also have a default value when declaring this parameter in the child yaml

the behavior is that it doesn't get executed although on 'true'
is this a bug or is there something i`m not doing right ?

@dhuesmann89
Copy link

I came across this issue yesterday and tried the solution mentioned above and I can confirm that it does not work, the way it is described. Here's part of my yaml:

  • ${{ if eq(variables['ExecuteTests'], '1') }}:
    • template: ExecuteTests.yml

The same syntax (eq(variables['ExecuteTests'], '1')) works in the condition tag of a single task.

@TingluoHuang
Copy link
Contributor

We have created a new repository for all YAML related issues, please move the current issue to there.
https://github.com/Microsoft/azure-pipelines-yaml

@TingluoHuang
Copy link
Contributor

move to microsoft/azure-pipelines-yaml#30

@MathieuBuisson
Copy link

this is supported:

- ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
  - template: sharedstep.yml@templates
    parameters:
       value: true

@ericsciple
How would this work using an output variable from another task in the same job ?
Thanks.

@ericsciple
Copy link
Contributor

@vtbassmatt to redirect

@vtbassmatt
Copy link
Member

@MathieuBuisson you could do something like this:

# azure-pipelines.yml
steps:
- script: <generate output variable here>
  name: deciderstep
- template: sharedstep.yml@templates
  parameters:
    doTheThing: $[ variables['deciderstep.myVariable'] ]

# sharedstep.yml
steps:
- script: echo Sometimes this happens!
  condition: eq(parameters.doTheThing, 'true')
- script: echo Sometimes this also happens!
  condition: eq(parameters.doTheThing, 'true')

(N.B. I didn't test that, just wrote it from memory, and it may need some tweaking.) It's not as elegant as a conditional inclusion of the template, but does get the scenario working.

@afeblot
Copy link

afeblot commented Dec 3, 2019

I tried that and got an error message stating that "parameters" was unknown here.
Then I tried something like

  condition: eq('${{parameters.doTheThing}}', 'true')

but this did not execute because the parameter was only evaluated as literally $[ variables['deciderstep.myVariable'], which was not true.

Or to be accurate in what I tested, in case it matters, I did not use an output variable but a simple one, and just used this syntax: $(myVariable).

@PaulVipond
Copy link

PaulVipond commented Feb 6, 2020

I'm using the suggested approach for optional execution of a template:

image

And get:

/build-templates/steps/node-app-deploy-steps.yml@templates: (Line: 76, Col: 19, Idx: 3882) - (Line: 76, Col: 19, Idx: 3882): Mapping values are not allowed in this context.

If I remove the parameters, then the pipeline compiles, but obviously I need my parameters!

This is a template within a template calling another template - don't know if that's significant.

Update:

It was an indentation issue - 'parameters' should be at the same level as the 'template' keyword. I'll leave it here so that future generations can laugh at my mistake :-)

@vtbassmatt
Copy link
Member

Nothing to laugh at -- YAML is a confusing beast 😁 Glad you got it solved.

@pbalexlear
Copy link

pbalexlear commented Apr 1, 2020

Hello @vtbassmat , I am trying to use the example you provided however having some issue such as the one described by @afeblot. When you use the parameter syntax as below
condition: eq(${{parameters.doThething}},true)
this evaluates the parameters at compilation time at which point the variables haven't been set so it evaluates as
condition: eq('variables['deciderstep.myVariable']',true )
which naturally doesn't work, do you have any advice on how to get this working?
also the syntax you described
eq(parameters.doTheThing, 'true')
gives the result:

##[error]Unrecognized value: 'parameters'.

@vtbassmatt
Copy link
Member

Hi @pbalexlear - we're not doing product support through GitHub issues anymore. Please open a ticket on Developer Community with your scenario.

I'll note a couple of things that may unblock you here:

  • Your condition section needs to evaluate to something that doesn't wrap the variables['deciderstep'] stuff in single-quotes. My example was correct in this regard.
  • The parameters suggestion is tricky and may only be applicable to the original person's scenario. The parameter gets consumed statically at compile time and refers to a $[ ] runtime expression. Then the runtime expression has to evaluate correctly at runtime.

@pbalexlear
Copy link

Thanks for coming back to me @vtbassmatt , I understand what you are saying however, the default action when a parameter is passed into a template seems to be to wrap it as a string or at least this is how it is displayed in the pipeline output as if it is treating it as a string value of the expression.

I understand what you are saying that if the value of a parameter was an expression and wasn't wrapped as a string it would be substituted in and evaluated at runtime, however because the system is wrapping the parameter as a string it doesn't behave this way.

I did manage to find a solution by passing the name of the variable as the value of the parameter, which isn't a solution I like but does work functionally.

If you could advise on how to prevent the system wrapping the expression as a string that would be greatly appreciated.

As I have resolved my issue although through an unideal solution I won't raise a ticket, but would appreciate it if you could advise on the above.

Thanks.

Alex

@vtbassmatt
Copy link
Member

Your comment made me dig in and try this, and I realized I had a couple of dumb mistakes. The below two files are complete and work the way you'd expect:

# azure-pipelines.yml
pool: { vmImage: ubuntu-latest }
steps:
- checkout: none
- bash: |
    echo Creating output variable...
    echo '##vso[task.setVariable variable=myVariable;isOutput=true]true' # also tested with 'false'
  name: deciderstep
- template: template.yml
  parameters:
    doTheThing: eq(variables['deciderstep.myVariable'], true)
# template.yml
parameters:
  doTheThing: 'false'
steps:
- script: echo This always happens!
  displayName: Always
- script: echo Sometimes this happens!
  condition: ${{ parameters.doTheThing }}
  displayName: Only if true

@pbalexlear
Copy link

Thanks @vtbassmatt , that's perfect and exactly what I was looking for, thanks very much for taking the time to look into this.

@cforce
Copy link

cforce commented Feb 26, 2021

Its still not solved how to work in that case with "succeeded()" as this is not supported for templates.
image

@adrian-skybaker
Copy link

This would be much simpler and less surprising if the condition element was supported on template.

@sebastianrogers
Copy link

Useful formulation for suppressing things, for example when you know that part of the environment isn't ready is:

- ${{ if False }}:

@JamesDLD
Copy link

Thanks! I confirm that the following code is working as expected.

  - ${{ if eq(variables['byPassSlotDeploymentOnDev'], 'true') }}:
    - template: pipeline/web-deploy.yml@Templates
      parameters:
        type: $(type)
        env: dev
        appRoleSuffix: $(appRoleSuffix)
        appRgSuffix: $(appRgSuffix)
        zipDeploymentFilePath: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip

@alfredo-milani
Copy link

alfredo-milani commented Jun 10, 2022

I need to set a variable using the statement:
echo "##vso[task.setvariable variable=environment]${some_value_for_environment}"

And then insert or not a template.
Using the following expression does not works:

- ${{if ne(parameters.some_parameter, variables.environment)}}:
      - script: |
          echo "diff"
- ${{if ne(parameters.some_parameter, variables['environment'])}}:
      - script: |
          echo "diff"

Any suggestions ?

@copdips
Copy link

copdips commented Jun 24, 2022

Thanks! I confirm that the following code is working as expected.

  - ${{ if eq(variables['byPassSlotDeploymentOnDev'], 'true') }}:
    - template: pipeline/web-deploy.yml@Templates
      parameters:
        type: $(type)
        env: dev
        appRoleSuffix: $(appRoleSuffix)
        appRgSuffix: $(appRgSuffix)
        zipDeploymentFilePath: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip

@JamesDLD
are you sure ? variables in the if clause is not evaluated in run time, in this case it should always be evaluated to null or the default value, but not the case for condition clause where from the need of this issue I think.

@copdips
Copy link

copdips commented Jun 24, 2022

I need to set a variable using the statement: echo "##vso[task.setvariable variable=environment]${some_value_for_environment}"

And then insert or not a template. Using the following expression does not works:

- ${{if ne(parameters.some_parameter, variables.environment)}}:
      - script: |
          echo "diff"
- ${{if ne(parameters.some_parameter, variables['environment'])}}:
      - script: |
          echo "diff"

Any suggestions ?

this is a pipeline limitation, the variables in the if clause is evaluated from the beginning of the pipeline, not in real time. but as you're using script, not template, so you can use the condition clause, it's real time.

@JPardinas
Copy link

JPardinas commented Oct 13, 2022

This is it! I think I got an example of what people is asking for (be creative to implement more custom conditions):

  1. Template condition-example.yml
parameters:
  - name: condition
    displayName: When should send the message
    default: always
    values:
      - always
      - canceled
      - failed
      - succeeded
      - succeededOrFailed

steps:
  - pwsh: |
      $status = "$(Agent.JobStatus)"
      Write-Host "This step was executed because the Pipeline Job Status is $status and the condition is ${{ parameters.condition }}"
    displayName: Echo if condition ${{ parameters.condition }} match
    condition: ${{ parameters.condition }}()
  1. Pipeline call test.yml
stages:
  - stage: TEST

    jobs:
      - job: TEST
        steps:
        - checkout: self
        
        - template: condition-example.yml
          parameters:
            condition: succeeded

        - pwsh: throw "TEST"
          displayName: Throw an error

        - template: condition-example.yml
          parameters:
            condition: succeeded
        
        - template: condition-example.yml
          parameters:
            condition: failed

        - template: condition-example.yml
          parameters:
            condition: succeededOrFailed

        - template: condition-example.yml
          parameters:
            condition: always

        - template: condition-example.yml
          parameters:
            condition: canceled
  1. Result
    image

@amanica
Copy link

amanica commented Nov 1, 2022

I wanted to do the below, but can't and I don't see an alternative :'(

    - template: my-template.yml
      condition: always()

@mikeblakeuk
Copy link

/ci/my.yml (Line: x, Col: y): Unrecognized value: 'succeeded'. Located at position 5 within expression: and(succeeded(), eq('${{ parameters.things}}', 'true')). For more help, refer to https://go.microsoft.com/fwlink/?linkid=842996

@syedhamjath
Copy link

This looks like simple ask. If we dig deeper, I don't how it can be implemented. The template expands during compile time, if we use runtime condition to a template, does that mean we don't want the template to expand during compile time? or we want the condition to applied to the expanded elements?

Not sure, how this can be added to product. Also I really see the benefits of have runtime template selection. This can also cause the pipeline to keep expanding and not know when it will end.

@copdips
Copy link

copdips commented Sep 21, 2023

@syedhamjath

if we use runtime condition to a template, does that mean we don't want the template to expand during compile time?

yes, it is, just skip the template as unexpanded (implies some azure pipeline GUI change), and mark it in gray,

@syedhamjath
Copy link

syedhamjath commented Sep 22, 2023

The feedback is given to the community in past and the request was closed due to lack of response from community.
https://developercommunity.visualstudio.com/t/Azure-Pipelines-YAML-Conditional-Run-T/1048083?q=YAML+-+Support+conditions+for+templates

I have recreated the same so please vote to make sure, it gains right attention.
https://developercommunity.visualstudio.com/t/Azure-Pipelines-YAML-Conditional-Run-T/10473620

@bakersagar
Copy link

  • ${{ if succeeded() }}:
    • template: mytemplate

image

What's the solution for checking if the previous task succeeded?

@JPardinas
Copy link

  • ${{ if succeeded() }}:

    • template: mytemplate

image

What's the solution for checking if the previous task succeeded?

You can implement something like the following:

  • mytemplate.yml
parameters:
  - name: condition
    displayName: When to create the tag and push it
    type: string
    default: succeeded()

steps:
    - bash: echo "${{ parameters.condition }}"
      displayName: Step with inherited condition from template parameter ${{ parameters.condition }}
      condition: ${{ parameters.condition }}

    - bash: |
           echo "${{ parameters.condition }}"
           echo "Executed because parameter condition is true or branch is main"
      displayName: Another step with inherited condition from template parameter ${{ parameters.condition }}
      condition: or(${{ parameters.condition }}, eq(variables['Build.SourceBranch'], 'refs/heads/main'))

pipeline.yml

...
    - template: mytemplate.yml
      parameters:
        condition: always()

@A-Rai-col
Copy link

A-Rai-col commented Feb 29, 2024

What if I want to run different templates based on conditions within the same job?

For example.

- template: build-template.yml
  condition: and(succeeded(), eq(variables.IsReleaseBranch, false))

- template: release-template.yml
  condition: and(succeeded(), eq(variables.IsReleaseBranch, true))

@copdips
Copy link

copdips commented Feb 29, 2024

What if I want to run different templates based on conditions within the same job?

For example.

- template: build-template.yml
  condition: and(succeeded(), eq(variables.IsReleaseBranch, false))

- template: release-template.yml
  condition: and(succeeded(), eq(variables.IsReleaseBranch, true))

is this working for you?

@A-Rai-col
Copy link

A-Rai-col commented Mar 4, 2024

Hi @copdips thanks for the reply!

is this working for you?

No it doesn't, with the following error message Unexpected value 'condition'.

Here's more code context:

variables:
  IsReleaseBranch: $[startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')]
stages:
  - stage: Build
    jobs: 
      - job: Deploy
        pool:
          vmImage: 'windows-latest'
        steps:
          - script: echo `build`

          - template: build-template.yml 
            condition: and(succeeded(), eq(variables.IsReleaseBranch, false))

          - template: release-template.yml
            condition: and(succeeded(), eq(variables.IsReleaseBranch, true))

The variable IsReleaseBranch can either be true or false on each stage:job run, therefore only one of the step templates should be used.

Want I want to do is to use different step/task level templates based on a variable (preferable) or parameter.
Is there a way this can be done?

@copdips
Copy link

copdips commented Mar 4, 2024

AFIK, and based on the other comments in this issue, no ...
This is a vendor design.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests