Skip to content
Latchkey

_build_publish workflow (kumahq/kuma)

The _build_publish workflow from kumahq/kuma, explained and optimized by Latchkey.

A

CI health: A - excellent

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: kumahq/kuma.github/workflows/_build_publish.yamlLicense Apache-2.0View source

What it does

This is the _build_publish workflow from the kumahq/kuma 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)
on:
  workflow_call:
    inputs:
      FULL_MATRIX:
        required: true
        type: string
      ALLOW_PUSH:
        required: true
        type: string
      BINARY_ARTIFACT_NAME:
        required: true
        type: string
      IMAGE_ARTIFACT_NAME:
        required: true
        type: string
      IMAGES:
        required: true
        type: string
      REGISTRY:
        required: true
        type: string
      VERSION_NAME:
        required: true
        type: string
      NOTARY_REPOSITORY:
        required: true
        type: string
    outputs:
      BINARY_ARTIFACT_DIGEST_BASE64:
        value: ${{ jobs.build-binaries.outputs.BINARY_ARTIFACT_DIGEST_BASE64 }}
      IMAGE_DIGESTS:
        value: ${{ jobs.digest-images.outputs.DIGESTS }}
permissions:
  contents: read
env:
  CI_TOOLS_DIR: "/home/runner/work/kuma/kuma/.ci_tools"
  FULL_MATRIX: ${{ inputs.FULL_MATRIX }}
  ALLOW_PUSH: ${{ inputs.ALLOW_PUSH }}
  GH_OWNER: ${{ github.repository_owner }}
  GH_USER: "github-actions[bot]"
  GH_EMAIL: "<41898282+github-actions[bot]@users.noreply.github.com>"
  GH_REPO: "charts"
  GITHUB_TOKEN: ${{ github.token }}
  MISE_DISABLE_TOOLS: "golangci-lint,skaffold"
jobs:
  build-binaries:
    timeout-minutes: 70
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    permissions:
      id-token: write
      contents: read
    outputs:
      BINARY_ARTIFACT_DIGEST_BASE64: ${{ steps.inspect-binary-output.outputs.binary_artifact_digest_base64 }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
          fetch-depth: 0
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
        with:
          # TODO: drop once https://github.com/jdx/mise/discussions/9889 is fixed (mise v2026.5.8 broke cosign public-key bundle verification).
          version: 2026.5.7
      - run: |
          make build
      - id: annotate-image-tag
        name: Image tag
        run: |
          echo "::notice title=Image tag::$(make build/info/version)"
      - run: |
          make -j build/distributions
      - id: inspect-binary-output
        run: |
          for i in build/distributions/out/*.tar.gz; do echo "$i"; tar -tvf "$i"; done
          echo "Artifact digest:"
          cat ./build/distributions/artifact_digest_file.text
          echo "binary_artifact_digest_base64=$(cat ./build/distributions/artifact_digest_file.text)" > "$GITHUB_OUTPUT"
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: binary-artifacts
        with:
          name: ${{ inputs.BINARY_ARTIFACT_NAME }}
          path: |
            ./build/distributions/out/*.tar.gz
            ./build/distributions/out/*.sha256
            !./build/distributions/out/*.tar.gz.sha256
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      - name: Get Cloudsmith OIDC token
        id: cloudsmith_token
        uses: cloudsmith-io/cloudsmith-cli-action@159f1619275d5d3147f059c3cc110938ec221d16 # v2.0.3
        with:
          oidc-namespace: kong
          oidc-service-slug: ${{ contains(inputs.VERSION_NAME, 'preview') && vars.CLOUDSMITH_PREVIEW_SERVICE_ACCOUNT || vars.CLOUDSMITH_PROD_SERVICE_ACCOUNT }}
          oidc-auth-only: true
      - name: publish binaries
        env:
          PULP_USERNAME: ${{ vars.PULP_USERNAME }}
          PULP_PASSWORD: ${{ secrets.PULP_PASSWORD }}
          CLOUDSMITH_API_KEY: ${{ steps.cloudsmith_token.outputs.oidc-token }}
        run: |
          make publish/pulp
  build-images:
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    permissions:
      id-token: write # Required for image signing
      contents: write # needed to upload SBOM assets to GitHub releases
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        image: ${{ fromJSON(inputs.images) }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Install dependencies for cross builds
        if: ${{ fromJSON(inputs.FULL_MATRIX) }}
        run: |
          sudo apt-get update; sudo apt-get install -y qemu-user-static binfmt-support
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
        with:
          # TODO: drop once https://github.com/jdx/mise/discussions/9889 is fixed (mise v2026.5.8 broke cosign public-key bundle verification).
          version: 2026.5.7
      - id: image_meta
        env:
          REGISTRY: ${{ inputs.REGISTRY }}
          IMAGE_NAME: ${{ matrix.image }}
          VERSION_NAME: ${{ inputs.VERSION_NAME }}
        run: |
          echo "Extracting image meta for $IMAGE_NAME"
          printf 'image=%s/%s:%s\n' "$REGISTRY" "$IMAGE_NAME" "$VERSION_NAME" >> "$GITHUB_OUTPUT"
      - run: |
          make images/${{ matrix.image }}
      - run: |
          make docker/save/${{ matrix.image }}
      - name: Run container structure test
        if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci/skip-container-structure-test') && !contains(github.event.pull_request.labels.*.name, 'ci/skip-test') }}
        run: |
          make test/container-structure/${{ matrix.image }}
      - name: scan amd64 image
        id: scan_image-amd64
        uses: Kong/public-shared-actions/security-actions/scan-docker-image@a92df3beb1e69e27d86be56346488f15fecaf0de # v6.1.1
        with:
          asset_prefix: image_${{ matrix.image }}-amd64
          image: ./build/docker/${{ matrix.image }}-amd64.tar
          upload-sbom-release-assets: true
          skip_cis_scan: false
      - name: scan arm64 image
        id: scan_image-arm64
        if: ${{ fromJSON(inputs.FULL_MATRIX) }}
        uses: Kong/public-shared-actions/security-actions/scan-docker-image@a92df3beb1e69e27d86be56346488f15fecaf0de # v6.1.1
        with:
          asset_prefix: image_${{ matrix.image }}-arm64
          image: ./build/docker/${{ matrix.image }}-arm64.tar
          upload-sbom-release-assets: true
          skip_cis_scan: false
        # TODO in the future we may want to have prerelease images and use `regctl image copy` to move them to their final location
      - name: publish images
        id: release_images
        env:
          DOCKER_API_KEY: ${{ secrets.DOCKER_API_KEY }}
          DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }}
        run: |-
          make docker/login
          # ensure we always logout
          function on_exit() {
            make docker/logout
          }
          trap on_exit EXIT
          make docker/push/${{ matrix.image }}
          make docker/manifest/${{ matrix.image }}
      - name: Install regctl
        uses: regclient/actions/regctl-installer@f07124ffba4b0cbf96b2a666d481ed9d44b5e7e4
      - name: image digest
        id: image_digest
        if: ${{ fromJSON(inputs.ALLOW_PUSH) }}
        run: |
          echo "Fetching image digest for ${{ matrix.image }}"
          digest=$(regctl image digest "${{ steps.image_meta.outputs.image }}")
          echo "Got digest: $digest"
          echo "digest=${digest}" >> "$GITHUB_OUTPUT"
          echo "{\"${{matrix.image}}\": \"${digest}\"}" > ./build/docker/${{ matrix.image }}.digest.json
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: image-artifacts
        with:
          name: image_${{ matrix.image }}
          path: |
            ./build/docker/*.tar
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: image-digest-artifacts
        with:
          name: image_${{ matrix.image }}.digest.json
          path: |
            ./build/docker/${{ matrix.image }}.digest.json
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      - name: sign image
        if: ${{ fromJSON(inputs.ALLOW_PUSH) }}
        id: sign
        uses: Kong/public-shared-actions/security-actions/sign-docker-image@a92df3beb1e69e27d86be56346488f15fecaf0de # v5.0.4
        with:
          image_digest: ${{ steps.image_digest.outputs.digest }}
          tags: ${{ steps.image_meta.outputs.image }}
          signature_registry: ${{ inputs.REGISTRY }}/${{inputs.NOTARY_REPOSITORY}}
          registry_username: ${{ vars.DOCKER_USERNAME }}
          registry_password: ${{ secrets.DOCKER_API_KEY }}
  digest-images:
    needs: [build-images]
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    if: ${{ fromJSON(inputs.ALLOW_PUSH) }}
    outputs:
      DIGESTS: ${{ steps.compute-digests.outputs.digests }}
    steps:
      - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: "image_*.digest.json"
          path: ./digests
          merge-multiple: true
      - id: compute-digests
        run: |
          # Create an object of digests indexed by image (.e.g: {"kuma-cp": "sha256:1234", "kuma-dp": "sha256:5678" ...})
          {
            echo "digests<<EOF"
            jq --slurp 'reduce .[] as $item ({}; . * $item)' ./digests/*.digest.json
            echo "EOF"
          } >> "$GITHUB_OUTPUT"
  publish-helm:
    needs: [build-images]
    timeout-minutes: 10
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Install dependencies for cross builds
        if: ${{ fromJSON(inputs.FULL_MATRIX) }}
        run: |
          sudo apt-get update; sudo apt-get install -y qemu-user-static binfmt-support
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
        with:
          # TODO: drop once https://github.com/jdx/mise/discussions/9889 is fixed (mise v2026.5.8 broke cosign public-key bundle verification).
          version: 2026.5.7
      - name: package-helm-chart
        id: package-helm
        env:
          HELM_DEV: ${{ github.ref_type != 'tag' }}
        run: |
          make helm/update-version

          git config user.name "${GH_USER}"
          git config user.email "${GH_EMAIL}"
          git add -u deployments/charts
          # This commit never ends up in the repo
          git commit --allow-empty -m "ci(helm): update versions"
          # To get an idea of what's in the commit to debug
          git show

          make helm/package
          PKG_FILENAME=$(find .cr-release-packages -type f -printf "%f\n")
          echo "filename=${PKG_FILENAME}" >> "$GITHUB_OUTPUT"
      - name: Upload packaged chart
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: ${{ steps.package-helm.outputs.filename }}
          path: .cr-release-packages/${{ steps.package-helm.outputs.filename }}
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      # Everything from here is only running on releases.
      # Ideally we'd finish the workflow early, but this isn't possible: https://github.com/actions/runner/issues/662
      - name: Generate GitHub app token
        id: github-app-token
        if: ${{ github.ref_type == 'tag' }}
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
          repositories: ${{ env.GH_REPO }}
      - name: Release chart
        if: ${{ github.ref_type == 'tag' }}
        env:
          GITHUB_APP: "true"
          GH_TOKEN: ${{ steps.github-app-token.outputs.token }}
        run: make helm/release

The same workflow, on Latchkey

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

on:
  workflow_call:
    inputs:
      FULL_MATRIX:
        required: true
        type: string
      ALLOW_PUSH:
        required: true
        type: string
      BINARY_ARTIFACT_NAME:
        required: true
        type: string
      IMAGE_ARTIFACT_NAME:
        required: true
        type: string
      IMAGES:
        required: true
        type: string
      REGISTRY:
        required: true
        type: string
      VERSION_NAME:
        required: true
        type: string
      NOTARY_REPOSITORY:
        required: true
        type: string
    outputs:
      BINARY_ARTIFACT_DIGEST_BASE64:
        value: ${{ jobs.build-binaries.outputs.BINARY_ARTIFACT_DIGEST_BASE64 }}
      IMAGE_DIGESTS:
        value: ${{ jobs.digest-images.outputs.DIGESTS }}
permissions:
  contents: read
env:
  CI_TOOLS_DIR: "/home/runner/work/kuma/kuma/.ci_tools"
  FULL_MATRIX: ${{ inputs.FULL_MATRIX }}
  ALLOW_PUSH: ${{ inputs.ALLOW_PUSH }}
  GH_OWNER: ${{ github.repository_owner }}
  GH_USER: "github-actions[bot]"
  GH_EMAIL: "<41898282+github-actions[bot]@users.noreply.github.com>"
  GH_REPO: "charts"
  GITHUB_TOKEN: ${{ github.token }}
  MISE_DISABLE_TOOLS: "golangci-lint,skaffold"
jobs:
  build-binaries:
    timeout-minutes: 70
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    permissions:
      id-token: write
      contents: read
    outputs:
      BINARY_ARTIFACT_DIGEST_BASE64: ${{ steps.inspect-binary-output.outputs.binary_artifact_digest_base64 }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
          fetch-depth: 0
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
        with:
          # TODO: drop once https://github.com/jdx/mise/discussions/9889 is fixed (mise v2026.5.8 broke cosign public-key bundle verification).
          version: 2026.5.7
      - run: |
          make build
      - id: annotate-image-tag
        name: Image tag
        run: |
          echo "::notice title=Image tag::$(make build/info/version)"
      - run: |
          make -j build/distributions
      - id: inspect-binary-output
        run: |
          for i in build/distributions/out/*.tar.gz; do echo "$i"; tar -tvf "$i"; done
          echo "Artifact digest:"
          cat ./build/distributions/artifact_digest_file.text
          echo "binary_artifact_digest_base64=$(cat ./build/distributions/artifact_digest_file.text)" > "$GITHUB_OUTPUT"
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: binary-artifacts
        with:
          name: ${{ inputs.BINARY_ARTIFACT_NAME }}
          path: |
            ./build/distributions/out/*.tar.gz
            ./build/distributions/out/*.sha256
            !./build/distributions/out/*.tar.gz.sha256
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      - name: Get Cloudsmith OIDC token
        id: cloudsmith_token
        uses: cloudsmith-io/cloudsmith-cli-action@159f1619275d5d3147f059c3cc110938ec221d16 # v2.0.3
        with:
          oidc-namespace: kong
          oidc-service-slug: ${{ contains(inputs.VERSION_NAME, 'preview') && vars.CLOUDSMITH_PREVIEW_SERVICE_ACCOUNT || vars.CLOUDSMITH_PROD_SERVICE_ACCOUNT }}
          oidc-auth-only: true
      - name: publish binaries
        env:
          PULP_USERNAME: ${{ vars.PULP_USERNAME }}
          PULP_PASSWORD: ${{ secrets.PULP_PASSWORD }}
          CLOUDSMITH_API_KEY: ${{ steps.cloudsmith_token.outputs.oidc-token }}
        run: |
          make publish/pulp
  build-images:
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    permissions:
      id-token: write # Required for image signing
      contents: write # needed to upload SBOM assets to GitHub releases
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        image: ${{ fromJSON(inputs.images) }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Install dependencies for cross builds
        if: ${{ fromJSON(inputs.FULL_MATRIX) }}
        run: |
          sudo apt-get update; sudo apt-get install -y qemu-user-static binfmt-support
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
        with:
          # TODO: drop once https://github.com/jdx/mise/discussions/9889 is fixed (mise v2026.5.8 broke cosign public-key bundle verification).
          version: 2026.5.7
      - id: image_meta
        env:
          REGISTRY: ${{ inputs.REGISTRY }}
          IMAGE_NAME: ${{ matrix.image }}
          VERSION_NAME: ${{ inputs.VERSION_NAME }}
        run: |
          echo "Extracting image meta for $IMAGE_NAME"
          printf 'image=%s/%s:%s\n' "$REGISTRY" "$IMAGE_NAME" "$VERSION_NAME" >> "$GITHUB_OUTPUT"
      - run: |
          make images/${{ matrix.image }}
      - run: |
          make docker/save/${{ matrix.image }}
      - name: Run container structure test
        if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci/skip-container-structure-test') && !contains(github.event.pull_request.labels.*.name, 'ci/skip-test') }}
        run: |
          make test/container-structure/${{ matrix.image }}
      - name: scan amd64 image
        id: scan_image-amd64
        uses: Kong/public-shared-actions/security-actions/scan-docker-image@a92df3beb1e69e27d86be56346488f15fecaf0de # v6.1.1
        with:
          asset_prefix: image_${{ matrix.image }}-amd64
          image: ./build/docker/${{ matrix.image }}-amd64.tar
          upload-sbom-release-assets: true
          skip_cis_scan: false
      - name: scan arm64 image
        id: scan_image-arm64
        if: ${{ fromJSON(inputs.FULL_MATRIX) }}
        uses: Kong/public-shared-actions/security-actions/scan-docker-image@a92df3beb1e69e27d86be56346488f15fecaf0de # v6.1.1
        with:
          asset_prefix: image_${{ matrix.image }}-arm64
          image: ./build/docker/${{ matrix.image }}-arm64.tar
          upload-sbom-release-assets: true
          skip_cis_scan: false
        # TODO in the future we may want to have prerelease images and use `regctl image copy` to move them to their final location
      - name: publish images
        id: release_images
        env:
          DOCKER_API_KEY: ${{ secrets.DOCKER_API_KEY }}
          DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }}
        run: |-
          make docker/login
          # ensure we always logout
          function on_exit() {
            make docker/logout
          }
          trap on_exit EXIT
          make docker/push/${{ matrix.image }}
          make docker/manifest/${{ matrix.image }}
      - name: Install regctl
        uses: regclient/actions/regctl-installer@f07124ffba4b0cbf96b2a666d481ed9d44b5e7e4
      - name: image digest
        id: image_digest
        if: ${{ fromJSON(inputs.ALLOW_PUSH) }}
        run: |
          echo "Fetching image digest for ${{ matrix.image }}"
          digest=$(regctl image digest "${{ steps.image_meta.outputs.image }}")
          echo "Got digest: $digest"
          echo "digest=${digest}" >> "$GITHUB_OUTPUT"
          echo "{\"${{matrix.image}}\": \"${digest}\"}" > ./build/docker/${{ matrix.image }}.digest.json
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: image-artifacts
        with:
          name: image_${{ matrix.image }}
          path: |
            ./build/docker/*.tar
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: image-digest-artifacts
        with:
          name: image_${{ matrix.image }}.digest.json
          path: |
            ./build/docker/${{ matrix.image }}.digest.json
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      - name: sign image
        if: ${{ fromJSON(inputs.ALLOW_PUSH) }}
        id: sign
        uses: Kong/public-shared-actions/security-actions/sign-docker-image@a92df3beb1e69e27d86be56346488f15fecaf0de # v5.0.4
        with:
          image_digest: ${{ steps.image_digest.outputs.digest }}
          tags: ${{ steps.image_meta.outputs.image }}
          signature_registry: ${{ inputs.REGISTRY }}/${{inputs.NOTARY_REPOSITORY}}
          registry_username: ${{ vars.DOCKER_USERNAME }}
          registry_password: ${{ secrets.DOCKER_API_KEY }}
  digest-images:
    timeout-minutes: 30
    needs: [build-images]
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    if: ${{ fromJSON(inputs.ALLOW_PUSH) }}
    outputs:
      DIGESTS: ${{ steps.compute-digests.outputs.digests }}
    steps:
      - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: "image_*.digest.json"
          path: ./digests
          merge-multiple: true
      - id: compute-digests
        run: |
          # Create an object of digests indexed by image (.e.g: {"kuma-cp": "sha256:1234", "kuma-dp": "sha256:5678" ...})
          {
            echo "digests<<EOF"
            jq --slurp 'reduce .[] as $item ({}; . * $item)' ./digests/*.digest.json
            echo "EOF"
          } >> "$GITHUB_OUTPUT"
  publish-helm:
    needs: [build-images]
    timeout-minutes: 10
    runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Install dependencies for cross builds
        if: ${{ fromJSON(inputs.FULL_MATRIX) }}
        run: |
          sudo apt-get update; sudo apt-get install -y qemu-user-static binfmt-support
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
        with:
          # TODO: drop once https://github.com/jdx/mise/discussions/9889 is fixed (mise v2026.5.8 broke cosign public-key bundle verification).
          version: 2026.5.7
      - name: package-helm-chart
        id: package-helm
        env:
          HELM_DEV: ${{ github.ref_type != 'tag' }}
        run: |
          make helm/update-version
 
          git config user.name "${GH_USER}"
          git config user.email "${GH_EMAIL}"
          git add -u deployments/charts
          # This commit never ends up in the repo
          git commit --allow-empty -m "ci(helm): update versions"
          # To get an idea of what's in the commit to debug
          git show
 
          make helm/package
          PKG_FILENAME=$(find .cr-release-packages -type f -printf "%f\n")
          echo "filename=${PKG_FILENAME}" >> "$GITHUB_OUTPUT"
      - name: Upload packaged chart
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: ${{ steps.package-helm.outputs.filename }}
          path: .cr-release-packages/${{ steps.package-helm.outputs.filename }}
          retention-days: ${{ github.event_name == 'pull_request' && 1 || 10 }}
      # Everything from here is only running on releases.
      # Ideally we'd finish the workflow early, but this isn't possible: https://github.com/actions/runner/issues/662
      - name: Generate GitHub app token
        id: github-app-token
        if: ${{ github.ref_type == 'tag' }}
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
          repositories: ${{ env.GH_REPO }}
      - name: Release chart
        if: ${{ github.ref_type == 'tag' }}
        env:
          GITHUB_APP: "true"
          GH_TOKEN: ${{ steps.github-app-token.outputs.token }}
        run: make helm/release
 

What changed

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 4 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow