Gitlab pipeline: Global variable not visible in Job - gitlab

I have the following .gitlab-ci.yml:
image:
name: my.private.registry.com/my-repository/ci-tools:latest
variables:
TF_IN_AUTOMATION: "true"
PLAN: "plan.tfplan"
application_name: "my_app"
variables: &eu-variables
TF_VAR_region: "westeurope"
variables: &us-variables
TF_VAR_region: "eastus"
variables: &dev-variables
TF_VAR_application_name: "$application_name"
stages:
- Validate
- Plan
dev-plan:
variables:
<<: *dev-variables
<<: *eu-variables
stage: Plan
script:
- echo "$PLAN"
- echo "$TF_VAR_application_name"
- echo "$application_name"
artifacts:
paths:
- "$PLAN"
When the pipeline runs, $PLAN, TF_VAR_application_name, and application_name are blank and the output is as follows:
.....
$ echo "$PLAN"
$ echo "$application_name"
$ echo "$TF_VAR_application_name"
..........
What am I missing? Why is the job variable not able to see the global variable?

By using global, just put all your variables in variables:
example
variables:
TF_VAR_region_WE: "westeurope"
TF_VAR_region_EU: "eastus"
.....
also by reading the docs, its not recommend to use " <<: "
see this documentation https://docs.gitlab.com/ee/ci/yaml/yaml_optimization.html#yaml-anchors-for-scripts

Related

Azure DevOps Azure PowerShell task output variable - Unable to pass value to third stage

I am trying to pass variables between stages. I was successfully able to pass values to immediate next stage, but after that the variables are blank. I tried all the ways to get this, but failing
stages:
- stage: InitialStage
jobs:
- job: StandAlone
displayName: Required Stand Alone Job
steps:
- bash: |
echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes"
name: bash_test
- stage: SecondStage
jobs:
- job:
displayName: SecondStage
variables:
variable1: $[ stageDependencies.InitialStage.StandAlone.outputs['bash_test.doThing'] ]
steps:
- task: CmdLine#2
name: test
inputs:
script: |
echo "$(variable1)"
- stage: ThirdStage
jobs:
- job:
displayName: ThirdStage
variables:
variable1: $[ stageDependencies.InitialStage.StandAlone.outputs['bash_test.doThing'] ]
steps:
- task: CmdLine#2
name: test
inputs:
script: |
echo "$(variable1)"
The echo at stage SecondStage will give value, but ThirdStage it is blank, I am having a doubt either we can access a variable only in immediate stage or else I am doing something wrong, any help on this will be much appreciated.
Option 1:
In order to pass variable to the another stage you need to add isOutput=true
See the example as shown below;
- task: Bash#3
name: setprojectName
inputs:
targetType: 'inline'
script: |
echo "##vso[task.setvariable variable=projectNameGlobal;isOutput=true]$(echo $(Build.Repository.Name)| sed 's|.*/||')"
echo "##vso[task.setvariable variable=projectName]$(echo $(Build.Repository.Name)| sed 's|.*/||')"
projectName is only available the stage where variable populated but projectNameGlobal is available in next stage also.
Also see the documentation
Option 2:
## azure-pipelines.yml
stages:
- stage: one
jobs:
- job: A
steps:
- task: Bash#3
inputs:
filePath: 'script-a.sh'
name: setvar
- bash: |
echo "##vso[task.setvariable variable=skipsubsequent;isOutput=true]true"
name: skipstep
- stage: two
jobs:
- job: B
variables:
- name: StageSauce
value: $[ stageDependencies.one.A.outputs['setvar.sauce'] ]
- name: skipMe
value: $[ stageDependencies.one.A.outputs['skipstep.skipsubsequent'] ]
steps:
- task: Bash#3
inputs:
filePath: 'script-b.sh'
name: fileversion
env:
StageSauce: $(StageSauce) # predefined in variables section
skipMe: $(skipMe) # predefined in variables section
- task: Bash#3
inputs:
targetType: 'inline'
script: |
echo 'Hello inline version'
echo $(skipMe)
echo $(StageSauce)
In above option you can see how to use one variable where populated by any of the prior stages

Using task output variables in template parameters

The first stage in my pipeline checks for what services have actually changed. This is in an effort to speed up the pipeline by avoiding rebuilding, retesting, redeploying services if there have been no changes.
This is the changed.yaml for that stage:
parameters:
- name: comparedTo
default: ''
stages:
- stage: Changed
displayName: Check for changes in services and configs...
jobs:
- job: Changes
displayName: Checking for changes in services and configs...
steps:
- bash: |
mapfile -t changed < <(git diff HEAD ${{ parameters.comparedTo }} --name-only | awk -F'/' 'NF!=1{print $1}' | sort -u)
servicesChanged=()
configChanged=()
echo ""
echo "Total Changed: ${#changed[#]}"
for i in "${changed[#]}"
do
echo $i
if [[ $i == 'admin' ]]; then
echo "##vso[task.setvariable variable=adminChanged;isOutput=True]true"
servicesChanged+=("admin")
elif [[ $i == 'admin-v2' ]]; then
echo "##vso[task.setvariable variable=adminV2Changed;isOutput=True]true"
servicesChanged+=("admin-v2")
elif [[ $i == 'api' ]]; then
echo "##vso[task.setvariable variable=apiChanged;isOutput=True]true"
servicesChanged+=("api")
elif [[ $i == 'client' ]]; then
echo "##vso[task.setvariable variable=clientChanged;isOutput=True]true"
servicesChanged+=("client")
elif [[ $i == 'k8s' ]]; then
echo "##vso[task.setvariable variable=k8sChanged;isOutput=True]true"
configsChanged+=("k8s")
elif [[ $i == 'pipelines' ]]; then
echo "##vso[task.setvariable variable=pipelineChanged;isOutput=True]true"
configsChanged+=("pipelines")
fi
done
echo ""
echo "Services Changed: ${#servicesChanged[#]}"
for i in "${servicesChanged[#]}"
do
echo $i
done
echo ""
echo "Configs Changed: ${#configsChanged[#]}"
for i in "${configsChanged[#]}"
do
echo $i
done
if [[ ${#servicesChanged[#]} > 0 ]]; then
echo ""
echo "Any services changed: True"
echo "##vso[task.setvariable variable=anyServicesChanged;isOutput=true]true"
echo "##vso[task.setvariable variable=servicesChanged;isOutput=true]${servicesChanged[#]}"
fi
if [[ ${#configsChanged[#]} > 0 ]]; then
echo ""
echo "Any configs changed: True"
echo "##vso[task.setvariable variable=anyConfigsChanged;isOutput=true]true"
echo "##vso[task.setvariable variable=configsChanged;isOutput=true]${configsChanged[#]}"
fi
echo ""
name: detectChanges
As you can see, it creates a number of task output variables:
# This just indicates if the service has changed: true/false
echo "##vso[task.setvariable variable=<service-name>;isOutput=True]true"
# This should be creating a an output variable that is an array of the services that have changed
echo "##vso[task.setvariable variable=servicesChanged;isOutput=true]${servicesChanged[#]}"
So I gave myself two options: just a true/false for each service or iterating (somewhow) through an array of the services that have changed.
Each stage basically has the following form:
# pr.yaml
...
- template: templates/unitTests.yaml
parameters:
services:
- api
- admin
- admin-v2
- client
...
parameters:
- name: services
type: object
default: []
stages:
- stage: UnitTests
displayName: Run unit tests on service...
dependsOn: Changed
condition: succeeded()
jobs:
- job: UnitTests
condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
displayName: Running unit tests...
steps:
- ${{ each service in parameters.services }}:
- bash: |
echo "Now running ${{ service }} unit tests..."
Here is what I've tried so far and the errors I get:
Adding each service conditionally to services array or adding the array of changed services:
- template: templates/changed.yaml
parameters:
comparedTo: origin/production
- template: templates/unitTests.yaml
dependsOn: Changed
parameters:
services:
- ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.apiChanged'], true) }}
- api
- ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.adminChanged'], true) }}
- admin
- ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.adminV2Changed'], true) }}
- admin-v2
- ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.clientChanged'], true) }}
- client
Or...
- template: templates/changed.yaml
parameters:
comparedTo: origin/production
- template: templates/unitTests.yaml
dependsOn: Changed
parameters:
services:
- ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.apiChanged'], true) }}
- api
- ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.adminChanged'], true) }}
- admin
- ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.adminV2Changed'], true) }}
- admin-v2
- ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.clientChanged'], true) }}
- client
Or...
- template: templates/changed.yaml
parameters:
comparedTo: origin/production
- template: templates/unitTests.yaml
dependsOn: Changed
parameters:
services:
- $[ stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged'] ]
This results in:
An error occurred while loading the YAML build pipeline. Object reference not set to an instance of an object.
I know variables: will only take strings and not arrays.
One solution would be to have a variables: for each of the true/false variables and then conditions based on the parameters.services and whether the task output variables is true.
Any suggestions?
Ref:
Task Output Variables: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#set-variables-in-scripts
Parameters: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops#parameters
Expressions: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops
The template expression ${{}} is evaluated at compile-time(before the jobs run), which means it cannot access to the variables that are dynamically set at the runtime(after the jobs start). So you cannot use template expression ${{}} in above scenario. See below description from here.
Within a template expression, you have access to the parameters context that contains the values of parameters passed in. Additionally, you have access to the variables context that contains all the variables specified in the YAML file plus many of the predefined variables (noted on each variable in that topic). Importantly, it doesn't have runtime variables such as those stored on the pipeline or given when you start a run. Template expansion happens very early in the run, so those variables aren't available
You can use conditions as a workaround. You need to add multiple tasks to be executed on the conditions. See below example:
- template: templates/changed.yaml
parameters:
comparedTo: origin/production
- template: templates/unitTests.yaml
dependsOn: Changed
#unitTests.yaml
stages:
- stage: UnitTests
displayName: Run unit tests on service...
dependsOn: Changed
condition: succeeded()
jobs:
- job: UnitTests
condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
displayName: Running unit tests...
variables:
changedServices: $[stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged']]
steps:
- bash: |
echo "Now running api unit tests..."
name: Api-unit-test
conidtion: contains(variables['changedServices'], 'api')
- bash: |
echo "Now running admin unit tests..."
name: admin-unit-test
conidtion: contains(variables['changedServices'], 'admin')
- bash: |
echo "Now running client unit tests..."
name: client-unit-test
conidtion: contains(variables['changedServices'], 'client')
Another workaround is to separate your pipeline into two pipelines. The fist pipeline to run the Changed stage. And Then call rest api in a script task to trigger the second pipeline and pass the variables in the request body. See this similar thread.

Azure Pipeline: Passing variable to multiple stages

is it possible to declare a variable and then pass it downstream?
I have an image below StageA -> StageB -> StageC
where I obtain a url for my storage account on StageA, and I want to use it for both Stage B and StageC
But if I use the [stagedependencies.StageA.JobA.outputs['var'], it only works on StageB and not on StageC
- stage: 'StageC'
dependsOn: 'StageB'
pool:
vmImage: 'windows-latest'
variables:
blobUri: $[stageDependencies.StageA.JobA.outputs['createOutput.blobUri']]
jobs:
- job: 'JobC'
steps:
- checkout: none
- download: none
- powershell: |
echo JobBUri: $(blobUri)
Maybe I missed it somewhere but does this mean you can only obtain the variable from the immediate stage you depend?
Example:
trigger:
- master
pool:
vmImage: ubuntu-latest
stages:
- stage: 'StageA'
jobs:
- job: 'JobA'
steps:
- task: Powershell#2
name: 'createOutput'
inputs:
targetType: 'inline'
script: |
Write-Output "##vso[task.setvariable variable=blobUri;isOutput=true]www.google.com"
- stage: 'StageB'
dependsOn: 'StageA'
pool:
vmImage: 'windows-latest'
variables:
blobUri: $[stageDependencies.StageA.JobA.outputs['createOutput.blobUri']]
jobs:
- job: 'JobB'
steps:
- powershell: |
echo JobBUri: $(blobUri)
- stage: 'StageC'
dependsOn: 'StageB'
pool:
vmImage: 'windows-latest'
variables:
blobUri: $[stageDependencies.StageA.JobA.outputs['createOutput.blobUri']]
jobs:
- job: 'JobC'
steps:
- powershell: |
echo JobBUri: $(blobUri)
Try this and probably variable set in stage A will be available at stage C by stageDependencies.StageA
- stage: 'StageC'
dependsOn:
- StageA
- StageB
Other workaround is to set again this variable as output variable in stage B and access it from C by stageDependencies.StageB(...)
From release notes
https://learn.microsoft.com/en-us/azure/devops/release-notes/2020/sprint-168-update#jobs-can-access-output-variables-from-previous-stages
By default, each stage in a pipeline depends on the one just before it in the YAML file. Therefore, each stage can use output variables from the prior stage. You can alter the dependency graph, which will also alter which output variables are available. For instance, if stage 3 needs a variable from stage 1, it will need to declare an explicit dependency on stage 1.
The following illustrates that the answer from Kontekst is correct (I was going to edit the post to add this, but don't know if that's appropriate here?):
The following confirms and illustrates that this is the correct answer:
stages:
- stage: A
jobs:
- job: A1
steps:
- pwsh: Write-Host "##vso[task.setvariable variable=varFromA;isOutput=true]A"
name: writevar
- stage: B
dependsOn: A
jobs:
- job: B1
variables:
varA: $[ stageDependencies.A.A1.outputs['writevar.varFromA'] ]
steps:
- pwsh: |
Write-Host "##vso[task.setvariable variable=varFromB;isOutput=true]B"
Write-Host "accesses $(VarA) from A"
name: writevar
- stage: C
dependsOn:
- A
- B
jobs:
- job: C1
variables:
varA: $[ stageDependencies.A.A1.outputs['writevar.varFromA'] ]
varB: $[ stageDependencies.B.B1.outputs['writevar.varFromB'] ]
steps:
- pwsh: |
Write-Host "##vso[task.setvariable variable=varFromC;isOutput=true]C"
Write-Host "accesses $(varB) from B"
Write-Host "accesses $(varA) from A"
name: writevar

need replace alias value of predefined variables in azure devops pipelines

I have enabled pipeline resource triggers between two pipelines. would like to replace alias value dynamically with triggering pipeline resource. below is the pipeline code
resources:
pipelines:
- pipeline: pipeline1
project: onecom
source: pipeline1-api
trigger:
branches:
- develop
- feat/*
- pipeline: pipeline2
project: onecom
source: pipeline2-api
trigger:
branches:
- develop
- feat
variables:
- name: apiname
value: $(resources.pipeline.<Alias>.pipelineName)
- name: dockertag
value: $(resources.pipeline.<Alias>.sourceCommit)
- name: runname
value: $(resources.pipeline.<Alias>.runName)
stages:
- stage: ScanImage
jobs:
- job: ScanImage
pool:
vmImage: 'ubuntu-16.04'
steps:
- script: echo $(apiname)
- script: echo $(runname)
I would like to replace Alias value in $(resources.pipeline..pipelineName) with value pipeline1 if build comes from source: pipeline1-api and with pipeline2 if build comes from source: pipeline2-api dynamically.
I would like to replace Alias value in $(resources.pipeline..pipelineName) with value pipeline1 if build comes from source: pipeline1-api and with pipeline2 if build comes from source: pipeline2-api dynamically.
Since the value of nested variables (like $(resources.pipeline.$(alias).pipelineName)) are not yet supported in the build/release pipelines. So we could not use it in the variable directly:
variables:
- name: apiname
value: $(resources.pipeline.$(alias).pipelineName)
To resolve this issue, we need add a inline powershell to set the variable resources.pipeline.<Alias>.pipelineName based on the value of the $(resources.triggeringAlias):
variables:
- name: alias
value: $(resources.triggeringAlias)
- task: InlinePowershell#1
inputs:
script: |
if ("$(alias)" -eq "PipelineA")
{
Write-Host ("##vso[task.setvariable variable=dockertag]$(resources.pipeline.PipelineA.sourceCommit) | cut -c -7")
}
elseif ("$(alias)" -eq "PipelineB")
{
Write-Host ("##vso[task.setvariable variable=dockertag]$(resources.pipeline.PipelineB.sourceCommit) | cut -c -7")
}
Update:
could you please help me same config in bash as we are using these
task in linux machines
- task: PowerShell#2
displayName: 'Inline Powershell'
inputs:
TargetType: inline
Script: |
if ("$(alias)" -eq "PipelineA")
{
Write-Host ("##vso[task.setvariable variable=dockertag]$(resources.pipeline.PipelineA.sourceCommit) | cut -c -7")
}
elseif ("$(alias)" -eq "PipelineB")
{
Write-Host ("##vso[task.setvariable variable=dockertag]$(resources.pipeline.PipelineB.sourceCommit) | cut -c -7")
}
pwsh: true
please try this
variables:
- name: alias
value: $(resources.triggeringAlias)
then you can try replace it as below
$(resources.pipeline.$(alias).pipelineName)

Azure DevOps pipelines - get build number of previous stage

I have a yml pipeline with 2 stages:
- stage:
- stage:
My second stage needs to refer to the $(Build.BuildNumber) of the previous stage. How can this be achieved? My understanding is output variables are scoped to the same stage and can't be used across-stages.
Trying to pull from stageDependencies:
stages:
- stage: BuildPublish
displayName: "Build & Publish"
jobs:
- job: BuildPublishJob
displayName: "Build & Publish"
steps:
- script: |
echo "Recording MSI version"
echo "##vso[task.setvariable variable=msiVersion;isOutput=true]$(Build.BuildNumber)"
name: MsiVersion
- script: echo $(MsiVersion.msiVersion)
name: echovar
- stage: DeployInstallerTest
displayName: "Deploy Installer Test"
jobs:
- job:
displayName: "Deploy Installer Test"
steps:
- task: AzurePowerShell#5
inputs:
azureSubscription: 'Spektrix Non-Prod'
ScriptType: 'InlineScript'
Inline: |
$msiVersion = stageDependencies.BuildPublish.BuildPublishJob.outputs['MsiVersion.msiVersion']
azurePowerShellVersion: 'LatestVersion'
Fails with:
##[error]The term 'stageDependencies.BuildPublish.BuildPublishJob.outputs[MsiVersion.msiVersion]' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
It changed recently:
stages:
- stage: A
jobs:
- job: JA
steps:
- script: |
echo "This is job Foo."
echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes" #The variable doThing is set to true
name: DetermineResult
- script: echo $(DetermineResult.doThing)
name: echovar
- job: JA_2
dependsOn: JA
condition: eq(dependencies.JA.outputs['DetermineResult.doThing'], 'Yes')
steps:
- script: |
echo "This is job Bar."
#stage B runs if DetermineResult task set doThing variable n stage A
- stage: B
dependsOn: A
jobs:
- job: JB
condition: eq(stageDependencies.A.JA.outputs['DetermineResult.doThing'], 'Yes') #map doThing and check if true
variables:
varFromStageA: $[ stageDependencies.A.JA.outputs['DetermineResult.doThing'] ]
steps:
- bash: echo "Hello world stage B first job"
- script: echo $(varFromStageA)
However, please be aware that stageDependencies is not available in condition at stage level. Of course you can use stageDependencies not only in condition.

Resources