GitFlow for MuleSoft: branch rules a pipeline can act on
Reference project: mulesoft-orders-api (application) and azure-devops-mulesoft-pipelines (pipeline templates). Every flow described in this series has run end to end, from a feature branch to production on CloudHub 2.0.
A branching model is only useful if everyone — people and pipelines — reads it the same way. This series builds a delivery flow for Mule applications on Azure DevOps where the branch name is the instruction: a push to develop deploys to test, a merge to main produces a release and promotes it to uat and production. This first article sets out the model and the reasons for it.
Why GitFlow, and when not to use it
Vincent Driessen described GitFlow in 2010. In 2020 he added a note to the original post: teams that continuously deliver a single version, such as most web apps, are better served by something simpler, like trunk-based development. That note is fair, and it is the first thing to check before adopting GitFlow.
Integration work often does not look like a web app:
- Releases are scheduled, and they depend on other teams’ changes going live at the same time.
- Code is promoted through several environments, usually with an approval before production.
- A production fix is sometimes needed while the next release is still being tested.
GitFlow is built for exactly that: it separates “what is in production” from “what is being prepared” and gives hotfixes their own path. If your Mule applications ship to production several times a day behind feature flags, choose trunk-based development instead — the rest of this series still applies, but the branch rules would be simpler.
The branches
| Branch | Created from | Merges into | Pipeline behaviour |
|---|---|---|---|
feature/<ticket>-<desc> |
develop |
develop (PR) |
Build |
develop |
— | — | Build, publish x.y.z-SNAPSHOT to Exchange, deploy to test |
release/x.y.z |
develop |
main (PR) |
Build |
main |
— | — | Build, Maven release (tag vx.y.z), deploy to uat, deploy to prod after approval |
hotfix/x.y.z |
main |
main (PR) |
Build |
After a release or hotfix lands on main, a back-merge PR main → develop brings the fix and the version change back.
Two rules keep this manageable:
- Nobody edits versions by hand.
developalways carries a-SNAPSHOT. The release pipeline removes it, tags the commit and moves to the next-SNAPSHOT(the next article covers how). - When you cut
release/x.y.z, bumpdevelopto the next minor-SNAPSHOTin the same change. Otherwise snapshots published fromdevelopcarry the same version as the release being stabilised.
Turning branch names into behaviour
The application’s pipeline file is deliberately small. Everything else lives in a shared templates repository, pinned to a tag:
trigger:
branches:
include: [develop, main, feature/*, release/*, hotfix/*]
pr:
branches:
include: [develop, main]
resources:
repositories:
- repository: templates
type: github
endpoint: github.com_brunosouzas
name: brunosouzas/azure-devops-mulesoft-pipelines
ref: refs/tags/v1.1.0
extends:
template: templates/stages/gitflow.yml@templates
parameters:
nextVersionBump: minor
The template decides which stages run from Build.SourceBranch. For example, the release stage:
- stage: release
displayName: Release
dependsOn: ci
condition: and(succeeded(),
eq(variables['Build.SourceBranch'], 'refs/heads/main'),
ne(variables['Build.Reason'], 'PullRequest'))
A pull request into main runs only the build, even though its target is main; the merge commit is what releases.
A trap: path filters
My first version of the trigger excluded documentation changes:
trigger:
paths:
exclude: ['*.md', docs/*]
It looks harmless. Then the first release — whose only change on the release branch was CHANGELOG.md — was merged into main, and nothing happened. The filter applies to the merge commit too, so main was never released. Path filters and GitFlow do not mix: a merge into main must always run, whatever it touches. The fix went out as the project’s first hotfix.
Protecting the branches
main and develop should only change through pull requests with a passing build. On GitHub (rulesets are available on the free plan for public repositories):
- target the default branch and
develop; - require a pull request, block force pushes and deletions;
- require the pipeline’s status check.
There is a catch: the Maven release pushes two commits and a tag straight to main. Whoever runs the release must be allowed to bypass the rule. The second article explains the options; the short version is that the bypass should be as narrow as your tooling allows.
Checklist
- Do your releases really need several environments and approvals? If not, consider trunk-based development.
- Can anyone tell from a branch name what the pipeline will do with it?
- Is
developbumped when a release branch is cut? - Does every merge into
maintrigger the pipeline, whatever files it changes? - Is there a back-merge after every release and hotfix?