Rewiring

Creating pull requests from Azure Pipelines

Since there isn't a built-in function for creating a PR from inside a pipeline, let's create it ourselves using az repos pr create.

Here's the template, which is nothing more than a wrapper around the command to support different use cases:

# /internal/shared/steps/create-pull-request.yml
# Create a pull request from the specified source branch to the target branch
# Requires that the System.AccessToken variable is populated with a token for a user with permissions to contribute to the branch
# This is done by using persistCredentials: true in a checkout task

parameters:
  # The source branch for the pull request
  - name: sourceBranch
    type: string
  # The target branch for the pull request. Optional - if empty, will use the default branch of the repo
  - name: targetBranch
    type: string
    default: ''
  # Whether to auto-complete the pull request.
  - name: autoComplete
    type: boolean
    default: true
  # Whether to delete the source branch after the pull request is completed.
  - name: deleteSourceBranch
    type: boolean
    default: true
  # Whether to squash the commits in the branch during merge.
  - name: squashMerge
    type: boolean
    default: true
  # The description for the pull request. If left empty, the description will be taken from the commit message
  - name: pullRequestDescription
    type: string
    default: ''
  # The title of the pull request. If left empty, the title will be taken from the commit message
  - name: pullRequestTitle
    type: string
    default: ''
  # If you're creating the PR to another project than the one the pipeline is running in, you need to specify the project name or ID
  - name: project
    type: string
    default: ''
  # If you're creating the PR to another repository than the one the pipeline is running in, you need to specify the repository name or ID
  - name: repository
    type: string
    default: ''
  # If there is a condition to satisfy before creating the pull request, define it here
  - name: condition
    type: string
    default: ''

steps:
  - bash: |
      params=(
      --auto-complete ${{ parameters.autoComplete }}
      --delete-source-branch ${{ parameters.deleteSourceBranch }}
      -s refs/heads/${{ parameters.sourceBranch }}
      --squash ${{ parameters.squashMerge }}
      )

      [[ -n "${{ parameters.targetBranch }}" ]] && params+=(-t refs/heads/${{ parameters.targetBranch }})
      [[ -n "${{ parameters.pullRequestTitle }}" ]] && params+=(--title "${{ parameters.pullRequestTitle }}")
      [[ -n "${{ parameters.pullRequestDescription }}" ]] && params+=(-d "${{ parameters.pullRequestDescription }}")
      [[ -n "${{ parameters.project }}" ]] && params+=(-p ${{ parameters.project }})
      [[ -n "${{ parameters.repository }}" ]] && params+=(-r ${{ parameters.repository }})
      az repos pr create "${params[@]}"
    displayName: Create a pull request
    env:
      AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)
    ${{ if parameters.condition }}:
      condition: ${{parameters.condition}}

NOTE: for this to work, the build account needs the following permissions in the repository security settings:

The easiest way to find out which user the pipeline is running with is to let this step fail, then copying the user id from the output and giving that account the required permissions.

To use this template, the pipeline needs to first create a branch and push some changes to it. Here's a simple sample:

jobs:
  - job:

    steps:
      - checkout: self
        persistCredentials: true # this is important

      - bash: |
          touch $(Build.Repository.LocalPath)\newfile.md
          git config user.email "some@email.com"
          git config user.name "Rewiring"
          git switch -c newbranch
          git commit -a -m "add new file" || echo "##vso[task.setvariable variable=NO_CHANGES]true"

      - bash: git push --set-upstream origin newbranch
        displayName: publish branch
        condition: ne(variables['NO_CHANGES'], 'true')

      - template: /internal/shared/steps/create-pull-request.yml
        parameters:
          sourceBranch: newbranch
          pullRequestTitle: 'This is the title'
          condition: ne(variables['NO_CHANGES'], 'true')

And that's it - a simple template for creating all sorts of pull requests. Very useful for e.g. automating package updates.

Thoughts, comments? Send me an email!

#azure #tech