diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..70b4b5f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,82 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: go.mod + - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 + with: + # Pinned to the exact local version: .golangci.yml uses `default: all`, + # so a floating version auto-enables new/renamed linters (verified: + # v2.13.1 surfaces 9 findings v2.11.4 doesn't, from linter renames + # like exhaustruct -> exhaustruct_v5) and breaks unrelated commits. + version: v2.11.4 + args: ./... + + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: go.mod + + - name: Build + run: make build + + - name: Test with coverage + run: make coverage + + - name: Summarise coverage + run: | + set -euo pipefail + total=$(go tool cover -func=coverage.out | awk '/^total:/ {print $NF}') + if [ -z "$total" ]; then + echo "no total: line found in coverage.out" >&2 + exit 1 + fi + { + echo "## Coverage" + echo + echo "**Total: ${total}**" + echo + echo "
Per-function breakdown" + echo + echo '```' + go tool cover -func=coverage.out + echo '```' + echo + echo "
" + } >> "$GITHUB_STEP_SUMMARY" + + - name: Upload coverage + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: coverage + path: | + coverage.out + coverage.html + if-no-files-found: warn + retention-days: 14 diff --git a/.gitignore b/.gitignore index 328d6d1..9d52e12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /darna bin/darna internal/validator/testdata/project/testproject +/coverage.out +/coverage.html diff --git a/.golangci.yml b/.golangci.yml index 749625e..d051371 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,4 +1,4 @@ -version: 2 +version: "2" run: timeout: 5m @@ -22,7 +22,6 @@ linters: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - check-shadowing: true # Enable shadow detection staticcheck: checks: ["all"] # Enables all 150+ checks across SA/S/ST/U/QF categories @@ -148,10 +147,6 @@ linters: scope: all capital: true - wsl_v5: - allow-cuddle-declarations: false - force-err-cuddling: true - exhaustruct: exclude: [] # Require all struct fields to be initialized @@ -181,7 +176,6 @@ linters: statements: 50 issues: - exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 new: false diff --git a/Makefile b/Makefile index be7a1a8..7d9f109 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all build test lint clean +.PHONY: all build test coverage lint clean # Build the binary build: @@ -8,6 +8,12 @@ build: test: go test -v -race -cover ./... +# Run tests and write a coverage profile +coverage: + go test -race -covermode=atomic -coverprofile=coverage.out ./... + go tool cover -html=coverage.out -o coverage.html + go tool cover -func=coverage.out | tail -1 + # Run linter lint: golangci-lint run ./... @@ -15,6 +21,7 @@ lint: # Clean build artifacts clean: rm -rf bin/ + rm -f coverage.out coverage.html go clean # Run all checks diff --git a/README.md b/README.md index 53a5ef2..6e4522f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # darna +[![CI](https://github.com/darccio/darna/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/darccio/darna/actions/workflows/ci.yml) + Atomic commit validator for Go. Ensures staged files form a self-contained commit where every symbol used is defined in the staged changeset or in already-committed code - never in unstaged or untracked files. The name comes from Lithuanian *darna*, meaning harmony - the quality of a codebase where every commit is complete and coherent. @@ -235,9 +237,14 @@ docs/decisions/ Architecture decision records make all # lint + test + build make lint # golangci-lint make test # tests with race detector and coverage +make coverage # tests with a coverage profile written to coverage.out make clean # remove artifacts ``` +Every push to `main` and every pull request runs lint and tests on GitHub Actions. +The test job publishes total coverage to the run summary and uploads `coverage.out` +and `coverage.html` as artifacts. + ## Documentation For design rationale and implementation details of commit message generation, see [ADR-002: LLM-powered commit message generation](docs/decisions/002.md).