Test kube-linter workflow (stackrox/kube-linter)
The Test kube-linter workflow from stackrox/kube-linter, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Test kube-linter workflow from the stackrox/kube-linter 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: Test kube-linter
on:
pull_request:
# Workflows triggered by Dependabot on the "push" event run with read-only access.
# Uploading Code Scanning results requires write access. Ignore dependabot branches for auto-merge.
push:
branches-ignore: "dependabot/**"
tags:
- "*"
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
# Checkout all repo history to make tags available for figuring out kube-linter version during build.
fetch-depth: 0
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Go Build Cache
uses: actions/cache@v6
with:
path: ~/.cache
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v6
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Build binaries
run: make build
- name: Upload binary
uses: actions/upload-artifact@v7
with:
name: bin
path: bin
- name: Run lint checks
run: make lint
- name: Ensure generated files are up-to-date
run: make generated-srcs && git diff --exit-code HEAD
- name: Run unit tests
run: make test
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: stackrox/kube-linter
flags: unit
- name: Run E2E tests
run: make e2e-test
- name: Setup BATS
uses: mig4/setup-bats@v1
with:
bats-version: 1.5.0
- name: Run bats tests
run: make e2e-bats
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: stackrox/kube-linter
flags: bats
- name: Upload Linux binary
uses: actions/upload-artifact@v7
with:
name: kube-linter
path: dist/kube-linter_linux_amd64_v1/kube-linter
- name: Upload Windows binary
uses: actions/upload-artifact@v7
with:
name: kube-linter.exe
path: dist/kube-linter_windows_amd64_v1/kube-linter.exe
test-sarif:
needs: build-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Download executable
uses: actions/download-artifact@v8
with:
name: kube-linter
- name: Set permissions to file
run: chmod +x kube-linter
- name: Print kube-linter version
run: ./kube-linter version
- name: Run kube-linter on a sample file with SARIF output
run: ./kube-linter lint --format=sarif tests/testdata/splunk.yaml > results.sarif
continue-on-error: true
- name: Dump output file and check it is not empty
# The if part will generate no-zero exit code if the file is empty. See https://github.com/stedolan/jq/issues/1142#issuecomment-432003984
run: jq -es 'if . == [] then null else .[] | . end' results.sarif
- name: Upload output file as GitHub artifact for manual investigation
uses: actions/upload-artifact@v7
with:
name: results.sarif
path: results.sarif
- name: Install yajsv
run: curl https://github.com/neilpa/yajsv/releases/download/v1.4.0/yajsv.linux.amd64 -LsSfo yajsv && chmod +x yajsv
- name: Check if output file is valid according to SARIF schema
run: |
set -ex
schema=$(jq -r '.["$schema"]' results.sarif)
[ "$schema" = https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json ]
./yajsv -s ./scripts/sarif/sarif-schema-2.1.0.json results.sarif
- name: Upload SARIF output file to GitHub
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
windows-sanity-test:
name: Windows sanity test
needs: build-and-test
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
with:
# Checkout all repo history to make tags available for figuring out kube-linter version during build.
fetch-depth: 0
- name: Download windows executable
uses: actions/download-artifact@v8
with:
name: kube-linter.exe
path: tmp/
- shell: bash
run: |
tmp/kube-linter.exe version
# Make sure the lint command can run without errors.
# TODO: run the full suite of E2E tests on Windows.
tmp/kube-linter.exe lint "tests/checks/access-to-create-pods.yml"
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Test kube-linter on: pull_request: # Workflows triggered by Dependabot on the "push" event run with read-only access. # Uploading Code Scanning results requires write access. Ignore dependabot branches for auto-merge. push: branches-ignore: "dependabot/**" tags: - "*" env: # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io # github.repository as <account>/<repo> IMAGE_NAME: ${{ github.repository }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-and-test: timeout-minutes: 30 runs-on: latchkey-small steps: - uses: actions/checkout@v7 with: # Checkout all repo history to make tags available for figuring out kube-linter version during build. fetch-depth: 0 - name: Setup Go environment uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Go Build Cache uses: actions/cache@v6 with: path: ~/.cache key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }} - name: Go Mod Cache uses: actions/cache@v6 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} - name: Build binaries run: make build - name: Upload binary uses: actions/upload-artifact@v7 with: name: bin path: bin - name: Run lint checks run: make lint - name: Ensure generated files are up-to-date run: make generated-srcs && git diff --exit-code HEAD - name: Run unit tests run: make test - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v7.0.0 with: token: ${{ secrets.CODECOV_TOKEN }} slug: stackrox/kube-linter flags: unit - name: Run E2E tests run: make e2e-test - name: Setup BATS uses: mig4/setup-bats@v1 with: bats-version: 1.5.0 - name: Run bats tests run: make e2e-bats - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v7.0.0 with: token: ${{ secrets.CODECOV_TOKEN }} slug: stackrox/kube-linter flags: bats - name: Upload Linux binary uses: actions/upload-artifact@v7 with: name: kube-linter path: dist/kube-linter_linux_amd64_v1/kube-linter - name: Upload Windows binary uses: actions/upload-artifact@v7 with: name: kube-linter.exe path: dist/kube-linter_windows_amd64_v1/kube-linter.exe test-sarif: timeout-minutes: 30 needs: build-and-test runs-on: latchkey-small steps: - uses: actions/checkout@v7 - name: Download executable uses: actions/download-artifact@v8 with: name: kube-linter - name: Set permissions to file run: chmod +x kube-linter - name: Print kube-linter version run: ./kube-linter version - name: Run kube-linter on a sample file with SARIF output run: ./kube-linter lint --format=sarif tests/testdata/splunk.yaml > results.sarif continue-on-error: true - name: Dump output file and check it is not empty # The if part will generate no-zero exit code if the file is empty. See https://github.com/stedolan/jq/issues/1142#issuecomment-432003984 run: jq -es 'if . == [] then null else .[] | . end' results.sarif - name: Upload output file as GitHub artifact for manual investigation uses: actions/upload-artifact@v7 with: name: results.sarif path: results.sarif - name: Install yajsv run: curl https://github.com/neilpa/yajsv/releases/download/v1.4.0/yajsv.linux.amd64 -LsSfo yajsv && chmod +x yajsv - name: Check if output file is valid according to SARIF schema run: | set -ex schema=$(jq -r '.["$schema"]' results.sarif) [ "$schema" = https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json ] ./yajsv -s ./scripts/sarif/sarif-schema-2.1.0.json results.sarif - name: Upload SARIF output file to GitHub uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif windows-sanity-test: timeout-minutes: 30 name: Windows sanity test needs: build-and-test runs-on: windows-latest steps: - uses: actions/checkout@v7 with: # Checkout all repo history to make tags available for figuring out kube-linter version during build. fetch-depth: 0 - name: Download windows executable uses: actions/download-artifact@v8 with: name: kube-linter.exe path: tmp/ - shell: bash run: | tmp/kube-linter.exe version # Make sure the lint command can run without errors. # TODO: run the full suite of E2E tests on Windows. tmp/kube-linter.exe lint "tests/checks/access-to-create-pods.yml"
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - 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.
2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
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:
- End-to-end and browser tests
- Network fetches
This workflow runs 3 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.