Reusable GitHub Actions workflows and composite actions for the pipelines
most repositories need: container images, Terraform, Python, Helm charts,
Kubernetes manifests, policy checks and releases. Each one is a single
uses: line in the consuming repository. The point is to fix a pipeline in
one place. When the Trivy scan needs a new flag or a runner image changes, the
change lands here once and every caller picks it up on its next run, instead
of the same YAML drifting apart across twenty repositories.
Add a workflow to the consuming repository that calls one of the reusable workflows:
# .github/workflows/image.yml
name: Container image
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
permissions:
contents: read
jobs:
image:
uses: michealzs/github-actions-workflows/.github/workflows/docker-build-push.yml@v1
permissions:
contents: read
packages: write
id-token: write
attestations: write
with:
image-name: acme/api
push: ${{ github.event_name != 'pull_request' }}Pull requests build and scan the image. Pushes to main and version tags also
push it to GHCR with provenance and SBOM attestations. Complete callers for
every workflow live in examples/caller-workflows/.
| Name | What it does | Reference |
|---|---|---|
docker-build-push.yml |
Buildx multi-platform build, Trivy scan, GHCR push, provenance and SBOM attestations | catalog |
terraform-plan-apply.yml |
OIDC to AWS, plan posted to the PR, apply of the reviewed plan behind an environment | catalog |
python-ci.yml |
ruff, mypy and pytest with coverage across a version matrix | catalog |
helm-chart-ci.yml |
helm lint, helm template piped to kubeconform, chart-testing on changed charts | catalog |
release-please.yml |
Release PRs and GitHub releases from conventional commits, optional moving major tag | catalog |
k8s-manifests-ci.yml |
kustomize build and kubeconform against Kubernetes and CRD schemas | catalog |
policy-check.yml |
Conftest with Rego policies, yours or the default set in policies/kubernetes |
catalog |
actions/setup-tools |
Pinned kubectl, kustomize, kubeconform, helm, terraform and conftest, checksum-verified and cached | catalog |
actions/slack-notify |
Block Kit status message to a Slack webhook with curl, no third party action | catalog |
actions/pr-labeler |
Label a pull request from the paths it changes with the gh CLI | catalog |
docs/conventions.md explains the rules behind all of
them.
- Least privilege permissions. Every job declares its own
permissions:block, starting fromcontents: readand adding a write scope only where a step needs it. The catalog lists what each workflow asks for. - OIDC, not long-lived keys. Cloud credentials are minted per job from
GitHub's OIDC token and expire when the job ends. Registry pushes and
attestations use
GITHUB_TOKEN. - Pinned majors. Actions are pinned to a major tag and Dependabot proposes bumps. CLI tools are pinned to exact versions and verified against upstream checksums before they run.
- Cache what is safe to cache. Docker layers, pip downloads and tool binaries are cached with keys that include everything they depend on. A cold cache changes the runtime, never the result.
- Fail fast. Cheap checks run before expensive ones, every job has a timeout, and the image scan blocks the push rather than reporting after it.
Consume by tag. v1 is a moving major tag that always points at the newest
v1.x.y release, so @v1 gets fixes and additions without breaking changes:
uses: michealzs/github-actions-workflows/.github/workflows/python-ci.yml@v1Releases are cut by release.yml, which calls
the release-please.yml workflow in this repository with update-major-tag
set, so the major tag moves on every release. Renaming or removing an input,
changing a default that alters behaviour, or raising a required permission is
a breaking change and gets a new major. Pin an exact tag (@v1.4.2) or a
commit SHA if you need a fixed version.
ci.yml runs on every push and pull request:
actionlinton the workflows and on the example callers,yamllintwith.yamllint.yaml, andshellcheckon every script underactions/.conftest verifyon the Rego unit tests inpolicies/kubernetes.actions/setup-toolswith its default pins on a real runner, checking each tool lands onPATH.- Self-tests that call the reusable workflows with relative
uses:paths:python-ci.ymlonexamples/python-app,k8s-manifests-ci.ymlandpolicy-check.ymlonexamples/k8s-manifests.
The same checks run locally from the repository root:
actionlint && actionlint examples/caller-workflows/*.yml
python3 -m yamllint --strict -c .yamllint.yaml .
shellcheck actions/**/*.sh
conftest verify --policy policies/kubernetesMaintained by Micheal (@michealzs).