Skip to content

GitLab CI

This project includes a GitLab pipeline to run Agronomist using GitLab Runners.

Available CLI Options

When running Agronomist in GitLab CI, you can use these CLI options (either directly in the script or mapped via variables):

  • --gitlab-token GitLab token for API calls and MR creation.
  • --root Root directory to scan. Default: .
  • --include Glob patterns to include. Can be specified multiple times.
  • --exclude Glob patterns to exclude. Can be specified multiple times.
  • --json JSON report file name. Required for multi-MR workflows.
  • --markdown Markdown report file (optional).
  • --gitlab-base-url GitLab API base URL (for self-hosted GitLab).
  • --resolver Version resolver strategy: git, github, or auto. Default: git
  • --config Path to configuration file. Default: .agronomist.yaml
  • --validate-token Validate API token before processing.

Requirements

  • GitLab Runner with Docker or shell executor.
  • GitLab token stored in GitLab CI variables.

Variables

  • GITLAB_TOKEN Token used by Agronomist for GitLab API calls and MR creation.
  • AGRONOMIST_VERSION Agronomist release tag (e.g. v1.2.3).
  • AGRONOMIST_ROOT Root directory to scan. Default: .
  • AGRONOMIST_RESOLVER Resolver strategy: git, github, or auto.
  • AGRONOMIST_CONFIG Path to configuration file (supports category rules and blacklist filters). Default: .agronomist.yaml.
  • MR_BODY Merge Request description. Default: Updates generated by Agronomist..
  • MR_TARGET_BRANCH Target branch for MR. Default: $CI_DEFAULT_BRANCH.

Pipeline overview

  • update stage runs Agronomist, applies changes, and automatically creates one Merge Request per updated module if updates are found.
  • Workflow rules: Only runs on main branch via manual trigger (web) or scheduled pipelines.
  • Dynamic branch naming: Each MR branch is named agronomist/update-<base-module>-<hash8>, where <hash8> is the first 8 characters of the SHA-256 hash of the full module ID. This keeps branch names short and guarantees uniqueness across files.
  • Git configuration: Automatically configures Git to use GitLab token for accessing private repositories.
  • MR cleanup: Deletes existing remote branch before pushing to avoid conflicts when re-running the pipeline.
  • Report handling: Generates report.json during the pipeline to extract per-module file lists. The report is consumed in-pipeline and is not committed to the repository.

Minimal Example (single MR)

Note on Versions

Replace vX.Y.Z with the latest stable version from the Releases page.

The following pipeline applies all updates in a single MR. Use it as a starting point for simple setups:

stages:
  - update

workflow:
  name: "Agronomist"
  rules:
    - if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME == "main"'
    - when: never

variables:
  AGRONOMIST_VERSION: "vX.Y.Z" # Use the latest version
  AGRONOMIST_ROOT: "."
  AGRONOMIST_RESOLVER: "git"
  AGRONOMIST_CONFIG: ".agronomist.yaml"
  MR_BODY: "Updates generated by Agronomist."
  MR_TARGET_BRANCH: "$CI_DEFAULT_BRANCH"

update:
  stage: update
  image: python:3.12
  rules:
    - if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "main"'
      when: manual
    - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME == "main"'
      when: always
  script:
    - apt-get update -qq && apt-get install -y -qq git curl
    - |
      WHEEL="agronomist-${AGRONOMIST_VERSION#v}-py3-none-any.whl"
      curl -sSL -o "$WHEEL" "https://github.com/Ops-Talks/agronomist/releases/download/${AGRONOMIST_VERSION}/${WHEEL}"
      pip install -q "$WHEEL"
    - |
      if [ -n "$GITLAB_TOKEN" ]; then
        git config --global url."https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/".insteadOf "https://${CI_SERVER_HOST}/"
      fi
    - |
      TOKEN_ARG=""
      if [ -n "$GITLAB_TOKEN" ]; then
        TOKEN_ARG="--gitlab-token $GITLAB_TOKEN"
      fi
      agronomist update --root "$AGRONOMIST_ROOT" \
        --config "$AGRONOMIST_CONFIG" \
        --resolver "$AGRONOMIST_RESOLVER" \
        $TOKEN_ARG
    - |
      if [ -z "$GITLAB_TOKEN" ]; then
        echo "GITLAB_TOKEN not set. Skipping MR creation."
        exit 0
      fi

      if [ -z "$(git status --porcelain)" ]; then
        echo "No changes to commit."
        exit 0
      fi

      MR_BRANCH="agronomist/updates-${CI_COMMIT_REF_SLUG}-${CI_PIPELINE_ID}"
      MR_TITLE="[Agronomist] - New modules version identified"

      git config user.email "ci@agronomist"
      git config user.name "agronomist-bot"
      git checkout -b "$MR_BRANCH"
      git add -u
      git commit -m "$MR_TITLE"
      git remote set-url origin "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
      git push origin --delete "$MR_BRANCH" 2>/dev/null || true
      git push -u origin "$MR_BRANCH"

      curl --silent --show-error --fail \
        --request POST \
        --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
        --form "source_branch=${MR_BRANCH}" \
        --form "target_branch=${MR_TARGET_BRANCH}" \
        --form "title=${MR_TITLE}" \
        --form "description=${MR_BODY}" \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests" || true

Full Multi-MR Example

For production use, the recommended pipeline creates one merge request per updated module. It generates report.json, iterates over each module with jq, and creates a dedicated branch and MR for each.

See examples/workflows/.gitlab-ci.yml for the full pipeline that:

  • Runs agronomist update --json report.json to generate a report.
  • Extracts unique modules from the report using jq.
  • Creates a branch per module with a deterministic hash-based name.
  • Pushes each branch and creates an MR via the GitLab API.

Branch naming

When creating one MR per module, each branch is named:

agronomist/update-<base-module>-<hash8>
  • <base-module> — the module name (path before @), with / and @ replaced by -, truncated to 50 characters.
  • <hash8> — first 8 characters of the SHA-256 hash of the full module ID (base module + file path). This ensures two files that reference the same upstream module produce distinct branch names.

The MR title and commit message use the same pattern: [Agronomist] Update <base-module> to <latest-ref>.

Examples

Module ID in report.json Branch name MR title
hashicorp/terraform-provider-aws@infra/versions.tf agronomist/update-hashicorp-terraform-provider-aws-ea7332eb [Agronomist] Update hashicorp/terraform-provider-aws to 5.1.0
terraform-aws-modules/vpc/aws@infra/network.tf agronomist/update-terraform-aws-modules-vpc-aws-3335a2ca [Agronomist] Update terraform-aws-modules/vpc/aws to v5.2.0
terraform-aws-modules/rds/aws@infra/database.tf agronomist/update-terraform-aws-modules-rds-aws-f7fab219 [Agronomist] Update terraform-aws-modules/rds/aws to v3.1.0
terraform-aws-modules/eks/aws@platform/eks/main.tf agronomist/update-terraform-aws-modules-eks-aws-55583821 [Agronomist] Update terraform-aws-modules/eks/aws to v20.0.0
cloudwatch-log-group@providers/aws/contoso/dev/us-east-2/cloudwatch-log-group/terragrunt.hcl agronomist/update-cloudwatch-log-group-8bfc52c9 [Agronomist] Update cloudwatch-log-group to v1.2.0
cloudwatch-log-group@providers/aws/contoso/prod/eu-west-1/cloudwatch-log-group/terragrunt.hcl agronomist/update-cloudwatch-log-group-4e428acf [Agronomist] Update cloudwatch-log-group to v1.2.0

The last two rows show the same upstream module (cloudwatch-log-group) referenced from two different files. Because the hash is derived from the full module ID (including the file path), each file gets a unique branch and MR — even though the base module name is identical.