From f01c18353fd6b7e9ad76716751ad8b3164e01065 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sun, 14 Dec 2025 19:40:55 +0000 Subject: [PATCH] Utilise composite workflows. --- .github/actions/go-test/action.yml | 27 ++++ .github/actions/goreleaser/action.yml | 68 +++++++++ .github/actions/node-build/action.yml | 29 ++++ .github/actions/rolling-release/action.yml | 36 +++++ .github/actions/semver/action.yml | 35 +++++ .github/workflows/go-release-cgo.yaml | 164 ++++++++++++++++++++ .github/workflows/go-release.yaml | 85 ++++------- README.md | 165 ++++++++++++++++++++- 8 files changed, 548 insertions(+), 61 deletions(-) create mode 100644 .github/actions/go-test/action.yml create mode 100644 .github/actions/goreleaser/action.yml create mode 100644 .github/actions/node-build/action.yml create mode 100644 .github/actions/rolling-release/action.yml create mode 100644 .github/actions/semver/action.yml create mode 100644 .github/workflows/go-release-cgo.yaml diff --git a/.github/actions/go-test/action.yml b/.github/actions/go-test/action.yml new file mode 100644 index 0000000..e7042fc --- /dev/null +++ b/.github/actions/go-test/action.yml @@ -0,0 +1,27 @@ +name: "Go Test" +description: "Run Go tests with race detection" + +inputs: + go-version: + description: "Go version to use" + required: false + default: ">=1.24" + working-directory: + description: "Working directory" + required: false + default: "." + +runs: + using: "composite" + steps: + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.go-version }} + + - name: Run tests + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + CGO_ENABLED: 1 + run: go test -race -v ./... diff --git a/.github/actions/goreleaser/action.yml b/.github/actions/goreleaser/action.yml new file mode 100644 index 0000000..93181af --- /dev/null +++ b/.github/actions/goreleaser/action.yml @@ -0,0 +1,68 @@ +name: "GoReleaser" +description: "Run GoReleaser with optional split/merge for CGO builds" + +inputs: + version-tag: + description: "Version tag to release" + required: true + mode: + description: "Mode: 'full' (single runner), 'split' (matrix build), 'merge' (combine split builds)" + required: false + default: "full" + cgo-enabled: + description: "Enable CGO" + required: false + default: "0" + github-token: + description: "GitHub token" + required: true + homebrew-tap-token: + description: "Homebrew tap token (optional)" + required: false + default: "" + +runs: + using: "composite" + steps: + - name: Create tag + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a ${{ inputs.version-tag }} -m "Release ${{ inputs.version-tag }}" || true + if [[ "${{ inputs.mode }}" != "split" ]]; then + git push origin ${{ inputs.version-tag }} || true + fi + + - name: Run GoReleaser (full) + if: inputs.mode == 'full' + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ inputs.github-token }} + HOMEBREW_TAP_TOKEN: ${{ inputs.homebrew-tap-token }} + CGO_ENABLED: ${{ inputs.cgo-enabled }} + + - name: Run GoReleaser (split) + if: inputs.mode == 'split' + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean --split + env: + GITHUB_TOKEN: ${{ inputs.github-token }} + CGO_ENABLED: ${{ inputs.cgo-enabled }} + + - name: Run GoReleaser (merge) + if: inputs.mode == 'merge' + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: continue --merge + env: + GITHUB_TOKEN: ${{ inputs.github-token }} diff --git a/.github/actions/node-build/action.yml b/.github/actions/node-build/action.yml new file mode 100644 index 0000000..f4f644f --- /dev/null +++ b/.github/actions/node-build/action.yml @@ -0,0 +1,29 @@ +name: "Node.js Build" +description: "Setup Node.js and run build script" + +inputs: + node-version: + description: "Node.js version to use" + required: false + default: "20" + build-script: + description: "Build script to run" + required: true + cache-dependency-path: + description: "Path to package-lock.json for caching" + required: false + default: "" + +runs: + using: "composite" + steps: + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: "npm" + cache-dependency-path: ${{ inputs.cache-dependency-path }} + + - name: Build frontend + shell: bash + run: ${{ inputs.build-script }} diff --git a/.github/actions/rolling-release/action.yml b/.github/actions/rolling-release/action.yml new file mode 100644 index 0000000..ce0eaa1 --- /dev/null +++ b/.github/actions/rolling-release/action.yml @@ -0,0 +1,36 @@ +name: "Rolling Release" +description: "Create or update a rolling release tag" + +inputs: + tag: + description: "Rolling release tag (e.g., 'v1')" + required: true + version-tag: + description: "Current version tag this points to" + required: true + github-token: + description: "GitHub token" + required: true + +runs: + using: "composite" + steps: + - name: Update rolling release tag + shell: bash + run: | + git tag -f ${{ inputs.tag }} + git push origin ${{ inputs.tag }} --force + + - name: Update or create rolling release + uses: ncipollo/release-action@v1 + with: + name: ${{ inputs.tag }} - ${{ inputs.version-tag }} + token: ${{ inputs.github-token }} + tag: ${{ inputs.tag }} + prerelease: false + allowUpdates: true + makeLatest: false + body: | + Rolling release pointing to version ${{ inputs.version-tag }}. + + Use `@${{ inputs.tag }}` in your GitHub Actions workflows for automatic updates. diff --git a/.github/actions/semver/action.yml b/.github/actions/semver/action.yml new file mode 100644 index 0000000..9669ea5 --- /dev/null +++ b/.github/actions/semver/action.yml @@ -0,0 +1,35 @@ +name: "Semantic Version" +description: "Calculate semantic version using semver-generator" + +inputs: + config-file: + description: "Path to semver config file" + required: false + default: "semver.yaml" + +outputs: + version: + description: "The calculated version (without v prefix)" + value: ${{ steps.format.outputs.version }} + version_tag: + description: "The version tag (with v prefix)" + value: ${{ steps.format.outputs.version_tag }} + +runs: + using: "composite" + steps: + - name: Calculate version + id: semver + uses: lukaszraczylo/semver-generator@v1 + with: + config_file: ${{ inputs.config-file }} + repository_local: true + + - name: Format version + id: format + shell: bash + run: | + VERSION="${{ steps.semver.outputs.semantic_version }}" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "version_tag=v${VERSION}" >> $GITHUB_OUTPUT + echo "Version: v${VERSION}" diff --git a/.github/workflows/go-release-cgo.yaml b/.github/workflows/go-release-cgo.yaml new file mode 100644 index 0000000..ae81726 --- /dev/null +++ b/.github/workflows/go-release-cgo.yaml @@ -0,0 +1,164 @@ +name: Go Release (CGO) + +on: + workflow_call: + inputs: + go-version: + description: "Go version to use" + required: false + type: string + default: ">=1.24" + semver-config: + description: "Path to semver config file" + required: false + type: string + default: "semver.yaml" + rolling-release-tag: + description: "Create a rolling release tag (e.g., 'v1')" + required: false + type: string + default: "" + # Node.js support (optional) + node-enabled: + description: "Enable Node.js for frontend builds" + required: false + type: boolean + default: false + node-version: + description: "Node.js version to use" + required: false + type: string + default: "20" + node-build-script: + description: "Shell script to build frontend" + required: false + type: string + default: "" + # Platform configuration + platforms: + description: "JSON array of platforms" + required: false + type: string + default: '[{"os":"macos-13","goos":"darwin","goarch":"amd64","platform":"darwin_amd64"},{"os":"macos-latest","goos":"darwin","goarch":"arm64","platform":"darwin_arm64"},{"os":"ubuntu-latest","goos":"linux","goarch":"amd64","platform":"linux_amd64"},{"os":"windows-latest","goos":"windows","goarch":"amd64","platform":"windows_amd64"}]' + outputs: + version: + description: "The calculated version (without v prefix)" + value: ${{ jobs.version.outputs.version }} + version_tag: + description: "The version tag (with v prefix)" + value: ${{ jobs.version.outputs.version_tag }} + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run tests + uses: lukaszraczylo/shared-actions/.github/actions/go-test@main + with: + go-version: ${{ inputs.go-version }} + + version: + name: Calculate Version + needs: test + runs-on: ubuntu-latest + outputs: + version: ${{ steps.semver.outputs.version }} + version_tag: ${{ steps.semver.outputs.version_tag }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Calculate version + id: semver + uses: lukaszraczylo/shared-actions/.github/actions/semver@main + with: + config-file: ${{ inputs.semver-config }} + + build: + name: Build (${{ matrix.platform }}) + needs: version + if: needs.version.outputs.version_tag != '' + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(inputs.platforms) }} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.go-version }} + cache: true + + - name: Build frontend + if: inputs.node-enabled && inputs.node-build-script != '' + uses: lukaszraczylo/shared-actions/.github/actions/node-build@main + with: + node-version: ${{ inputs.node-version }} + build-script: ${{ inputs.node-build-script }} + + - name: Run GoReleaser (split) + uses: lukaszraczylo/shared-actions/.github/actions/goreleaser@main + with: + version-tag: ${{ needs.version.outputs.version_tag }} + mode: split + cgo-enabled: "1" + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: release-${{ matrix.platform }} + path: dist/*.* + retention-days: 1 + + release: + name: Release + needs: [version, build] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.go-version }} + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist + pattern: release-* + merge-multiple: true + + - name: List artifacts + run: ls -la dist/ + + - name: Run GoReleaser (merge) + uses: lukaszraczylo/shared-actions/.github/actions/goreleaser@main + with: + version-tag: ${{ needs.version.outputs.version_tag }} + mode: merge + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Rolling release + if: inputs.rolling-release-tag != '' + uses: lukaszraczylo/shared-actions/.github/actions/rolling-release@main + with: + tag: ${{ inputs.rolling-release-tag }} + version-tag: ${{ needs.version.outputs.version_tag }} + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/go-release.yaml b/.github/workflows/go-release.yaml index d0d257c..46ff5d5 100644 --- a/.github/workflows/go-release.yaml +++ b/.github/workflows/go-release.yaml @@ -7,14 +7,14 @@ on: description: "Go version to use" required: false type: string - default: ">=1.21" + default: ">=1.24" semver-config: description: "Path to semver config file" required: false type: string default: "semver.yaml" docker-enabled: - description: "Enable Docker builds (requires QEMU, Buildx, GHCR login)" + description: "Enable Docker builds" required: false type: boolean default: false @@ -24,13 +24,17 @@ on: type: string default: "ghcr.io" rolling-release-tag: - description: "Create a rolling release tag (e.g., 'v1') that always points to latest" + description: "Create a rolling release tag (e.g., 'v1')" required: false type: string default: "" - -# Permissions are inherited from the caller workflow via secrets: inherit -# Caller must declare: contents: write (required), packages: write (if docker-enabled) + outputs: + version: + description: "The calculated version (without v prefix)" + value: ${{ jobs.version.outputs.version }} + version_tag: + description: "The version tag (with v prefix)" + value: ${{ jobs.version.outputs.version_tag }} jobs: test: @@ -40,21 +44,18 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Setup Go - uses: actions/setup-go@v5 + - name: Run tests + uses: lukaszraczylo/shared-actions/.github/actions/go-test@main with: go-version: ${{ inputs.go-version }} - - name: Run tests - run: go test -race -v ./... - version: name: Calculate Version needs: test runs-on: ubuntu-latest outputs: - version: ${{ steps.version_formatted.outputs.version }} - version_tag: ${{ steps.version_formatted.outputs.version_tag }} + version: ${{ steps.semver.outputs.version }} + version_tag: ${{ steps.semver.outputs.version_tag }} steps: - name: Checkout uses: actions/checkout@v4 @@ -63,22 +64,9 @@ jobs: - name: Calculate version id: semver - uses: lukaszraczylo/semver-generator@v1 + uses: lukaszraczylo/shared-actions/.github/actions/semver@main with: - config_file: ${{ inputs.semver-config }} - repository_local: true - - - name: Format version - id: version_formatted - run: | - VERSION="${{ steps.semver.outputs.semantic_version }}" - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "version_tag=v${VERSION}" >> $GITHUB_OUTPUT - - - name: Print version - run: | - echo "Version: ${{ steps.version_formatted.outputs.version }}" - echo "Version tag: ${{ steps.version_formatted.outputs.version_tag }}" + config-file: ${{ inputs.semver-config }} release: name: Release @@ -113,41 +101,18 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Create and push tag - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -a ${{ needs.version.outputs.version_tag }} -m "Release ${{ needs.version.outputs.version_tag }}" || true - git push origin ${{ needs.version.outputs.version_tag }} || true - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: lukaszraczylo/shared-actions/.github/actions/goreleaser@main with: - distribution: goreleaser - version: "~> v2" - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + version-tag: ${{ needs.version.outputs.version_tag }} + mode: full + github-token: ${{ secrets.GITHUB_TOKEN }} + homebrew-tap-token: ${{ secrets.HOMEBREW_TAP_TOKEN }} - # Rolling release (optional, e.g., v1 for GitHub Actions) - - name: Update rolling release tag + - name: Rolling release if: inputs.rolling-release-tag != '' - run: | - git tag -f ${{ inputs.rolling-release-tag }} - git push origin ${{ inputs.rolling-release-tag }} --force - - - name: Update or create rolling release - if: inputs.rolling-release-tag != '' - uses: ncipollo/release-action@v1 + uses: lukaszraczylo/shared-actions/.github/actions/rolling-release@main with: - name: ${{ inputs.rolling-release-tag }} - ${{ needs.version.outputs.version_tag }} - token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ inputs.rolling-release-tag }} - prerelease: false - allowUpdates: true - makeLatest: false - body: | - Rolling release pointing to version ${{ needs.version.outputs.version_tag }}. - - Use `@${{ inputs.rolling-release-tag }}` in your GitHub Actions workflows for automatic updates. + version-tag: ${{ needs.version.outputs.version_tag }} + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 94ceb9a..b6a25c1 100644 --- a/README.md +++ b/README.md @@ -1 +1,164 @@ -# shared-actions +# Shared GitHub Actions + +Reusable workflows and composite actions for Go projects. + +## Reusable Workflows + +### `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. + +```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. + +```yaml +jobs: + autoupdate: + uses: lukaszraczylo/shared-actions/.github/workflows/go-autoupdate.yaml@main + with: + go-version: ">=1.24" + release-workflow: "release.yaml" + secrets: inherit +``` + +## 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/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`)