Skip to main content

GitOps and Value Sync

The gitops-update-values-and-sync job is used to update GitOps repositories with new image tags and commit information, then synchronize Argo CD applications. It follows GitOps principles by updating configuration in Git and triggering deployments.

Overview

This job:

  • Updates Helm values files in a GitOps repository with new image tags and commit information
  • Optionally converts .env files to env.yaml format
  • Commits and pushes changes to the GitOps repository
  • Synchronizes Argo CD applications to trigger deployments
  • Supports both token-based and username/password authentication for Argo CD

Variables

The following variables can be configured:

VariableDescriptionDefaultRequired
TARGET_REPOGitOps repository URL""Yes
TARGET_BRANCHBranch to update in GitOps repo"main"No
VALUES_FILEPath to values.yaml file in GitOps repo"environments/dev/values.yaml"Yes
ENV_LOCAL_PATHPath to local .env file".env"No
ENV_OUT_FILEOutput path for env.yaml in GitOps repo"environments/dev/env.yaml"No
WRITE_ENV_FILEEnable .env to env.yaml conversion"true"No
COMMIT_MESSAGEGit commit message"chore(gitops): set image tag & commit info + env"No
GIT_USER_NAMEGit commit author name"CI Bot"No
GIT_USER_EMAILGit commit author email"[email protected]"No
GIT_HTTP_USERNAMEGitLab username or "oauth2" for PAT""Yes
GIT_HTTP_PASSWORDGitLab token/password""Yes
ARGOCD_SERVERArgo CD server URL""Yes
ARGOCD_APPArgo CD application name""Yes
ARGOCD_GRPC_WEBUse gRPC-Web for Argo CD"true"No
ARGOCD_INSECURESkip TLS verification"true"No
ARGOCD_SYNC_TIMEOUTArgo CD sync timeout in seconds"600"No
IMAGE_TAGImage tag to set (auto-derived if not set)Auto-derivedNo
COMMIT_SHACommit SHA (auto-derived if not set)Auto-derivedNo
COMMIT_BRANCHCommit branch (auto-derived if not set)Auto-derivedNo
COMMIT_URLCommit URL (auto-derived if not set)Auto-derivedNo
ARGOCD_AUTH_TOKENArgo CD authentication token-No
ARGOCD_USERNAMEArgo CD username (if no token)-No
ARGOCD_PASSWORDArgo CD password (if no token)-No

Variable Details

Git Target Configuration

  • TARGET_REPO: Full HTTPS URL to the GitOps repository (e.g., https://gitlab.com/welance/platform/gitops/app-configs.git)
  • TARGET_BRANCH: Branch name in the GitOps repository to update (default: main)
  • VALUES_FILE: Path to the Helm values file within the GitOps repository (e.g., environments/dev/values.yaml)

Environment File Conversion

  • ENV_LOCAL_PATH: Path to the local .env file to convert (default: .env)
  • ENV_OUT_FILE: Path where the converted env.yaml file will be written in the GitOps repository
  • WRITE_ENV_FILE: Set to "true" to enable .env to env.yaml conversion, "false" to disable

Git Configuration

  • COMMIT_MESSAGE: Custom commit message for GitOps repository updates
  • GIT_USER_NAME: Name to use for Git commits
  • GIT_USER_EMAIL: Email to use for Git commits

Git Authentication

  • GIT_HTTP_USERNAME:
    • Use "oauth2" for Personal Access Tokens (PAT)
    • Use bot username for Project/Group Access Tokens
  • GIT_HTTP_PASSWORD: The token value (should be stored as a masked CI/CD variable)

Argo CD Configuration

  • ARGOCD_SERVER: Argo CD server URL (e.g., https://argocd.example.com)
  • ARGOCD_APP: Name of the Argo CD application to sync
  • ARGOCD_GRPC_WEB: Set to "true" to use gRPC-Web protocol (useful for browser-based connections)
  • ARGOCD_INSECURE: Set to "true" to skip TLS certificate verification
  • ARGOCD_SYNC_TIMEOUT: Timeout in seconds for Argo CD sync operations (default: 600 seconds)

Auto-Derived Variables

If not explicitly set, these variables are automatically derived from GitLab CI/CD variables:

  • IMAGE_TAG: {CI_COMMIT_REF_SLUG}-{CI_PIPELINE_ID} (e.g., main-12345)
  • COMMIT_SHA: CI_COMMIT_SHA
  • COMMIT_BRANCH: CI_COMMIT_REF_NAME
  • COMMIT_URL: {CI_PROJECT_URL}/-/commit/{CI_COMMIT_SHA}

Argo CD Authentication

Either use:

  • ARGOCD_AUTH_TOKEN: Authentication token (preferred)
  • ARGOCD_USERNAME + ARGOCD_PASSWORD: Username and password authentication

Usage

Basic Usage

.gitops-update-values-and-sync:
image: alpine:3.20
variables:
TARGET_REPO: "https://gitlab.com/welance/platform/gitops/app-configs.git"
TARGET_BRANCH: "main"
VALUES_FILE: "environments/dev/values.yaml"
ENV_LOCAL_PATH: ".env"
ENV_OUT_FILE: "environments/dev/env.yaml"
WRITE_ENV_FILE: "true"
COMMIT_MESSAGE: "chore(gitops): set image tag & commit info + env"
GIT_USER_NAME: "CI Bot"
GIT_USER_EMAIL: "[email protected]"
GIT_HTTP_USERNAME: "oauth2"
GIT_HTTP_PASSWORD: "${CI_JOB_TOKEN}"
ARGOCD_SERVER: "https://argocd.example.com"
ARGOCD_APP: "my-app-dev"
ARGOCD_GRPC_WEB: "true"
ARGOCD_INSECURE: "true"
ARGOCD_SYNC_TIMEOUT: "600"
before_script:
- apk add --no-cache git curl bash yq
- git config --global user.name "$GIT_USER_NAME"
- git config --global user.email "$GIT_USER_EMAIL"
- git config --global credential.helper 'store --file ~/.git-credentials'
- printf "https://%s:%[email protected]\n" "$GIT_HTTP_USERNAME" "$GIT_HTTP_PASSWORD" > ~/.git-credentials
- git config --global url."https://gitlab.com/".insteadOf "[email protected]:"
- git config --global url."https://gitlab.com/".insteadOf "ssh://[email protected]/"
- curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
- chmod +x /usr/local/bin/argocd
script:
# ... (see full script in job definition)
rules:
- if: '$CI_PIPELINE_SOURCE == "pipeline" || $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web"'
when: on_success

Job Details

Before Script

The before_script section performs the following setup:

  1. Install dependencies:

    apk add --no-cache git curl bash yq
    • Installs Git, curl, bash, and yq (YAML processor)
  2. Configure Git identity:

    git config --global user.name  "$GIT_USER_NAME"
    git config --global user.email "$GIT_USER_EMAIL"
  3. Configure Git credentials:

    git config --global credential.helper 'store --file ~/.git-credentials'
    printf "https://%s:%[email protected]\n" "$GIT_HTTP_USERNAME" "$GIT_HTTP_PASSWORD" > ~/.git-credentials
    • Stores Git credentials for HTTPS authentication
  4. Configure URL rewriting:

    git config --global url."https://gitlab.com/".insteadOf "[email protected]:"
    git config --global url."https://gitlab.com/".insteadOf "ssh://[email protected]/"
    • Automatically converts SSH URLs to HTTPS
  5. Install Argo CD CLI:

    curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
    chmod +x /usr/local/bin/argocd

Script

The main script performs the following steps:

  1. Validate required variables:

    : "${TARGET_REPO:?TARGET_REPO is required}"
    : "${TARGET_BRANCH:?TARGET_BRANCH is required}"
    : "${VALUES_FILE:?VALUES_FILE is required}"
  2. Derive default values:

    IMAGE_TAG="${IMAGE_TAG:-${CI_COMMIT_REF_SLUG:-latest}-${CI_PIPELINE_ID:-0}}"
    COMMIT_SHA="${COMMIT_SHA:-${CI_COMMIT_SHA:-unknown}}"
    COMMIT_BRANCH="${COMMIT_BRANCH:-${CI_COMMIT_REF_NAME:-unknown}}"
    COMMIT_URL="${COMMIT_URL:-${CI_PROJECT_URL:-unknown}/-/commit/${CI_COMMIT_SHA:-unknown}}"
  3. Clone GitOps repository:

    git clone --branch "$TARGET_BRANCH" "$TARGET_REPO" repo
    cd repo
    git config --global --add safe.directory "$(pwd)"
  4. Ensure values file exists:

    mkdir -p "$(dirname "$VALUES_FILE")"
    touch "$VALUES_FILE"
  5. Update image tag and commit info in values.yaml:

    yq -i '
    .image.tag = env(IMAGE_TAG) |
    .image.commit.sha = env(COMMIT_SHA) |
    .image.commit.url = env(COMMIT_URL) |
    .image.commit.branch = env(COMMIT_BRANCH)
    ' "$VALUES_FILE"
    • Uses yq to update only the image tag and commit information
    • Preserves the rest of the image: section
  6. Optional: Convert .env to env.yaml:

    if [ "${WRITE_ENV_FILE}" = "true" ] && [ -f "$WORKDIR/${ENV_LOCAL_PATH}" ]; then
    mkdir -p "$(dirname "$ENV_OUT_FILE")"
    {
    echo "envFileContent: |-"
    sed 's/^/ /' "$WORKDIR/${ENV_LOCAL_PATH}"
    } > "$ENV_OUT_FILE"
    fi
    • Converts .env file to YAML format with proper indentation
    • Creates envFileContent key with the .env content
  7. Commit and push changes:

    if [ -z "$(git status --porcelain)" ]; then
    echo "[INFO] No changes to commit."
    else
    git add -A
    git commit -m "$COMMIT_MESSAGE"
    git push origin "HEAD:$TARGET_BRANCH"
    fi
    • Only commits if there are changes
  8. Argo CD login and sync:

    # Login with token or username/password
    # Sync application
    argocd app sync "$ARGOCD_APP" --timeout "$ARGOCD_SYNC_TIMEOUT" --server "$ARGOCD_SERVER" $ARGO_FLAGS
    # Wait for sync to complete
    argocd app wait "$ARGOCD_APP" --timeout "$ARGOCD_SYNC_TIMEOUT" --server "$ARGOCD_SERVER" $ARGO_FLAGS

Configuration Examples

Development Environment

variables:
TARGET_REPO: "https://gitlab.com/welance/platform/gitops/app-configs.git"
TARGET_BRANCH: "main"
VALUES_FILE: "environments/dev/values.yaml"
ENV_OUT_FILE: "environments/dev/env.yaml"
ARGOCD_SERVER: "https://argocd.example.com"
ARGOCD_APP: "my-app-dev"
GIT_HTTP_USERNAME: "oauth2"
GIT_HTTP_PASSWORD: "${CI_JOB_TOKEN}"

deploy-gitops-dev:
extends: .gitops-update-values-and-sync
stage: deploy
only:
- develop

Production Environment

variables:
TARGET_REPO: "https://gitlab.com/welance/platform/gitops/app-configs.git"
TARGET_BRANCH: "main"
VALUES_FILE: "environments/prod/values.yaml"
ENV_OUT_FILE: "environments/prod/env.yaml"
ARGOCD_SERVER: "https://argocd.example.com"
ARGOCD_APP: "my-app-prod"
ARGOCD_AUTH_TOKEN: "${ARGOCD_PROD_TOKEN}"
GIT_HTTP_USERNAME: "oauth2"
GIT_HTTP_PASSWORD: "${CI_JOB_TOKEN}"

deploy-gitops-prod:
extends: .gitops-update-values-and-sync
stage: deploy
only:
- main
- tags

Complete Pipeline Example

stages:
- build
- deploy

# Build job creates .env and .env.version
build:
stage: build
script:
- echo "VERSION=main-12345" > .env.version
- echo "DB_HOST=db.example.com" > .env
artifacts:
paths:
- .env
- .env.version

.gitops-update-values-and-sync:
image: alpine:3.20
variables:
TARGET_REPO: "https://gitlab.com/welance/platform/gitops/app-configs.git"
TARGET_BRANCH: "main"
VALUES_FILE: "environments/dev/values.yaml"
ENV_LOCAL_PATH: ".env"
ENV_OUT_FILE: "environments/dev/env.yaml"
WRITE_ENV_FILE: "true"
COMMIT_MESSAGE: "chore(gitops): set image tag & commit info + env"
GIT_USER_NAME: "CI Bot"
GIT_USER_EMAIL: "[email protected]"
GIT_HTTP_USERNAME: "oauth2"
GIT_HTTP_PASSWORD: "${CI_JOB_TOKEN}"
ARGOCD_SERVER: "https://argocd.example.com"
ARGOCD_APP: "my-app-dev"
ARGOCD_GRPC_WEB: "true"
ARGOCD_INSECURE: "true"
ARGOCD_SYNC_TIMEOUT: "600"
before_script:
- apk add --no-cache git curl bash yq
- git config --global user.name "$GIT_USER_NAME"
- git config --global user.email "$GIT_USER_EMAIL"
- git config --global credential.helper 'store --file ~/.git-credentials'
- printf "https://%s:%[email protected]\n" "$GIT_HTTP_USERNAME" "$GIT_HTTP_PASSWORD" > ~/.git-credentials
- git config --global url."https://gitlab.com/".insteadOf "[email protected]:"
- git config --global url."https://gitlab.com/".insteadOf "ssh://[email protected]/"
- curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
- chmod +x /usr/local/bin/argocd
script:
- set -euo pipefail
- |
: "${TARGET_REPO:?TARGET_REPO is required}"
: "${TARGET_BRANCH:?TARGET_BRANCH is required}"
: "${VALUES_FILE:?VALUES_FILE is required}"
- |
IMAGE_TAG="${IMAGE_TAG:-${CI_COMMIT_REF_SLUG:-latest}-${CI_PIPELINE_ID:-0}}"
COMMIT_SHA="${COMMIT_SHA:-${CI_COMMIT_SHA:-unknown}}"
COMMIT_BRANCH="${COMMIT_BRANCH:-${CI_COMMIT_REF_NAME:-unknown}}"
COMMIT_URL="${COMMIT_URL:-${CI_PROJECT_URL:-unknown}/-/commit/${CI_COMMIT_SHA:-unknown}}"
- git clone --branch "$TARGET_BRANCH" "$TARGET_REPO" repo
- WORKDIR="$(pwd)"
- cd repo
- git config --global --add safe.directory "$(pwd)"
- mkdir -p "$(dirname "$VALUES_FILE")"
- touch "$VALUES_FILE"
- |
echo "[INFO] Updating only .image.tag and .image.commit.* in $VALUES_FILE"
export IMAGE_TAG COMMIT_SHA COMMIT_URL COMMIT_BRANCH
yq -i '
.image.tag = env(IMAGE_TAG) |
.image.commit.sha = env(COMMIT_SHA) |
.image.commit.url = env(COMMIT_URL) |
.image.commit.branch = env(COMMIT_BRANCH)
' "$VALUES_FILE"
- |
if [ "${WRITE_ENV_FILE}" = "true" ] && [ -f "$WORKDIR/${ENV_LOCAL_PATH}" ]; then
echo "[INFO] Creating/overwriting ${ENV_OUT_FILE} from ${ENV_LOCAL_PATH}"
mkdir -p "$(dirname "$ENV_OUT_FILE")"
{
echo "envFileContent: |-"
sed 's/^/ /' "$WORKDIR/${ENV_LOCAL_PATH}"
} > "$ENV_OUT_FILE"
else
echo "[INFO] ${ENV_LOCAL_PATH} not found or WRITE_ENV_FILE disabled; skipping env.yaml generation."
fi
- |
if [ -z "$(git status --porcelain)" ]; then
echo "[INFO] No changes to commit."
else
git add -A
git commit -m "$COMMIT_MESSAGE"
git push origin "HEAD:$TARGET_BRANCH"
fi
- |
: "${ARGOCD_SERVER:?ARGOCD_SERVER is required}"
: "${ARGOCD_APP:?ARGOCD_APP is required}"
ARGO_FLAGS=""
[ "$ARGOCD_GRPC_WEB" = "true" ] && ARGO_FLAGS="$ARGO_FLAGS --grpc-web"
[ "$ARGOCD_INSECURE" = "true" ] && ARGO_FLAGS="$ARGO_FLAGS --insecure"
if [ -n "${ARGOCD_AUTH_TOKEN:-}" ]; then
argocd login "$ARGOCD_SERVER" --auth-token "$ARGOCD_AUTH_TOKEN" $ARGO_FLAGS
else
: "${ARGOCD_USERNAME:?Set ARGOCD_USERNAME or ARGOCD_AUTH_TOKEN}"
: "${ARGOCD_PASSWORD:?Set ARGOCD_PASSWORD or ARGOCD_AUTH_TOKEN}"
argocd login "$ARGOCD_SERVER" --username "$ARGOCD_USERNAME" --password "$ARGOCD_PASSWORD" $ARGO_FLAGS
fi
echo "[INFO] Syncing $ARGOCD_APP"
argocd app sync "$ARGOCD_APP" --timeout "$ARGOCD_SYNC_TIMEOUT" --server "$ARGOCD_SERVER" $ARGO_FLAGS
echo "[INFO] Waiting for Healthy/Synced..."
argocd app wait "$ARGOCD_APP" --timeout "$ARGOCD_SYNC_TIMEOUT" --server "$ARGOCD_SERVER" $ARGO_FLAGS
rules:
- if: '$CI_PIPELINE_SOURCE == "pipeline" || $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web"'
when: on_success
dependencies:
- build

deploy-gitops:
extends: .gitops-update-values-and-sync
stage: deploy

Prerequisites

  • GitOps repository: A Git repository containing Helm values files
  • GitLab access: Token with write access to the GitOps repository
  • Argo CD: Argo CD server must be accessible and configured
  • Argo CD application: An Argo CD application must exist and be configured
  • Values file structure: The values file should have an image: section with tag and commit subsections
  • .env file (optional): If WRITE_ENV_FILE is enabled, a .env file must exist

Values File Structure

The job expects the values file to have this structure:

image:
tag: "main-12345"
commit:
sha: "abc123def456"
url: "https://gitlab.com/group/project/-/commit/abc123def456"
branch: "main"

The job will update only these fields, preserving the rest of the image: section.

Security Considerations

  • GitLab Token: Store GIT_HTTP_PASSWORD as a masked CI/CD variable
  • Argo CD Credentials: Store ARGOCD_AUTH_TOKEN or ARGOCD_PASSWORD as masked variables
  • Token Permissions: Ensure tokens have minimal required permissions
  • Insecure Mode: ARGOCD_INSECURE should be false in production if possible
  • Environment Files: Ensure .env files don't contain sensitive data or are properly secured

Notes

  • The job uses yq to update YAML files, preserving the existing structure
  • Only commits if there are actual changes to the repository
  • Automatically derives image tags and commit information from GitLab CI/CD variables
  • Supports both token-based and username/password authentication for Argo CD
  • The .env to env.yaml conversion adds proper YAML indentation
  • Argo CD sync waits for the application to become healthy/synced
  • The job runs only on pipeline, push, or web triggers (not on scheduled pipelines)
  • SSH URLs are automatically converted to HTTPS for Git operations
  • The job uses Alpine Linux for a minimal image size