Azure DevOps Integration
For projects hosted on Azure DevOps, you can use git-cliff to add the following to your changelog:
- Azure DevOps usernames
- Contributors list (all contributors / first time)
- Pull request links (associated with commits)
- PR titles and labels
If you have built from source, enable the azure_devops feature flag for the integration to work.
Setting up the remote
As default, remote upstream URL is automatically retrieved from the Git repository.
If that doesn’t work or if you want to set a custom remote, there are several ways to configure it:
Configuration file
Use the remote.azure_devops section in your cliff.toml:
[remote.azure_devops]
owner = "organization/project"
repo = "repository-name"
token = "" # Leave empty to use environment variable
Important: For Azure DevOps, the owner field must be in the format organization/project (e.g., myorg/myproject), not just the organization name.
Command line arguments
Use the --azure-devops-repo argument (takes values in ORGANIZATION/PROJECT/REPO format):
git cliff --azure-devops-repo myorg/myproject/myrepo
Environment variables
Use the AZURE_DEVOPS_REPO environment variable:
AZURE_DEVOPS_REPO="myorg/myproject/myrepo" git cliff
Authentication
Understanding rate limits
Create a Personal Access Token (PAT)
Follow
this guide for creating a personal access token.
Code (Read): To access repository information
Work Items (Read): Optional, for enhanced metadata
Navigate to User Settings → Personal Access Tokens
Click New Token
Set Organization: Select your organization or “All accessible organizations”
Set Scopes: Select Code (Read)
Create and copy the token
You can provide the access token in multiple ways:
Environment Variable (Recommended)
AZURE_DEVOPS_TOKEN="your_pat_here" git cliff --azure-devops-repo "myorg/myproject/myrepo"
git cliff --azure-devops-token "your_pat_here" --azure-devops-repo "myorg/myproject/myrepo"
Configuration File (Not Recommended)
[remote.azure_devops]
owner = "myorg/myproject"
repo = "myrepo"
token = "your_pat_here" # Not recommended for security
variables:
AZURE_DEVOPS_TOKEN: $(System.AccessToken)
steps:
- script: |
git cliff --azure-devops-repo "$(System.TeamProject)/$(Build.Repository.Name)"
displayName: 'Generate Changelog'
Azure DevOps Services vs Server
Azure DevOps Services (Cloud)
The default configuration works with Azure DevOps Services (dev.azure.com):
# Default: Azure DevOps Services
AZURE_DEVOPS_TOKEN="your_token" \
git cliff --azure-devops-repo "myorg/myproject/myrepo"
Azure DevOps Server (On-Premises)
For Azure DevOps Server (on-premises installations), use the AZURE_DEVOPS_API_URL environment variable:
AZURE_DEVOPS_API_URL="https://tfs.company.com" \
AZURE_DEVOPS_TOKEN="your_token" \
git cliff --azure-devops-repo "DefaultCollection/myproject/myrepo"
Or configure it in cliff.toml:
[remote.azure_devops]
owner = "DefaultCollection/myproject"
repo = "myrepo"
api_url = "https://tfs.company.com"
Advanced Configuration
Organization and Project Structure
Azure DevOps uses a hierarchical structure:
https://dev.azure.com/{organization}/{project}/_git/{repository}
In git-cliff configuration:
- owner:
{organization}/{project}
- repo:
{repository}
Example:
[remote.azure_devops]
owner = "mycompany/backend-team" # organization/project
repo = "api-service" # repository
TLS Certificate Issues
If you encounter certificate errors with on-premises servers, use the --use-native-tls flag:
git cliff --azure-devops-repo "org/project/repo" --use-native-tls
Or configure it in cliff.toml:
[remote.azure_devops]
owner = "myorg/myproject"
repo = "myrepo"
native_tls = true
Template Variables
You can use the following context for adding the remote to the changelog:
{
"azure_devops": {
"owner": "myorg/myproject",
"repo": "myrepo"
}
}
Example template:
https://dev.azure.com/{{ remote.azure_devops.owner }}/_git/{{ remote.azure_devops.repo }}/branchCompare?baseVersion=GT{{ previous.version }}&targetVersion=GT{{ version }}
Commit authors and PR data
For each commit, Azure DevOps-related values are added as a nested remote object:
{
"id": "8edec7fd50f703811d55f14a3c5f0fd02b43d9e7",
"message": "refactor(config): remove unnecessary newline from configs\n",
"group": "🚜 Refactor",
"remote": {
"username": "orhun",
"pr_title": "some things have changed",
"pr_number": 420,
"pr_labels": ["enhancement", "documentation"],
"is_first_time": false
}
}
Example template:
{% for commit in commits %}
* {{ commit.message | split(pat="\n") | first | trim }}\
{% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\
{% if commit.remote.pr_number %} in #{{ commit.remote.pr_number }}{%- endif %}
{%- endfor -%}
Output:
- feat(commit): add merge_commit flag to the context by @orhun in #389
- feat(args): set `CHANGELOG.md` as default missing value for output option by @sh-cho in #354
Contributors list
For each release, contributors data is added to the template context:
{
"version": "v1.4.0",
"commits": [],
"azure_devops": {
"contributors": [
{
"username": "orhun",
"pr_title": "some things have changed",
"pr_number": 420,
"pr_labels": ["enhancement"],
"is_first_time": true
},
{
"username": "cliffjumper",
"pr_title": "I love jumping",
"pr_number": 999,
"pr_labels": ["feature"],
"is_first_time": true
}
]
}
}
Example template for first-time contributors:
{% for contributor in azure_devops.contributors | filter(attribute="is_first_time", value=true) %}
* @{{ contributor.username }} made their first contribution in #{{ contributor.pr_number }}
{%- endfor -%}
Output:
- @orhun made their first contribution in #420
- @cliffjumper made their first contribution in #999
Group commits by PR labels
You can organize changelog entries by pull request labels:
{% for group, commits in commits | group_by(attribute="remote.pr_labels") %}
### {{ group | upper }}
{% for commit in commits %}
- {{ commit.message | split(pat="\n") | first }} (#{{ commit.remote.pr_number }})
{%- endfor %}
{% endfor %}
Configuration Example
Complete configuration example for an Azure DevOps-hosted project:
[remote.azure_devops]
owner = "myorganization/backend-project"
repo = "api-service"
# token = "" # Use AZURE_DEVOPS_TOKEN environment variable instead
[changelog]
header = """
# Changelog
All notable changes to this project will be documented in this file.
"""
body = """
{% for commit in commits %}
* {{ commit.message | split(pat="\n") | first | trim }}\
{% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\
{% if commit.remote.pr_number %} in #{{ commit.remote.pr_number }}{%- endif %}
{% endfor %}
{% if azure_devops.contributors | filter(attribute="is_first_time", value=true) %}
## New Contributors
{% for contributor in azure_devops.contributors | filter(attribute="is_first_time", value=true) %}
* @{{ contributor.username }} made their first contribution in #{{ contributor.pr_number }}
{%- endfor %}
{% endif %}
"""
footer = "<!-- generated by git-cliff -->"
Azure Pipelines Integration
Example azure-pipelines.yml configuration:
trigger:
tags:
include:
- v*
pool:
vmImage: 'ubuntu-latest'
variables:
AZURE_DEVOPS_TOKEN: $(System.AccessToken)
steps:
- script: |
# Install git-cliff
curl -sSL https://github.com/orhun/git-cliff/releases/latest/download/git-cliff-$(git-cliff --version | awk '{print $2}')-x86_64-unknown-linux-gnu.tar.gz | tar xz
displayName: 'Install git-cliff'
- script: |
./git-cliff --azure-devops-repo "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_git/$(Build.Repository.Name)" -o CHANGELOG.md
displayName: 'Generate Changelog'
env:
AZURE_DEVOPS_TOKEN: $(System.AccessToken)
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: 'CHANGELOG.md'
ArtifactName: 'changelog'
displayName: 'Publish Changelog'
Azure DevOps Changelog Example
For a Keep a Changelog format with Azure DevOps integration, use the azure-devops-keepachangelog.toml example:
git cliff -c azure-devops-keepachangelog
This generates:
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2024-01-15
### Added
- feat: add new feature by @orhun in #123
- feat: support Azure DevOps integration by @contributor in #456
### Fixed
- fix: resolve bug in parser by @orhun in #789
### Changed
- refactor: improve performance by @cliffjumper in #202
[1.0.0]: https://dev.azure.com/myorg/myproject/_git/myrepo/branchCompare?baseVersion=GTv0.9.0&targetVersion=GTv1.0.0
<!-- generated by git-cliff -->
Common Issues
Authentication Failures
If you encounter 401 Unauthorized errors:
- Verify your PAT has Code (Read) permissions
- Ensure the token hasn’t expired
- Check that the organization/project is accessible with your account
- For Azure Pipelines, use
$(System.AccessToken) instead of a PAT
Make sure to use the correct format:
- ✅ Correct:
owner = "myorg/myproject"
- ❌ Incorrect:
owner = "myorg"
On-Premises URL
For Azure DevOps Server, include the collection name:
- ✅ Correct:
owner = "DefaultCollection/myproject"
- ❌ Incorrect:
owner = "myproject"