Branching and Merging Strategy

Guidelines for implementing branching and merging policies in GitOps pipelines for Ansible inventory projects to promote changes across environments.

Problem

In a GitOps setup with Ansible, managing changes across multiple environments like development, staging, and production requires a structured approach to avoid errors, ensure testing, and maintain security. Without clear branching policies, teams risk deploying untested code, conflicting changes, or unauthorized modifications to critical branches.

Context

GitOps practices involve using Git as the single source of truth for infrastructure and application configurations. In Ansible-based projects, this means storing inventory, playbooks, and variables in a Git repository, in a so-called Ansible inventory project. Branches can represent different environments, allowing changes to be promoted via merge requests (MRs). Protected branches in tools like GitLab or GitHub enforce reviews, approvals, and CI/CD pipelines before merges, ensuring changes are vetted and tested automatically.

Solution

Implement a branching strategy that aligns with GitOps principles, using environment-specific environment branches and merge requests for promotions. Enforce policies with protected branches to maintain control and auditability.

Follow these guidelines:

  1. Define Core Branches: Create permanent branches for each environment. These are so-called environment branches. It is important that they match an environment consisting of hosts that are part of the same Ansible environment group.

    • master: Serves as the stable, long-term branch that reflects the latest production state after successful promotions.
    • development: For ongoing development and feature work.
    • staging: For testing changes before production.
    • production: Represents the live production environment; only merge from staging after approvals.
  2. Branching Policies:

    • Feature branches: Create short-lived branches off development for new features or bug fixes (e.g., feature/add-new-playbook).
    • Hotfix branches: For urgent production fixes, branch directly from production (e.g., hotfix/issue-12345).
    • Avoid direct commits to environment branches; always use MRs for changes.
  3. Merging Workflow:

    • Develop and test in development.
    • Merge from development to staging via MR for integration testing.
    • Merge from staging to production via MR after approvals and successful CI/CD runs.
    • Finally, merge production back to master to keep it updated.
    • For hotfixes: Apply to production first via MR, then backport to other branches.
  4. Protected Branches:

    • Configure branch protection for all environment branches and for master in your Git platform (e.g., GitLab):
      • Require MR approvals (e.g., at least 2 reviewers).
      • Enforce passing CI/CD pipelines before merging.
      • Restrict who can push or merge (e.g., only admins for production).
      • Prevent force pushes and branch deletion.
  5. Automation Integration:

    • Use GitLab CI/CD or similar to trigger Ansible playbooks on merges, deploying changes to the corresponding environment. Typically AAP is used; sometimes when AAP is not used, GitLab CI/CD can be used.
    • Tag releases (e.g., semantic versioning) on merges to track versions.

Benefits

  • Consistency and Safety: Reduces deployment risks by enforcing reviews and automated testing.
  • Traceability: MRs provide an audit trail of changes and approvals.
  • Scalability: Supports team collaboration without overwriting work.
  • Rollback Ease: Environment branches allow quick reverts if issues arise.

Alternatives (Optional)

While Git Flow or GitHub Flow are common, they may not align perfectly with GitOps’ environment-based promotions. A simpler trunk-based development could work for smaller teams but lacks the staged promotion needed for regulated environments; the described strategy is preferred for Ansible GitOps setups requiring clear separation.

Examples and Implementation

In a typical Ansible GitOps project, structure your repository with branches representing environments. Use merge requests to promote changes, triggering CI/CD pipelines that run Ansible playbooks for deployment.

Here’s an example Git workflow visualized with Mermaid:

gitGraph
    commit id: "Initial commit"

    branch development
    checkout development

    branch staging
    checkout staging

    branch production
    checkout production

    checkout development
    commit
    commit
    commit id: " " tag: "1.0.0"
    checkout staging
    merge development tag: "1.0.0"
    checkout production
    merge staging tag: "1.0.0"
    commit id: "Production release 1.0.0"

    checkout main
    merge production tag: "1.0.0"

    checkout production
    branch hotfix-12345-fix-something
    commit id: "Hotfix applied" tag: "1.0.1-hotfix-issue-12345"

    checkout main
    merge hotfix-12345-fix-something  tag: "1.0.1-hotfix-issue-12345"

    checkout development
    merge hotfix-12345-fix-something tag: "1.0.1-hotfix-issue-12345"

    checkout staging
    merge hotfix-12345-fix-something tag: "1.0.1-hotfix-issue-12345"

This diagram illustrates:

  • Initial setup of branches.
  • Development work merged to staging, then production.
  • A hotfix applied to production and backported to other branches.
  • Final merge to master for a stable record.

To implement in GitLab:

  • Navigate to Repository > Branches > Protected branches.
  • Set production as protected, requiring MRs and approvals.
  • Configure .gitlab-ci.yml to run Ansible jobs on merge events. For example:
stages:
  - deploy

deploy_to_staging:
  stage: deploy
  script:
    - ansible-playbook -i inventory/staging site.yml
  only:
    - staging

Additional Information

  • Ansible Inventory Project: A structured collections of files used for managing hosts and configurations. It typically includes inventory files, playbooks, host configurations, group variables, and Ansible vault files.


Last modified September 15, 2025: guideline branching PHX-162 (38d5773)