GitHub Actions

Mastering Monorepo Merges: Guaranteeing Order in GitHub Actions for Peak Productivity

The Monorepo Merge Challenge: When Concurrency Isn't Enough

In the fast-paced world of monorepos, it's common for multiple Pull Requests (PRs) to be merged into the main branch almost simultaneously. For teams relying on GitHub Actions to build critical artifacts like Docker images, this can create a significant challenge. While GitHub Actions offers concurrency groups with cancel-in-progress to prevent parallel runs, it explicitly states that "ordering is not guaranteed." This means an older workflow run might inadvertently cancel a newer one, or worse, complete an unnecessary build that's immediately superseded by a subsequent merge.

This exact pain point was highlighted in a recent GitHub Community discussion (#185892), where a user described wanting to build Docker images only for the latest PR merged into main, canceling all other concurrent builds. The arbitrary ordering of concurrency groups meant this desired "latest PR wins" behavior was not reliably achievable out-of-the-box, leading to wasted CI minutes and potential confusion. As dev teams, product managers, and CTOs, understanding and mitigating such inefficiencies is crucial for maintaining healthy software project KPIs.

The Unreliable Nature of Concurrent Builds

Imagine three PRs merging into main within seconds of each other. Each merge triggers a workflow. With standard GitHub Actions concurrency and cancel-in-progress, GitHub guarantees that only one workflow from that group will run at a time, and a newer run will cancel an older one. However, the critical caveat is that it doesn't guarantee which run is considered "newer" when they start almost simultaneously. The second PR's workflow might initiate, cancel the first, and then itself be canceled by the third, or it might complete before the third even registers its cancellation request. This race condition means you cannot reliably ensure that the final, most up-to-date commit is the one whose build completes.

Visual metaphor of workflows racing, where an older workflow might finish or cancel others due to arbitrary ordering in GitHub Actions concurrency.
Visual metaphor of workflows racing, where an older workflow might finish or cancel others due to arbitrary ordering in GitHub Actions concurrency.

Reframing the Goal: Build Only If This Commit is Still HEAD of main

The community quickly converged on a crucial reframing of the problem: instead of trying to force GitHub Actions to guarantee a Last-In, First-Out (LIFO) queue for concurrency, the true objective is to "only build if this commit is still the tip of main." Once this perspective is adopted, the solution becomes robust and reliable, independent of the order in which workflows are initiated. This shift in thinking moves us from trying to control an inherently unpredictable timing mechanism to verifying the state of the repository at the point of execution.

The Reliable Solution: Concurrency + HEAD Check

The recommended approach combines GitHub Actions' built-in concurrency feature with a crucial runtime check. This two-step strategy ensures that only the most relevant build proceeds, effectively eliminating race conditions and ensuring your CI/CD pipeline is always working on the correct version.

Step 1: Leverage concurrency for Single-Run Control

The first step is to set up your workflow with a concurrency group and cancel-in-progress: true. This is foundational for preventing multiple, redundant builds from running simultaneously for the same branch or context.

jobs:
  build:
    runs-on: ubuntu-latest
    concurrency:
      group: build-main-${{ github.ref }}
      cancel-in-progress: true
    steps:
      # ... your build steps will go here ...

By using ${{ github.ref }}, you ensure that each branch has its own concurrency group, preventing builds from different branches from canceling each other, while still managing concurrent merges to main effectively.

Step 2: Implement a Runtime HEAD Check for Absolute Certainty

This is where the magic happens. Before your workflow performs any heavy lifting (like building Docker images or deploying), add a step to verify that the commit currently being processed is still the absolute latest commit on the main branch. If it's not, the workflow should exit gracefully.

jobs:
  build:
    runs-on: ubuntu-latest
    concurrency:
      group: build-main-${{ github.ref }}
      cancel-in-progress: true
    steps:
      - name: Check if this is the latest commit on main
        run: |
          git fetch origin main
          LATEST_MAIN_SHA=$(git rev-parse origin/main)
          CURRENT_WORKFLOW_SHA="${{ github.sha }}"

          echo "Latest SHA on main: $LATEST_MAIN_SHA"
          echo "Current workflow SHA: $CURRENT_WORKFLOW_SHA"

          if [ "$LATEST_MAIN_SHA" != "$CURRENT_WORKFLOW_SHA" ]; then
            echo "A newer commit exists on main. This run is for an outdated commit. Exiting."
            exit 0 # Exit successfully, but do no work
          fi
          echo "This commit is the latest on main. Proceeding with build."
      
      - name: Build Docker Image # This step only runs if the HEAD check passes
        run: |
          echo "Building Docker image..."
          # Your actual Docker build commands here

How this works:

  • When multiple PRs merge, multiple workflows start.
  • concurrency ensures only one is actively running at a time, canceling older ones.
  • Even if a slightly older workflow manages to start its execution before being canceled, the "Check if latest commit" step will immediately identify that its GITHUB_SHA is no longer the HEAD of main.
  • It then exits early (exit 0), preventing wasted resources on an outdated build.
  • The truly latest workflow, when its turn comes, will pass the HEAD check and proceed with the build.
Diagram illustrating the reliable solution: GitHub Actions concurrency funneling runs, followed by a 'HEAD check' gate ensuring only the latest commit proceeds to build.
Diagram illustrating the reliable solution: GitHub Actions concurrency funneling runs, followed by a 'HEAD check' gate ensuring only the latest commit proceeds to build.

Why This Strategy Elevates Your Software Project KPIs

Implementing this robust CI/CD pattern has direct, measurable benefits for your organization, impacting key software project KPIs:

  • Increased Productivity: Developers get faster, more accurate feedback. No more waiting for builds that will be immediately superseded. This directly contributes to higher developer velocity.
  • Optimized Resource Utilization: By canceling unnecessary builds early, you significantly reduce wasted CI minutes and associated cloud costs. This is a tangible win for operational efficiency.
  • Enhanced Delivery Efficiency: You guarantee that only the most relevant artifacts (e.g., Docker images) are produced and potentially deployed. This reduces confusion, prevents deploying outdated versions, and streamlines your release process.
  • Improved Reliability: Eliminating race conditions and ensuring build integrity leads to a more stable and predictable CI/CD pipeline, a critical factor for any engineering leader.
  • Better Data for Analytics: When your CI/CD pipeline is clean and efficient, the data it generates for tools that track engineering metrics becomes more accurate. While sophisticated platforms like Allstacks free alternative or Haystack alternative provide deep insights into engineering velocity and project health, implementing fundamental, reliable CI/CD patterns like this ensures the data they collect is clean and reflective of true progress, not wasted effort.

This isn't just a technical tweak; it's a strategic move that empowers your delivery managers and CTOs with greater confidence in the integrity and efficiency of your development lifecycle.

Beyond the Hack: A Pattern for Robust CI/CD

The initial thought might be to introduce random delays or other timing-based "hacks" to manage concurrency. However, as the GitHub discussion highlights, you cannot reliably depend on timing in distributed systems. The `concurrency` + HEAD check pattern is not a hack; it's a battle-tested, reliable strategy used in large-scale monorepos (think Google or Meta-style CI setups) precisely because it removes all timing ambiguity. It's a proactive measure that ensures your CI/CD pipeline is resilient and always aligned with the latest state of your codebase.

Conclusion: Empowering Your Dev Team with Smarter Workflows

For dev teams grappling with the complexities of monorepos and concurrent merges, this GitHub Actions pattern offers a clear path to greater efficiency and reliability. By combining GitHub's native concurrency features with a simple, yet powerful, runtime check, you can guarantee that your critical builds always reflect the latest state of your main branch. This not only saves valuable CI minutes but also fosters a more productive development environment, ultimately contributing positively to your organization's overall software project KPIs and delivery excellence. Implement this, share it with your team, and watch your CI/CD pipeline become a true asset, not a source of frustration.

Share:

Track, Analyze and Optimize Your Software DeveEx!

Effortlessly implement gamification, pre-generated performance reviews and retrospective, work quality analytics, alerts on top of your code repository activity

 Install GitHub App to Start
devActivity Screenshot