Skip to content

GitOps converge

Use case: desired state lives in version control and changes through merge requests; an automated trigger converges the instance after every merge. The playbook is deliberately thin — it loads a reviewed vars file and applies it — so the file is the interface: preview any change with --check --diff in the merge request pipeline, converge on merge, and rely on idempotent re-runs.

# Preview the diff without writing (check + diff):
ansible-playbook -i localhost, examples/gitops-converge.yml --check --diff
# Converge:
ansible-playbook -i localhost, examples/gitops-converge.yml

Source: examples/gitops-converge.yml

---
# GitOps pattern: desired state lives in version control (here, an external
# vars file that a merge request edits), and an automated trigger runs this
# playbook after merge to converge the instance. This example is designed for
# repeated convergence, subject to the documented idempotence limits.
#
#   # Preview the diff without writing (check + diff):
#   ansible-playbook -i localhost, examples/gitops-converge.yml --check --diff
#   # Converge:
#   ansible-playbook -i localhost, examples/gitops-converge.yml
- name: Converge GitLab settings from version-controlled desired state
  hosts: localhost
  gather_facts: false
  connection: local
  vars_files:
    - desired-state.yml
  vars:
    gitlab_settings_target:
      url: https://gitlab.example.com
      auth:
        type: personal_access_token
        token: "{{ vault_gitlab_admin_token }}"
    # Persist a result document (changed labels, skips, and the per-op coverage
    # manifest) for the pipeline to archive as an audit record.
    gitlab_settings_output_file: /tmp/gitlab-settings-result.json
  tasks:
    - name: Apply the declared desired state
      ansible.builtin.include_role:
        name: primetheus.gitlab_settings
        tasks_from: apply
      vars:
        gitlab_settings_instance: "{{ desired_instance | default({}) }}"
        gitlab_settings_groups: "{{ desired_groups | default([]) }}"
        gitlab_settings_projects: "{{ desired_projects | default([]) }}"

    - name: Show what changed
      ansible.builtin.debug:
        var: gitlab_settings_apply_result['changed']
        verbosity: 1

The version-controlled desired state

The vars file a merge request edits (examples/desired-state.yml):

---
# Version-controlled desired state consumed by gitops-converge.yml. A merge
# request edits this file; the trigger converges it after merge. Keep it under
# review like any code.
desired_instance:
  application:
    account:
      signup_enabled: false

desired_groups:
  - path: platform
    push_rules:
      reject_unsigned_commits: true
    labels:
      - name: security
        color: "#d9534f"

desired_projects:
  - path: platform/service
    protected_branches:
      - name: main
        allowed_to_push:
          - access_level: maintainer
    feature_flags:
      - name: beta_ui
        active: true