Co-authored-by: tozuo-home-cluster-eso[bot] <287217661+tozuo-home-cluster-eso[bot]@users.noreply.github.com>
Shared GitHub Actions
Reusable workflows and composite actions for Go projects.
Reusable Workflows
Runner selection
Every reusable workflow accepts runner (label, or a JSON object to pick a runner group):
with:
runner: self-hosted
# or
runner: '{"group":"ci-linux","labels":["x64"]}'
Resolution order: inputs.runner, repository/org variable SHARED_ACTIONS_RUNNER, ubuntu-latest.
Set the variable once to move every consumer. In go-release-cgo.yaml the build matrix keeps its
per-platform os; runner covers the other jobs. Runner groups exist only for organisations and enterprises.
go-release.yaml
Standard Go release workflow using GoReleaser.
jobs:
release:
uses: lukaszraczylo/shared-actions/.github/workflows/go-release.yaml@main
with:
go-version: ">=1.24"
docker-enabled: true # optional
secrets: inherit
Inputs:
| Input | Default | Description |
|---|---|---|
go-version |
>=1.24 |
Go version |
semver-config |
semver.yaml |
Path to semver config |
docker-enabled |
false |
Enable Docker builds |
docker-registry |
ghcr.io |
Docker registry |
rolling-release-tag |
"" |
Rolling release tag (e.g., v1) |
go-release-cgo.yaml
Go release workflow for CGO-enabled projects. Builds natively on each platform.
jobs:
release:
uses: lukaszraczylo/shared-actions/.github/workflows/go-release-cgo.yaml@main
with:
go-version: ">=1.24"
node-enabled: true
node-build-script: "cd ui && npm ci && npm run build"
secrets: inherit
Inputs:
| Input | Default | Description |
|---|---|---|
go-version |
>=1.24 |
Go version |
semver-config |
semver.yaml |
Path to semver config |
rolling-release-tag |
"" |
Rolling release tag |
node-enabled |
false |
Enable Node.js |
node-version |
20 |
Node.js version |
node-build-script |
"" |
Frontend build script |
platforms |
(all 4) | JSON array of platforms |
go-pr.yaml
Pull request checks: tests, linting, security scans.
go vet/staticcheckfindings show as inline annotations on the PR diff; gosec and CodeQL post to code scanning.- The
reportjob posts one sticky comment with every check's result and coverage, and fails if any check fails or coverage is belowcoverage-threshold. RequirePR Checks Reportin branch protection. - A push to a branch with an open PR skips the duplicate run; stale runs on the same branch are cancelled.
- Fork PRs cannot receive the comment; the report still lands in the job summary.
jobs:
pr-checks:
uses: lukaszraczylo/shared-actions/.github/workflows/go-pr.yaml@main
with:
go-version: ">=1.24"
secrets: inherit
node-pr.yaml
Pull request checks for a Node, Vue or Astro project. It installs without changing the lockfile, then runs lint, typecheck, test and build. A script that does not exist is skipped. A push to a branch with an open PR skips the duplicate run, and stale runs on the same branch are cancelled. Call it once per project directory.
name: Pull Request
on:
pull_request:
branches:
- main
push:
branches:
- "**"
- "!main"
permissions:
contents: read
pull-requests: read
jobs:
frontend:
uses: lukaszraczylo/shared-actions/.github/workflows/node-pr.yaml@main
with:
working-directory: web
Inputs:
| Input | Default | Description |
|---|---|---|
node-version |
22 |
Node.js version |
working-directory |
. |
Directory that holds package.json |
scripts |
lint,typecheck,test,build |
Scripts to run in order. A missing script is skipped. |
pnpm-version |
pnpm version for a repo with no packageManager field. Empty uses the field, or the newest pnpm. |
|
require-tests |
false |
Fail when there is no test script |
lfs |
false |
Git LFS checkout |
runner |
Runner label or runner group, as in the other workflows |
pnpm is used when pnpm-lock.yaml exists, yarn for yarn.lock, otherwise npm. pnpm and yarn run through Corepack, so set packageManager in package.json.
go-autoupdate.yaml
Automatic dependency updates for Go. See renovate-autoupdate.yaml for the Renovate-based replacement that also covers npm, pnpm and yarn.
jobs:
autoupdate:
uses: lukaszraczylo/shared-actions/.github/workflows/go-autoupdate.yaml@main
with:
go-version: ">=1.24"
release-workflow: "release.yaml"
secrets: inherit
renovate-autoupdate.yaml
Daily dependency updates for Go and npm/pnpm/yarn projects, built on Renovate. It replaces go-autoupdate.yaml.
- Renovate upgrades everything that can be upgraded on branches named
deps-autoupdate/*and opens no PR. All minor and patch updates (direct and indirect Go modules, npm, pnpm and yarn packages) go ondeps-autoupdate/batch. Each major update gets its own branch. - For each branch, one at a time, the workflow merges it into the default branch locally and runs the tests on that result:
go buildandgo test -race -coverfor Go, thepackage.jsonscripts for Node. - If the tests pass, it opens a PR and squash-merges it. If they fail, no PR is opened. The test output goes into a comment on one sticky issue (see below), and Renovate retries on the next run.
- If anything merged and
release-workflowis set, it dispatches the release once.
name: Update dependencies
on:
workflow_dispatch:
schedule:
- cron: "0 3 * * *"
permissions:
actions: write # only to dispatch release-workflow
contents: write
issues: write # only to report failing updates
pull-requests: write
jobs:
update:
uses: lukaszraczylo/shared-actions/.github/workflows/renovate-autoupdate.yaml@main
with:
go-version: ">=1.24"
release-workflow: release.yaml
The workflow finds go.mod and package.json itself and tests what exists. A repo with both, such as a Go backend with a Vue frontend, gets both tested for every branch.
Inputs:
| Input | Default | Description |
|---|---|---|
go-version |
>=1.24 |
Go version |
go |
true |
Update and test the Go module. Turn off for a repo with no Go code. |
go-working-directory |
. |
Directory that holds go.mod. Only this module is updated and tested. |
go-mod-tidy |
true |
Run go mod tidy after a Go update. Turn it off for an Encore app, where tidy prunes go.sum entries the build needs. |
go-prepare-command |
Shell commands to run before the Go tests, for example installing a CLI. Append a tool directory to $GITHUB_PATH to put it on PATH. |
|
go-test-command |
Replaces the default go build and go test, for example encore test ./... for an Encore app. |
|
node-version |
22 |
Node.js version |
node |
true |
Update and test the Node project. Turn off for a repo with no frontend to test. |
node-working-directory |
. |
Directory that holds package.json. Only this project is updated and tested. |
node-scripts |
lint,typecheck,test,build |
Scripts to run in order. A missing script is skipped. |
pnpm-version |
pnpm version for a repo with no packageManager field. Empty uses the field, or the newest pnpm. |
|
require-node-tests |
true |
Fail when there is no test script, so an untested update never merges |
major-updates |
separate |
separate: each major gets its own branch, tested and merged on its own. batch: majors join the batch. ignore: skip majors. |
minimum-release-age |
2 days |
Skip releases younger than this |
renovate-version |
44 |
Renovate version |
release-workflow |
Workflow file to dispatch after a merge, for example release.yaml. Give several as a comma-separated list. Each needs a workflow_dispatch trigger. Empty means no dispatch. |
|
admin-merge |
false |
Merge with --admin. The workflow token cannot bypass branch protection. |
lfs |
false |
Git LFS checkout |
runner |
Runner label or runner group, as in the other workflows |
Failing updates. The last 50 KB of the test output goes into a comment on one issue titled "Dependency updates failing tests". Each failing branch has its own comment, updated on every run, and a header with the commit and the run link. When a branch passes and merges, its comment is removed. When no failing branch is left, the issue closes. The output is also in the run summary. Without issues: write the run still works, but the issue is skipped.
Only what is tested is updated. Renovate manages one Go module and one Node project: the ones in go-working-directory and node-working-directory. A second package.json (docs, e2e tests, another frontend) or a second go.mod is left alone, because nothing would test its updates.
Branches run one at a time, so each one is tested on top of the ones merged before it. A branch that conflicts with the default branch is skipped, and Renovate rebases it on the next run.
Package manager. pnpm is used when pnpm-lock.yaml exists, yarn for yarn.lock, otherwise npm. The install step never changes the lockfile (--frozen-lockfile, --immutable, npm ci), so a lockfile that does not match package.json fails the run. pnpm and yarn run through Corepack, so set packageManager in package.json.
Per-project rules. Put a renovate.json in the calling repository. Renovate reads it and merges it with the defaults, so no extra input is needed:
{
"ignoreDeps": ["some/package"],
"packageRules": [{ "matchPackageNames": ["vite"], "enabled": false }]
}
The defaults already cap typescript below 7, which breaks vue-tsc.
Token. No secret is needed. Everything runs inside the calling repository with the workflow's own token, which needs contents: write and pull-requests: write. Two repository settings matter:
- Settings, Actions, General: tick "Allow GitHub Actions to create and approve pull requests". Without it the workflow cannot open the PR.
- A push made with the workflow token does not trigger other workflows, so a release workflow that runs on
pushwill not start after a merge. Setrelease-workflowto dispatch it, and grantactions: write.
Commit message. The squash commit subject is the Renovate commit message, for example chore(deps): update dependencies, and the body is fixed text. Neither contains major or breaking, which semver-generator matches in commit messages.
Composite Actions
actions/go-test
Run Go tests.
- uses: lukaszraczylo/shared-actions/.github/actions/go-test@main
with:
go-version: ">=1.24"
cgo-enabled: "1" # optional, default "0"
actions/semver
Calculate semantic version.
- id: semver
uses: lukaszraczylo/shared-actions/.github/actions/semver@main
with:
config-file: semver.yaml
- run: echo "Version: ${{ steps.semver.outputs.version_tag }}"
actions/goreleaser
Run GoReleaser with mode support.
# Full release (single runner)
- uses: lukaszraczylo/shared-actions/.github/actions/goreleaser@main
with:
version-tag: v1.0.0
mode: full
github-token: ${{ secrets.GITHUB_TOKEN }}
# Split build (matrix)
- uses: lukaszraczylo/shared-actions/.github/actions/goreleaser@main
with:
version-tag: v1.0.0
mode: split
cgo-enabled: "1"
github-token: ${{ secrets.GITHUB_TOKEN }}
# Merge artifacts
- uses: lukaszraczylo/shared-actions/.github/actions/goreleaser@main
with:
version-tag: v1.0.0
mode: merge
github-token: ${{ secrets.GITHUB_TOKEN }}
actions/preflight
Fail in seconds when a prerequisite is missing, instead of at the end of a long run. Reads names from the step env, so map secrets there.
- uses: lukaszraczylo/shared-actions/.github/actions/preflight@main
with:
require: GITHUB_TOKEN,NPM_TOKEN # must be non-empty
repo-access: lukaszraczylo/helm-charts # token needs push access
repo-token-env: HOMEBREW_TAP_TOKEN
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
Optional probes use the real token, so they catch caller permissions: that are too narrow:
release-write: "true"runsgit push --dry-run, which fails when the token cannot push (no tag or release possible).ghcr-push: true|autoasks ghcr.io for a push token and opens a blob upload session (no package is created).autoprobes only when the config hasdockers/dockers_v2/docker_manifestsand mentionsghcr.io. The image is the firstghcr.io/...path in the config, else the repository (ghcr-imageoverrides).- A probe that cannot reach a verdict (network error, odd status) warns and does not fail.
It also scans .goreleaser.y*ml and requires every {{ .Env.NAME }} (comment lines ignored) to be set. All problems are listed in one run.
go-release.yaml and go-release-cgo.yaml run it in a first preflight job before tests. Set preflight: false to skip it, and preflight-repos to check access to a tap or helm-charts repo. An empty secret usually means the caller omitted secrets: inherit.
actions/rolling-release
Create/update a rolling release tag.
- uses: lukaszraczylo/shared-actions/.github/actions/rolling-release@main
with:
tag: v1
version-tag: v1.2.3
github-token: ${{ secrets.GITHUB_TOKEN }}
actions/node-build
Setup Node.js and run build script.
- uses: lukaszraczylo/shared-actions/.github/actions/node-build@main
with:
node-version: "20"
build-script: "cd ui && npm ci && npm run build"
Outputs
Both release workflows output:
version- Calculated version withoutvprefix (e.g.,1.2.3)version_tag- Version withvprefix (e.g.,v1.2.3)