This is known as ‘Continuous Integration’ (CI). But how do you do this in Azure Devops?
This article succintly introduces the concept of Pipelines in Azure Devops.
Azure Devops calls this ‘Pipelines’ - the two main sections are ‘Builds’ and ‘Releases’. Both are ‘pipelines’
A ‘Build pipeline’ means to check out the code and have the compiler (or whatever exe) build the code. You can specify what the build task is using a PowerShell script. A build pipeline can be auto-triggered when code is checked in.
A build pipeline generates an “artifact” (a zip file of the compiled source).
A ‘Release pipeline’ means to take the output of the build and actually deploy it on a server. A release pipeline can be triggered after an artifact is created (from the build pipeline).
Let’s take this site as an example. It’s a bunch of .md files and images which need to be run through Hugo.exe
(a static site generator).
A build pipeline is created that checks out the repository (of .md files) and calls a Hugo task to output the statically generated html into an “artifact”.
The release pipeline takes the artifact and deploys it (my deploy task is a PowerShell script, you can also use Bash scripts too if that’s your thing).
Generally, there are tokens that are contained within the deploy script in the release pipeline. This is so the “artifact” can be deployed on an unrelated server. You should always use tokens (as opposed to credentials) as tokens can be changed and updated easily.
Be very careful of who can view your Azure Devops Pipelines!
Even if you mask variables, there are ways to print out the variables into the log files.
By default, git does not check out submodules (a reference to another git repo), You have to add submodules: true
in the checkout step.
Here is what my build definition looks like - you can go to https://aka.ms/yaml for the specifications.
trigger:
- master
pool:
vmImage: 'vs2017-win2016'
steps:
- checkout: self
submodules: true
displayName: 'Checkout repository including submodules'
- task: HugoTask@1
displayName: 'Generate Hugo site'
inputs:
destination: '$(Build.ArtifactStagingDirectory)'
- task: PublishPipelineArtifact@0
displayName: 'Publish Hugo site as artifact'
inputs:
artifactName: 'docs' #Note the artifact name, which you might need to reference in your scripts in the 'release' pipeline
targetPath: '$(Build.ArtifactStagingDirectory)'
Ref: Official doc for predefined variables.
Notes
In 2019, there is a ‘classic editor’ version of this where you can pick the tasks from a list, not sure if this will be removed, but it is more user friendly.
When you create a new build pipeline, you have to select where the source is coming from, here is what’s supported (2019):
When you edit a release, it looks like this:
This says “Take the output artifact from the build pipeline, and run a task.”.
“Continuous deployment trigger” means that this will automatically happen when an artifact is available.
When you click on 1 job, 1 task
, you will be able to see exactly what the release does.
In my case, it takes the artifact contents and pushes changes into my “github pages” repository (which is a totally different account than Azure Devops).
The PowerShell script is as follows - (note that I hardcode the variables here to make things simpler):
$docPath = '$(System.DefaultWorkingDirectory)\_coderdocs\*'
$githubusername = 'efficienthacks'
$githubemail = 'email@doesnt-matter.com'
$githubaccesstoken = '[generate from github]'
$repositoryname = 'coderdocs'
$commitMessage = 'Automated Release $(Release.ReleaseId)'
$branchName = 'master'
$defaultWorkingDirectory = '$(System.DefaultWorkingDirectory)'
Write-Host "Cloning existing GitHub repository" # these will be seen in the logs
git clone https://${githubusername}:$githubaccesstoken@github.com/$githubusername/$repositoryname.git --branch=$branchName $defaultWorkingDirectory\coderdocs
if ($lastexitcode -gt 0)
{
Write-Host "##vso[task.logissue type=error;]Unable to clone repository - check username, access token and repository name. Error code $lastexitcode"
[Environment]::Exit(1)
}
$to = "$defaultWorkingDirectory\coderdocs"
Write-Host "Copying new documentation into branch"
Copy-Item $docPath $to -recurse -Force #this copies my artifact which I named 'docs' (from the build pipeline) so I will have all the html files in $defaultWorkingDirectory\coderdocs\docs
Write-Host "Committing the GitHub repository"
cd $defaultWorkingDirectory\coderdocs
git config core.autocrlf false
git config user.email $githubemail
git config user.name $githubusername
git add *
git commit --allow-empty -m $commitMessage
if ($lastexitcode -gt 0)
{
Write-Host "##vso[task.logissue type=error;]Error committing - see earlier log, error code $lastexitcode"
[Environment]::Exit(1)
}
git push --quiet
if ($lastexitcode -gt 0)
{
Write-Host "##vso[task.logissue type=error;]Error pushing to $branchName branch, probably an incorrect Personal Access Token, error code $lastexitcode"
[Environment]::Exit(1)
}
Credit: JamesRandall
Notes
When you create a release, you will choose the artifact(s), note that the artifact does not need to be generated from your Build Pipeline. As of 2019, here are the supported artifact sources:
get yourself on the email list
comments powered by Disqus