mirror of
https://github.com/lukaszraczylo/shared-actions.git
synced 2026-10-14 05:23:05 +00:00
A failing update has no PR, so its test output had nowhere to go. Capture the output of the Go and Node steps. On failure, write it as a comment on one sticky issue, one comment per branch, updated on every run. A final job removes comments for branches that are gone and closes the issue when no update fails. The step is best effort and also writes the run summary.
284 lines
11 KiB
Markdown
284 lines
11 KiB
Markdown
# 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 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 on `deps-autoupdate/batch`. Each major update gets its own branch.
|
|
2. For each branch, one at a time, the workflow merges it into the default branch locally and runs the tests on that result: `go build` and `go test -race -cover` for Go, the `package.json` scripts for Node.
|
|
3. 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.
|
|
4. If anything merged and `release-workflow` is set, it dispatches the release once.
|
|
|
|
```yaml
|
|
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-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 |
|
|
| `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`. 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 |
|
|
|
|
**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.
|
|
|
|
**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:
|
|
|
|
```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 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 `push` will not start after a merge. Set `release-workflow` to dispatch it, and grant `actions: 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.
|
|
|
|
```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`)
|