Azure DevOps: How to commit from a pipeline
How to make a commit from an Azure pipeline triggered by a CI change, on the same branch.
This is another post on Azure Pipelines, this time on how to commit from a pipeline. I had to this recently, my use-case was a CI-triggered pipeline who needed to make a commit on the branch it was triggered from.
Let’s see how to do this in a few steps.
Allow the Build service to make changes on the repo
Before jumping in some yaml, we need to grant the Contribute permission to the Build Service account, aka the identity used by the future pipeline.
This is done in the project’s settings, in the security settings of your repository:
Depending on what you need to do, you might need to set other permissions, like Create branch or Create tag. In my case Contribute was enough.
Allow the pipeline to run Git commands
The first steps of the pipeline should be the following:
1
2
3
4
5
6
7
steps:
- checkout: self
persistCredentials: true
- bash: |
git config --global user.email "pipeline@dev.azure.com"
git config --global user.name "Azure DevOps Pipeline"
name: GitConfig
The checkout
step with persistCredentials
set to true
allows the next steps to access to OAuth token for interacting with Azure Repos.
The GitConfig
step configures the name and email of the “user” associated to the future commits.
Make some changes to commit
Then you can add the step(s) that will make the changes to commit. This will depend on your scenario, as an example I will append a line in a log file at the root of the repo:
1
2
3
4
5
steps:
# ...Previous steps hidden for readability...
- bash: |
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") - Pipeline processed by agent $AGENT_NAME" >> pipeline-history.log
name: ChangeSomethingToCommit
Of course my real world scenario was way more interesting than that, and will deserve an upcoming post 😉
Finally, commit the changes !
This is getting interesting as we add a final step in the pipeline to commit the changes:
1
2
3
4
5
6
7
8
9
10
steps:
# ...Previous steps hidden for readability...
- bash: |
[[ $(git status --porcelain) ]] || { echo "No changes, exiting now..."; exit 0; }
echo "Changes detected, let's commit them !"
git add -A
git commit -m "feat: Update pipeline history from Azure Pipelines [skip ci]"
branchName=$(echo "$BUILD_SOURCEBRANCH" | sed 's/refs\/heads\///')
git push origin "HEAD:$branchName"
name: CommitChanges
There are a few interesting things in this script:
- Line 4: the
git status --porcelain
command detects if there is anything to commit, so the script can end early if needed - Line 7: adding
[skip ci]
in the commit message prevents another pipeline to trigger (source), we want to avoid infinite pipeline loops to trigger 😅 - Line 8: the use of the
Build.SourceBranch
predefined variable with thesed
command extracts the full name of the branch, even if if contains slashes:refs/head/feat/your-feature
becomesfeat/your-feature
- Line 9: when pushing the changes, the pipeline will be in detached head, so the way to overcome this is to use the
git push origin HEAD:name-of-the-branch
syntax
Once again this script fits my case as needed, you might adapt it to fit yours.
Full pipeline
Here is the full pipeline for the record:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
trigger:
branches:
exclude:
- main
steps:
- checkout: self
persistCredentials: true
- bash: |
git config --global user.email "pipeline@dev.azure.com"
git config --global user.name "Azure DevOps Pipeline"
name: GitConfig
- bash: |
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") - Pipeline processed by agent $AGENT_NAME" >> pipeline-history.log
name: ChangeSomethingToCommit
- bash: |
[[ $(git status --porcelain) ]] || { echo "No changes, exiting now..."; exit 0; }
echo "Changes detected, let's commit them !"
git add -A
git commit -m "feat: Update pipeline history from Azure Pipelines [skip ci]"
branchName=$(echo "$BUILD_SOURCEBRANCH" | sed 's/refs\/heads\///')
git push origin "HEAD:$branchName"
name: CommitChanges
All the steps were present in the previous snippets, just notice the trigger
who prevents the pipeline to run on the main
branch, as committing on the main requires a pull request. In my real world scenaro there is also a path filter so that the pipeline is triggered only when selected files are changed. And yes, this is also another thing that depends on your use-case.
To show that it works, triggering the pipeline will add commits like this one in the history: Notice the Azure DevOps Pipeline “user”
Wrapping-up
In this post we have seen how to make a commit from a pipeline triggered by a CI change. Nothing new or fancy, but a few gotchas along the way, and something that can be useful in many situations. Thanks for reading 🤓