Azure pipeline with custom variables as condition - yaml

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

Related

Azure Spring Gradle: files in src/test/resouces not accessible

Im building Spring boot app using gradle. Integration tests (Spock) need to access code/src/resouces/docker-compose.yml file to prepare TestContainers container:
static DockerComposeContainer postgresContainer = new DockerComposeContainer(
ResourceUtils.getFile("classpath:docker-compose.yml"))
The git file structure is:
- code
- src
- main
- test
- resources
- docker-compose.yml
This is working fine on my local machine, but once I run it in Azure pipeline, it gives
No such file or directory: '/__w/1/s/code/build/resources/test/docker-compose.yml'
My pipeline yaml is like bellow. I use Ubuntu container with Java17 as I need to build with 17 but Azure's latest is 11 (maybe this plays any role in the error I get?)
trigger: none
stages:
- stage: Test
displayName: Test Stage
jobs:
- job: Test
pool:
vmImage: 'ubuntu-22.04'
container: gradle:7.6.0-jdk17
variables:
- name: JAVA_HOME_11_X64
value: /opt/java/openjdk
displayName: Test
steps:
- script: java --version
displayName: Java version
- script: |
echo "Build reason is: $(Build.Reason)"
displayName: Build reason
- checkout: self
clean: true
- task: Gradle#2
displayName: 'Gradle Build'
enabled: True
inputs:
javaHomeSelection: 'path'
jdkDirectory: '/opt/java/openjdk'
wrapperScript: code/gradlew
cwd: code
tasks: clean build
publishJUnitResults: true
jdkVersionOption: 1.17
Thanks for help!
I've solved it by "workarround" - I realied that I dont need to use the container with jdk17 that was causing the problem (could not access files on host machine of course)
The true is the Azure silently supports Jkd17 by directive: jdkVersionOption: 1.17
But once someone will need to use the container to build the code and access the repository files that are not on classpath, the problem will raise again
trigger: none
stages:
- stage: Test
displayName: Test Stage
jobs:
- job: Test
pool:
vmImage: 'ubuntu-22.04'
displayName: Test
steps:
- script: java --version
displayName: Java version
- script: |
echo "Build reason is: $(Build.Reason)"
displayName: Build reason
- checkout: self
clean: true
- task: Gradle#2
displayName: 'Gradle Build'
enabled: True
inputs:
wrapperScript: server/code/gradlew
cwd: server/code
tasks: test
publishJUnitResults: true
jdkVersionOption: 1.17
Please follow Azure pipeline issue for more details

.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

Problem in creating a Yaml pipeline in azure Devops

these are the error that I can't fix image
trigger:
- master
schedules:
- cron: "0 17 * * * 6"
displayName: ogni giorno alle 17
branches:
include:
- master
always: false
pool:
vmImage: 'windows-2019'
variables:
buildConfiguration: 'Release'
parameters:
RestoreBuildProjects: '**/*.csproj'
steps:
- task: DotNetCoreCLI#2
displayName: 'Install .NET Core SDK'
inputs:
version: 5.0.x
performMultiLevelLookup: true
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: 'restore'
projects: '$(Parameters.RestoreBuildProjects)'
vstsFeed:'f52d4c22-658b-47a0-a62f-134d5435cc85'
- task: DotNetCoreCLI#2
displayName: Build
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
publishWebProjects: True
arguments: '-c Release --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: True
- task: PublishBuildArtifacts#1
displayName: 'Publish Artifact'
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'myWebsiteName'
I created this pipeline for an API ASP.NET Core based on the restful model but I can't make it work, as I try to run it it breaks, I've looked for the errors that Azuredevops gave me but I can't for any reason fix them. see attached email. Hope someone can help me. this is my first approach with YAML pipelines as I used to make them with the graphic tool integrated in Azure DevOPs.
I have helped to modify your yaml file and it shows no error on my test. Here is the yaml:
parameters:
- name: RestoreBuildProjects
type: string
default: '**/*.csproj'
trigger:
- master
schedules:
- cron: "0 17 * * * 6"
displayName: ogni giorno alle 17
branches:
include:
- master
always: false
pool:
vmImage: 'windows-2019'
variables:
buildConfiguration: 'Release'
steps:
- task: UseDotNet#2
inputs:
packageType: 'sdk'
version: '5.0.X'
performMultiLevelLookup: true
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: 'restore'
projects: '$(Parameters.RestoreBuildProjects)'
feedsToUse: 'select'
# vstsFeed:'f52d4c22-658b-47a0-a62f-134d5435cc85'
- task: DotNetCoreCLI#2
displayName: Build
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
publishWebProjects: True
arguments: '-c Release --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: True
- task: PublishBuildArtifacts#1
displayName: 'Publish Artifact'
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'myWebsiteName'
And please follow this step to use the yaml:
Create a new pipeline with the yaml edit.
Select the 'Starter Pipeline', this will help you create a new yaml file.
Copy the script to the pipeline.
Click the 'settings' on your restore task and select the feed you want to use. Then click the Add button. This step will help you input the full feed ID.

azure hosted mac pipeline , suggestion on how to make it faster , currently very slow , on desktop 5 min , on server near 20

i have this pipeline which works great but is very time consuming each build when in
desktop xcode it takes less then 5 min to build, in the mac cloud server its takes 20
especlly the parts where it downloads configures and compiles native gems in the
"installing gems and cocoapods script" task bellow
and then there is the Fastlane task bellow : "run fastlane"
where the compilation part is very slow ...
I'm looking for tips and tricks or organize better my pipeline for faster build
pool:
vmImage: 'macOS 10.14'
variables:
scheme: ''
sdk: 'iphoneos'
configuration: 'Release'
jobs:
- job: self_hosted_connect
timeoutInMinutes: 10
pool: Default
steps:
- task: CopyFiles#2
inputs:
SourceFolder: '$(Agent.HomeDirectory)/../${{parameters.Folderpath}}'
Contents: '**'
TargetFolder: '$(build.artifactstagingdirectory)'
- task: PublishBuildArtifacts#1
inputs:
pathToPublish: '$(build.artifactstagingdirectory)'
artifactName: 'ios_artifacts'
- job: mac_agent
dependsOn: self_hosted_connect
timeoutInMinutes: 30
pool:
vmImage: 'macOS 10.14'
steps:
- script: echo 'Setting up macOS 10.14'
- task: UseRubyVersion#0
inputs:
versionSpec: '>= 2.4'
addToPath: true
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: 'ios_artifacts'
downloadPath: '$(System.ArtifactsDirectory)'
- script: |
gem install --no-document bundler
bundle update --bundler
bundle install --retry=3 --jobs=4
gem install --no-document fastlane
pod deintegrate
gem install cocoapods
pod install
pod --version
workingDirectory: '$(System.ArtifactsDirectory)/ios_artifacts'
displayName: 'installing gems and cocoapods'
- script: |
echo 'Start invoking Fastfile'
fastlane release --verbose
echo 'Done invoking Fastfile'
failOnStderr: false
workingDirectory: '$(System.ArtifactsDirectory)/ios_artifacts'
displayName: 'run fastlane'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'Artifacts'
publishLocation: 'Container'
- job: copy_back_files_to_self_hosted_connect
dependsOn: mac_agent
timeoutInMinutes: 30
pool: Default
steps:
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: 'Artifacts'
itemPattern: |
**/*.ipa
**/*manifest.plist*
downloadPath: '$(System.ArtifactsDirectory)'
- task: CopyFiles#2
inputs:
SourceFolder: '$(System.ArtifactsDirectory)'
Contents: |
**/*.ipa
**/*manifest.plist*
TargetFolder: '$(Agent.HomeDirectory)/../${{parameters.FolderCompile}}'
UPDATE USING CACHE
i added the cache section but it gives me an error when i run it
Resolving key:
2020-10-01T11:39:48.3151700Z - gems [string]
2020-10-01T11:39:48.3241700Z - "Darwin" [string]
2020-10-01T11:39:48.3720810Z - my.gemspec [file] (not found)
2020-10-01T11:39:48.3882910Z ##[error]One or more errors occurred. (File not found: my.gemspec)
2020-10-01T11:39:48.4094600Z ##[section]Finishing: Cache gems
pool:
vmImage: 'macOS 10.14'
variables:
scheme: ''
sdk: 'iphoneos'
configuration: 'Release'
BUNDLE_PATH: $(Pipeline.Workspace)/.bundle
jobs:
- job: self_hosted_connect
timeoutInMinutes: 10
pool: Default
steps:
- task: CopyFiles#2
inputs:
SourceFolder: '$(Agent.HomeDirectory)/../${{parameters.Folderpath}}'
Contents: '**'
TargetFolder: '$(build.artifactstagingdirectory)'
- task: PublishBuildArtifacts#1
inputs:
pathToPublish: '$(build.artifactstagingdirectory)'
artifactName: 'ios_artifacts'
- job: mac_agent
dependsOn: self_hosted_connect
timeoutInMinutes: 30
pool:
vmImage: 'macOS 10.14'
steps:
- script: echo 'Setting up macOS 10.14'
- task: UseRubyVersion#0
inputs:
versionSpec: '>= 2.4'
addToPath: true
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: 'ios_artifacts'
downloadPath: '$(System.ArtifactsDirectory)'
- task: Cache#2
inputs:
key: 'gems | "$(Agent.OS)" | my.gemspec'
restoreKeys: |
gems | "$(Agent.OS)"
gems
path: $(BUNDLE_PATH)
displayName: Cache gems
- script: |
gem install --no-document bundler
bundle update --bundler
bundle install --retry=3 --jobs=4
gem install --no-document fastlane
workingDirectory: '$(System.ArtifactsDirectory)/ios_artifacts'
displayName: 'installing gems and cocoapods'
- script: |
pod deintegrate
gem install cocoapods
pod install
pod --version
workingDirectory: '$(System.ArtifactsDirectory)/ios_artifacts'
displayName: 'pod and cocoapods install '
- script: |
echo 'Start invoking Fastfile'
fastlane release --verbose
echo 'Done invoking Fastfile'
failOnStderr: false
workingDirectory: '$(System.ArtifactsDirectory)/ios_artifacts'
displayName: 'run fastlane'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'Artifacts'
publishLocation: 'Container'
- job: copy_back_files_to_self_hosted_connect
dependsOn: mac_agent
timeoutInMinutes: 30
pool: Default
steps:
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: 'Artifacts'
itemPattern: |
**/*.ipa
**/*manifest.plist*
downloadPath: '$(System.ArtifactsDirectory)'
- task: CopyFiles#2
inputs:
SourceFolder: '$(System.ArtifactsDirectory)'
Contents: |
**/*.ipa
**/*manifest.plist*
TargetFolder: '$(Agent.HomeDirectory)/../${{parameters.FolderCompile}}'
You may consider and check caching gems as it is shown here:
variables:
BUNDLE_PATH: $(Pipeline.Workspace)/.bundle
steps:
- task: Cache#2
inputs:
key: 'gems | "$(Agent.OS)" | my.gemspec'
restoreKeys: |
gems | "$(Agent.OS)"
gems
path: $(BUNDLE_PATH)
displayName: Cache gems
- script: bundle install
You can try to use Self-hosted agents instead of Microsoft-hosted agents.
In Microsoft-hosted agents, each time you run a pipeline, you get a
fresh virtual machine. The virtual machine is discarded after one
use.
In Self-hosted agents, machine-level caches and configuration persist
from run to run , which can boost speed.
See Capabilities and limitations of Microsoft-hosted agents in this document.
If you want to install and use a self-hosted agent, you can refer to this document.

Migrating Azure Pipelines Deployments to GitHub Actions

I have an Azure Pipeline build which builds NuGet packages on Windows, MacOS and Linux and a deployment job that then takes the NuGet packages built from the Windows image and publishes them to Azure Artefacts, GitHub packages and NuGet.
With GitHub actions, I have managed to build NuGet packages on Windows, MacOS and Linux but I don't know how to then create a deployment job as that feature doesn't exist. I think I need to create a new deployment job that is triggered from a successful build job, pick up the NuGet packages from the successful build and then push them. However, I don't see a trigger type that can do that.
GitHub Actions Build YAML
name: Build
on:
push:
branches:
- '*'
tags:
- '*'
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
MINVERBUILDMETADATA: build.$(Build.BuildId)
jobs:
build:
name: Build-${{matrix.os}}
runs-on: ${{matrix.os}}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout#v2
with:
lfs: true
fetch-depth: 0
- name: 'Git Fetch Tags'
run: git fetch --tags
shell: pwsh
- name: 'Install .NET Core SDK'
uses: actions/setup-dotnet#v1
with:
dotnet-version: 3.1.301
- name: 'Dotnet Tool Restore'
run: dotnet tool restore
shell: pwsh
- name: 'Dotnet Cake Build'
run: dotnet cake --target=Build
shell: pwsh
- name: 'Dotnet Cake Test'
run: dotnet cake --target=Test
shell: pwsh
- name: 'Dotnet Cake Pack'
run: dotnet cake --target=Pack
shell: pwsh
- name: 'Publish Artefacts'
uses: actions/upload-artifact#v1.0.0
with:
name: ${{matrix.os}}
path: './Artefacts'
Azure Pipeline Build & Deployment YAML
trigger:
branches:
include:
- '*'
tags:
include:
- '*'
variables:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
MINVERBUILDMETADATA: build.$(Build.BuildId)
stages:
- stage: Build
jobs:
- job: Build
strategy:
matrix:
Linux:
matrixName: Ubuntu
vmImageName: ubuntu-latest
Mac:
matrixName: Mac
vmImageName: macos-latest
Windows:
matrixName: Windows
vmImageName: windows-latest
pool:
vmImage: $(vmImageName)
timeoutInMinutes: 10
steps:
- checkout: self
lfs: true
- task: UseDotNet#2
displayName: 'Install .NET Core SDK'
inputs:
packageType: 'sdk'
useGlobalJson: true
- pwsh: 'dotnet tool restore'
displayName: 'Dotnet Tool Restore'
failOnStderr: true
- pwsh: 'dotnet cake --target=Build'
displayName: 'Dotnet Cake Build'
failOnStderr: true
- pwsh: 'dotnet cake --target=Test'
displayName: 'Dotnet Cake Test'
failOnStderr: true
- pwsh: 'dotnet cake --target=Pack'
displayName: 'Dotnet Cake Pack'
failOnStderr: true
- task: PublishTestResults#2
displayName: 'Publish Test Results'
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '**/*.trx'
- task: PublishCodeCoverageResults#1
inputs:
codeCoverageTool: cobertura
summaryFileLocation: '**/*.cobertura.xml'
- publish: './Artefacts'
artifact: $(matrixName)
displayName: 'Publish Artefacts'
- stage: Deploy
jobs:
- deployment: AzureArtefacts
displayName: 'Azure Artefacts'
condition: ne(variables['Build.Reason'], 'PullRequest')
pool:
vmImage: windows-latest
environment: 'Azure Artefacts'
strategy:
runOnce:
deploy:
steps:
- task: NuGetToolInstaller#1
displayName: 'NuGet Install'
- task: NuGetAuthenticate#0
displayName: 'NuGet Authenticate'
- pwsh: nuget push $(Agent.BuildDirectory)\Windows\*.nupkg -Source $(AzureArtefactsSource) -ApiKey AzureArtifacts -SkipDuplicate
displayName: 'NuGet Push'
failOnStderr: true
- deployment: GitHub
condition: ne(variables['Build.Reason'], 'PullRequest')
pool:
vmImage: windows-latest
environment: 'GitHub'
strategy:
runOnce:
deploy:
steps:
- task: NuGetToolInstaller#1
displayName: 'NuGet Install'
- pwsh: nuget source Add -Name GitHub -Source https://nuget.pkg.github.com/GITHUB-USERNAME -UserName GITHUB-USERNAME -Password $(GitHubPersonalAccessToken)
displayName: 'NuGet Add Source'
failOnStderr: true
- pwsh: nuget push $(Agent.BuildDirectory)\Windows\*.nupkg -Source GitHub -SkipDuplicate
displayName: 'NuGet Push'
failOnStderr: true
- deployment: NuGet
condition: and(ne(variables['Build.Reason'], 'PullRequest'), startsWith(variables['Build.sourceBranch'], 'refs/tags/'))
pool:
vmImage: windows-latest
environment: 'NuGet'
strategy:
runOnce:
deploy:
steps:
- task: NuGetToolInstaller#1
displayName: 'Install NuGet'
- pwsh: |
Get-ChildItem $(Agent.BuildDirectory)\Windows -Filter *.nupkg |
Where-Object { !$_.Name.Contains('preview') } |
ForEach-Object { nuget push $_ -Source https://api.nuget.org/v3/index.json -ApiKey $(NuGetApiKey) -SkipDuplicate }
displayName: 'NuGet Push'
failOnStderr: true
Migrating Azure Pipelines Deployments to GitHub Actions
Indeed, there is no such multi-stage feature for Github action at this moment.
Just as you suspect, we could create a deployment job to deploy the artifact. We could try to create a new job which needs the existing build job, in the new job, download the artifacts and push them to azure artifact, github packages, nuget:
jobs:
job_1:
name: Build
job_2:
name: Deploy
needs: job_1
runs-on: windows-latest
steps:
- name: Download math result for job 1
uses: actions/download-artifact#v1
with:
name: xxx
You could check the Github action jobs.<job_id>.needs and the sample for some more details.
Hope this helps.

Resources