# 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): ```yaml 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. ```yaml 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. ```yaml 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` / `staticcheck` findings show as inline annotations on the PR diff; gosec and CodeQL post to code scanning. - The `report` job posts one sticky comment with every check's result and coverage, and fails if any check fails or coverage is below `coverage-threshold`. Require `PR Checks Report` in 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. ```yaml 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. ```yaml 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`. 1. 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`. 2. The workflow checks out that branch and runs the tests: `go build`, `go test -race -cover` for Go, and the `package.json` scripts for Node. 3. 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. ```yaml 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: ```json { "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 `push` will not start after the merge. Set `release-workflow` to dispatch it explicitly, and grant `actions: 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. ```yaml - 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. ```yaml - 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. ```yaml # 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. ```yaml - 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"` runs `git push --dry-run`, which fails when the token cannot push (no tag or release possible). - `ghcr-push: true|auto` asks ghcr.io for a push token and opens a blob upload session (no package is created). `auto` probes only when the config has `dockers`/`dockers_v2`/`docker_manifests` and mentions `ghcr.io`. The image is the first `ghcr.io/...` path in the config, else the repository (`ghcr-image` overrides). - 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. ```yaml - 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. ```yaml - 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 without `v` prefix (e.g., `1.2.3`) - `version_tag` - Version with `v` prefix (e.g., `v1.2.3`)