Build Already in Pull Request and Builds Won’t Fail

Failing builds, everyone has seen them. Someone merged to the master branch and build is now failing even if the code was reviewed. There can be many reasons: code doesn’t compile, some unit test fails, etc. Many times fix is done fast, but too many times no one does it, at least it feels so. If someone creates a new feature branch bfore the fix, it will have that broken code and things can escalate. “We need to review code better”, is a common fix for this which warms up for just a moment.

Fortunately, there is a fix for this. I call it “pull request build”. Run a build pipeline before the merge in the feature branch. Do it automatically and don’t let merge happen if the build doesn’t pass. This way there will be close to zero failing builds in the master branch. In practice, the build pipeline will be one of the reviewers in the pull request.

I will demo pull request build with the Azure DevOps but it is not only Azure DevOps’ feature. Other continuous integration tools have this feature:

Code Example

We have following code:

public class MyName
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public bool IsValid()
  {
    if (string.IsNullOrEmpty(FirstName) || string.IsNullOrEmpty(LastName))
    {
      return false;
    }
    return true;
  }
}

Here is a parameterized unit test for it with MSTest:

[DataRow("Lassi", "Autio", true)]
[DataRow("", "Autio", false)]
[DataRow("Lassi", "", false)]
[DataRow("", "", false)]
[DataTestMethod]
public void IsValidTest(string firstName, string lastName, bool expected)
{
  var name = new MyName { FirstName = firstName, LastName = lastName };
  Assert.AreEqual(expected, name.IsValid());
}

Build pipeline is even too simple; it only runs unit tests and even builds with the ‘dotnet test’ command. But I wanted to keep it simple for this demo:

trigger:
- master

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: DotNetCoreCLI@2
  displayName: 'dotnet test'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    publishTestResults: false

Configure Branch Policy

To enable pull request build in Azure Repos, we will configure branch policy for the target branch. If we target pull requests to the master branch, then we configure branch policy for the master branch. Branch policy is a set of rules for a given branch. For example, we can require pull requests, how many reviewers are needed in pull requests, etc. Below two images show how to get to configure branch policy for the master branch.

Start with Repos -> Branches.
Select branch to configure (1), three dots (2) and Branch policies (3).

Now we come to the branch policies page where we can configure various policies. To add pull request build click “+ Add build policy”.

Click “+ Add build policy” on build validation to configure the build pipeline that is run in pull requests.

In the Add build policy dialog we just have to select the build pipeline we want (and make other configurations if want to).

Select the build pipeline (1) and click Save (2). Notice there is other configurations but only few of them are shown here.

Everything is set up! Now is the time for a demo how this practically works.

Demo

We create a new branch amd make unit test to fail by simply removing null&empty check for the last name:

public bool IsValid()
{
  // removed "|| string.IsNullOrEmpty(LastName)"
  if (string.IsNullOrEmpty(FirstName))
  {
    return false;
  }
  return true;
}

When we create a pull request, and open it, there is the “PR Build” build running that we configured earlier:

Pull request build is running!

But because unit tests don’t pass, the build fails, and we can see it in the pull request:

Pull request build failed.

If we try to merge the pull request it shows that build failed and we can’t complete the merge. We have to fix the code to pass the build and then we can merge. No broken code to the master branch!

When trying to complete the pull request, it shows that the build failed (1) and the complete merge (2) button is disabled.

How can we know what made the build fail? Fortunately, “build failed” panel is the link to the build and there we can figure out what broke the pull request build.

The whole build panel in the pull request is a link to the build.
Here is the build and we can figure out details why the pull request build failed.

Conclusion

I really like this feature. It will save time because errors are fixed before the broken code is merged to the master branch. No more failing unit tests or “it compiles on my machine” in the master branch.

I started with continuous integration quite late, at the beginning of 2016. It took me immediately, and I wanted to learn it more and more. I thought that it was good to have build on the master branch because we will find errors fast. But at that time I didn’t know about pull request builds. When I first time heard of them I thought it would be nice but didn’t have a chance to try it. Then I got a chance to try it and it was amazing. I realized the impact this would have on code quality.

In this demo, we used only the failing unit test as an example but there are many other capabilities. One of the best is static code analysis done, again, before merge to the master branch. For example, SonarCloud can be integrated to Azure DevOps’ pull request. SonarCloud will make comments on a pull request and the programmer can see where he/she should make fixes to make code quality better. It is too late to give these hints after the merge because code is done and seldom after the merge, we bother to make such changes. Another example could be code coverage. We can make pull request build fail if code coverage decreases.

If you haven’t tried pull request build I highly recommend it. It will make you more efficient and have better code in the master branch.

One thought on “Build Already in Pull Request and Builds Won’t Fail

Leave a comment