argoproj/argo-cd

14 workflows · maturity 83% · 12 patterns · GitHub ↗

Security 77.26/100

Security dimensions

permissions
21.4
security scan
20.8
supply chain
20
secret handling
15
harden runner
0

Tools: github/codeql-action/analyze, github/codeql-action/autobuild, github/codeql-action/init, github/codeql-action/upload-sarif, ossf/scorecard-action

Workflows (14)

bump-major-version perms .github/workflows/bump-major-version.yaml
Triggers
workflow_dispatch
Runs on
ubuntu-24.04
Jobs
prepare-release
Actions
peter-evans/create-pull-request
Commands
  • set -ue CURRENT_VERSION=$(grep 'module github.com/argoproj/argo-cd' go.mod | awk '{print $2}' | sed 's/.*\/v//') echo "TARGET_VERSION=$((CURRENT_VERSION + 1))" >> $GITHUB_OUTPUT
  • mkdir -p ~/go/src/github.com/argoproj cp -a ../argo-cd ~/go/src/github.com/argoproj
  • hack/bump-major-version.sh
  • echo "/home/runner/go/bin" >> $GITHUB_PATH
  • echo "/usr/local/bin" >> $GITHUB_PATH
  • # We need to vendor go modules for codegen yet go mod download go mod vendor -v
  • make install-codegen-tools-local make install-go-tools-local
  • echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
View raw YAML
name: Bump major version
on:
  workflow_dispatch: {}

permissions: {}

jobs:
  prepare-release:
    permissions:
      contents: write  # for peter-evans/create-pull-request to create branch
      pull-requests: write  # for peter-evans/create-pull-request to create a PR
    name: Automatically update major version
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      # Get the current major version from go.mod and save it as a variable.
      - name: Get target version
        id: get-target-version
        run: |
          set -ue
          CURRENT_VERSION=$(grep 'module github.com/argoproj/argo-cd' go.mod | awk '{print $2}' | sed 's/.*\/v//')
          echo "TARGET_VERSION=$((CURRENT_VERSION + 1))" >> $GITHUB_OUTPUT

      - name: Copy source code to GOPATH
        run: |
          mkdir -p ~/go/src/github.com/argoproj
          cp -a ../argo-cd ~/go/src/github.com/argoproj

      - name: Run script to bump the version
        run: |
          hack/bump-major-version.sh
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd

      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Download & vendor dependencies
        run: |
          # We need to vendor go modules for codegen yet
          go mod download
          go mod vendor -v
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
      - name: Install toolchain for codegen
        run: |
          make install-codegen-tools-local
          make install-go-tools-local
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
        # We install kustomize in the dist directory
      - name: Add dist to PATH
        run: |
          echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
      - name: Run codegen
        run: |
          set -x
          export GOPATH=$(go env GOPATH)
          make codegen-local
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd

      - name: Copy changes back
        run: |
          # Copy the contents back, but skip the .git directory
          rsync -a --exclude=.git /home/runner/go/src/github.com/argoproj/argo-cd/ ../argo-cd

      - name: Create pull request
        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0  # v8.1.0
        with:
          commit-message: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
          title: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
          body: |
            Congrats! You've just bumped the major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}.
            
            Next steps:
            - [ ] Merge this PR
            - [ ] Add an upgrade guide to the docs for this version
          branch: bump-major-version
          branch-suffix: random
          signoff: true
cherry-pick matrix .github/workflows/cherry-pick.yml
Triggers
pull_request_target
Runs on
ubuntu-24.04
Jobs
find-labels, cherry-pick
Matrix
include→ ${{ fromJSON(needs.find-labels.outputs.labels) }}
Commands
  • if [[ "${{ github.event.action }}" == "labeled" ]]; then # Label was just added - use it directly LABEL_NAME="${{ github.event.label.name }}" VERSION="${LABEL_NAME#cherry-pick/}" CHERRY_PICK_DATA='[{"label":"'$LABEL_NAME'","version":"'$VERSION'"}]' else # PR was closed - find all cherry-pick labels CHERRY_PICK_DATA=$(echo '${{ toJSON(github.event.pull_request.labels) }}' | jq -c '[.[] | select(.name | startswith("cherry-pick/")) | {label: .name, version: (.name | sub("cherry-pick/"; ""))}]') fi echo "labels=$CHERRY_PICK_DATA" >> "$GITHUB_OUTPUT" echo "Found cherry-pick data: $CHERRY_PICK_DATA"
View raw YAML
name: Cherry Pick

on:
  pull_request_target:
    branches:
      - master
    types: ["labeled", "closed"]

jobs:
  find-labels:
    name: Find Cherry Pick Labels
    if: |
      github.event.pull_request.merged == true && (
        (github.event.action == 'labeled' && startsWith(github.event.label.name, 'cherry-pick/')) ||
        (github.event.action == 'closed' && contains(toJSON(github.event.pull_request.labels.*.name), 'cherry-pick/'))
      )
    runs-on: ubuntu-24.04
    outputs:
      labels: ${{ steps.extract-labels.outputs.labels }}
    steps:
      - name: Extract cherry-pick labels
        id: extract-labels
        run: |
          if [[ "${{ github.event.action }}" == "labeled" ]]; then
            # Label was just added - use it directly
            LABEL_NAME="${{ github.event.label.name }}"
            VERSION="${LABEL_NAME#cherry-pick/}"
            CHERRY_PICK_DATA='[{"label":"'$LABEL_NAME'","version":"'$VERSION'"}]'
          else
            # PR was closed - find all cherry-pick labels
            CHERRY_PICK_DATA=$(echo '${{ toJSON(github.event.pull_request.labels) }}' | jq -c '[.[] | select(.name | startswith("cherry-pick/")) | {label: .name, version: (.name | sub("cherry-pick/"; ""))}]')
          fi

          echo "labels=$CHERRY_PICK_DATA" >> "$GITHUB_OUTPUT"
          echo "Found cherry-pick data: $CHERRY_PICK_DATA"

  cherry-pick:
    name: Cherry Pick
    needs: find-labels
    if: needs.find-labels.outputs.labels != '[]'
    strategy:
      matrix:
        include: ${{ fromJSON(needs.find-labels.outputs.labels) }}
      fail-fast: false
    uses: ./.github/workflows/cherry-pick-single.yml
    with:
      merge_commit_sha: ${{ github.event.pull_request.merge_commit_sha }}
      version_number: ${{ matrix.version }}
      pr_number: ${{ github.event.pull_request.number }}
      pr_title: ${{ github.event.pull_request.title }}
    secrets:
      CHERRYPICK_APP_ID: ${{ vars.CHERRYPICK_APP_ID }}
      CHERRYPICK_APP_PRIVATE_KEY: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
cherry-pick-single .github/workflows/cherry-pick-single.yml
Triggers
workflow_call
Runs on
ubuntu-24.04
Jobs
cherry-pick
Actions
actions/create-github-app-token
Commands
  • git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com"
  • set -e MERGE_COMMIT="${{ inputs.merge_commit_sha }}" TARGET_BRANCH="release-${{ inputs.version_number }}" echo "🍒 Cherry-picking commit $MERGE_COMMIT to branch $TARGET_BRANCH" # Check if target branch exists if ! git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then echo "❌ Target branch '$TARGET_BRANCH' does not exist" exit 1 fi # Create new branch for cherry-pick CHERRY_PICK_BRANCH="cherry-pick-${{ inputs.pr_number }}-to-${TARGET_BRANCH}" git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH" # Perform cherry-pick if git cherry-pick -m 1 "$MERGE_COMMIT"; then echo "✅ Cherry-pick successful" # Extract Signed-off-by from the cherry-pick commit SIGNOFF=$(git log -1 --pretty=format:"%B" | grep -E '^Signed-off-by:' || echo "") # Push the new branch. Force push to ensure that in case the original cherry-pick branch is stale, # that the current state of the $TARGET_BRANCH + cherry-pick gets in $CHERRY_PICK_BRANCH. git push origin -f "$CHERRY_PICK_BRANCH" # Save data for PR creation echo "branch_name=$CHERRY_PICK_BRANCH" >> "$GITHUB_OUTPUT" { echo "signoff<<EOF" echo "$SIGNOFF" echo "EOF" } >> "$GITHUB_OUTPUT" echo "target_branch=$TARGET_BRANCH" >> "$GITHUB_OUTPUT" else echo "❌ Cherry-pick failed due to conflicts" git cherry-pick --abort exit 1 fi
  • # Create cherry-pick PR TITLE="${PR_TITLE} (cherry-pick #${{ inputs.pr_number }} for ${{ inputs.version_number }})" BODY=$(cat <<EOF Cherry-picked ${PR_TITLE} (#${{ inputs.pr_number }}) ${{ steps.cherry-pick.outputs.signoff }} EOF ) gh pr create \ --title "$TITLE" \ --body "$BODY" \ --base "${{ steps.cherry-pick.outputs.target_branch }}" \ --head "${{ steps.cherry-pick.outputs.branch_name }}" # Comment on original PR gh pr comment ${{ inputs.pr_number }} \ --body "🍒 Cherry-pick PR created for ${{ inputs.version_number }}: #$(gh pr list --head ${{ steps.cherry-pick.outputs.branch_name }} --json number --jq '.[0].number')"
  • gh pr comment ${{ inputs.pr_number }} \ --body "❌ Cherry-pick failed for ${{ inputs.version_number }}. Please check the [workflow logs](https://github.com/argoproj/argo-cd/actions/runs/${{ github.run_id }}) for details."
View raw YAML
name: Cherry Pick Single

on:
  workflow_call:
    inputs:
      merge_commit_sha:
        required: true
        type: string
        description: "The merge commit SHA to cherry-pick"
      version_number:
        required: true
        type: string
        description: "The version number (from cherry-pick/ label)"
      pr_number:
        required: true
        type: string
        description: "The original PR number"
      pr_title:
        required: true
        type: string
        description: "The original PR title"
    secrets:
      CHERRYPICK_APP_ID:
        required: true
      CHERRYPICK_APP_PRIVATE_KEY:
        required: true

jobs:
  cherry-pick:
    name: Cherry Pick to ${{ inputs.version_number }}
    runs-on: ubuntu-24.04
    steps:
      - name: Generate a token
        id: generate-token
        uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
        with:
          app-id: ${{ secrets.CHERRYPICK_APP_ID }}
          private-key: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}

      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ steps.generate-token.outputs.token }}

      - name: Configure Git
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

      - name: Cherry pick commit
        id: cherry-pick
        run: |
          set -e

          MERGE_COMMIT="${{ inputs.merge_commit_sha }}"
          TARGET_BRANCH="release-${{ inputs.version_number }}"

          echo "🍒 Cherry-picking commit $MERGE_COMMIT to branch $TARGET_BRANCH"

          # Check if target branch exists
          if ! git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then
            echo "❌ Target branch '$TARGET_BRANCH' does not exist"
            exit 1
          fi

          # Create new branch for cherry-pick
          CHERRY_PICK_BRANCH="cherry-pick-${{ inputs.pr_number }}-to-${TARGET_BRANCH}"

          git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH"

          # Perform cherry-pick
          if git cherry-pick -m 1 "$MERGE_COMMIT"; then
            echo "✅ Cherry-pick successful"

            # Extract Signed-off-by from the cherry-pick commit
            SIGNOFF=$(git log -1 --pretty=format:"%B" | grep -E '^Signed-off-by:' || echo "")

            # Push the new branch. Force push to ensure that in case the original cherry-pick branch is stale, 
            # that the current state of the $TARGET_BRANCH + cherry-pick gets in $CHERRY_PICK_BRANCH.
            git push origin -f "$CHERRY_PICK_BRANCH"

            # Save data for PR creation
            echo "branch_name=$CHERRY_PICK_BRANCH" >> "$GITHUB_OUTPUT"
            {
              echo "signoff<<EOF"
              echo "$SIGNOFF"
              echo "EOF"
            } >> "$GITHUB_OUTPUT"
            echo "target_branch=$TARGET_BRANCH" >> "$GITHUB_OUTPUT"
          else
            echo "❌ Cherry-pick failed due to conflicts"
            git cherry-pick --abort
            exit 1
          fi

      - name: Create Pull Request
        run: |
          # Create cherry-pick PR
          TITLE="${PR_TITLE} (cherry-pick #${{ inputs.pr_number }} for ${{ inputs.version_number }})"
          BODY=$(cat <<EOF
          Cherry-picked ${PR_TITLE} (#${{ inputs.pr_number }})

          ${{ steps.cherry-pick.outputs.signoff }}
          EOF
          )

          gh pr create \
            --title "$TITLE" \
            --body "$BODY" \
            --base "${{ steps.cherry-pick.outputs.target_branch }}" \
            --head "${{ steps.cherry-pick.outputs.branch_name }}"

          # Comment on original PR
          gh pr comment ${{ inputs.pr_number }} \
            --body "🍒 Cherry-pick PR created for ${{ inputs.version_number }}: #$(gh pr list --head ${{ steps.cherry-pick.outputs.branch_name }} --json number --jq '.[0].number')"
        env:
          PR_TITLE: ${{ inputs.pr_title }}
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}

      - name: Comment on failure
        if: failure()
        run: |
          gh pr comment ${{ inputs.pr_number }} \
            --body "❌ Cherry-pick failed for ${{ inputs.version_number }}. Please check the [workflow logs](https://github.com/argoproj/argo-cd/actions/runs/${{ github.run_id }}) for details."
        env:
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}
ci-build matrix perms .github/workflows/ci-build.yaml
Triggers
push, pull_request
Runs on
ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ${{ github.repository == 'argoproj/argo-cd' && 'oracle-vm-16cpu-64gb-x86-64' || 'ubuntu-24.04' }}, ubuntu-24.04
Jobs
changes, check-go, build-go, lint-go, test-go, test-go-race, codegen, build-ui, shellcheck, analyze, test-e2e, test-e2e-composite-result
Matrix
k3s, k3s.latest, k3s.version→ False, True, v1.32.1, v1.33.1, v1.34.2, v1.35.0
Actions
tj-actions/changed-files, golangci/golangci-lint-action, codecov/codecov-action, codecov/codecov-action, SonarSource/sonarqube-scan-action, jlumbroso/free-disk-space
Commands
  • go mod download
  • go mod tidy git diff --exit-code -- .
  • go mod download
  • make build-local
  • mkdir -p ~/go/src/github.com/argoproj
  • ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
  • sudo apt-get install git -y
  • git switch -c temporal-pr-branch git status
View raw YAML
name: Integration tests
on:
  push:
    branches:
      - 'master'
      - 'release-*'
      - '!release-1.4'
      - '!release-1.5'
  pull_request:
    branches:
      - 'master'
      - 'release-*'

env:
  # Golang version to use across CI steps
  # renovate: datasource=golang-version packageName=golang
  GOLANG_VERSION: '1.26.0'

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}

permissions:
  contents: read

jobs:
  changes:
    runs-on: ubuntu-24.04
    outputs:
      backend: ${{ steps.filter.outputs.backend_any_changed }}
      frontend: ${{ steps.filter.outputs.frontend_any_changed }}
      docs: ${{ steps.filter.outputs.docs_any_changed }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
        id: filter
        with:
          # Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
          files_yaml: |
            backend:
              - '!ui/**'
              - '!**.md'
              - '!**/*.md'
              - '!docs/**'
            frontend:
              - 'ui/**'
              - Dockerfile
            docs:
              - 'docs/**'
  check-go:
    name: Ensure Go modules synchronicity
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - changes
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Download all Go modules
        run: |
          go mod download
      - name: Check for tidiness of go.mod and go.sum
        run: |
          go mod tidy
          git diff --exit-code -- .
  build-go:
    name: Build & cache Go code
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - changes
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Restore go build and module cache
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: ${{ runner.os }}-go-build-v1-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-build-v1-
      - name: Download Go modules
        run: |
          go mod download
      - name: Compile all packages
        run: make build-local

  lint-go:
    permissions:
      contents: read # for actions/checkout to fetch code
      pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
    name: Lint Go code
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - changes
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Run golangci-lint
        uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
        with:
          # renovate: datasource=go packageName=github.com/golangci/golangci-lint/v2 versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
          version: v2.11.4
          args: --verbose

  test-go:
    name: Run unit tests for Go packages
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - build-go
      - changes
    env:
      GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
      GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
    steps:
      - name: Create checkout directory
        run: mkdir -p ~/go/src/github.com/argoproj
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Create symlink in GOPATH
        run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Install required packages
        run: |
          sudo apt-get install git -y
      - name: Switch to temporal branch so we re-attach head
        run: |
          git switch -c temporal-pr-branch
          git status
      - name: Fetch complete history for blame information
        run: |
          git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Restore go build and module cache
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: ${{ runner.os }}-go-build-v1-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-build-v1-
      - name: Install all tools required for building & testing
        run: |
          make install-test-tools-local
        # We install kustomize in the dist directory
      - name: Add dist to PATH
        run: |
          echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
      - name: Setup git username and email
        run: |
          git config --global user.name "John Doe"
          git config --global user.email "john.doe@example.com"
      - name: Download Go modules
        run: |
          go mod download
      - name: Run all unit tests
        run: make test-local
      - name: Generate test results artifacts
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: test-results
          path: test-results

  test-go-race:
    name: Run unit tests with -race for Go packages
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - build-go
      - changes
    env:
      GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
      GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
    steps:
      - name: Create checkout directory
        run: mkdir -p ~/go/src/github.com/argoproj
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Create symlink in GOPATH
        run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Install required packages
        run: |
          sudo apt-get install git -y
      - name: Switch to temporal branch so we re-attach head
        run: |
          git switch -c temporal-pr-branch
          git status
      - name: Fetch complete history for blame information
        run: |
          git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Restore go build and module cache
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: ${{ runner.os }}-go-build-v1-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-build-v1-
      - name: Install all tools required for building & testing
        run: |
          make install-test-tools-local
        # We install kustomize in the dist directory
      - name: Add dist to PATH
        run: |
          echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
      - name: Setup git username and email
        run: |
          git config --global user.name "John Doe"
          git config --global user.email "john.doe@example.com"
      - name: Download Go modules
        run: |
          go mod download
      - name: Run all unit tests
        run: make test-race-local
      - name: Generate test results artifacts
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: race-results
          path: test-results/

  codegen:
    name: Check changes to generated code
    if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.docs == 'true'}}
    runs-on: ubuntu-24.04
    needs:
      - changes
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Create symlink in GOPATH
        # generalizing repo name for forks:  ${{ github.event.repository.name }}
        run: |
          mkdir -p ~/go/src/github.com/argoproj
          cp -a ../${{ github.event.repository.name }} ~/go/src/github.com/argoproj
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Download & vendor dependencies
        run: |
          # We need to vendor go modules for codegen yet
          go mod download
          go mod vendor -v
        # generalizing repo name for forks:  ${{ github.event.repository.name }}
        working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
      - name: Install toolchain for codegen
        run: |
          make install-codegen-tools-local
          make install-go-tools-local
        # generalizing repo name for forks:  ${{ github.event.repository.name }}
        working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
        # We install kustomize in the dist directory
      - name: Add dist to PATH
        run: |
          echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
      - name: Run codegen
        run: |
          set -x
          export GOPATH=$(go env GOPATH)
          git checkout -- go.mod go.sum
          make codegen-local
        # generalizing repo name for forks:  ${{ github.event.repository.name }}
        working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
      - name: Check nothing has changed
        run: |
          set -xo pipefail
          git diff --exit-code -- . ':!go.sum' ':!go.mod' ':!assets/swagger.json' | tee codegen.patch
        # generalizing repo name for forks:  ${{ github.event.repository.name }}
        working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}

  build-ui:
    name: Build, test & lint UI code
    # We run UI logic for backend changes so that we have a complete set of coverage documents to send to codecov.
    if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - changes
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup NodeJS
        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          # renovate: datasource=node-version packageName=node versioning=node
          node-version: '22.9.0'
      - name: Restore node dependency cache
        id: cache-dependencies
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: ui/node_modules
          key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
      - name: Install node dependencies
        run: |
          cd ui && yarn install --frozen-lockfile --ignore-optional --non-interactive
      - name: Build UI code
        run: |
          yarn test
          yarn build
        env:
          NODE_ENV: production
          NODE_ONLINE_ENV: online
          HOST_ARCH: amd64
          # If we're on the master branch, set the codecov token so that we upload bundle analysis
          CODECOV_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.CODECOV_TOKEN || '' }}
        working-directory: ui/
      - name: Run ESLint
        run: yarn lint
        working-directory: ui/

  shellcheck:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - run: |
          sudo apt-get install shellcheck
          shellcheck -e SC2059 -e SC2154 -e SC2034 -e SC2016 -e SC1091 $(find . -type f -name '*.sh' | grep -v './ui/node_modules') | tee sc.log
          test ! -s sc.log

  analyze:
    name: Process & analyze test artifacts
    if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
    runs-on: ubuntu-24.04
    needs:
      - test-go
      - build-ui
      - changes
      - test-e2e
    env:
      sonar_secret: ${{ secrets.SONAR_TOKEN }}
      codecov_secret: ${{ secrets.CODECOV_TOKEN }}
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
      - name: Restore node dependency cache
        id: cache-dependencies
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: ui/node_modules
          key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
      - name: Remove other node_modules directory
        run: |
          rm -rf ui/node_modules/argo-ui/node_modules
      - name: Get e2e code coverage
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: e2e-code-coverage
          path: e2e-code-coverage
      - name: Get unit test code coverage
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: test-results
          path: test-results
      - name: combine-go-coverage
        # We generate coverage reports for all Argo CD components, but only the applicationset-controller,
        # app-controller, repo-server, and commit-server report contain coverage data. The other components currently
        # don't shut down gracefully, so no coverage data is produced. Once those components are fixed, we can add
        # references to their coverage output directories.
        run: |
          go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
      - name: Upload code coverage information to codecov.io
        # Only run when the workflow is for upstream (PR target or push is in argoproj/argo-cd).
        if: github.repository == 'argoproj/argo-cd'
        uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3
        with:
          files: test-results/full-coverage.out
          fail_ci_if_error: true
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
      - name: Upload test results to Codecov
        # Codecov uploads test results to Codecov.io on upstream master branch.
        if: github.repository == 'argoproj/argo-cd' && github.ref == 'refs/heads/master' && github.event_name == 'push'
        uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3
        with:
          files: test-results/junit.xml
          fail_ci_if_error: true
          token: ${{ secrets.CODECOV_TOKEN }}
          report_type: test_results
      - name: Perform static code analysis using SonarCloud
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7.0.0
        if: env.sonar_secret != ''
  test-e2e:
    name: Run end-to-end tests
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ${{ github.repository == 'argoproj/argo-cd' && 'oracle-vm-16cpu-64gb-x86-64' || 'ubuntu-24.04' }}
    strategy:
      fail-fast: false
      matrix:
        # latest: true means that this version mush upload the coverage report to codecov.io
        # We designate the latest version because we only collect code coverage for that version.
        k3s:
          - version: v1.35.0
            latest: true
          - version: v1.34.2
            latest: false
          - version: v1.33.1
            latest: false
          - version: v1.32.1
            latest: false
    needs:
      - build-go
      - changes
    env:
      ARGOCD_FAKE_IN_CLUSTER: 'true'
      ARGOCD_E2E_K3S: 'true'
      ARGOCD_IN_CI: 'true'
      ARGOCD_E2E_APISERVER_PORT: '8088'
      ARGOCD_APPLICATION_NAMESPACES: 'argocd-e2e-external,argocd-e2e-external-2'
      ARGOCD_SERVER: '127.0.0.1:8088'
      GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
      GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
        with:
          large-packages: false
          docker-images: false
          swap-storage: false
          tool-cache: false
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Set GOPATH
        run: |
          echo "GOPATH=$HOME/go" >> $GITHUB_ENV
      - name: GH actions workaround - Kill XSP4 process
        run: |
          sudo pkill mono || true
      - name: Install K3S
        env:
          INSTALL_K3S_VERSION: ${{ matrix.k3s.version }}+k3s1
        run: |
          set -x
          curl -sfL https://get.k3s.io | sh -
          sudo chmod -R a+rw /etc/rancher/k3s
          sudo mkdir -p $HOME/.kube && sudo chown -R $(whoami) $HOME/.kube
          sudo k3s kubectl config view --raw > $HOME/.kube/config
          sudo chown $(whoami) $HOME/.kube/config
          sudo chmod go-r $HOME/.kube/config
          kubectl version
      - name: Restore go build and module cache
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: ${{ runner.os }}-go-build-v1-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-build-v1-
      - name: Add ~/go/bin to PATH
        run: |
          echo "$HOME/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Add ./dist to PATH
        run: |
          echo "$(pwd)/dist" >> $GITHUB_PATH
      - name: Download Go modules
        run: |
          go mod download
      - name: Install goreman
        run: |
          go install github.com/mattn/goreman@latest
      - name: Install all tools required for building & testing
        run: |
          make install-test-tools-local
      - name: Setup git username and email
        run: |
          git config --global user.name "John Doe"
          git config --global user.email "john.doe@example.com"
      - name: Pull Docker image required for tests
        run: |
          docker pull ghcr.io/dexidp/dex:v2.45.0
          docker pull argoproj/argo-cd-ci-builder:v1.0.0
          docker pull redis:8.2.3-alpine
      - name: Create target directory for binaries in the build-process
        run: |
          mkdir -p dist
          chown $(whoami) dist
      - name: Run E2E server and wait for it being available
        timeout-minutes: 30
        run: |
          set -x
          # Something is weird in GH runners -- there's a phantom listener for
          # port 8080 which is not visible in netstat -tulpen, but still there
          # with a HTTP listener. We have API server listening on port 8088
          # instead.
          make start-e2e-local COVERAGE_ENABLED=true 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log &
          count=1
          until curl -f http://127.0.0.1:8088/healthz; do
            sleep 10;
            if test $count -ge 180; then
              echo "Timeout"
              exit 1
            fi
            count=$((count+1))
          done
      - name: Run E2E testsuite
        run: |
          set -x
          make test-e2e-local
          goreman run stop-all || echo "goreman trouble"
          sleep 30
      - name: Upload e2e coverage report
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: e2e-code-coverage
          path: /tmp/coverage
        if: ${{ matrix.k3s.latest }}
      - name: Upload e2e-server logs
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: e2e-server-k8s${{ matrix.k3s.version }}.log
          path: /tmp/e2e-server.log
        if: ${{ failure() }}

  # workaround for status checks -- check this one job instead of each individual E2E job in the matrix
  # this allows us to skip the entire matrix when it doesn't need to run while still having accurate status checks
  # see:
  # https://github.com/argoproj/argo-workflows/pull/12006
  # https://github.com/orgs/community/discussions/9141#discussioncomment-2296809
  # https://github.com/orgs/community/discussions/26822#discussioncomment-3305794
  test-e2e-composite-result:
    name: E2E Tests - Composite result
    if: ${{ always() }}
    needs:
      - test-e2e
      - changes
    runs-on: ubuntu-24.04
    steps:
      - run: |
          result="${{ needs.test-e2e.result }}"
          # mark as successful even if skipped
          if [[ $result == "success" || $result == "skipped" ]]; then
            exit 0
          else
            exit 1
          fi
codeql perms security .github/workflows/codeql.yml
Triggers
push, pull_request, schedule
Runs on
ubuntu-24.04
Jobs
CodeQL-Build
Actions
github/codeql-action/init, github/codeql-action/autobuild, github/codeql-action/analyze
View raw YAML
name: "Code scanning - action"

on:
  push:
    # Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot
    branches-ignore:
      - 'dependabot/**'
      - 'cherry-pick-*'
      
    # Skip CodeQL for documentation-only changes
    paths-ignore:
      - '**.md'
      - 'docs/**'

  pull_request:
    # Skip CodeQL for documentation-only changes
    paths-ignore:
      - '*.md'
      - '**/*.md'
      - 'docs/assets/**'
  schedule:
    - cron: '0 19 * * 0'

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  CodeQL-Build:
    permissions:
      actions: read  # for github/codeql-action/init to get workflow details
      contents: read  # for actions/checkout to fetch code
      security-events: write  # for github/codeql-action/autobuild to send a status report
    if: github.repository == 'argoproj/argo-cd' || vars.enable_codeql

    # CodeQL runs on ubuntu-latest and windows-latest
    runs-on: ubuntu-24.04
    steps:
    - name: Checkout repository
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

    # Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
    - name: Setup Golang
      uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
      with:
        go-version-file: go.mod
      
    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
      # Override language selection by uncommenting this and choosing your languages
      # with:
      #   languages: go, javascript, csharp, python, cpp, java

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
image perms .github/workflows/image.yaml
Triggers
push, pull_request
Runs on
ubuntu-24.04, ubuntu-24.04
Jobs
set-vars, build-only, build-and-publish, build-and-publish-provenance, Deploy
Commands
  • # Calculate image tag TAG="$(cat ./VERSION)-${GITHUB_SHA::8}" echo "tag=$TAG" >> $GITHUB_OUTPUT # Calculate image names with defaults IMAGE_NAMESPACE="${{ vars.IMAGE_NAMESPACE || 'argoproj' }}" IMAGE_REPOSITORY="${{ vars.IMAGE_REPOSITORY || 'argocd' }}" GHCR_NAMESPACE="${{ vars.GHCR_NAMESPACE || github.repository }}" GHCR_REPOSITORY="${{ vars.GHCR_REPOSITORY || 'argocd' }}" echo "image_namespace=$IMAGE_NAMESPACE" >> $GITHUB_OUTPUT echo "image_repository=$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT # Construct image name echo "quay_image_name=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY:latest" >> $GITHUB_OUTPUT ALLOW_GHCR_PUBLISH=false if [[ "${{ github.repository }}" == "argoproj/argo-cd" || "$GHCR_NAMESPACE" != argoproj/* ]]; then ALLOW_GHCR_PUBLISH=true echo "ghcr_image_name=ghcr.io/$GHCR_NAMESPACE/$GHCR_REPOSITORY:$TAG" >> $GITHUB_OUTPUT echo "ghcr_provenance_image=ghcr.io/$GHCR_NAMESPACE/$GHCR_REPOSITORY" >> $GITHUB_OUTPUT else echo "GhCR publish skipped: refusing to push to namespace '$GHCR_NAMESPACE'. Please override GHCR_* for forks." >&2 echo "ghcr_image_name=" >> $GITHUB_OUTPUT echo "ghcr_provenance_image=" >> $GITHUB_OUTPUT fi echo "allow_ghcr_publish=$ALLOW_GHCR_PUBLISH" >> $GITHUB_OUTPUT
  • IMAGE_PLATFORMS=linux/amd64 if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-multi-image') }}" == "true" ]] then IMAGE_PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le fi echo "Building image for platforms: $IMAGE_PLATFORMS" echo "platforms=$IMAGE_PLATFORMS" >> $GITHUB_OUTPUT
  • git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
  • docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} git config --global user.email 'ci@argoproj.com' git config --global user.name 'CI' git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ needs.set-vars.outputs.image-tag }}' && git push)
View raw YAML
name: Image

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
    types: [labeled, unlabeled, opened, synchronize, reopened]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  set-vars:
    permissions:
      contents: read
    # Always run to calculate variables - other jobs check outputs
    runs-on: ubuntu-24.04
    outputs:
      image-tag: ${{ steps.image.outputs.tag}}
      platforms: ${{ steps.platforms.outputs.platforms }}
      image_namespace: ${{ steps.image.outputs.image_namespace }}
      image_repository: ${{ steps.image.outputs.image_repository }}
      quay_image_name: ${{ steps.image.outputs.quay_image_name }}
      ghcr_image_name: ${{ steps.image.outputs.ghcr_image_name }}
      ghcr_provenance_image: ${{ steps.image.outputs.ghcr_provenance_image }}
      allow_ghcr_publish: ${{ steps.image.outputs.allow_ghcr_publish }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set image tag and names
        run: |
          # Calculate image tag
          TAG="$(cat ./VERSION)-${GITHUB_SHA::8}"
          echo "tag=$TAG" >> $GITHUB_OUTPUT
          
          # Calculate image names with defaults
          IMAGE_NAMESPACE="${{ vars.IMAGE_NAMESPACE || 'argoproj' }}"
          IMAGE_REPOSITORY="${{ vars.IMAGE_REPOSITORY || 'argocd' }}"
          GHCR_NAMESPACE="${{ vars.GHCR_NAMESPACE || github.repository }}"
          GHCR_REPOSITORY="${{ vars.GHCR_REPOSITORY || 'argocd' }}"
          
          echo "image_namespace=$IMAGE_NAMESPACE" >> $GITHUB_OUTPUT
          echo "image_repository=$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT
          
          # Construct image name
          echo "quay_image_name=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY:latest" >> $GITHUB_OUTPUT
          
          ALLOW_GHCR_PUBLISH=false
          if [[ "${{ github.repository }}" == "argoproj/argo-cd" || "$GHCR_NAMESPACE" != argoproj/* ]]; then
            ALLOW_GHCR_PUBLISH=true
            echo "ghcr_image_name=ghcr.io/$GHCR_NAMESPACE/$GHCR_REPOSITORY:$TAG" >> $GITHUB_OUTPUT
            echo "ghcr_provenance_image=ghcr.io/$GHCR_NAMESPACE/$GHCR_REPOSITORY" >> $GITHUB_OUTPUT
          else
            echo "GhCR publish skipped: refusing to push to namespace '$GHCR_NAMESPACE'. Please override GHCR_* for forks." >&2
            echo "ghcr_image_name=" >> $GITHUB_OUTPUT
            echo "ghcr_provenance_image=" >> $GITHUB_OUTPUT
          fi
          echo "allow_ghcr_publish=$ALLOW_GHCR_PUBLISH" >> $GITHUB_OUTPUT
        id: image

      - name: Determine image platforms to use
        id: platforms
        run: |
          IMAGE_PLATFORMS=linux/amd64
          if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-multi-image') }}" == "true" ]]
          then
            IMAGE_PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
          fi
          echo "Building image for platforms: $IMAGE_PLATFORMS"
          echo "platforms=$IMAGE_PLATFORMS" >> $GITHUB_OUTPUT

  build-only:
    needs: [set-vars]
    permissions:
      contents: read
      packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
      id-token: write # for creating OIDC tokens for signing.
    if: ${{ (github.repository == 'argoproj/argo-cd' || needs.set-vars.outputs.image_namespace != 'argoproj') && github.event_name != 'push' }}
    uses: ./.github/workflows/image-reuse.yaml
    with:
      # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
      # renovate: datasource=golang-version packageName=golang
      go-version: 1.26.0
      platforms: ${{ needs.set-vars.outputs.platforms }}
      push: false

  build-and-publish:
    needs: [set-vars]
    permissions:
      contents: read
      packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
      id-token: write # for creating OIDC tokens for signing.
    if: ${{ (github.repository == 'argoproj/argo-cd' || needs.set-vars.outputs.image_namespace != 'argoproj') && github.event_name == 'push' }}
    uses: ./.github/workflows/image-reuse.yaml
    with:
      quay_image_name: ${{ needs.set-vars.outputs.quay_image_name }}
      ghcr_image_name: ${{ needs.set-vars.outputs.ghcr_image_name }}
      # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
      # renovate: datasource=golang-version packageName=golang
      go-version: 1.26.0
      platforms: ${{ needs.set-vars.outputs.platforms }}
      push: true
    secrets:
      quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
      quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
      ghcr_username: ${{ github.actor }}
      ghcr_password: ${{ secrets.GITHUB_TOKEN }}

  build-and-publish-provenance: # Push attestations to GHCR, latest image is polluting quay.io
    needs:
      - set-vars
      - build-and-publish
    permissions:
      actions: read # for detecting the Github Actions environment.
      id-token: write # for creating OIDC tokens for signing.
      packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
    if: ${{ (github.repository == 'argoproj/argo-cd' || needs.set-vars.outputs.image_namespace != 'argoproj') && github.event_name == 'push' && needs.set-vars.outputs.allow_ghcr_publish == 'true'}}
    # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
    with:
      image: ${{ needs.set-vars.outputs.ghcr_provenance_image }}
      digest: ${{ needs.build-and-publish.outputs.image-digest }}
      registry-username: ${{ github.actor }}
    secrets:
      registry-password: ${{ secrets.GITHUB_TOKEN }}

  Deploy:
    needs:
      - build-and-publish
      - set-vars
    permissions:
      contents: write # for git to push upgrade commit if not already deployed
      packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
    if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
        env:
          TOKEN: ${{ secrets.TOKEN }}
      - run: |
          docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
          git config --global user.email 'ci@argoproj.com'
          git config --global user.name 'CI'
          git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ needs.set-vars.outputs.image-tag }}' && git push)
        working-directory: argoproj-deployments/argocd
image-reuse perms .github/workflows/image-reuse.yaml
Triggers
workflow_call
Runs on
ubuntu-24.04
Jobs
publish
Actions
sigstore/cosign-installer, docker/setup-qemu-action, docker/setup-buildx-action, docker/login-action, docker/login-action, docker/login-action, jlumbroso/free-disk-space, docker/build-push-action
Commands
  • IMAGE_TAGS=$(for str in \ ${{ inputs.quay_image_name }} \ ${{ inputs.ghcr_image_name }} \ ${{ inputs.docker_image_name}}; do echo -n "${str}",;done | sed 's/,$//') echo $IMAGE_TAGS echo "TAGS=$IMAGE_TAGS" >> $GITHUB_ENV
  • TAGS=$(for tag in \ ${{ inputs.quay_image_name }} \ ${{ inputs.ghcr_image_name }} \ ${{ inputs.docker_image_name}}; do echo -n "${tag}" | awk -F ":" '{print $1}' -;done) echo $TAGS echo 'SIGNING_TAGS<<EOF' >> $GITHUB_ENV echo $TAGS >> $GITHUB_ENV echo 'EOF' >> $GITHUB_ENV
  • echo "GIT_TAG=$(if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)" >> $GITHUB_ENV echo "GIT_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
  • for signing_tag in $SIGNING_TAGS; do cosign sign \ -a "repo=${{ github.repository }}" \ -a "workflow=${{ github.workflow }}" \ -a "sha=${{ github.sha }}" \ -y \ "$signing_tag"@${{ steps.image.outputs.digest }} done
View raw YAML
name: Publish and Sign Container Image
on:
  workflow_call:
    inputs:
      go-version:
        required: true
        type: string
      quay_image_name:
        required: false
        type: string
      ghcr_image_name:
        required: false
        type: string
      docker_image_name:
        required: false
        type: string
      platforms:
        required: true
        type: string
      push:
        required: true
        type: boolean
      target:
        required: false
        type: string

    secrets:
      quay_username:
        required: false
      quay_password:
        required: false
      ghcr_username:
        required: false
      ghcr_password:
        required: false
      docker_username:
        required: false
      docker_password:
        required: false

    outputs:
      image-digest:
        description: "sha256 digest of container image"
        value: ${{ jobs.publish.outputs.image-digest }}

permissions: {}

jobs:
  publish:
    permissions:
      contents: read
      packages: write # Used to push images to `ghcr.io` if used.
      id-token: write # Needed to create an OIDC token for keyless signing
    runs-on: ubuntu-24.04
    outputs:  
      image-digest: ${{ steps.image.outputs.digest }}
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
        if: ${{ github.ref_type == 'tag'}}

      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        if: ${{ github.ref_type != 'tag'}}

      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ inputs.go-version }}
          cache: false

      - name: Install cosign
        uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0

      - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
      - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

      - name: Setup tags for container image as a CSV type
        run: |
          IMAGE_TAGS=$(for str in \
            ${{ inputs.quay_image_name }} \
            ${{ inputs.ghcr_image_name }} \
            ${{ inputs.docker_image_name}}; do
            echo -n "${str}",;done | sed 's/,$//')

          echo $IMAGE_TAGS
          echo "TAGS=$IMAGE_TAGS" >> $GITHUB_ENV

      - name: Setup image namespace for signing, strip off the tag
        run: |
          TAGS=$(for tag in \
            ${{ inputs.quay_image_name }} \
            ${{ inputs.ghcr_image_name }} \
            ${{ inputs.docker_image_name}}; do
            echo -n "${tag}" | awk -F ":" '{print $1}' -;done)
          
            echo $TAGS
            echo 'SIGNING_TAGS<<EOF' >> $GITHUB_ENV
            echo $TAGS >> $GITHUB_ENV
            echo 'EOF' >> $GITHUB_ENV

      - name: Login to Quay.io
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: quay.io
          username: ${{ secrets.quay_username }}
          password: ${{ secrets.quay_password }}
        if: ${{ inputs.quay_image_name && inputs.push }}

      - name: Login to GitHub Container Registry
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          registry: ghcr.io
          username: ${{ secrets.ghcr_username }}
          password: ${{ secrets.ghcr_password }}
        if: ${{ inputs.ghcr_image_name && inputs.push }}

      - name: Login to dockerhub Container Registry
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        with:
          username: ${{ secrets.docker_username }}
          password: ${{ secrets.docker_password }}
        if: ${{ inputs.docker_image_name && inputs.push }}

      - name: Set up build args for container image
        run: |
            echo "GIT_TAG=$(if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)" >> $GITHUB_ENV
            echo "GIT_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
            echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
            echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV

      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
        with:
          large-packages: false
          docker-images: false
          swap-storage: false
          tool-cache: false

      - name: Build and push container image
        id: image
        uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 #v7.0.0
        with:
          context: .
          platforms: ${{ inputs.platforms }}
          push: ${{ inputs.push }}
          tags: ${{ env.TAGS }}
          target: ${{ inputs.target }}
          provenance: false
          sbom: false
          build-args: |
            GIT_TAG=${{env.GIT_TAG}}
            GIT_COMMIT=${{env.GIT_COMMIT}}
            BUILD_DATE=${{env.BUILD_DATE}}
            GIT_TREE_STATE=${{env.GIT_TREE_STATE}}

      - name: Sign container images
        run: |
          for signing_tag in $SIGNING_TAGS; do
            cosign sign \
            -a "repo=${{ github.repository }}" \
            -a "workflow=${{ github.workflow }}" \
            -a "sha=${{ github.sha }}" \
            -y \
            "$signing_tag"@${{ steps.image.outputs.digest }}
          done
        if: ${{ inputs.push }}
init-release perms .github/workflows/init-release.yaml
Triggers
workflow_dispatch
Runs on
ubuntu-24.04
Jobs
prepare-release
Actions
peter-evans/create-pull-request
Commands
  • set -xue # Target version must not contain 'v' prefix if echo "${{ inputs.TARGET_VERSION }}" | grep -e '^v'; then echo "::error::Target version '${{ inputs.TARGET_VERSION }}' should not begin with a 'v' prefix, refusing to continue." >&2 exit 1 fi
  • set -ue echo "Bumping version from $(cat VERSION) to ${{ inputs.TARGET_VERSION }}" echo "${{ inputs.TARGET_VERSION }}" > VERSION
  • echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
  • set -ue make install-codegen-tools-local make manifests-local VERSION=${{ inputs.TARGET_VERSION }} git diff
  • git stash bash hack/update-supported-versions.sh git add -u . git stash pop
View raw YAML
name: Init ArgoCD Release
on:
  workflow_dispatch:
    inputs:
      TARGET_BRANCH:
        description: 'TARGET_BRANCH to checkout (e.g. release-2.5)'
        required: true
        type: string

      TARGET_VERSION:
        description: 'TARGET_VERSION to build manifests (e.g. 2.5.0-rc1) Note: the `v` prefix is not used'
        required: true
        type: string

permissions: {}

jobs:
  prepare-release:
    permissions:
      contents: write  # for peter-evans/create-pull-request to create branch
      pull-requests: write  # for peter-evans/create-pull-request to create a PR
    name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }}
    runs-on: ubuntu-24.04
    env:
      # Calculate image names with defaults, this will be used in the make manifests-local command
      # to generate the correct image name in the manifests
      IMAGE_REGISTRY: ${{ vars.IMAGE_REGISTRY || 'quay.io' }}
      IMAGE_NAMESPACE: ${{ vars.IMAGE_NAMESPACE || 'argoproj' }}
      IMAGE_REPOSITORY: ${{ vars.IMAGE_REPOSITORY || 'argocd' }}
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
          ref: ${{ inputs.TARGET_BRANCH }}

      - name: Check if TARGET_VERSION is well formed.
        run: |
          set -xue
          # Target version must not contain 'v' prefix
          if echo "${{ inputs.TARGET_VERSION }}" | grep -e '^v'; then
            echo "::error::Target version '${{ inputs.TARGET_VERSION }}' should not begin with a 'v' prefix, refusing to continue." >&2
            exit 1
          fi

      - name: Create VERSION information
        run: |
          set -ue
          echo "Bumping version from $(cat VERSION) to ${{ inputs.TARGET_VERSION }}"
          echo "${{ inputs.TARGET_VERSION }}" > VERSION

        # We install kustomize in the dist directory
      - name: Add dist to PATH
        run: |
          echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH

      - name: Generate new set of manifests
        run: |
          set -ue
          make install-codegen-tools-local
          make manifests-local VERSION=${{ inputs.TARGET_VERSION }}
          git diff

      - name: Generate version compatibility table
        run: |
          git stash
          bash hack/update-supported-versions.sh
          git add -u .
          git stash pop

      - name: Create pull request
        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0  # v8.1.0
        with:
          commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
          title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
          body: Updating VERSION and manifests to ${{ inputs.TARGET_VERSION }}
          branch: update-version
          branch-suffix: random
          signoff: true
          labels: release


pr-title-check perms .github/workflows/pr-title-check.yml
Triggers
pull_request
Runs on
ubuntu-24.04
Jobs
validate
Actions
thehanimo/pr-title-checker
View raw YAML
name: "Lint PR"

on:
  pull_request:
    types: [opened, edited, reopened, synchronize]

permissions: {}

# PR updates can happen in quick succession leading to this
# workflow being trigger a number of times. This limits it
# to one run per PR.
concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref }}
  cancel-in-progress: true

jobs:
  validate:
    permissions:
      contents: read
      pull-requests: read
    name: Validate PR Title
    runs-on: ubuntu-24.04
    steps:
      - uses: thehanimo/pr-title-checker@7fbfe05602bdd86f926d3fb3bccb6f3aed43bc70 # v1.4.3
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          configuration_path: ".github/pr-title-checker-config.json"
release perms .github/workflows/release.yaml
Triggers
push
Runs on
ubuntu-24.04, ubuntu-24.04, ubuntu-24.04, ubuntu-24.04
Jobs
argocd-image, setup-variables, argocd-image-provenance, goreleaser, goreleaser-provenance, generate-sbom, sbom-provenance, post-release
Actions
jlumbroso/free-disk-space, goreleaser/goreleaser-action, softprops/action-gh-release, peter-evans/create-pull-request
Commands
  • set -xue # Fetch all tag information git fetch --prune --tags --force LATEST_RELEASE_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | grep -v '-' | tail -n1) PRE_RELEASE=false # Check if latest tag is a pre-release if echo ${{ github.ref_name }} | grep -E -- '-rc[0-9]+$';then PRE_RELEASE=true fi IS_LATEST=false # Ensure latest release tag matches github.ref_name if [[ $LATEST_RELEASE_TAG == ${{ github.ref_name }} ]];then IS_LATEST=true fi echo "is_pre_release=$PRE_RELEASE" >> $GITHUB_OUTPUT echo "is_latest_release=$IS_LATEST" >> $GITHUB_OUTPUT # Calculate configuration with defaults ENABLE_FORK_RELEASES="${{ vars.ENABLE_FORK_RELEASES || 'false' }}" IMAGE_NAMESPACE="${{ vars.IMAGE_NAMESPACE || 'argoproj' }}" IMAGE_REPOSITORY="${{ vars.IMAGE_REPOSITORY || 'argocd' }}" echo "enable_fork_releases=$ENABLE_FORK_RELEASES" >> $GITHUB_OUTPUT echo "image_namespace=$IMAGE_NAMESPACE" >> $GITHUB_OUTPUT echo "image_repository=$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT echo "quay_image_name=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY:${{ github.ref_name }}" >> $GITHUB_OUTPUT echo "provenance_image=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT ALLOW_FORK_RELEASE=false if [[ "${{ github.repository_owner }}" != "argoproj" && "$ENABLE_FORK_RELEASES" == "true" && "$IMAGE_NAMESPACE" != "argoproj" && "${{ github.ref }}" == refs/tags/* ]]; then ALLOW_FORK_RELEASE=true fi echo "allow_fork_release=$ALLOW_FORK_RELEASE" >> $GITHUB_OUTPUT
  • git fetch --force --tags
  • set -xue GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }}) || exit 1 echo "GORELEASER_PREVIOUS_TAG=$GORELEASER_PREVIOUS_TAG" >> $GITHUB_ENV
  • echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
  • set -euo pipefail hashes=$(echo $ARTIFACTS | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join(" ") | sub("^sha256:";"")' | base64 -w0) if test "$hashes" = ""; then # goreleaser < v1.13.0 checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path') hashes=$(cat $checksum_file | base64 -w0) fi echo "hashes=$hashes" >> $GITHUB_OUTPUT
  • yarn install --cwd ./ui go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION go install sigs.k8s.io/bom/cmd/bom@$SIGS_BOM_VERSION # Generate SPDX for project dependencies analyzing package managers for folder in $(echo $PROJECT_FOLDERS | sed "s/,/ /g") do generator -p $folder -o /tmp done # Generate SPDX for binaries analyzing the docker image if [[ ! -z $DOCKER_IMAGE ]]; then bom generate -o /tmp/bom-docker-image.spdx -i $DOCKER_IMAGE fi cd /tmp && tar -zcf sbom.tar.gz *.spdx
  • # sha256sum generates sha256 hash for sbom. # base64 -w0 encodes to base64 and outputs on a single line. # sha256sum /tmp/sbom.tar.gz ... | base64 -w0 echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
  • set -ue git config --global user.email 'ci@argoproj.com' git config --global user.name 'CI'
View raw YAML
name: Publish ArgoCD Release
on:
  push:
    tags:
      - 'v*'
      - '!v2.4*'
      - '!v2.5*'
      - '!v2.6*'

permissions: {}

env:
  # renovate: datasource=golang-version packageName=golang
  GOLANG_VERSION: '1.26.0' # Note: go-version must also be set in job argocd-image.with.go-version

jobs:
  argocd-image:
    needs: [setup-variables]
    permissions:
      contents: read
      id-token: write # for creating OIDC tokens for signing.
      packages: write # used to push images to `ghcr.io` if used.
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    uses: ./.github/workflows/image-reuse.yaml
    with:
      quay_image_name: ${{ needs.setup-variables.outputs.quay_image_name }}
      # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
      # renovate: datasource=golang-version packageName=golang
      go-version: 1.26.0
      platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
      push: true
    secrets:
      quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
      quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}

  setup-variables:
    name: Setup Release Variables
    if: github.repository == 'argoproj/argo-cd' || (github.repository_owner != 'argoproj' && vars.ENABLE_FORK_RELEASES == 'true' && vars.IMAGE_NAMESPACE && vars.IMAGE_NAMESPACE != 'argoproj')
    runs-on: ubuntu-24.04
    outputs:
      is_pre_release: ${{ steps.var.outputs.is_pre_release }}
      is_latest_release: ${{ steps.var.outputs.is_latest_release }}
      enable_fork_releases: ${{ steps.var.outputs.enable_fork_releases }}
      image_namespace: ${{ steps.var.outputs.image_namespace }}
      image_repository: ${{ steps.var.outputs.image_repository }}
      quay_image_name: ${{ steps.var.outputs.quay_image_name }}
      provenance_image: ${{ steps.var.outputs.provenance_image }}
      allow_fork_release: ${{ steps.var.outputs.allow_fork_release }}
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Setup variables
        id: var
        run: |
          set -xue
          # Fetch all tag information
          git fetch --prune --tags --force

          LATEST_RELEASE_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | grep -v '-' | tail -n1)

          PRE_RELEASE=false
          # Check if latest tag is a pre-release
          if echo ${{ github.ref_name }} | grep -E -- '-rc[0-9]+$';then
            PRE_RELEASE=true
          fi

          IS_LATEST=false
          # Ensure latest release tag matches github.ref_name
          if [[ $LATEST_RELEASE_TAG == ${{ github.ref_name }} ]];then
            IS_LATEST=true
          fi
          echo "is_pre_release=$PRE_RELEASE" >> $GITHUB_OUTPUT
          echo "is_latest_release=$IS_LATEST" >> $GITHUB_OUTPUT
          
          # Calculate configuration with defaults
          ENABLE_FORK_RELEASES="${{ vars.ENABLE_FORK_RELEASES || 'false' }}"
          IMAGE_NAMESPACE="${{ vars.IMAGE_NAMESPACE || 'argoproj' }}"
          IMAGE_REPOSITORY="${{ vars.IMAGE_REPOSITORY || 'argocd' }}"
          
          echo "enable_fork_releases=$ENABLE_FORK_RELEASES" >> $GITHUB_OUTPUT
          
          echo "image_namespace=$IMAGE_NAMESPACE" >> $GITHUB_OUTPUT
          echo "image_repository=$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT
          echo "quay_image_name=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY:${{ github.ref_name }}" >> $GITHUB_OUTPUT
          echo "provenance_image=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT
          
          ALLOW_FORK_RELEASE=false
          if [[ "${{ github.repository_owner }}" != "argoproj" && "$ENABLE_FORK_RELEASES" == "true" && "$IMAGE_NAMESPACE" != "argoproj" && "${{ github.ref }}" == refs/tags/* ]]; then
            ALLOW_FORK_RELEASE=true
          fi
          echo "allow_fork_release=$ALLOW_FORK_RELEASE" >> $GITHUB_OUTPUT

  argocd-image-provenance:
    needs: [setup-variables, argocd-image]
    permissions:
      actions: read # for detecting the Github Actions environment.
      id-token: write # for creating OIDC tokens for signing.
      packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
    # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
    with:
      image: ${{ needs.setup-variables.outputs.provenance_image }}
      digest: ${{ needs.argocd-image.outputs.image-digest }}
    secrets:
      registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
      registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}

  goreleaser:
    needs:
      - setup-variables
      - argocd-image
      - argocd-image-provenance
    permissions:
      contents: write # used for uploading assets
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    runs-on: ubuntu-24.04
    env:
      GORELEASER_MAKE_LATEST: ${{ needs.setup-variables.outputs.is_latest_release }}
    outputs:
      hashes: ${{ steps.hash.outputs.hashes }}
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Fetch all tags
        run: git fetch --force --tags

      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
          cache: false

      - name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in release branches.
        run: |
          set -xue
          GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }}) || exit 1
          echo "GORELEASER_PREVIOUS_TAG=$GORELEASER_PREVIOUS_TAG" >> $GITHUB_ENV

      - name: Set environment variables for ldflags
        id: set_ldflag
        run: |
          echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV
          echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV

      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
        with:
          large-packages: false
          docker-images: false
          swap-storage: false
          tool-cache: false

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
        id: run-goreleaser
        with:
          version: latest
          args: release --clean --timeout 55m
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
          GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }}
          # Used to determine the current repository in the goreleaser config to display correct manifest links
          GORELEASER_CURRENT_REPOSITORY: ${{ github.repository }}

      - name: Generate subject for provenance
        id: hash
        env:
          ARTIFACTS: '${{ steps.run-goreleaser.outputs.artifacts }}'
        run: |
          set -euo pipefail

          hashes=$(echo $ARTIFACTS | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join("  ") | sub("^sha256:";"")' | base64 -w0)
          if test "$hashes" = ""; then # goreleaser < v1.13.0
            checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path')
            hashes=$(cat $checksum_file | base64 -w0)
          fi
          echo "hashes=$hashes" >> $GITHUB_OUTPUT

  goreleaser-provenance:
    needs: [goreleaser, setup-variables]
    permissions:
      actions: read # for detecting the Github Actions environment
      id-token: write # Needed for provenance signing and ID
      contents: write #  Needed for release uploads
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
    with:
      base64-subjects: '${{ needs.goreleaser.outputs.hashes }}'
      provenance-name: 'argocd-cli.intoto.jsonl'
      upload-assets: true

  generate-sbom:
    name: Create SBOM and generate hash
    needs:
      - argocd-image
      - goreleaser
      - setup-variables
    permissions:
      contents: write # Needed for release uploads
    outputs:
      hashes: ${{ steps.sbom-hash.outputs.hashes }}
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup Golang
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
          cache: false

      - name: Generate SBOM (spdx)
        id: spdx-builder
        env:
          # defines the spdx/spdx-sbom-generator version to use.
          SPDX_GEN_VERSION: v0.0.13
          # defines the sigs.k8s.io/bom version to use.
          SIGS_BOM_VERSION: v0.2.1
          # comma delimited list of project relative folders to inspect for package
          # managers (gomod, yarn, npm).
          PROJECT_FOLDERS: '.,./ui'
          # full qualified name of the docker image to be inspected
          DOCKER_IMAGE: ${{ needs.setup-variables.outputs.quay_image_name }}
        run: |
          yarn install --cwd ./ui
          go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION
          go install sigs.k8s.io/bom/cmd/bom@$SIGS_BOM_VERSION

          # Generate SPDX for project dependencies analyzing package managers
          for folder in $(echo $PROJECT_FOLDERS | sed "s/,/ /g")
          do
            generator -p $folder -o /tmp
          done

          # Generate SPDX for binaries analyzing the docker image
          if [[ ! -z $DOCKER_IMAGE ]]; then
            bom generate -o /tmp/bom-docker-image.spdx -i $DOCKER_IMAGE
          fi

          cd /tmp && tar -zcf sbom.tar.gz *.spdx

      - name: Generate SBOM hash
        shell: bash
        id: sbom-hash
        run: |
          # sha256sum generates sha256 hash for sbom.
          # base64 -w0 encodes to base64 and outputs on a single line.
          # sha256sum /tmp/sbom.tar.gz ... | base64 -w0
          echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"

      - name: Upload SBOM
        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          files: |
            /tmp/sbom.tar.gz

  sbom-provenance:
    needs: [generate-sbom, setup-variables]
    permissions:
      actions: read # for detecting the Github Actions environment
      id-token: write # Needed for provenance signing and ID
      contents: write #  Needed for release uploads
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    # Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
    with:
      base64-subjects: '${{ needs.generate-sbom.outputs.hashes }}'
      provenance-name: 'argocd-sbom.intoto.jsonl'
      upload-assets: true

  post-release:
    needs:
      - setup-variables
      - argocd-image
      - goreleaser
      - generate-sbom
    permissions:
      contents: write # Needed to push commit to update stable tag
      pull-requests: write # Needed to create PR for VERSION update.
    if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
    runs-on: ubuntu-24.04
    env:
      TAG_STABLE: ${{ needs.setup-variables.outputs.is_latest_release }}
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup Git author information
        run: |
          set -ue
          git config --global user.email 'ci@argoproj.com'
          git config --global user.name 'CI'

      - name: Update stable tag to latest version
        run: |
          git tag -f stable ${{ github.ref_name }}
          git push -f origin stable
        if: ${{ env.TAG_STABLE == 'true' }}

      - name: Check to see if VERSION should be updated on master branch
        run: |
          set -xue
          SOURCE_TAG=${{ github.ref_name }}
          VERSION_REF="${SOURCE_TAG#*v}"
          COMMIT_HASH=$(git rev-parse HEAD)
          if echo "$VERSION_REF" | grep -E -- '^[0-9]+\.[0-9]+\.0-rc1';then
            VERSION=$(awk 'BEGIN {FS=OFS="."} {$2++; print}' <<< "${VERSION_REF%-rc1}")
            echo "Updating VERSION to: $VERSION"
            echo "UPDATE_VERSION=true" >> $GITHUB_ENV
            echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV
            echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV
          else
            echo "Not updating VERSION"
            echo "UPDATE_VERSION=false" >> $GITHUB_ENV
          fi

      - name: Update VERSION on master branch
        run: |
          echo ${{ env.NEW_VERSION }} > VERSION
          # Replace the 'project-release: vX.X.X-rcX' line in SECURITY-INSIGHTS.yml
          sed -i "s/project-release: v.*$/project-release: v${{ env.NEW_VERSION }}/" SECURITY-INSIGHTS.yml
          # Update the 'commit-hash: XXXXXXX' line in SECURITY-INSIGHTS.yml
          sed -i "s/commit-hash: .*/commit-hash: ${{ env.COMMIT_HASH }}/" SECURITY-INSIGHTS.yml
        if: ${{ env.UPDATE_VERSION == 'true' }}

      - name: Create PR to update VERSION on master branch
        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
        with:
          commit-message: Bump version in master
          title: 'chore: Bump version in master'
          body: All images built from master should indicate which version we are on track for.
          signoff: true
          branch: update-version
          branch-suffix: random
          base: master
        if: ${{ env.UPDATE_VERSION == 'true' }}
renovate perms .github/workflows/renovate.yaml
Triggers
schedule, workflow_dispatch
Runs on
ubuntu-24.04
Jobs
renovate
Actions
actions/create-github-app-token, renovatebot/github-action
View raw YAML
name: Renovate
on:
  schedule:
    - cron: '0 * * * *'
  workflow_dispatch: {}

permissions:
  contents: read

jobs:
  renovate:
    runs-on: ubuntu-24.04
    if: github.repository == 'argoproj/argo-cd'
    steps:
      - name: Get token
        id: get_token
        uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
        with:
          app-id: ${{ vars.RENOVATE_APP_ID }}
          private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}

      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2

      - name: Self-hosted Renovate
        uses: renovatebot/github-action@68a3ea99af6ad249940b5a9fdf44fc6d7f14378b #46.1.6
        with:
          configurationFile: .github/configs/renovate-config.js
          token: '${{ steps.get_token.outputs.token }}'
        env:
          LOG_LEVEL: 'debug'
          RENOVATE_REPOSITORIES: '${{ github.repository }}'
scorecard perms security .github/workflows/scorecard.yaml
Triggers
branch_protection_rule, schedule, push
Runs on
ubuntu-24.04
Jobs
analysis
Actions
ossf/scorecard-action, github/codeql-action/upload-sarif
View raw YAML
name: Scorecards supply-chain security
on:
  # Only the default branch is supported.
  branch_protection_rule:
  schedule:
    - cron: "39 9 * * 2"
  push:
    branches: ["master"]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# Declare default permissions as read only.
permissions: read-all

jobs:
  analysis:
    name: Scorecards analysis
    runs-on: ubuntu-24.04
    permissions:
      # Needed to upload the results to code-scanning dashboard.
      security-events: write
      # Used to receive a badge. (Upcoming feature)
      id-token: write
      # Needs for private repositories.
      contents: read
      actions: read
    if: github.repository == 'argoproj/argo-cd'

    steps:
      - name: "Checkout code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false

      - name: "Run analysis"
        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
        with:
          results_file: results.sarif
          results_format: sarif
          # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
          # - you want to enable the Branch-Protection check on a *public* repository, or
          # - you are installing Scorecards on a *private* repository
          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
          # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}

          # Publish the results for public repositories to enable scorecard badges. For more details, see
          # https://github.com/ossf/scorecard-action#publishing-results.
          # For private repositories, `publish_results` will automatically be set to `false`, regardless
          # of the value entered here.
          publish_results: true

      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
      # format to the repository Actions tab.
      - name: "Upload artifact"
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: SARIF file
          path: results.sarif
          retention-days: 5

      # Upload the results to GitHub's code scanning dashboard.
      - name: "Upload to code-scanning"
        uses: github/codeql-action/upload-sarif@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
        with:
          sarif_file: results.sarif
stale perms .github/workflows/stale.yaml
Triggers
schedule
Runs on
ubuntu-24.04
Jobs
stale
Actions
actions/stale
View raw YAML
name: "Label stale issues and PRs"
on:
  schedule:
    - cron: "0 0 * * *" #Runs midnight 12AM UTC

#Added Recommended permissions
permissions:
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

          stale-issue-message: >
            This issue has been marked as stale because it has had no activity for 90 days. Please comment if this is still relevant.

          stale-pr-message: >
            This pull request has been marked as stale because it has had no activity for 90 days. Please comment if this is still relevant.

          days-before-stale: 90
          days-before-close: -1   # Auto-close diabled

          exempt-issue-labels: >
            bug, security, breaking/high, breaking/medium, breaking/low

          # General configuration
          operations-per-run: 200
          remove-stale-when-updated: true #Remove stale label when issue/pr is updated
update-snyk perms .github/workflows/update-snyk.yaml
Triggers
workflow_dispatch, schedule
Runs on
ubuntu-24.04
Jobs
snyk-report
Commands
  • make snyk-report pr_branch="snyk-update-$(echo $RANDOM | md5sum | head -c 20)" git checkout -b "$pr_branch" git config --global user.email 'ci@argoproj.com' git config --global user.name 'CI' git add docs/snyk git commit -m "[Bot] docs: Update Snyk reports" --signoff git push --set-upstream origin "$pr_branch" gh pr create -B master -H "$pr_branch" --title '[Bot] docs: Update Snyk report' --body ''
View raw YAML
name: Snyk report update
on:
  workflow_dispatch: {}
  schedule:
    - cron: '0 0 * * 0' # midnight every Sunday

permissions:
  contents: read

jobs:
  snyk-report:
    permissions:
      contents: write
      pull-requests: write
    if: github.repository == 'argoproj/argo-cd'
    name: Update Snyk report in the docs directory
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Build reports
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        run: |
          make snyk-report
          pr_branch="snyk-update-$(echo $RANDOM | md5sum | head -c 20)"
          git checkout -b "$pr_branch"
          git config --global user.email 'ci@argoproj.com'
          git config --global user.name 'CI'
          git add docs/snyk
          git commit -m "[Bot] docs: Update Snyk reports" --signoff
          git push --set-upstream origin "$pr_branch"
          gh pr create -B master -H "$pr_branch" --title '[Bot] docs: Update Snyk report' --body ''