Skip to content

ci(deps): bump actions/checkout from 4 to 6 #366

ci(deps): bump actions/checkout from 4 to 6

ci(deps): bump actions/checkout from 4 to 6 #366

Workflow file for this run

name: CI
on:
push:
branches: [main, master, develop]
tags: ['v*']
pull_request:
branches: [main, master, develop]
env:
GO_VERSION: '1.26'
jobs:
# Job 1: Lint and Format Check
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Run gofmt
run: |
if [ "$(gofmt -l . | wc -l)" -gt 0 ]; then
echo "The following files need formatting:"
gofmt -l .
exit 1
fi
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.64.8
args: --timeout=5m
continue-on-error: true
- name: go staticcheck (fallback)
if: failure()
run: |
go install honnef.co/go/tools/cmd/staticcheck@v0.6.1 2>/dev/null || true
which staticcheck && staticcheck ./... || echo "staticcheck not available, skipping"
# Job 2: Build Frontend (shared by test, test-race, build jobs)
build-frontend:
name: Build Frontend
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Cache npm modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('internal/webui/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install frontend dependencies
working-directory: ./internal/webui
run: npm ci --legacy-peer-deps
- name: Lint frontend
working-directory: ./internal/webui
run: npm run lint
continue-on-error: true
# - name: Run frontend tests
# working-directory: ./internal/webui
# run: npm test
- name: Install Playwright browsers
working-directory: ./internal/webui
run: npx playwright install --with-deps chromium
- name: Run E2E tests
working-directory: ./internal/webui
run: npm run test:e2e
- name: Build frontend
working-directory: ./internal/webui
run: npm run build
continue-on-error: true
- name: Upload frontend artifacts
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: frontend-assets
path: |
internal/webui/assets/
internal/webui/index.html
internal/webui/favicon.svg
retention-days: 1
# Job 3: Test (multi-OS matrix for cross-platform correctness)
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [build-frontend]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go-version: ['1.26']
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download frontend artifacts
uses: actions/download-artifact@v8
with:
name: frontend-assets
path: ${{ runner.os == 'Windows' && 'internal/webui/' || '/tmp/frontend-assets/' }}
- name: Move frontend artifacts to webui (Unix fix)
if: runner.os != 'Windows'
shell: bash
run: |
rm -rf internal/webui/assets internal/webui/index.html internal/webui/favicon.svg 2>/dev/null || true
cp -r /tmp/frontend-assets/* internal/webui/
rm -rf /tmp/frontend-assets/
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go-version }}
- name: Cache Go modules
if: runner.os != 'macOS'
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Download dependencies
run: go mod download
- name: Run tests (Unix with coverage)
if: runner.os != 'Windows'
shell: bash
run: |
if [ "${{ runner.os }}" = "macOS" ]; then
# Skip flaky packages on macOS (timing-sensitive tests)
go test -p 1 -short -v -count=1 -timeout=600s $(go list ./... | grep -v 'internal/health' | grep -v 'internal/proxy/l7')
else
go test -p 1 -short -v -count=1 -coverprofile=coverage.out -timeout=600s ./...
fi
- name: Run tests (Windows)
if: runner.os == 'Windows'
shell: bash
run: go test -p 1 -short -v -count=1 -coverprofile=coverage.out -timeout=600s $(go list ./... | grep -v 'test/e2e')
- name: Generate coverage report
if: runner.os != 'macOS'
shell: bash
run: go tool cover -func=coverage.out
- name: Enforce coverage threshold
if: runner.os == 'Linux'
shell: bash
run: |
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
THRESHOLD=85
echo "Total coverage: ${COVERAGE}%"
echo "Required threshold: ${THRESHOLD}%"
if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then
echo "::error::Coverage ${COVERAGE}% is below threshold ${THRESHOLD}%"
exit 1
fi
echo "Coverage check passed"
- name: Check per-package coverage
if: runner.os == 'Linux'
shell: bash
run: |
THRESHOLD=85
echo "Checking per-package coverage (threshold: ${THRESHOLD}%)..."
FAIL=0
go tool cover -func=coverage.out | grep -v total | \
awk -F'/' '{pkg=""; for(i=1;i<=NF-1;i++){if(i>1)pkg=pkg"/";pkg=pkg$$i}} {print pkg, $$NF}' | \
awk '{cov[$1]+=$3; cnt[$1]++} END {for(p in cov) {printf "%s %.1f\n", p, cov[p]/cnt[p]}}' | \
sort | \
while read pkg pct; do
IPCT=$(echo "$pct" | sed 's/\..*//')
if [ "$IPCT" -lt "$THRESHOLD" ] 2>/dev/null; then
echo "::warning::$pkg ${pct}% is below ${THRESHOLD}%"
fi
done
echo "Per-package coverage check complete"
- name: Upload coverage to Codecov
if: runner.os == 'Linux' && matrix.go-version == env.GO_VERSION
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
continue-on-error: true
# Job 4: Race Detector Tests (Linux only, requires CGO/GCC)
test-race:
name: Test (Race Detector)
runs-on: ubuntu-latest
needs: [test, build-frontend]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download frontend artifacts
uses: actions/download-artifact@v8
with:
name: frontend-assets
path: ${{ runner.os == 'Windows' && 'internal/webui/' || '/tmp/frontend-assets/' }}
- name: Move frontend artifacts to webui (Unix fix)
if: runner.os != 'Windows'
shell: bash
run: |
rm -rf internal/webui/assets internal/webui/index.html internal/webui/favicon.svg 2>/dev/null || true
cp -r /tmp/frontend-assets/* internal/webui/
rm -rf /tmp/frontend-assets/
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
if: runner.os != 'macOS'
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Download dependencies
run: go mod download
- name: Run tests with race detector
run: go test -p 1 -race -short -count=1 -timeout=600s ./...
# Job 5: Build
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, test, build-frontend]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download frontend artifacts
uses: actions/download-artifact@v8
with:
name: frontend-assets
path: ${{ runner.os == 'Windows' && 'internal/webui/' || '/tmp/frontend-assets/' }}
- name: Move frontend artifacts to webui (Unix fix)
if: runner.os != 'Windows'
shell: bash
run: |
rm -rf internal/webui/assets internal/webui/index.html internal/webui/favicon.svg 2>/dev/null || true
cp -r /tmp/frontend-assets/* internal/webui/
rm -rf /tmp/frontend-assets/
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
if: runner.os != 'macOS'
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build binary
run: make build
- name: Check binary size
run: |
BINARY="bin/olb"
if [ ! -f "$BINARY" ]; then
echo "ERROR: Binary not found at $BINARY"
exit 1
fi
SIZE=$(stat --format=%s "$BINARY")
SIZE_MB=$((SIZE / 1024 / 1024))
echo "Binary size: ${SIZE} bytes (${SIZE_MB} MB)"
MAX_SIZE=$((20 * 1024 * 1024))
if [ "$SIZE" -gt "$MAX_SIZE" ]; then
echo "ERROR: Binary size ${SIZE_MB}MB exceeds 20MB limit"
exit 1
fi
echo "PASS: Binary size is within the 20MB limit"
- name: Measure startup time
run: |
BINARY="bin/olb"
echo "Measuring startup time (olb version)..."
# Warm up filesystem cache
"$BINARY" version > /dev/null 2>&1 || true
# Measure 5 runs
TOTAL=0
for i in 1 2 3 4 5; do
START=$(date +%s%N)
"$BINARY" version > /dev/null 2>&1 || true
END=$(date +%s%N)
ELAPSED=$(( (END - START) / 1000000 ))
echo " Run $i: ${ELAPSED}ms"
TOTAL=$((TOTAL + ELAPSED))
done
AVG=$((TOTAL / 5))
echo "Average startup time: ${AVG}ms"
if [ "$AVG" -gt 5000 ]; then
echo "WARNING: Startup time exceeds 5 seconds"
fi
- name: Build all platforms
run: make build-all
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: binaries
path: bin/
# Job 6: Integration Tests
integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download frontend artifacts
uses: actions/download-artifact@v8
with:
name: frontend-assets
path: internal/webui/
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Run integration tests
run: go test -v -short -count=1 -p 1 -tags=integration -timeout=300s ./test/...
# Job 7: Benchmark
benchmark:
name: Benchmark
runs-on: ubuntu-latest
needs: [test]
if: github.event_name == 'pull_request'
permissions:
pull-requests: write
steps:
- name: Checkout PR branch
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
if: runner.os != 'macOS'
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install benchstat
run: go install golang.org/x/perf/cmd/benchstat@v0.0.0-20250306133421-1eb4f625a7d6
- name: Run baseline benchmarks (base branch)
run: |
git fetch origin ${{ github.base_ref }}
git checkout origin/${{ github.base_ref }}
go test -bench=. -benchmem -count=5 -run=^$ -timeout=600s ./... > baseline.txt 2>&1 || true
- name: Run PR benchmarks
run: |
git checkout ${{ github.event.pull_request.head.sha }}
go test -bench=. -benchmem -count=5 -run=^$ -timeout=600s ./... > pr.txt 2>&1 || true
- name: Compare benchmarks
id: benchstat
run: |
benchstat baseline.txt pr.txt > comparison.md 2>&1 || true
echo "## Benchmark Comparison" > comment.md
echo "" >> comment.md
echo '```' >> comment.md
cat comparison.md >> comment.md
echo '```' >> comment.md
cat comparison.md
- name: Post benchmark comparison
if: success()
continue-on-error: true
run: gh pr comment ${{ github.event.pull_request.number }} --body-file comment.md
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: |
baseline.txt
pr.txt
comparison.md
retention-days: 30
# Job 8: Docker Build
docker:
name: Docker Build
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup QEMU
uses: docker/setup-qemu-action@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Docker image
uses: docker/build-push-action@v7
with:
context: .
push: false
tags: ghcr.io/openloadbalancer/olb:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# Job 9: Security Scan
security:
name: Security Scan
runs-on: ubuntu-latest
needs: [build]
permissions:
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Run Gosec Security Scanner
uses: securego/gosec@v2.22.3
with:
args: '-severity high -quiet ./...'
- name: Verify module checksums
run: go mod verify
- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
govulncheck ./...
- name: Run Nancy (dependency vulnerability scanner)
run: |
go install github.com/sonatypecommunity/nancy@v1.0.106
go list -json -deps ./... | nancy sleuth
continue-on-error: true
# Job 10: Binary Analysis
binary:
name: Binary Analysis
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: binaries
path: bin/
- name: Binary size analysis
run: |
ls -lh bin/
for f in bin/olb-*; do
SIZE=$(stat -c%s "$f")
echo "$f: $SIZE bytes"
if [ "$SIZE" -gt 20971520 ]; then
echo "ERROR: $f exceeds 20MB limit"
exit 1
fi
done
- name: Binary verification
run: |
file bin/olb-linux-amd64
chmod +x bin/olb-linux-amd64
bin/olb-linux-amd64 version
- name: Check for debug symbols
run: |
if nm bin/olb-linux-amd64 2>/dev/null | head -5 | grep -q "T main"; then
echo "WARNING: Binary contains debug symbols"
else
echo "Debug symbols stripped: OK"
fi
# Job 11: Container Image Security Scan
image-scan:
name: Container Image Scan
runs-on: ubuntu-latest
needs: [docker]
permissions:
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build Docker image for scanning
uses: docker/build-push-action@v7
with:
context: .
push: false
load: true
tags: ghcr.io/openloadbalancer/olb:scan-target
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.35.0
with:
image-ref: ghcr.io/openloadbalancer/olb:scan-target
exit-code: "0"
format: "sarif"
output: "trivy-results.sarif"
severity: "CRITICAL,HIGH"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-results.sarif"
# Job 12: Release (delegated to release.yml for full publishing including GHCR)
# release.yml handles: binary builds, GitHub release, SBOM, Docker publish to GHCR
# This job is intentionally left out of ci.yml to avoid duplicate release creation.