Reusable workflow for comprehensive Go PR analysis in monorepos. Handles change detection, linting, security scanning, testing, coverage checks, and build verification - all per changed app using a matrix strategy.
Note: This workflow replaces the standalone
go-coverage-check.ymlandgo-unit-tests.ymlworkflows, consolidating all Go PR analysis into a single, unified workflow.
- Change Detection: Automatically detects which apps changed in the PR
- Matrix Execution: Runs all checks per changed app in parallel
- Makefile Integration: Auto-detects and uses Makefile targets when available (lint, test, sec, build)
- GolangCI-Lint: Configurable linting with custom version and arguments
- Security Scanning: GoSec and govulncheck for vulnerability detection
- Unit Tests: Runs tests with race detection and coverage
- Coverage Check: Threshold enforcement with PR comments
- Coverage Filtering: Supports
.ignorecoverunitfile to exclude patterns from coverage - Build Verification: Ensures code compiles successfully
- Skip Logic: Gracefully skips when no Go changes detected
name: Go Analysis
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
analysis:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/go-pr-analysis.yml@v1.0.0
secrets: inheritname: Go Analysis
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
analysis:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/go-pr-analysis.yml@v1.0.0
with:
filter_paths: '["apps/api", "apps/worker", "apps/gateway"]'
secrets: inheritname: Go Analysis
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
analysis:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/go-pr-analysis.yml@v1.0.0
with:
filter_paths: '["apps/control-plane", "apps/agent", "apps/lambda-authorizer"]'
path_level: 2
app_name_prefix: "platform"
go_version: "1.23"
golangci_lint_version: "v1.62.2"
golangci_lint_args: "--timeout=5m"
coverage_threshold: 80
fail_on_coverage_threshold: false
enable_lint: true
enable_security: true
enable_tests: true
enable_coverage: true
enable_build: true
secrets: inheritjobs:
analysis:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/go-pr-analysis.yml@v1.0.0
with:
filter_paths: '["src/services"]'
enable_security: false
enable_coverage: false
enable_build: false| Input | Description | Required | Default |
|---|---|---|---|
runner_type |
GitHub runner type | No | firmino-lxc-runners |
filter_paths |
JSON array of paths to monitor for changes. If empty, treats repo as single-app. | No | '' |
path_level |
Directory depth level to extract app name | No | 2 |
app_name_prefix |
Prefix for app names in matrix output | No | '' |
go_version |
Go version to use | No | 1.23 |
golangci_lint_version |
GolangCI-Lint version | No | v1.62.2 |
golangci_lint_args |
Additional golangci-lint arguments | No | --timeout=5m |
coverage_threshold |
Minimum coverage percentage (0-100) | No | 80 |
fail_on_coverage_threshold |
Fail if coverage below threshold | No | false |
enable_lint |
Enable GolangCI-Lint | No | true |
enable_security |
Enable security scanning (gosec, govulncheck) | No | true |
enable_tests |
Enable unit tests | No | true |
enable_coverage |
Enable coverage check with PR comment | No | true |
enable_build |
Enable build verification | No | true |
go_private_modules |
GOPRIVATE pattern for private Go modules (e.g., github.com/LerianStudio/*) |
No | '' |
enable_integration_tests |
Enable integration tests job | No | false |
integration_test_command |
Command to run integration tests | No | make test-integration |
enable_test_determinism |
Enable test determinism check (runs tests multiple times with shuffle) | No | false |
test_determinism_runs |
Number of times to run tests for determinism check | No | 3 |
jobs:
analysis:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/go-pr-analysis.yml@v1.0.0
with:
filter_paths: '["components/api"]'
go_private_modules: "github.com/MyOrg/*"
secrets: inheritUses secrets: inherit pattern. Required secrets:
| Secret | Description | Required When |
|---|---|---|
MANAGE_TOKEN |
GitHub token for PR comments and private module access | Private modules or PR comments |
SLACK_WEBHOOK_URL |
Slack webhook for notifications | Optional |
Detects which apps have changes based on filter_paths. Outputs a matrix of changed apps for subsequent jobs.
Runs GolangCI-Lint per changed app. Configurable version and arguments.
Runs security scanners per changed app:
- GoSec: Static analysis for security issues (uploads SARIF to GitHub Security tab)
- govulncheck: Official Go vulnerability database check
Runs unit tests per changed app with:
- Race detection (
-race) withCGO_ENABLED=1 - Coverage profiling
- Uploads coverage artifacts
- Supports private Go modules via
go_private_modulesinput
Calculates coverage and posts PR comment per changed app:
- Downloads coverage artifact from
testsjob - Compares against threshold
- Posts formatted coverage report as PR comment
Verifies code compiles successfully per changed app.
Runs integration tests per changed app using a configurable command (default: make test-integration). Disabled by default — enable with enable_integration_tests: true.
Runs unit tests multiple times with -shuffle=on to detect flaky or order-dependent tests. Always uses go test directly (bypasses Makefile) to guarantee shuffle flags are applied. Excludes /tests/ and /api/ packages. Disabled by default — enable with enable_test_determinism: true.
Runs when no Go changes are detected - outputs skip message.
- Compares changed files between
github.event.beforeand current SHA - Extracts directory paths up to
path_leveldepth - Filters paths matching
filter_pathsarray - Builds matrix with
nameandworking_dirfor each changed app
Example:
filter_paths: '["apps/api", "apps/worker"]'
path_level: 2
Changed files:
- apps/api/handlers/user.go
- apps/api/models/user.go
- apps/worker/jobs/sync.go
Resulting matrix:
[
{"name": "api", "working_dir": "apps/api"},
{"name": "worker", "working_dir": "apps/worker"}
]
With app_name_prefix: "myapp":
[
{"name": "myapp-api", "working_dir": "apps/api"},
{"name": "myapp-worker", "working_dir": "apps/worker"}
]
Coverage reports are posted as PR comments in this format:
## 📊 Unit Test Coverage Report: `platform-api`
| Metric | Value |
|--------|-------|
| **Coverage** | `85.5%` ✅ PASS |
| **Threshold** | `80%` |
---
*Generated by Go PR Analysis workflow*The workflow automatically detects and uses Makefile targets when available, providing consistency between local development and CI pipelines.
| Job | Make Target | Fallback (No Makefile) |
|---|---|---|
| Lint | make lint |
golangci-lint-action |
| Tests | make coverage-unit or make test |
go test -race -coverprofile=coverage.txt ./... |
| Security | make sec SARIF=1 |
gosec + govulncheck |
| Build | make build |
go build -v ./... |
- Checks if
Makefile,makefile, orGNUmakefileexists in working directory - Runs
make -n <target>to verify target exists (dry-run) - Uses make target if available, otherwise falls back to direct commands
.PHONY: lint test coverage-unit sec build
lint:
golangci-lint run ./...
test:
go test -v ./...
coverage-unit:
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
sec:
ifdef SARIF
gosec -fmt sarif -out gosec-report.sarif ./...
else
gosec ./...
endif
govulncheck ./...
build:
go build -v ./...Exclude files from coverage reports using the .ignorecoverunit file. This is useful for excluding generated code, mocks, infrastructure adapters, etc.
Create a .ignorecoverunit file in your app directory:
# Patterns to exclude from coverage
# One pattern per line, glob style
# Auto-generated mocks
*_mock.go
# Infrastructure adapters (tested via integration tests)
*.postgresql.go
*.mongodb.go
*.redis.go
# Bootstrap code
/cmd/
bootstrap.go
# Auto-generated API docs
/api/
swagger.go
*.mock.go- Any file ending in.mock.go/cmd/- Any file in cmd directorybootstrap.go- Specific file name- Lines starting with
#are comments - Empty lines are ignored
- After tests run and generate
coverage.txt, the workflow checks for.ignorecoverunit - If found, patterns are converted to regex and used to filter coverage data
- Filtered coverage is used for threshold checks and PR comments
- Works regardless of whether Makefile or direct Go commands were used
- Pin to version tag: Use
@v1.0.0instead of@v1.0.0for production stability - Custom linting: Place
.golangci.ymlin each app directory for app-specific rules - Coverage threshold: Start with
fail_on_coverage_threshold: falseand enable once baseline is established - Security findings: GoSec results appear in GitHub Security tab when SARIF upload succeeds
- Performance: Jobs run in parallel per app - more apps = more parallelism
- Makefile consistency: Use Makefiles to ensure local dev matches CI behavior
- Coverage exclusions: Use
.ignorecoverunitto exclude generated/infrastructure code from coverage
The workflow requires these permissions:
contents: read- To checkout codepull-requests: write- To post coverage commentssecurity-events: write- To upload SARIF results
- Go CI - Multi-version/multi-OS CI pipeline
- Go Security - Comprehensive security scanning (8 tools)
- Changed Paths - Standalone change detection
- Build - Docker image builds
- Slack Notify - Workflow notifications
Last Updated: 2026-03-12 Version: 1.3.0