CI with GitHub Actions
Your Node CI workflow, translated to Go
Section titled “Your Node CI workflow, translated to Go”If you have set up CI for a TypeScript project, you already understand the structure: check out code, set up the language runtime, install dependencies, run checks. The Go version is nearly identical — with a few welcome simplifications.
Go CI workflow
Section titled “Go CI workflow”Create .github/workflows/ci.yml in your repository:
name: Go CI
on: push: branches: [main, dev] pull_request: branches: [main, dev]
jobs: test: name: Test & Lint runs-on: ubuntu-latest
steps: - name: Checkout code uses: actions/checkout@v4
- name: Set up Go uses: actions/setup-go@v5 with: go-version: "1.22" cache: true # caches the module download cache
- name: Download dependencies run: go mod download
- name: Verify dependencies run: go mod verify
- name: Run go vet run: go vet ./...
- name: Run golangci-lint uses: golangci/golangci-lint-action@v6 with: version: latest
- name: Run tests run: go test -race -coverprofile=coverage.out -covermode=atomic ./...
- name: Upload coverage report uses: codecov/codecov-action@v4 with: file: coverage.out fail_ci_if_error: false
build: name: Build Binary runs-on: ubuntu-latest needs: test
steps: - name: Checkout code uses: actions/checkout@v4
- name: Set up Go uses: actions/setup-go@v5 with: go-version: "1.22" cache: true
- name: Build run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/app ./cmd/app
- name: Upload binary artifact uses: actions/upload-artifact@v4 with: name: app-linux-amd64 path: bin/appKey flags on the test step:
-raceenables the Go race detector — it catches data races in concurrent code at a ~2–20x runtime cost (acceptable in CI)-coverprofile=coverage.outwrites coverage data for upload-covermode=atomicis required for accurate coverage when-raceis on
Node CI workflow (for comparison)
Section titled “Node CI workflow (for comparison)”name: Node CI
on: push: branches: [main, dev] pull_request: branches: [main, dev]
jobs: test: runs-on: ubuntu-latest
steps: - uses: actions/checkout@v4
- name: Set up Node uses: actions/setup-node@v4 with: node-version: "20" cache: "npm"
- name: Install dependencies run: npm ci
- name: Type-check run: npx tsc --noEmit
- name: Lint run: npx eslint src --ext .ts
- name: Test run: npx jest --coverage
build: runs-on: ubuntu-latest needs: test
steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: "npm" - run: npm ci --omit=dev - run: npm run build# Node CI: needs setup-node + npm ci# Type-check: tsc --noEmit (separate step)# Lint: ESLint (separate step + config file)# Test: Jest (separate step + config)# Build: tsc/webpack output is JS files
steps: - uses: actions/setup-node@v4 with: node-version: "20" cache: "npm" - run: npm ci - run: npx tsc --noEmit - run: npx eslint src --ext .ts - run: npx jest --coverage - run: npm run build# Go CI: needs setup-go + go mod download# Type-check: already part of go build/vet# Lint: go vet + golangci-lint-action# Test: go test -race (built-in)# Build: single static binary
steps: - uses: actions/setup-go@v5 with: go-version: "1.22" cache: true - run: go mod download - run: go vet ./... - uses: golangci/golangci-lint-action@v6 - run: go test -race -coverprofile=coverage.out ./... - run: CGO_ENABLED=0 GOOS=linux go build -o bin/app ./cmd/appSave this as
.github/workflows/ci.ymlin your repository. Push to a branch and watch the Actions tab — no local setup required to verify the workflow.
go test flags worth knowing in CI
Section titled “go test flags worth knowing in CI”# Run all tests with race detectorgo test -race ./...
# Run with coverage and write to filego test -race -coverprofile=coverage.out -covermode=atomic ./...
# Print coverage summary to stdoutgo tool cover -func=coverage.out
# Fail if coverage drops below a threshold (useful in CI)go test ./... && go tool cover -func=coverage.out | tail -1 | awk '{if ($3+0 < 80) exit 1}'
# Run only tests matching a patterngo test ./... -run TestUser
# Run tests with verbose outputgo test -v ./...