diff --git a/.github/workflows/ci.yml b/.github/workflows/pr.yml similarity index 52% rename from .github/workflows/ci.yml rename to .github/workflows/pr.yml index 0713b60..32877ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/pr.yml @@ -1,9 +1,6 @@ -name: thebus_ci +name: PR Checks on: - push: - branches: - - main pull_request: branches: - main @@ -12,12 +9,21 @@ jobs: test: name: thebus_test runs-on: ubuntu-22.04 - steps: - name: Check out code into the Go module directory uses: actions/checkout@v5 - - name: Set up Go 1.24 + - name: Setup Go 1.24 uses: actions/setup-go@v6 with: go-version: "1.24" - - run: go version \ No newline at end of file + - run: go version + - name: go fmt (check diff) + run: | + make fmt + git diff --exit-code + + - name: go vet + run: make vet + + - name: Test (race) + run: make test-race diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fda949d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,77 @@ +name: Release on merge + +on: + pull_request: + types: [closed] + branches: [main] + +permissions: + contents: write + +jobs: + release: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + env: + MODULE_PATH: ${{ github.repository }} + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup Go 1.24 + uses: actions/setup-go@v6 + with: + go-version: "1.24" + - name: Decide bump from PR labels (default=patch) + id: decide + run: | + echo '${{ toJson(github.event.pull_request.labels) }}' > /tmp/labels.json + BUMP=patch + grep -qi 'release:major' /tmp/labels.json && BUMP=major + grep -qi 'release:minor' /tmp/labels.json && BUMP=minor + grep -qi 'release:patch' /tmp/labels.json && BUMP=patch + echo "bump=$BUMP" >> $GITHUB_OUTPUT + echo "BUMP=$BUMP" + + + - name: Run tests (race + coverage) + run: | + make fmt + make vet + make test-race-cover + go tool cover -func=coverage.out | tail -n1 || true + + - name: Compute next semver tag + id: bump + shell: bash + run: | + set -euo pipefail + LAST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || echo 'v0.1.0')" + echo "LAST_TAG=$LAST_TAG" + VER="${LAST_TAG#v}" + IFS='.' read -r MA MI PA <<< "$VER" + case "${{ steps.decide.outputs.bump }}" in + major) MA=$((MA+1)); MI=0; PA=0;; + minor) MI=$((MI+1)); PA=0;; + patch) PA=$((PA+1));; + esac + NEXT_TAG="v${MA}.${MI}.${PA}" + echo "next_tag=$NEXT_TAG" >> $GITHUB_OUTPUT + echo "NEXT_TAG=$NEXT_TAG" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.bump.outputs.next_tag }} + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Prime Go proxy (make the tag discoverable) + run: | + MOD="github.com/${{ github.repository }}" + TAG="${{ steps.bump.outputs.next_tag }}" + GOPROXY=proxy.golang.org go list -m -versions "$MOD" || true + GOPROXY=proxy.golang.org go list -m "$MOD@$TAG" || true \ No newline at end of file diff --git a/Makefile b/Makefile index 23f1557..94b58dd 100644 --- a/Makefile +++ b/Makefile @@ -1,58 +1,22 @@ -# Based on amazing example from https://gist.github.com/serinth/16391e360692f6a000e5a10382d1148c -SERVICE ?= $(shell basename `go list`) -VERSION ?= $(shell git describe --tags --always --dirty --match=v* 2> /dev/null || cat $(PWD)/.version 2> /dev/null || echo v0) -PACKAGE ?= $(shell go list) -PACKAGES ?= $(shell go list ./...) -FILES ?= $(shell find . -type f -name '*.go' -not -path "./vendor/*") +.PHONY: fmt vet clean test test-it test-race test-race-cover -# Binaries -PROTOC ?= protoc +fmt: ## run go fmt + go fmt ./... -.PHONY: help clean fmt lint vet test test-cover all - -default: help - -help: ## show this help - @echo 'usage: make [target] ...' - @echo '' - @echo 'targets:' - @egrep '^(.+)\:\ .*##\ (.+)' ${MAKEFILE_LIST} | sed 's/:.*##/#/' | column -t -c 2 -s '#' - -all: ## clean, format, build and unit test - make clean-all - make gofmt - make build - make test - -env: ## Print useful environment variables to stdout - echo $(CURDIR) - echo $(SERVICE) - echo $(PACKAGE) - echo $(VERSION) +vet: ## run go vet + go vet ./... clean: ## go clean go clean -build: - go build main.go - -test: +test: ## run unit tests (short) go test -v ./... -short -test-it: +test-it: ## run integration tests go test -v ./... -test-bench: ## run benchmark tests - go test -bench ./... - -# Generate test coverage -test-cover: ## Run test coverage and generate html report - rm -fr coverage - mkdir coverage - go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} - echo "mode: count" > coverage/cover.out - grep -h -v "^mode:" *.coverprofile >> "coverage/cover.out" - rm *.coverprofile - go tool cover -html=coverage/cover.out -o=coverage/cover.html +test-race: ## run tests with race detector + go test -race -count=1 ./... -test-all: test test-bench test-cover \ No newline at end of file +test-race-cover: ## run tests with race detector and coverage + go test -race -count=1 -coverprofile=coverage.out ./... \ No newline at end of file diff --git a/bus_test.go b/bus_test.go index 8a2af37..b33b836 100644 --- a/bus_test.go +++ b/bus_test.go @@ -305,20 +305,29 @@ func TestRunFanOutDeliveredCounter(t *testing.T) { topic: "t", ts: time.Now(), seq: 0, - payload: nil}: + payload: []byte("test")}: default: t.Fatal("should not receive any messages") } return nil }) - time.Sleep(20 * time.Millisecond) - bb.mutex.RLock() - ts := bb.subscriptions["t"] - del := ts.counters.Delivered.Load() - bb.mutex.RUnlock() - if del < 1 { - t.Fatalf("expected Delivered>=1, got %d", del) + // ugly, but no use of fancy libs for testing + timeout := time.After(1 * time.Second) + tick := time.NewTicker(10 * time.Millisecond) + defer tick.Stop() + for { + select { + case <-timeout: + t.Fatal("timeout waiting for Delivered counter") + case <-tick.C: + bb.mutex.RLock() + delivered := bb.subscriptions["t"].counters.Delivered.Load() + bb.mutex.RUnlock() + if delivered >= 1 { + return // Test is ok if we catch this line + } + } } }