The workflow runs inside the calling repository, so its own token is enough and no PAT is needed. Drop the PAT fallbacks and make admin merge opt-in, because the workflow token cannot bypass branch protection. A merge made with that token does not trigger push workflows, so add a release-workflow input that dispatches the release after the merge.
10 KiB
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
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 puts every minor and patch update (direct and indirect Go modules, npm, pnpm and yarn packages) in one PR on the branch
deps-autoupdate/batch. - The workflow checks out that branch and runs the tests:
go build,go test -race -coverfor Go, and thepackage.jsonscripts for Node. - If everything passes, it squash-merges the PR. If a test fails, the PR stays open with a comment and Renovate retries on the next run.
name: Update dependencies
on:
workflow_dispatch:
schedule:
- cron: "0 3 * * *"
permissions:
actions: write # only to dispatch release-workflow
contents: write
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 in one run.
Inputs:
| Input | Default | Description |
|---|---|---|
go-version |
>=1.24 |
Go version |
go-working-directory |
. |
Directory that holds go.mod |
node-version |
22 |
Node.js version |
node-working-directory |
. |
Directory that holds package.json |
node-scripts |
lint,typecheck,test,build |
Scripts to run in order. A missing script is skipped. |
require-node-tests |
true |
Fail when there is no test script, so an untested update never merges |
include-major |
false |
Put major updates in the batch. When false, each major gets its own PR that is never merged automatically. |
minimum-release-age |
2 days |
Skip releases younger than this |
renovate-version |
44 |
Renovate version |
commit-subject |
chore(deps): update dependencies |
Squash commit subject |
release-workflow |
Workflow file to dispatch after the merge, for example release.yaml. It 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 |
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 Renovate 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 the merge. Setrelease-workflowto dispatch it explicitly, and grantactions: write.
Commit message. The squash commit uses a fixed subject and body without the words major, minor or breaking, because semver-generator matches release keywords 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)