Dedupe release drafts (#50) #180
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| on: | |
| push: | |
| branches: [ main ] | |
| tags: | |
| - 'v*' | |
| pull_request: | |
| types: [opened, reopened, synchronize, ready_for_review] | |
| workflow_dispatch: {} | |
| name: CI | |
| jobs: | |
| pr-allow: | |
| name: Check PR allow | |
| runs-on: ubuntu-latest | |
| outputs: | |
| allowed: ${{ steps.check.outputs.allowed }} | |
| reason: ${{ steps.check.outputs.reason }} | |
| steps: | |
| - id: check | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const evt = context.eventName; | |
| if (evt !== 'pull_request') { | |
| core.setOutput('allowed', 'true'); | |
| core.setOutput('reason', 'not-a-pr'); | |
| return; | |
| } | |
| const pr = context.payload.pull_request; | |
| const sameRepo = pr.head.repo.full_name === pr.base.repo.full_name; | |
| let approved = false; | |
| try { | |
| const { owner, repo } = context.repo; | |
| const number = pr.number; | |
| const reviews = await github.paginate(github.rest.pulls.listReviews, { owner, repo, pull_number: number }); | |
| approved = reviews.some(r => r.state === 'APPROVED'); | |
| } catch (e) { | |
| core.info('Failed to list reviews: ' + e.message); | |
| } | |
| const allowed = sameRepo || approved; | |
| core.setOutput('allowed', allowed ? 'true' : 'false'); | |
| core.setOutput('reason', allowed ? (sameRepo ? 'same-repo' : 'approved') : 'fork-unapproved'); | |
| # ================ | |
| # TEST JOB | |
| # runs on every push and allowed PR | |
| # runs 2x3 times (see matrix) | |
| # ================ | |
| test: | |
| name: Test | |
| strategy: | |
| matrix: | |
| go-version: [1.23.x] | |
| platform: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.platform }} | |
| needs: pr-allow | |
| if: needs.pr-allow.outputs.allowed == 'true' | |
| steps: | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ matrix.go-version }} | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Build (CGO) | |
| shell: bash | |
| run: | | |
| export CGO_ENABLED=1 | |
| go version | |
| go build -v -o /dev/null . | |
| - name: Unit tests (exclude legacy e2e) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| export CGO_ENABLED=1 | |
| pkgs=$(go list ./... | grep -v '/test/e2e') | |
| go test -v -count=1 $pkgs | |
| - name: Security tests (explicit) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| export CGO_ENABLED=1 | |
| go test -v -count=1 ./tests | |
| # ================ | |
| # RELEASE JOBS | |
| # runs after a success test | |
| # only runs on push "v*" tag | |
| # ================ | |
| release_linuxwin: | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| repository-projects: write | |
| name: Release Binaries (Linux/Windows) | |
| needs: test | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| - name: Install cross toolchains and libc headers (Linux CGO) | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| gcc-arm-linux-gnueabihf gcc-arm-linux-gnueabi gcc-aarch64-linux-gnu \ | |
| libc6-dev-armhf-cross libc6-dev-arm64-cross \ | |
| linux-libc-dev-armhf-cross linux-libc-dev-arm64-cross | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 1.23.x | |
| check-latest: true | |
| - name: Run GoReleaser [Linux+Windows] | |
| uses: goreleaser/goreleaser-action@v5 | |
| with: | |
| distribution: goreleaser | |
| version: v2.4.0 | |
| args: release --verbose --config .github/goreleaser.linuxwin.yml | |
| env: | |
| GORELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| CGO_ENABLED: 1 | |
| release_darwin: | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| repository-projects: write | |
| name: Release Binaries (Darwin) | |
| needs: release_linuxwin | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: macos-latest | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 1.23.x | |
| check-latest: true | |
| - name: Run GoReleaser [Darwin] | |
| uses: goreleaser/goreleaser-action@v5 | |
| with: | |
| distribution: goreleaser | |
| version: v2.4.0 | |
| args: release --verbose --config .github/goreleaser.darwin.yml | |
| env: | |
| GORELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| finalize_release: | |
| name: Publish GitHub Release (mark latest) | |
| needs: release_darwin | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Publish release (unset draft) | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const tag = context.ref.replace('refs/tags/', ''); | |
| core.info(`Publishing release for tag ${tag}`); | |
| try { | |
| // getReleaseByTag does NOT return drafts; list and filter instead | |
| const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 }); | |
| const matches = releases.filter(r => r.tag_name === tag); | |
| if (matches.length === 0) { | |
| core.setFailed(`No release found for ${tag} (did GoReleaser create it?)`); | |
| return; | |
| } | |
| // Choose a canonical release (most assets, then most recently updated) | |
| matches.sort((a,b) => (b.assets?.length||0) - (a.assets?.length||0) || new Date(b.updated_at) - new Date(a.updated_at)); | |
| const primary = matches[0]; | |
| core.info(`Primary release selected id=${primary.id}, assets=${primary.assets?.length||0}`); | |
| if (primary.draft) { | |
| await github.rest.repos.updateRelease({ owner, repo, release_id: primary.id, draft: false }); | |
| core.info(`Published primary release id=${primary.id}`); | |
| } else { | |
| core.info(`Primary release already published id=${primary.id}`); | |
| } | |
| const others = matches.slice(1); | |
| if (others.length > 0) { | |
| core.warning(`There are ${others.length} additional releases with the same tag still in draft; leaving them as-is.`); | |
| } | |
| } catch (e) { | |
| core.setFailed(`Failed to publish release for ${tag}: ${e.message}`); | |
| } | |
| build_clients: | |
| name: Build clients (CGO off) | |
| needs: test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 1.23.x | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Build all client binaries | |
| run: | | |
| make client-all VERSION=dev | |
| build_server_linux: | |
| name: Build server (Linux only, CGO on) | |
| needs: test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 1.23.x | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install cross toolchains and libc headers (Linux CGO) | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| gcc-arm-linux-gnueabihf gcc-arm-linux-gnueabi gcc-aarch64-linux-gnu \ | |
| libc6-dev-armhf-cross libc6-dev-arm64-cross \ | |
| linux-libc-dev-armhf-cross linux-libc-dev-arm64-cross | |
| - name: Build server linux binaries | |
| run: | | |
| make server-linux-all VERSION=dev | |
| legacy_e2e: | |
| name: Legacy e2e (non-blocking) | |
| needs: test | |
| continue-on-error: true | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 1.21.x | |
| - name: Checkout PR head (on approved review) | |
| if: github.event_name == 'pull_request_review' | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| ref: ${{ github.event.pull_request.head.ref }} | |
| fetch-depth: 0 | |
| - name: Checkout code (push/tags/manual) | |
| if: github.event_name != 'pull_request_review' | |
| uses: actions/checkout@v4 | |
| - name: Run legacy e2e tests | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| export CGO_ENABLED=1 | |
| go test -v -count=1 ./test/e2e |