0

We currently have the following branch strategy:

Develop > Release > Master

Devs create a local branch from Develop, make changes merge to develop. Our dev environment builds off that branch. When they want their changes tested they push to QA environment, which also uses the Develop branch. This cycle goes on until functional testing for the iteration is done. At that point the code is merged into the Release branch and is deployed through staging and then to prod. After deploying to prod the code should be merged to Master, but it's often forgotten about. This causes problems in niche scenarios.

Is there a way to use perhaps devops pipelines to conditionally raise a PR automatically? So I'm thinking this would need 2 PRs:

  1. PR raised for Master branch after successful release to prod. Idea here would be once sign off has been granted someone from the team can just approve.
  2. PR raised for Develop branch if the first PR is approved and the code in Master now differs from Develop. In a lot of cases it won't as therefore wouldn't need a PR.

I've been googling for this and found the api methods like this but I can't see how you could put this in a pipeline and make it conditional.

Additional Info:

My understanding is that the build definition needs to know what branch to build as per the image below. So, creating a new release branch every sprint would either result in having to update the build definition every time or creating a new build definition, that essentially would be a complete replica in most cases except for the branch name. Unless I'm misunderstanding, which I think I am.

enter image description here

sr28
  • 4,728
  • 5
  • 36
  • 67
  • Why don't you just do the sane thing that everyone else does: merge `release` back to `master` after that release is done? – Ian Kemp Sep 11 '20 at 10:48
  • @IanKemp - I'd love for that to be trusted but we have multiple teams and sometimes it's missed. This ends up in complications where hotfixes need to go out via the release branch, whilst a planned change is already in the release branch. In this scenario a copy should be able to be taken from Master but as it wasn't merged this couldn't be done. – sr28 Sep 11 '20 at 10:53
  • Wait, you only have a **single** forever-living `release` branch? That defeats the entire purpose. Make a new `release-` branch off `master` at the beginning of each new sprint (i.e. it will contain the previous sprint's work), put whatever hotfixes in there that need to go for that release as they come along during the sprint, and merge it back to `master` at the end of that sprint - then wash, rinse, repeat. You can easily write a `start-new-sprint` to automate this. – Ian Kemp Sep 11 '20 at 11:12
  • What I'm trying to say is that this question seems like an X-Y problem: you have a problem with people forgetting to merge code back and you are trying to find an automated solution to remind them to do so. Such a solution is going to be non-trivial to write, fragile, and will require maintenance. Based on experience, your real problem seems like a convoluted branching strategy that allows for things to fall through the cracks, and/or devs lacking merge discipline. Those are completely different issues and trying to solve them with code is a band-aid on a bullet wound. – Ian Kemp Sep 11 '20 at 11:17
  • https://stackoverflow.com/questions/56184170/ci-cd-pipelines-azure-devops-automatic-merge-after-deploy-release/56211646#56211646 – Shayki Abramczyk Sep 11 '20 at 13:22
  • @IanKemp - our team did talk about moving to a release-version strategy and I think it's still up for discussion. This complicates other things though surely, such as release dependencies, release naming conventions, storage of release branches & builds across multiple apps that may be dependent on each other etc. – sr28 Sep 14 '20 at 13:57
  • @sr28 Have you tried Shayki Abramczyk‘s answer? I tried his method and automatic merge successfully. – Jane Ma-MSFT Sep 15 '20 at 09:39
  • @JaneMa-MSFT - yes I had a look at that and previously how to use the REST api. However, the crucial thing here is that it's conditional. In this case the first PR can be created as part of the pipeline, which is fine. However, the 2nd PR back to develop is conditional on the first PR completing AND there being differences between Master and Develop. – sr28 Sep 15 '20 at 10:28
  • @sr28 You can create a pipeline and set a trigger for the target branch of PR1. This way, when PR1 is approved, the target branch is modified and the pipeline runs automatically. In the pipeline, set a `if` statement to determine whether the two branches are different, and if they are, create a new PR. One problem with this method is that it is not known whether the change to the target branch was caused by the completion of PR1. – Jane Ma-MSFT Sep 16 '20 at 09:39
  • @sr28 Depending on your problem description, it may not be necessary to check if the two branches are different. Because PR will not be created successfully when two branches are exactly the same. – Jane Ma-MSFT Sep 16 '20 at 09:46
  • @JaneMa-MSFT - in most cases I don't think a PR from Master to Develop would be needed as the change will have been pushed through from Develop to Release and finally Master, so at the point of approving the PR to Master, the change will already be in Develop. It's only in the circumstance that a hotfix has come through from Release (not Develop) that it would be out of sync. – sr28 Sep 16 '20 at 12:14
  • @sr28 As of this time, however, it is not supported to run a pipeline automatically when a PR is approved. So using the REST API to do what you need may not work. – Jane Ma-MSFT Sep 17 '20 at 07:58
  • @IanKemp - going back to your previous comment about creating a release- prior to every sprint, wouldn't this mean updating / creating new build definitions and release definitions every time? If you're creating a new build and release definition for each sprint, which in most cases will be identical to the previous one bar the branch used to build from, it seems like a maintenance headache. – sr28 Sep 24 '20 at 10:10
  • @sr28 You don't need to create new build definitions and release definitions. You can choose which branch you want to run on before you run the pipeline. – Jane Ma-MSFT Sep 28 '20 at 09:36
  • @JaneMa-MSFT - yes, I know. That's how it's setup now i.e. our PROD release build runs off the release branch. However, if you're creating a new release branch every release e.g. release_5.1.2 that would mean updating the build definition manually each time to point to the right branch. To make this automated would there be a way that could create a new release branch and updating the existing build definition to the new release branch created programmatically? – sr28 Sep 28 '20 at 10:30
  • 1
    @sr28 You don't need to make any updates to the builds definition. Running on which branch is not part of the builds definition, but only its runtime resources. You can refer to this REST API [Runs - Run Pipeline](https://learn.microsoft.com/en-us/rest/api/azure/devops/pipelines/runs/run%20pipeline?view=azure-devops-rest-6.0). You can use the parameter `resources` to specify which branch the pipeline will run on without updating the pipeline. – Jane Ma-MSFT Sep 30 '20 at 09:48
  • @JaneMa-MSFT - I'm still a little confused by your comment that no updates would be needed on the build definition. A definition has the step 'Get resources' which defines the branch to build doesn't it? I've added an image of what I'm talking about in the question. – sr28 Oct 21 '20 at 10:06

1 Answers1

1

The following PowerShell example creates a pull request from master to some target branch as a build task. You can add it to your release as an additional stage "Create PR to some branch" with one PowerShell task. Additionally, you can create periodically build to check diff and create pull requests.

$user = ""
$token = "$(System.AccessToken)"
$branchTarget = "$(Build.SourceBranch)"
$branchSource = "refs/heads/master"
$branchTragetPath = $branchTarget -replace "refs/heads/", ""
$teamProject = "$(System.TeamProject)"
$repoName = "$(Build.Repository.Name)"
$orgUrl = "$(System.CollectionUri)"

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
 
$uriBranchStatus = "$orgUrl/$teamProject/_apis/git/repositories/$repoName/stats/branches?name=$branchTragetPath&api-version=5.1"
$uriCheckActivePR = "$orgUrl/$teamProject/_apis/git/repositories/$repoName/pullrequests?searchCriteria.targetRefName=$branchTarget&searchCriteria.sourceRefName=$branchSource&api-version=5.1"
$uriCreatePR = "$orgUrl/$teamProject/_apis/git/repositories/$repoName/pullrequests?api-version=5.1"

$resultStatus = Invoke-RestMethod -Uri $uriBranchStatus -Method Get -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}

if ($resultStatus.behindCount -eq 0)
{
    Write-Host "Current branch contains last changes from master"
    Return
}

$resultActivePR = Invoke-RestMethod -Uri $uriCheckActivePR -Method Get -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}

if ($resultActivePR.count -gt 0) 
{
    Write-Host "PR exists already"
    Return
}

$bodyCreatePR = "{sourceRefName:'$branchSource',targetRefName:'$branchTarget',title:'Sync changes from $branchSource'}"

$result = Invoke-RestMethod -Uri $uriCreatePR -Method Post -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Body $bodyCreatePR

Write-Host "Created PR" $result.pullRequestId
Shamrai Aleksander
  • 13,096
  • 3
  • 24
  • 31