Skip to content
Latchkey

Build workflow (gardener/gardener)

The Build workflow from gardener/gardener, explained and optimized by Latchkey.

C

CI health: C - fair

Run this on Latchkey for self-healing, caching, and up to 58% lower cost.

Grade your own workflow free or run it on Latchkey →
Source: gardener/gardener.github/workflows/build.yamlLicense Apache-2.0View source

What it does

This is the Build workflow from the gardener/gardener repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Build

on:
  workflow_call:
    inputs:
      mode:
        required: true
        type: string
        default: snapshot
        description: |
          the mode to use. either `snapshot` or `release`. Will affect effective version, as well
          as target-oci-registry.
      commit-objects-artefact:
        type: string
        description: |
          the github-artifact-name containing a captured commit as output by the `capture-commit`
          action. The default value will import the commit emitted by the `prepare` workflow
          (which will result in the build to see the to-be-released commit, with correct
          commit-digest).
        default: release-commit-objects # exported from prepare.yaml
  pull_request:
    branches:
      - master
      - 'release-v*'

jobs:
  prepare:
    uses: gardener/cc-utils/.github/workflows/prepare.yaml@v1
    with:
      mode: ${{ inputs.mode || 'snapshot' }}
    permissions:
      id-token: write

  tests:
    runs-on: ${{ github.repository_owner == 'gardener' && 'ubuntu-latest-large' || 'ubuntu-latest' }}
    if: github.event_name != 'pull_request'
    steps:
      - uses: gardener/cc-utils/.github/actions/trusted-checkout@v1
      - uses: gardener/cc-utils/.github/actions/setup-git-identity@v1
      - uses: actions/setup-go@v6
        with:
          go-version: '1.26'
      - name: run-tests
        shell: bash
        run: |
          set -euo pipefail
          mkdir -p /tmp/blobs.d
          if [ '${{ inputs.mode }}' == 'release' ]; then
            make verify-extended |& tee /tmp/blobs.d/tests-log.txt
            tar czf /tmp/blobs.d/tests-log.tar.gz -C /tmp/blobs.d tests-log.txt
          else
            # tests are run using prow for non-release-runs, so early-exit
            echo "dummy" > /tmp/blobs.d/tests-log.tar.gz
            exit 0
          fi
      - name: add-tests-report-to-component-descriptor
        uses: gardener/cc-utils/.github/actions/export-ocm-fragments@v1
        with:
          blobs-directory: /tmp/blobs.d
          ocm-resources: |
            name: test-results
            relation: local
            access:
              type: localBlob
              localReference: tests-log.tar.gz
            labels:
              - name: gardener.cloud/purposes
                value:
                  - test

  sast-lint:
    uses: gardener/cc-utils/.github/workflows/sastlint-ocm.yaml@v1
    if: github.event_name != 'pull_request'
    permissions:
      contents: read
    with:
      linter: gosec
      run: |
        if [ '${{ inputs.mode }}' != 'release' ]; then
          # tests are run using prow for non-release-runs, so exit early
          mkdir /tmp/blobs.d
          echo "dummy" > /tmp/blobs.d/gosec-report.tar.gz
          exit 0
        fi
        git config --global user.email some.one@some.where
        git config --global user.name 'Some One'
        make sast-report

  oci-images:
    name: Build OCI-Images
    if: ${{ always() && needs.prepare.result == 'success' && needs.tests.result != 'failure' && needs.sast-lint.result != 'failure' }}
    needs:
      - prepare
      - tests
      - sast-lint
    permissions:
      contents: read
      packages: write
      id-token: write
    secrets: inherit
    uses: gardener/cc-utils/.github/workflows/oci-ocm.yaml@v1
    strategy:
      matrix:
        args:
          - name: admission-controller
            target: admission-controller
            oci-repository: gardener/admission-controller
          - name: apiserver
            target: apiserver
            oci-repository: gardener/apiserver
          - name: controller-manager
            target: controller-manager
            oci-repository: gardener/controller-manager
          - name: gardenadm
            target: gardenadm
            oci-repository: gardener/gardenadm
          - name: gardenlet
            target: gardenlet
            oci-repository: gardener/gardenlet
          - name: operator
            target: operator
            oci-repository: gardener/operator
          - name: resource-manager
            target: resource-manager
            oci-repository: gardener/resource-manager
          - name: scheduler
            target: scheduler
            oci-repository: gardener/scheduler
          - name: node-agent
            target: node-agent
            oci-repository: gardener/node-agent
    with:
      name: ${{ matrix.args.name }}
      version: ${{ needs.prepare.outputs.version }}
      target: ${{ matrix.args.target }}
      oci-registry: ${{ needs.prepare.outputs.oci-registry }}
      oci-repository: ${{ matrix.args.oci-repository }}
      oci-platforms: linux/amd64,linux/arm64
      ocm-labels:
      extra-tags: latest
      push: ${{ github.event_name != 'pull_request' && 'if-possible' || 'never' }}

  helmcharts:
    name: Build Helmcharts
    if: github.event_name != 'pull_request'
    needs:
      - prepare
      - oci-images
    permissions:
      contents: read
      packages: write
      id-token: write
    uses: gardener/cc-utils/.github/workflows/helmchart-ocm.yaml@v1
    strategy:
      matrix:
        args:
          - name: operator
            dir: charts/gardener/operator
            oci-repository: charts/gardener
            ocm-mappings:
              - ref: ocm-resource:operator.repository
                attribute: image.repository
              - ref: ocm-resource:operator.tag
                attribute: image.tag
          - name: gardenlet
            dir: charts/gardener/gardenlet
            oci-repository: charts/gardener
            ocm-mappings:
              - ref: ocm-resource:gardenlet.repository
                attribute: image.repository
              - ref: ocm-resource:gardenlet.tag
                attribute: image.tag
          - name: resource-manager
            dir: charts/gardener/resource-manager
            oci-repository: charts/gardener
            ocm-mappings:
              - ref: ocm-resource:resource-manager.repository
                attribute: global.image.repository
              - ref: ocm-resource:resource-manager.tag
                attribute: global.image.tag
    with:
      name: ${{ matrix.args.name }}
      dir: ${{ matrix.args.dir }}
      oci-registry: ${{ needs.prepare.outputs.oci-registry }}
      oci-repository: ${{ matrix.args.oci-repository }}
      ocm-mappings: ${{ toJSON(matrix.args.ocm-mappings) }}

  gardenadm:
    if: ${{ always() && needs.tests.result != 'failure' && needs.sast-lint.result != 'failure' }}
    permissions:
      contents: read
    needs:
      - prepare
      - tests
      - sast-lint
    strategy:
      matrix:
        args:
          - os: linux
            arch: amd64
            archive_suffix: .tar.gz
            runner: ubuntu-latest
          - os: linux
            arch: arm64
            archive_suffix: .tar.gz
            runner: ubuntu-24.04-arm
          - os: darwin
            arch: amd64
            archive_suffix: .tar.gz
            runner: ubuntu-latest
          - os: darwin
            arch: arm64
            archive_suffix: .tar.gz
            runner: ubuntu-24.04-arm
          - os: windows
            arch: amd64
            archive_suffix: .zip
            runner: ubuntu-latest
          - os: windows
            arch: arm64
            archive_suffix: .zip
            runner: ubuntu-24.04-arm
    runs-on: ${{ matrix.args.runner }}
    steps:
      - uses: gardener/cc-utils/.github/actions/trusted-checkout@v1
      - uses: actions/setup-go@v6
        with:
          go-version: '1.26'
      - name: import-release-commit
        if: ${{ inputs.commit-objects-artefact != '' }}
        uses: gardener/cc-utils/.github/actions/import-commit@v1
        with:
          commit-objects-artefact: ${{ inputs.commit-objects-artefact }}
          after-import: rebase
      - name: build-garden-adm
        shell: bash
        run: |
          set -euo pipefail

          ld_flags="$(hack/get-build-ld-flags.sh)"
          os=${{ matrix.args.os }}
          arch=${{ matrix.args.arch }}
          archive_suffix=${{ matrix.args.archive_suffix }}

          if [ ${os} == 'windows' ]; then
            exe_suffix='.exe'
          else
            exe_suffix=''
          fi

          mkdir -p /tmp/blobs.d
          prefix=/tmp/blobs.d/gardenadm
          platform_suffix="-${os}-${arch}"
          outpath="${prefix}${platform_suffix}${exe_suffix}"
          archive="${prefix}${platform_suffix}${archive_suffix}.archive"

          GOOS=${os} \
          GOARCH=${arch} \
          LD_FLAGS=${ld_flags} \
          BUILD_PACKAGES=./cmd/gardenadm \
          BUILD_OUTPUT_FILE=${outpath} \
            make build

          echo "built into ${outpath}"

          if ${{ matrix.args.os == 'windows' }}; then
            (
              cd $(dirname ${outpath})
              zip -j ${archive} $(basename ${outpath})
            )
          else
            tar cvzf ${archive} -C/tmp/blobs.d $(basename ${outpath})
          fi

          unlink ${outpath}
      - uses: gardener/cc-utils/.github/actions/export-ocm-fragments@v1
        if: github.event_name != 'pull_request'
        with:
          blobs-directory: /tmp/blobs.d
          ocm-resources: |
            name: gardenadm
            relation: local
            type: executable
            extraIdentity:
              os: ${{ matrix.args.os }}
              architecture: ${{ matrix.args.arch }}
            access:
              type: localBlob
              localReference: >-
                gardenadm-${{ matrix.args.os }}-${{ matrix.args.arch }}${{ matrix.args.archive_suffix }}.archive

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: Build
 
on:
  workflow_call:
    inputs:
      mode:
        required: true
        type: string
        default: snapshot
        description: |
          the mode to use. either `snapshot` or `release`. Will affect effective version, as well
          as target-oci-registry.
      commit-objects-artefact:
        type: string
        description: |
          the github-artifact-name containing a captured commit as output by the `capture-commit`
          action. The default value will import the commit emitted by the `prepare` workflow
          (which will result in the build to see the to-be-released commit, with correct
          commit-digest).
        default: release-commit-objects # exported from prepare.yaml
  pull_request:
    branches:
      - master
      - 'release-v*'
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  prepare:
    timeout-minutes: 30
    uses: gardener/cc-utils/.github/workflows/prepare.yaml@v1
    with:
      mode: ${{ inputs.mode || 'snapshot' }}
    permissions:
      id-token: write
 
  tests:
    timeout-minutes: 30
    runs-on: ${{ github.repository_owner == 'gardener' && 'ubuntu-latest-large' || 'ubuntu-latest' }}
    if: github.event_name != 'pull_request'
    steps:
      - uses: gardener/cc-utils/.github/actions/trusted-checkout@v1
      - uses: gardener/cc-utils/.github/actions/setup-git-identity@v1
      - uses: actions/setup-go@v6
        with:
          go-version: '1.26'
      - name: run-tests
        shell: bash
        run: |
          set -euo pipefail
          mkdir -p /tmp/blobs.d
          if [ '${{ inputs.mode }}' == 'release' ]; then
            make verify-extended |& tee /tmp/blobs.d/tests-log.txt
            tar czf /tmp/blobs.d/tests-log.tar.gz -C /tmp/blobs.d tests-log.txt
          else
            # tests are run using prow for non-release-runs, so early-exit
            echo "dummy" > /tmp/blobs.d/tests-log.tar.gz
            exit 0
          fi
      - name: add-tests-report-to-component-descriptor
        uses: gardener/cc-utils/.github/actions/export-ocm-fragments@v1
        with:
          blobs-directory: /tmp/blobs.d
          ocm-resources: |
            name: test-results
            relation: local
            access:
              type: localBlob
              localReference: tests-log.tar.gz
            labels:
              - name: gardener.cloud/purposes
                value:
                  - test
 
  sast-lint:
    timeout-minutes: 30
    uses: gardener/cc-utils/.github/workflows/sastlint-ocm.yaml@v1
    if: github.event_name != 'pull_request'
    permissions:
      contents: read
    with:
      linter: gosec
      run: |
        if [ '${{ inputs.mode }}' != 'release' ]; then
          # tests are run using prow for non-release-runs, so exit early
          mkdir /tmp/blobs.d
          echo "dummy" > /tmp/blobs.d/gosec-report.tar.gz
          exit 0
        fi
        git config --global user.email some.one@some.where
        git config --global user.name 'Some One'
        make sast-report
 
  oci-images:
    timeout-minutes: 30
    name: Build OCI-Images
    if: ${{ always() && needs.prepare.result == 'success' && needs.tests.result != 'failure' && needs.sast-lint.result != 'failure' }}
    needs:
      - prepare
      - tests
      - sast-lint
    permissions:
      contents: read
      packages: write
      id-token: write
    secrets: inherit
    uses: gardener/cc-utils/.github/workflows/oci-ocm.yaml@v1
    strategy:
      matrix:
        args:
          - name: admission-controller
            target: admission-controller
            oci-repository: gardener/admission-controller
          - name: apiserver
            target: apiserver
            oci-repository: gardener/apiserver
          - name: controller-manager
            target: controller-manager
            oci-repository: gardener/controller-manager
          - name: gardenadm
            target: gardenadm
            oci-repository: gardener/gardenadm
          - name: gardenlet
            target: gardenlet
            oci-repository: gardener/gardenlet
          - name: operator
            target: operator
            oci-repository: gardener/operator
          - name: resource-manager
            target: resource-manager
            oci-repository: gardener/resource-manager
          - name: scheduler
            target: scheduler
            oci-repository: gardener/scheduler
          - name: node-agent
            target: node-agent
            oci-repository: gardener/node-agent
    with:
      name: ${{ matrix.args.name }}
      version: ${{ needs.prepare.outputs.version }}
      target: ${{ matrix.args.target }}
      oci-registry: ${{ needs.prepare.outputs.oci-registry }}
      oci-repository: ${{ matrix.args.oci-repository }}
      oci-platforms: linux/amd64,linux/arm64
      ocm-labels:
      extra-tags: latest
      push: ${{ github.event_name != 'pull_request' && 'if-possible' || 'never' }}
 
  helmcharts:
    timeout-minutes: 30
    name: Build Helmcharts
    if: github.event_name != 'pull_request'
    needs:
      - prepare
      - oci-images
    permissions:
      contents: read
      packages: write
      id-token: write
    uses: gardener/cc-utils/.github/workflows/helmchart-ocm.yaml@v1
    strategy:
      matrix:
        args:
          - name: operator
            dir: charts/gardener/operator
            oci-repository: charts/gardener
            ocm-mappings:
              - ref: ocm-resource:operator.repository
                attribute: image.repository
              - ref: ocm-resource:operator.tag
                attribute: image.tag
          - name: gardenlet
            dir: charts/gardener/gardenlet
            oci-repository: charts/gardener
            ocm-mappings:
              - ref: ocm-resource:gardenlet.repository
                attribute: image.repository
              - ref: ocm-resource:gardenlet.tag
                attribute: image.tag
          - name: resource-manager
            dir: charts/gardener/resource-manager
            oci-repository: charts/gardener
            ocm-mappings:
              - ref: ocm-resource:resource-manager.repository
                attribute: global.image.repository
              - ref: ocm-resource:resource-manager.tag
                attribute: global.image.tag
    with:
      name: ${{ matrix.args.name }}
      dir: ${{ matrix.args.dir }}
      oci-registry: ${{ needs.prepare.outputs.oci-registry }}
      oci-repository: ${{ matrix.args.oci-repository }}
      ocm-mappings: ${{ toJSON(matrix.args.ocm-mappings) }}
 
  gardenadm:
    timeout-minutes: 30
    if: ${{ always() && needs.tests.result != 'failure' && needs.sast-lint.result != 'failure' }}
    permissions:
      contents: read
    needs:
      - prepare
      - tests
      - sast-lint
    strategy:
      matrix:
        args:
          - os: linux
            arch: amd64
            archive_suffix: .tar.gz
            runner: ubuntu-latest
          - os: linux
            arch: arm64
            archive_suffix: .tar.gz
            runner: ubuntu-24.04-arm
          - os: darwin
            arch: amd64
            archive_suffix: .tar.gz
            runner: ubuntu-latest
          - os: darwin
            arch: arm64
            archive_suffix: .tar.gz
            runner: ubuntu-24.04-arm
          - os: windows
            arch: amd64
            archive_suffix: .zip
            runner: ubuntu-latest
          - os: windows
            arch: arm64
            archive_suffix: .zip
            runner: ubuntu-24.04-arm
    runs-on: ${{ matrix.args.runner }}
    steps:
      - uses: gardener/cc-utils/.github/actions/trusted-checkout@v1
      - uses: actions/setup-go@v6
        with:
          go-version: '1.26'
      - name: import-release-commit
        if: ${{ inputs.commit-objects-artefact != '' }}
        uses: gardener/cc-utils/.github/actions/import-commit@v1
        with:
          commit-objects-artefact: ${{ inputs.commit-objects-artefact }}
          after-import: rebase
      - name: build-garden-adm
        shell: bash
        run: |
          set -euo pipefail
 
          ld_flags="$(hack/get-build-ld-flags.sh)"
          os=${{ matrix.args.os }}
          arch=${{ matrix.args.arch }}
          archive_suffix=${{ matrix.args.archive_suffix }}
 
          if [ ${os} == 'windows' ]; then
            exe_suffix='.exe'
          else
            exe_suffix=''
          fi
 
          mkdir -p /tmp/blobs.d
          prefix=/tmp/blobs.d/gardenadm
          platform_suffix="-${os}-${arch}"
          outpath="${prefix}${platform_suffix}${exe_suffix}"
          archive="${prefix}${platform_suffix}${archive_suffix}.archive"
 
          GOOS=${os} \
          GOARCH=${arch} \
          LD_FLAGS=${ld_flags} \
          BUILD_PACKAGES=./cmd/gardenadm \
          BUILD_OUTPUT_FILE=${outpath} \
            make build
 
          echo "built into ${outpath}"
 
          if ${{ matrix.args.os == 'windows' }}; then
            (
              cd $(dirname ${outpath})
              zip -j ${archive} $(basename ${outpath})
            )
          else
            tar cvzf ${archive} -C/tmp/blobs.d $(basename ${outpath})
          fi
 
          unlink ${outpath}
      - uses: gardener/cc-utils/.github/actions/export-ocm-fragments@v1
        if: github.event_name != 'pull_request'
        with:
          blobs-directory: /tmp/blobs.d
          ocm-resources: |
            name: gardenadm
            relation: local
            type: executable
            extraIdentity:
              os: ${{ matrix.args.os }}
              architecture: ${{ matrix.args.arch }}
            access:
              type: localBlob
              localReference: >-
                gardenadm-${{ matrix.args.os }}-${{ matrix.args.arch }}${{ matrix.args.archive_suffix }}.archive
 

What changed

1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

This workflow runs 6 jobs (21 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow