_build_publish workflow (kumahq/kuma)
The _build_publish workflow from kumahq/kuma, explained and optimized by Latchkey.
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 →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
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
- Add a job timeout so a hung step cannot burn hours of runner time.
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:
- Dependency installs
This workflow runs 4 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.