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-tokenGitLab token for API calls and MR creation.--rootRoot directory to scan. Default:.--includeGlob patterns to include. Can be specified multiple times.--excludeGlob patterns to exclude. Can be specified multiple times.--jsonJSON report file name. Required for multi-MR workflows.--markdownMarkdown report file (optional).--gitlab-base-urlGitLab API base URL (for self-hosted GitLab).--resolverVersion resolver strategy:git,github, orauto. Default:git--configPath to configuration file. Default:.agronomist.yaml--validate-tokenValidate API token before processing.
Requirements¶
- GitLab Runner with Docker or shell executor.
- GitLab token stored in GitLab CI variables.
Variables¶
GITLAB_TOKENToken used by Agronomist for GitLab API calls and MR creation.AGRONOMIST_VERSIONAgronomist release tag (e.g.v1.2.3).AGRONOMIST_ROOTRoot directory to scan. Default:.AGRONOMIST_RESOLVERResolver strategy:git,github, orauto.AGRONOMIST_CONFIGPath to configuration file (supports category rules and blacklist filters). Default:.agronomist.yaml.MR_BODYMerge Request description. Default:Updates generated by Agronomist..MR_TARGET_BRANCHTarget branch for MR. Default:$CI_DEFAULT_BRANCH.
Pipeline overview¶
updatestage runs Agronomist, applies changes, and automatically creates one Merge Request per updated module if updates are found.- Workflow rules: Only runs on
mainbranch 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.jsonduring 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.jsonto 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:
<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.