Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ jobs:
go-version: "1.26.6"
cache: true

- name: Setup Bats
if: runner.os == 'Linux'
uses: bats-core/bats-action@3.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Check formatting
if: runner.os != 'Windows'
run: test -z "$(gofmt -l .)"
Expand All @@ -36,3 +42,12 @@ jobs:

- name: Build
run: go build -trimpath ./...

- name: Test Unix release installer
if: runner.os == 'Linux'
run: bats test/install-release.bats

- name: Test Windows release installer
if: runner.os == 'Windows'
shell: pwsh
run: ./test/install-release.ps1
163 changes: 163 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: Release

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: Existing semantic-version tag to publish
required: true
type: string

permissions: {}

concurrency:
group: release-${{ inputs.tag || github.ref_name }}
cancel-in-progress: false

jobs:
validate:
name: Validate release tag
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
outputs:
commit: ${{ steps.release_commit.outputs.sha }}
tag: ${{ steps.release_commit.outputs.tag }}

steps:
- name: Checkout release tag
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.tag || github.ref }}

- name: Validate release tag
id: release_commit
shell: bash
env:
RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
run: |
SEMVER_TAG_PATTERN='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$'
if [[ ! "$RELEASE_TAG" =~ $SEMVER_TAG_PATTERN ]]; then
echo "Release tag must be semantic and start with v: $RELEASE_TAG" >&2
exit 1
fi
git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"
git fetch --no-tags origin main
if ! git merge-base --is-ancestor "$RELEASE_TAG^{commit}" FETCH_HEAD; then
echo "Release tag is not reachable from origin/main: $RELEASE_TAG" >&2
exit 1
fi
printf 'sha=%s\n' "$(git rev-parse "$RELEASE_TAG^{commit}")" >> "$GITHUB_OUTPUT"
printf 'tag=%s\n' "$RELEASE_TAG" >> "$GITHUB_OUTPUT"

test:
name: Test ${{ matrix.os }}
needs: validate
runs-on: ${{ matrix.os }}
timeout-minutes: 20
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Checkout validated release
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ needs.validate.outputs.tag }}

- name: Verify validated commit
shell: bash
env:
EXPECTED_COMMIT: ${{ needs.validate.outputs.commit }}
run: |
actual_commit=$(git rev-parse HEAD)
if [[ "$actual_commit" != "$EXPECTED_COMMIT" ]]; then
echo "Release tag changed after validation." >&2
exit 1
fi

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.26.6"
cache: true

- name: Setup Bats
if: runner.os == 'Linux'
uses: bats-core/bats-action@3.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Check formatting
if: runner.os != 'Windows'
shell: bash
run: test -z "$(gofmt -l .)"

- name: Vet
run: go vet ./...

- name: Test
run: go test -race -cover ./...

- name: Build
run: go build -trimpath ./...

- name: Test Unix release installer
if: runner.os == 'Linux'
run: bats test/install-release.bats

- name: Test Windows release installer
if: runner.os == 'Windows'
shell: pwsh
run: ./test/install-release.ps1

release:
name: Build and publish
needs: [validate, test]
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write

steps:
- name: Checkout validated release
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ needs.validate.outputs.tag }}

- name: Verify validated commit
shell: bash
env:
EXPECTED_COMMIT: ${{ needs.validate.outputs.commit }}
run: |
actual_commit=$(git rev-parse HEAD)
if [[ "$actual_commit" != "$EXPECTED_COMMIT" ]]; then
echo "Release tag changed after validation." >&2
exit 1
fi

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.26.6"
cache: true

- name: Build and publish GitHub release
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 changes: 35 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: 2

project_name: codexometer

builds:
- id: codexometer
main: .
binary: codexometer
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
flags:
- -buildvcs=false
- -trimpath
ldflags:
- -s -w -X github.com/merefield/codexometer/internal/version.buildVersion={{ .Version }}

archives:
- formats: [tar.gz]
format_overrides:
- goos: windows
formats: [zip]
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

checksum:
name_template: checksums.txt

changelog:
sort: asc
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build test fmt vet
.PHONY: build test integration-test check fmt vet release-snapshot

# A tagged checkout gets the nearest semantic Git description. Repositories
# without a reachable tag leave this empty so Go's embedded VCS revision and
Expand All @@ -12,8 +12,16 @@ build:
test:
go test ./...

integration-test:
bats test/install-release.bats

check: vet test integration-test

fmt:
go fmt ./...

vet:
go vet ./...

release-snapshot:
goreleaser release --snapshot --clean --skip=publish
Loading
Loading