An Encore app cannot be tested with plain go build and go test. Add go-prepare-command, which runs before the tests, and go-test-command, which replaces the default Go build and test commands.
14 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
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-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)