Azure Pipeline - Control deployment with SonarQube Quality Gates - sonarqube

I am using Azure yaml pipeline with SonarQube and here's how my pipeline is setup:
- checkout: self
- task: SonarQubePrepare#5
displayName: 'SonarQube - Prepare'
inputs:
SonarQube: '$(sonarQubeConnection)'
scannerMode: 'CLI'
configMode: 'manual'
cliProjectKey: $(sonarQubeProject)
cliProjectName: $(sonarQubeProject)
#cliSources: '.'
- script: |
sudo update-alternatives --set php /usr/bin/php$(phpVersion)
sudo update-alternatives --set phar /usr/bin/phar$(phpVersion)
sudo update-alternatives --set phpdbg /usr/bin/phpdbg$(phpVersion)
sudo update-alternatives --set php-cgi /usr/bin/php-cgi$(phpVersion)
sudo update-alternatives --set phar.phar /usr/bin/phar.phar$(phpVersion)
php -version
workingDirectory: $(rootFolder)
displayName: 'Use PHP version $(phpVersion)'
- script: composer install --no-interaction --prefer-dist
workingDirectory: $(rootFolder)
displayName: 'Composer install'
- task: SonarQubeAnalyze#5
displayName: 'SonarQube - Analysis'
- task: SonarQubePublish#5
displayName: 'SonarQube - Publish Quality Gate Result'
inputs:
pollingTimeoutSec: $(sonarQubePollingTimeout)
- script: echo "After code scan..."
displayName: 'Test Message'
But I see it runs ALL steps irrespective of the result from Quality Gate - the 'Test Message' shows up even when the quality gate fails.
Here's my Quality Gate in SonarQube:
I want to run subsequent steps from 'SonarQube - Publish Quality Gate Result' only when its "Passed" - please advise.
PS - I am using the DVWA app from GitHub - https://github.com/digininja/DVWA

Looks like we can invoke REST API and throw exception if the status is not OK:
http://SonarFQDN:9000/api/qualitygates/project_status?projectKey=SonarProjectKey

Related

.NET CORE - OWASP-ZAP PLUGIN - CI/CD -ENOENT: no such file or directory, open '/home/vsts/work/r1/a/owaspzap/report.json

I have been trying to add the zap owasp scanner plugin on azure DevOps,I cant get the Results tab to display
this is the documentation that I am following
https://marketplace.visualstudio.com/items?itemName=CSE-DevOps.zap-scanner
https://dzone.com/articles/owasp-zap-security-tests-in-azure-devops-pipeline
the document says the following
"Note: This script contains a Relative path to the repository and folder, the content of the script may change based on the name you specified in your project."
PowerShell
1
$XslPath = "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/_Quality/SecurityTesting/OWASPToNUnit3.xslt"
2
$XmlInputPath = "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/OWASP-ZAP-Report.xml"
3
$XmlOutputPath = "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/Converted-OWASP-ZAP-Report.xml"
4
$XslTransform = New-Object System.Xml.Xsl.XslCompiledTransform
5
$XslTransform.Load($XslPath)
6
$XslTransform.Transform($XmlInputPath, $XmlOutputPath)
Please see below my YAML source code and the errors.
**<filePath>$(Build.SourcesDirectory)/owaspzap/report.html</filePath>**
Below is the error from the pipeline
##[section]Starting: owaspzap
==============================================================================
Task : OWASP Zap Scanner
Description : Utilize the OWASP/ZAP scanner within Azure DevOps
Version : 1.0.1
Author : Doyle Turner, Anthony Turner
Help :
==============================================================================
[command]"C:\Program Files\Git\usr\bin\chmod.exe" 777 D:\a\1\s\owaspzap
[command]"C:\Program Files\Git\usr\bin\id.exe" -u root
/usr/bin/id: 'root': no such user
[command]"C:\Program Files\Docker\docker.exe" run -u "" -v D:\a\1\s\owaspzap:/zap/wrk/:rw owasp/zap2docker-stable zap-baseline.py -t https://MYWEBSITE.azurewebsites.net:80 -J report.json -r report.html
Unable to find image 'owasp/zap2docker-stable:latest' locally
latest: Pulling from owasp/zap2docker-stable
docker: image operating system "linux" cannot be used on this platform.
See 'docker run --help'.
##[error]The process 'C:\Program Files\Docker\docker.exe' failed with exit code 125
##[error]ENOENT: no such file or directory, open 'D:\a\1\s\owaspzap\report.json'
##[section]Finishing: owaspzap
Here's my configuration.
YAML
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller#1
- task: NuGetCommand#2
inputs:
restoreSolution: '$(solution)'
- task: DotNetCoreCLI#2
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: true
- task: AzureRmWebAppDeployment#4
inputs:
ConnectionType: 'AzureRM'
azureSubscription: 'owasService'
appType: 'webApp'
WebAppName: 'dev-sowasp'
packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
- task: owaspzap#1
inputs:
scantype: 'targetedScan'
url: 'http://mywebsite.azurewebsites.net'
threshold: '2'
- task: owaspzap#1
inputs:
url: 'http://mywebsite.azurewebsites.net'
aggressivemode: true
threshold: '1'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.SourcesDirectory)/owaspzap'
ArtifactName: 'zap results'
publishLocation: 'Container'
- bash: |
sudo npm install -g handlebars-cmd
cat <<EOF > owaspzap/nunit-template.hbs
{{#each site}}
<test-run
id="2"
name="Owasp test"
start-time="{{../[#generated]}}" >
<test-suite
id="{{#index}}"
type="Assembly"
name="{{[#name]}}"
result="Failed"
failed="{{alerts.length}}">
<attachments>
<attachment>
<filePath>$(Build.SourcesDirectory)/owaspzap/report.html</filePath>
</attachment>
</attachments>
{{#each alerts}}<test-case
id="{{#index}}"
name="{{alert}}"
result="Failed"
fullname="{{alert}}"
time="1">
<failure>
<message>
<![CDATA[{{{desc}}}]]>
</message>
<stack-trace>
<![CDATA[
Solution:
{{{solution}}}
Reference:
{{{reference}}}
instances:{{#each instances}}
* {{uri}}
- {{method}}
{{#if evidence}}- {{{evidence}}}{{/if}}
{{/each}}]]>
</stack-trace>
</failure>
</test-case>
{{/each}}
</test-suite>
</test-run>
{{/each}}
EOF
displayName: 'owasp nunit template'
condition: always()
- bash: ' handlebars owaspzap/report.json < owaspzap/nunit-template.hbs > owaspzap/test-results.xml'
displayName: 'generate nunit type file'
condition: always()
- task: PublishTestResults#2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
testResultsFormat: NUnit
testResultsFiles: 'owaspzap/test-results.xml'
condition: always()
- task: publishhtmlreport#1
inputs:
htmlType: 'genericHTML'
htmlPath: '$(Build.SourcesDirectory)/owaspzap/report.html'
- task: DockerInstaller#0
inputs:
dockerVersion: '17.09.0-ce'
- task: Bash#3
inputs:
targetType: 'inline'
script: |
chmod -R 777 ./
docker run --rm -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable zap-full-scan.py -t http://mywebsite.azurewebsites.net -g gen.conf -x OWASP-ZAP-Report.xml -r scan-report.html
true
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
$XslPath = "$(Build.SourcesDirectory)/owaspzap/report.html"
$XmlInputPath = "$(Build.SourcesDirectory)/OWASP-ZAP-Report.xml"
$XmlOutputPath = "$(Build.SourcesDirectory)/Converted-OWASP-ZAP-Report.xml"
$XslTransform = New-Object System.Xml.Xsl.XslCompiledTransform
$XslTransform.Load($XslPath)
$XslTransform.Transform($XmlInputPath, $XmlOutputPath)
- task: PublishTestResults#2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**Converted-OWASP-ZAP-Report*.xml'
searchFolder: '$(Build.SourcesDirectory)'
`
The issue is noted in the exception log:
docker: image operating system "linux" cannot be used on this platform.
The Azure VM needs to be configured to allow Linux docker containers to run on the VM.
See these links to help you get that installed / provisioned:
How to Setup Docker Engine Containers on Windows Server Azure/AWS/GCP
RUN LINUX CONTAINER ON WINDOWS IN AZURE VM

The deployment environment 'Staging' in your bitbucket-pipelines.yml file occurs multiple times in the pipeline

I'm trying to get Bitbucket Pipelines to work with multiple steps that define the deployment area. When I do, I get the error
Configuration error The deployment environment 'Staging' in your
bitbucket-pipelines.yml file occurs multiple times in the pipeline.
Please refer to our documentation for valid environments and their
ordering.
From what I read, the deployment variable has to happen on a step by step basis.
How would I set up this example pipelines file to not hit that error?
image: ubuntu:18.04
definitions:
steps:
- step: &build
name: npm-build
condition:
changesets:
includePaths:
# Only run npm if anything in the build directory was touched
- "build/**"
image: node:14.17.5
script:
- echo 'build initiated'
- cd build
- npm install
- npm run dev
- echo 'build complete'
artifacts:
- themes/factor/css/**
- themes/factor/js/**
- step: &deploychanges
name: Deploy_Changes
deployment: Staging
script:
- echo 'Installing server dependencies'
- apt-get update -q
- apt-get install -qy software-properties-common
- add-apt-repository -y ppa:git-ftp/ppa
- apt-get update -q
- apt-get install -qy git-ftp
- echo 'All dependencies installed'
- echo 'Transferring changes'
- git ftp init --user $FTP_USER --passwd $FTP_PASSWORD $FTP_ADDRESS push --force --changed-only -vv
- echo 'File transfer complete'
- step: &deploycompiled
name: Deploy_Compiled
deployment: Staging
condition:
changesets:
includePaths:
# Only run npm if anything in the build directory was touched
- "build/**"
script:
- echo 'Installing server dependencies'
- apt-get update -q
- apt-get install -qy software-properties-common
- add-apt-repository -y ppa:git-ftp/ppa
- apt-get update -q
- apt-get install -qy git-ftp
- echo 'All dependencies installed'
- echo 'Transferring compiled assets'
- git ftp init --user $FTP_USER --passwd $FTP_PASSWORD $FTP_ADDRESS push --all --syncroot themes/factor/css/ -vv
- git ftp init --user $FTP_USER --passwd $FTP_PASSWORD $FTP_ADDRESS push --all --syncroot themes/factor/js/ -vv
- echo 'File transfer complete'
pipelines:
branches:
master:
- step: *build
<<: *deploychanges
deployment: Production
- step:
<<: *deploycompiled
deployment: Production
dev:
- step: *build
- step: *deploychanges
- step: *deploycompiled
The workaround for the issue with reusing Environment Variables without using the deployment clause for more than one steps in a pipeline I have found is to dump ENV VARS to a file and save it as an artifact that will be sourced in the following steps.
The code snippet for it would look like:
steps:
- step: &set-environment-variables
name: 'Set environment variables'
script:
- printenv | xargs echo export > .envs
artifacts:
- .envs
- step: &next-step
name: "Next step in the pipeline"
script:
- source .envs
- next_actions
pipelines:
pull-requests:
'**':
- step:
<<: *set-environment-variables
deployment: my-deployment
- step:
<<: *next-step
name: "Name of the next step being executed"
branches:
staging:
- step:
<<: *set-environment-variables
deployment: my-deployment
- step:
<<: *next-step
name: "Name of the next step being executed"
So far this solution works for me.
Update:
after having an issue of "%s" appearing in the .envs file, which caused the later source .envs statement to fail, here is a slightly different approach to the initial step. It gets around that particular issue, but also only exports those variables you know you need in your pipeline - noting that there are a lot of bitbucket environment variables available to the first script step which will be available naturally to later scripts anyway, and it makes more sense (to me anyway) that you don't just dump out all environment variables to the .env artifact, but do it in a much more controlled manner.
- step: &set-environment-variables
name: 'Set environment variables'
script:
- echo "export SSH_USER=$SSH_USER" > .envs
- echo "export SERVER_IP=$SERVER_IP" >> .envs
- echo "export ANOTHER_ENV_VAR=$ANOTHER_ENV_VAR" >> .envs
artifacts:
- .envs
In this example, .envs will now contain only those 3 environment variables, and not a whole heap of system + bitbucket variables (and of course, no pesky %s characters either!)
Normally what happens is that you deploy to an environment. So there is one step which deploys. So particularly u should put your "deployment" group to it specifically. This is how Bitbucket manages that if the deployment of the code happened or not. So its like you can have multiple steps where in one you are testing unit cases, integration cases, another one for building the binaries and the last one as an artifact deploys to the env marking deployment group. see the below example.
definitions:
steps:
- step: &test-vizdom-services
name: "Vizdom services unit Tests"
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- cd ./vizdom/vizdom.services.Tests
- dotnet test vizdom.services.Tests.csproj
pipelines:
custom:
DEV-AWS-api-deploy:
- step: *test-vizdom-services
- step:
name: "Vizdom Webapi unit Tests"
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- export ASPNETCORE_ENVIRONMENT=Dev
- cd ./vizdom/vizdom.webapi.tests
- dotnet test vizdom.webapi.tests.csproj
- step:
deployment: DEV-API
name: "API: Build > Zip > Upload > Deploy"
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- apt-get update
- apt-get install zip -y
- mkdir -p ~/deployment/release_dll
- cd ./vizdom/vizdom.webapi
- cp -r ../shared_settings ~/deployment
- dotnet publish vizdom.webapi.csproj -c Release -o ~/deployment/release_dll
- cp Dockerfile ~/deployment/
- cp -r deployment_scripts ~/deployment
- cp deployment_scripts/appspec_dev.yml ~/deployment/appspec.yml
- cd ~/deployment
- zip -r $BITBUCKET_CLONE_DIR/dev-webapi-$BITBUCKET_BUILD_NUMBER.zip .
- cd $BITBUCKET_CLONE_DIR
- pipe: atlassian/aws-code-deploy:0.5.3
variables:
AWS_DEFAULT_REGION: 'us-east-1'
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
COMMAND: 'upload'
APPLICATION_NAME: 'ss-webapi'
ZIP_FILE: 'dev-webapi-$BITBUCKET_BUILD_NUMBER.zip'
S3_BUCKET: 'ss-codedeploy-repo'
VERSION_LABEL: 'dev-webapi-$BITBUCKET_BUILD_NUMBER.zip'
- pipe: atlassian/aws-code-deploy:0.5.3
variables:
AWS_DEFAULT_REGION: 'us-east-1'
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
COMMAND: 'deploy'
APPLICATION_NAME: 'ss-webapi'
DEPLOYMENT_GROUP: 'dev-single-instance'
WAIT: 'false'
S3_BUCKET: 'ss-codedeploy-repo'
VERSION_LABEL: 'dev-webapi-$BITBUCKET_BUILD_NUMBER.zip'
So as you can see I have multiple steps for running test cases but I would finally build the binaries and deploy the code in final step. I could have broken it into separate steps but I dont want to waste the minutes of having to use another step because cloning and copying the artifact takes some time. Right now there are three steps it could have been broken into 4. where the 4th one would have been the deployment step. I hope this brings some clarity.
Also you can modify the names of the deployment groups as per your needs and can have up to 50 deployment groups :)
Little did I know, it's intentional that deploy happens in one step and you can only define on one step the deployment environment. The following setup is what worked for us (plus the appropriate separate git-ftp files):
image: ubuntu:18.04
definitions:
steps:
- step: &build
name: Build
condition:
changesets:
includePaths:
# Only run npm if anything in the build directory was touched
- "build/**"
image: node:15.0.1
script:
- echo 'build initiated'
- cd build
- npm install
- npm run prod
- echo 'build complete'
artifacts:
- themes/factor/css/**
- themes/factor/js/**
- step: &deploy
name: Deploy
deployment: Staging
script:
- echo 'Installing server dependencies'
- apt-get update -q
- apt-get install -qy software-properties-common
- add-apt-repository -y ppa:git-ftp/ppa
- apt-get update -q
- apt-get install -qy git-ftp
- echo 'All dependencies installed'
- echo 'Transferring changes'
- git ftp init --user $FTP_USER --passwd $FTP_PASSWORD $FTP_ADDRESS push --force --changed-only -vv
- echo 'File transfer complete'
pipelines:
branches:
master:
- step: *build
- step:
<<: *deploy
deployment: Production
dev:
- step: *build
- step: *deploy
I guess we cannot use combine the Deployment with either Artifact or Cache.
If I use standalone Deployment, so I can use the same deployment for multiple steps (as my screenshot).
In case I add cache/artifact, will get same error as yours.
Just got the same issue today.
I don't think there's currently a solution for this except rewrite the steps to not run two steps in on environment.
Waiting on https://jira.atlassian.com/browse/BCLOUD-18261 which planned to be released in July.
Related https://community.atlassian.com/t5/Bitbucket-questions/The-deployment-environment-test-in-your-bitbucket-pipelines-yml/qaq-p/971584
This is currently not available. They do have a ticket and it says it's being worked on. The best workaround currently appears to be creating multiple developer variable environments for steps that use the same variables.
Ex:
- step:
<<: *InitialSetup
deployment: Beta-Setup
- step:
<<: *Build
deployment: Beta-Build
From the comments on the ticket:
Hey everyone,
I know this is a long-winded workaround, and someone has probably already mentioned it, but I got around the issue by setting up "sub environments", one for each step. E.g. instead of having a "Staging" environment, I set up a "Staging Build" and "Staging Deploy" environment, and just had to duplicate the variables if necessary. I did the same for production.
Having to setup and maintain all these environments and variables can be a pain, but one can automate this to prevent human error, through setting up an OAuth client tool that interfaces with the API (you just need the "pipelines" scope), if one can be bothered to go to the effort (as I have: https://blog.programster.org/bitbucket-create-oauth-client-credentials).
I can't wait for this feature to be completed as that is the "real" solution, and a lot less effort!
With your cases, to solve the problems, you either solve the errors as following options:
Combine all steps into one big step
Or create different deployment variable group Staging DeployChanges and Staging DeployComplied, this way may lead to duplicate variable
ex:
- step: &deploychanges
name: Deploy_Changes
deployment: Staging DeployChanges
script:
- ....
- step: &deploycompiled
name: Deploy_Compiled
deployment: Staging DeployComplied
....

gitlab Cypress generate allure report

I have my .gitlab-ci.yml file as follows:
image: cypress/base:14.16.0
stages:
- test
test:
stage: test
script:
- npm install
- npm run scripts
where scripts is --> cypress run --spec cypress/integration/UI/myScript.feature
when adding another command after the scripts parameter to generate allure report, The gitlab pipeline is throwing me an error of JAVA home path not being set to generate allure report.
ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH
So I updated my script to something like this:
image: cypress/base:14.16.0
stages:
- test
- allure
test:
stage: test
script:
- npm install
- npm run clean:allure
- npm run scripts
allure_report:
stage: allure
when: always
image: timbru31/java-node
dependencies:
- test
script:
- npm install
- npm run generate-allure-report
artifacts:
when: always
paths:
- cypress/reportsAllure/allure-report/
- cypress/reportsAllure/allure-results/
where generate-allure-report is --> allure generate cypress/reportsAllure/allure-results --clean -o cypress/reportsAllure/allure-report
But here empty reports are generated. does anyone know what artifacts I need to pass from the first stage onto next in order to generate allure report ?
This works for me, but I'm using the default folder locations so you'd need to change those same as artifacts paths and append -o folder/allure-report where appropriate
stages:
- test
- allure
- deploy
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules
.download_history: &download_history
after_script:
- apt-get install -y unzip
- mkdir backup && cd backup || true
- "curl --location --output report.zip --request GET \"https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/artifacts/master/download?job=pages\" --header \"Authorization: Bearer ${CI_DEPLOY_TOKEN}\" || true"
- (unzip report.zip) || true
- cd ../
- (cp -r backup/public/history/ allure-results/history/) || true
.test_template: &test_template
image:
name: cypress/included:7.5.0
entrypoint: [""]
stage: test
variables:
CY_RUN_ID: ${CI_JOB_ID}
script:
- export CYPRESS_VIDEO=false
- npm install
- ./node_modules/.bin/cypress run --headless --env allure=true
artifacts:
when: always
paths:
- allure-results/
smoke:
<<: *test_template
<<: *download_history
allure_report:
stage: allure
when: always
image:
name: ubuntu:latest
entrypoint: [""]
dependencies:
- smoke
variables:
DEBIAN_FRONTEND: noninteractive
TZ: Europe/London
before_script:
- apt-get update
- apt-get install -y default-jdk wget unzip
- mkdir /work/
- wget https://github.com/allure-framework/allure2/releases/download/2.13.8/allure-2.13.8.zip -P /work/
- unzip /work/allure-2.13.8.zip -d /work/
script:
- /work/allure-2.13.8/bin/allure generate allure-results --clean -o allure-report
artifacts:
when: always
paths:
- allure-report/
- allure-results/
only:
- master
pages:
stage: deploy
when: always
dependencies:
- allure_report
script:
- mv allure-report/ public/
artifacts:
paths:
- public
expire_in: 30 days
only:
- master

How to start a selenoid on gitlab-ci?

I'm trying to run tests on gitlab-ci, but I don't know which command to start selenoid.
Locally, this command looks like ./ Cm selenoid start But how to specify in the case of starting selenoid from the service, I do not know.
This is my .gitlab-ci.yml:
stages:
- testing
ya_test:
stage: testing
tags:
- docker
services:
- selenoid/chrome
image: python:3.9-alpine
before_script:
- pip3 install -r requirements.txt
- selenoid/chrome start #???????
script:
- pytest -s
allow_failure: true
And what address to specify in the test fixture? localhost:4444?
Thanks for the help!
For launching Selenoid with Chrome try this yml.
Use webdriver.Remote(command_executor="http://selenoid__chrome:4444",
options=chrome_options, desired_capabilities=DesiredCapabilities.CHROME) to connect Chrome and add no-sandbox to options.
image: python:3.8
stages:
- test
test:
stage: test
services:
- name: aerokube/selenoid
- name: selenoid/chrome:89.0
before_script:
- echo "Install environment"
- apt-get update -q -y
- pip3 install -r requirements.txt
script:
- echo "Run all tests"
- py.test -s -v --junitxml=report.xml test.py
# if you want detailed report at GitLab add artifacts
artifacts:
when: always
reports:
junit: report.xml

Azure pipeline with custom variables as condition

We would like to optimize our pipelines to avoid running steps that are not needed for the particular case. I have created the variable shouldTriggerAnyBuild but it always seems to be true (or ignored), as the specified steps always runs, even though the none of the steps later, where the conditions are combined from, are run.
What is the problem with the script, or how can I debug it?
trigger:
- master
- stage
- release/*
pool:
vmImage: 'macOS-latest'
variables:
shouldTriggerAnyBuild: $[ or(and(eq(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceVersionMessage'], 'release:'))), eq(variables['Build.SourceBranch'], 'refs/heads/stage'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')) ]
steps:
- task: UseRubyVersion#0
inputs:
versionSpec: '~> 2.6'
- script: echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;android-29;google_apis;x86'
displayName: 'install/setup android sdkmanager'
condition: variables.shouldTriggerAnyBuild
- script: gem install bundler
displayName: 'gem install bundler'
condition: variables.shouldTriggerAnyBuild
- script: bundle install
displayName: 'bundle install'
condition: variables.shouldTriggerAnyBuild
- script: bundle exec fastlane ciBuildDev
displayName: 'build dev'
condition: and(eq(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceVersionMessage'], 'release:')))
- script: bundle exec fastlane ciDeployToTest
displayName: 'build stage'
condition: eq(variables['Build.SourceBranch'], 'refs/heads/stage')
- script: bundle exec fastlane ciDeployToGooglePlay
displayName: 'build release'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')
- task: PublishBuildArtifacts#1
displayName: "Publish artifacts .apk"
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')
inputs:
PathtoPublish: ./app/build/outputs/apk/prod/app-prod-unsigned.apk
ArtifactName: Prod_app_apk
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops
What is the problem with the script?
Conditions are written as expressions. condition: variables.shouldTriggerAnyBuild won't take effect.
Instead you can use condition: eq(variables['SHOULDTRIGGERANYBUILD'], 'True') for conditional step. I think it's the direct cause of your issue. Also, feel free to use condition: eq(variables['shouldTriggerAnyBuild'], 'True') if you want, it also works.
How to debug it?
Here's a quick way to debug the value when necessary:
1.Change the second value to a impossible variable, then you can check the real value for your custom variable when the step is skipped:
2.If you want to make all thing clear, then you can do something like:
- task: CmdLine#2
inputs:
script: |
echo Hello world
condition: eq(and(eq(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceVersionMessage'], 'release:'))),'True')
- task: CmdLine#2
inputs:
script: |
echo Hello world
condition: eq(eq(variables['Build.SourceBranch'], 'refs/heads/stage'),'True')
- task: CmdLine#2
inputs:
script: |
echo Hello world
condition: eq(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'),'True')
Since your variable is the combination of and(eq(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceVersionMessage'], 'release:'))), eq(variables['Build.SourceBranch'], 'refs/heads/stage') and startsWith(variables['Build.SourceBranch'], 'refs/heads/release/') via Or function, you can divide them to debug how the shouldTriggerAnyBuild is expanded as always True.
In this way, you can debug them easily to find how the variable is expanded:
Note:
1.Or function: Evaluates True if any parameter is true.
2.Most of the time we choose to skip current task if one of previous tasks have failed, so you can consider combining succeeded() and your variable shouldTriggerAnyBuild. Examples here.
After a lot of trial and error we found that there was Build.SourceVersionMessage that meant we could not use it as a variable, so we ended up with a hack as shown below:
trigger:
- master
- stage
- release/*
pool:
vmImage: "macOS-latest"
variables:
# we can't use Build.SourceVersionMessage up here because it's not defined when the variables is set here
isMaster: ${{eq(variables['Build.SourceBranch'], 'refs/heads/master')}}
isStage: ${{eq(variables['Build.SourceBranch'], 'refs/heads/stage')}}
isRelease: ${{startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')}}
releaseMessagePrefix: "release:"
jobs:
- job:
steps:
- task: UseRubyVersion#0
displayName: "set ruby version"
inputs:
versionSpec: "~> 2.6"
- task: Bash#3
displayName: "set commitMessage variable"
inputs:
targetType: inline
script: echo '##vso[task.setvariable variable=commitMessage]$(Build.SourceVersionMessage)'
- script: echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;android-29;google_apis;x86'
displayName: "install/setup android sdkmanager"
condition: or(and(variables.isMaster, not(startsWith(variables.commitMessage, variables.releaseMessagePrefix))), ${{variables.isStage}}, ${{variables.isRelease}})
- script: gem install bundler
displayName: "gem install bundler"
condition: or(and(variables.isMaster, not(startsWith(variables.commitMessage, variables.releaseMessagePrefix))), ${{variables.isStage}}, ${{variables.isRelease}})
- script: bundle install
displayName: "bundle install"
condition: or(and(variables.isMaster, not(startsWith(variables.commitMessage, variables.releaseMessagePrefix))), ${{variables.isStage}}, ${{variables.isRelease}})
- script: bundle exec fastlane ciBuildDev
displayName: "Build: dev"
condition: and(variables.isMaster, not(startsWith(variables.commitMessage, variables.releaseMessagePrefix)))
- script: bundle exec fastlane ciDeployToTest
displayName: "Build: stage"
condition: ${{variables.isStage}}
- script: bundle exec fastlane ciDeployToGooglePlay
displayName: "Build: release"
condition: ${{variables.isRelease}}
- task: PublishBuildArtifacts#1
displayName: "Publish: .apk"
condition: ${{variables.isRelease}}
inputs:
PathtoPublish: ./app/build/outputs/apk/prod/app-prod-unsigned.apk
ArtifactName: Prod_app_apk

Resources