Build workflow (gardener/gardener)
The Build workflow from gardener/gardener, explained and optimized by Latchkey.
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 →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
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
- Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
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.