name: CI Pipeline
on:
pull_request:
push:
tags:
- 'v*'
branches:
- 'master'
schedule:
# Run security checks daily
- cron: '0 2 * * *'
env:
GOLANGCI_LINT_VERSION: v1.64.5
defaults:
run:
shell: bash --noprofile --norc -eo pipefail {0}
jobs:
# Quick checks that should fail fast
quick-checks:
name: Quick Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Check formatting
run: |
if [ -n "$(gofmt -l .)" ]; then
echo "Go code is not formatted:"
gofmt -d .
exit 1
fi
- name: Run go vet
run: go vet ./...
- name: Check go mod tidy
run: |
go mod tidy
git diff --exit-code go.mod go.sum
- name: Run flake8
run: |
sudo apt-get update && sudo apt-get install -y flake8
make flake8
# Security scanning
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
# Linting
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: ${{ env.GOLANGCI_LINT_VERSION }}
args: --timeout=5m
# Unit tests with coverage
test-unit:
name: Unit Tests
runs-on: ubuntu-latest
needs: quick-checks
strategy:
matrix:
go: ['1.23', '1.24']
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
cache: true
- name: Install etcd
run: |
sudo apt-get update
sudo apt-get install -y etcd
- name: Run tests
run: |
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
env:
RUN_LONG_TESTS: yes
- name: Upload coverage
uses: codecov/codecov-action@v4
if: matrix.go == '1.24'
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
# Integration tests
test-integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: quick-checks
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
bzip2 \
xz-utils \
graphviz \
gnupg2 \
gpgv2 \
etcd \
azurite \
git \
gcc \
make \
devscripts \
python3 \
python3-requests-unixsocket \
python3-termcolor \
python3-swiftclient \
python3-boto \
python3-azure-storage \
python3-etcd3 \
python3-plyvel \
faketime
- name: Install Python dependencies
run: |
pip install -r system/requirements.txt
- name: Download test fixtures
run: |
git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/
git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/
- name: Run system tests
run: |
make system-test
env:
RUN_LONG_TESTS: yes
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }}
AZURE_STORAGE_KEY: ${{ secrets.AZURE_STORAGE_KEY }}
# Benchmarks
benchmarks:
name: Benchmarks
runs-on: ubuntu-latest
needs: quick-checks
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install etcd
run: |
sudo apt-get update
sudo apt-get install -y etcd
- name: Run benchmarks
run: |
make bench
# Extended test job with coverage merging
test-extended:
name: Extended Tests with Coverage
runs-on: ubuntu-latest
needs: quick-checks
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y etcd
go install github.com/wadey/gocovmerge@latest
- name: Run unit tests with coverage
run: |
go test -v -coverprofile=unit.coverage -covermode=count ./...
env:
RUN_LONG_TESTS: yes
- name: Run benchmarks with coverage
run: |
go test -v -bench=. -benchmem -coverprofile=bench.coverage -covermode=count ./...
- name: Merge coverage files
run: |
gocovmerge unit.coverage bench.coverage > coverage.out
- name: Upload merged coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: extended
name: codecov-extended
# Advanced coverage analysis with PR comments
advanced-coverage:
name: Advanced Coverage Analysis
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Checkout base branch
run: |
git fetch origin ${{ github.base_ref }}:base
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y etcd
go install github.com/jandelgado/gcov2lcov@latest
go install github.com/nikolaydubina/go-cover-treemap@latest
- name: Run tests with coverage on PR branch
run: |
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
go tool cover -func=coverage.out > coverage-summary.txt
go tool cover -html=coverage.out -o coverage.html
# Generate treemap
go-cover-treemap -coverprofile coverage.out > coverage-treemap.svg
# Calculate total coverage
TOTAL_COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
echo "TOTAL_COVERAGE=$TOTAL_COVERAGE" >> $GITHUB_ENV
- name: Run tests with coverage on base branch
run: |
git checkout base
go test -coverprofile=base-coverage.out -covermode=atomic ./... || true
BASE_COVERAGE=$(go tool cover -func=base-coverage.out | grep total | awk '{print $3}' | sed 's/%//' || echo "0")
echo "BASE_COVERAGE=$BASE_COVERAGE" >> $GITHUB_ENV
git checkout -
- name: Generate coverage report
run: |
# Generate detailed package coverage
echo "# Coverage Report" > coverage-report.md
echo "" >> coverage-report.md
echo "## Summary" >> coverage-report.md
echo "- **Total Coverage**: ${{ env.TOTAL_COVERAGE }}%" >> coverage-report.md
echo "- **Base Coverage**: ${{ env.BASE_COVERAGE }}%" >> coverage-report.md
COVERAGE_DIFF=$(echo "${{ env.TOTAL_COVERAGE }} - ${{ env.BASE_COVERAGE }}" | bc)
echo "- **Change**: ${COVERAGE_DIFF}%" >> coverage-report.md
echo "" >> coverage-report.md
echo "## Package Coverage" >> coverage-report.md
echo '```' >> coverage-report.md
cat coverage-summary.txt >> coverage-report.md
echo '```' >> coverage-report.md
- name: Upload coverage artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: |
coverage.html
coverage-treemap.svg
coverage-report.md
coverage-summary.txt
- name: Comment PR
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body-path: coverage-report.md
# Performance profiling
performance-profile:
name: Performance Profiling
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y etcd graphviz
go install github.com/google/pprof@latest
- name: Run benchmarks with profiling
run: |
# Run benchmarks with CPU and memory profiling
go test -bench=. -benchmem -cpuprofile=cpu.prof -memprofile=mem.prof -benchtime=10s ./deb || true
# Generate reports
go tool pprof -svg cpu.prof > cpu-profile.svg || true
go tool pprof -svg mem.prof > mem-profile.svg || true
# Generate text reports
go tool pprof -text cpu.prof > cpu-profile.txt || true
go tool pprof -text mem.prof > mem-profile.txt || true
# Create summary
echo "# Performance Profile Report" > performance-report.md
echo "" >> performance-report.md
echo "## CPU Profile Top Functions" >> performance-report.md
echo '```' >> performance-report.md
head -20 cpu-profile.txt >> performance-report.md || echo "No CPU profile data" >> performance-report.md
echo '```' >> performance-report.md
echo "" >> performance-report.md
echo "## Memory Profile Top Allocations" >> performance-report.md
echo '```' >> performance-report.md
head -20 mem-profile.txt >> performance-report.md || echo "No memory profile data" >> performance-report.md
echo '```' >> performance-report.md
- name: Upload profile artifacts
uses: actions/upload-artifact@v4
with:
name: performance-profiles
path: |
*.prof
*.svg
performance-report.md
# Fuzz testing
fuzz-test:
name: Fuzz Testing
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Run fuzz tests
run: |
echo "# Fuzz Testing Report" > fuzz-report.md
echo "" >> fuzz-report.md
echo "## Results" >> fuzz-report.md
# Find and run any fuzz tests
FUZZ_FOUND=false
for pkg in $(go list ./...); do
if go test -list "^Fuzz" $pkg 2>/dev/null | grep -q "^Fuzz"; then
FUZZ_FOUND=true
echo "Running fuzz tests in $pkg..." >> fuzz-report.md
if go test -fuzz=. -fuzztime=30s $pkg 2>&1 | tee fuzz-output.txt; then
echo "✅ Passed" >> fuzz-report.md
else
echo "❌ Failed" >> fuzz-report.md
cat fuzz-output.txt >> fuzz-report.md
fi
echo "" >> fuzz-report.md
fi
done
if [ "$FUZZ_FOUND" = false ]; then
echo "ℹ️ No fuzz tests found in the codebase" >> fuzz-report.md
fi
- name: Upload fuzz artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: fuzz-test-results
path: |
fuzz-report.md
testdata/fuzz/
# Static analysis suite
deep-analysis:
name: Deep Static Analysis
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
security-events: write
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install analysis tools
run: |
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
go install github.com/gordonklaus/ineffassign@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
go install github.com/nishanths/exhaustive/cmd/exhaustive@latest
- name: Run analysis suite
run: |
echo "# Static Analysis Report" > analysis-report.md
echo "" >> analysis-report.md
# Shadow detection
echo "## Shadow Variable Detection" >> analysis-report.md
if go vet -vettool=$(which shadow) ./... 2>&1 | tee shadow.txt | grep -q "shadowed variable"; then
echo "⚠️ Shadow variables detected:" >> analysis-report.md
echo '```' >> analysis-report.md
cat shadow.txt >> analysis-report.md
echo '```' >> analysis-report.md
else
echo "✅ No shadow variables detected" >> analysis-report.md
fi
echo "" >> analysis-report.md
# Ineffectual assignments
echo "## Ineffectual Assignments" >> analysis-report.md
if ineffassign ./... 2>&1 | tee ineffassign.txt | grep -q ".go:"; then
echo "⚠️ Ineffectual assignments found:" >> analysis-report.md
echo '```' >> analysis-report.md
cat ineffassign.txt >> analysis-report.md
echo '```' >> analysis-report.md
else
echo "✅ No ineffectual assignments" >> analysis-report.md
fi
echo "" >> analysis-report.md
# Security scan
echo "## Security Scan (gosec)" >> analysis-report.md
gosec -fmt=json -out=gosec.json ./... || true
if [ -s gosec.json ] && [ $(jq '.Issues | length' gosec.json) -gt 0 ]; then
echo "⚠️ Security issues found:" >> analysis-report.md
echo '```' >> analysis-report.md
jq -r '.Issues[] | "[\(.severity)] \(.file):\(.line) - \(.details)"' gosec.json >> analysis-report.md
echo '```' >> analysis-report.md
else
echo "✅ No security issues found" >> analysis-report.md
fi
echo "" >> analysis-report.md
# Staticcheck
echo "## Staticcheck Analysis" >> analysis-report.md
if staticcheck ./... 2>&1 | tee staticcheck.txt | grep -q ".go:"; then
echo "ℹ️ Staticcheck suggestions:" >> analysis-report.md
echo '```' >> analysis-report.md
cat staticcheck.txt >> analysis-report.md
echo '```' >> analysis-report.md
else
echo "✅ No staticcheck issues" >> analysis-report.md
fi
echo "" >> analysis-report.md
# Exhaustive enum checks
echo "## Exhaustive Enum Switch Analysis" >> analysis-report.md
if exhaustive ./... 2>&1 | tee exhaustive.txt | grep -q ".go:"; then
echo "⚠️ Non-exhaustive switches found:" >> analysis-report.md
echo '```' >> analysis-report.md
cat exhaustive.txt >> analysis-report.md
echo '```' >> analysis-report.md
else
echo "✅ All enum switches are exhaustive" >> analysis-report.md
fi
- name: Upload analysis artifacts
uses: actions/upload-artifact@v4
with:
name: static-analysis-results
path: |
analysis-report.md
*.json
*.txt
- name: Upload SARIF if available
uses: github/codeql-action/upload-sarif@v3
if: always()
continue-on-error: true
with:
sarif_file: gosec.sarif
# Mutation testing
mutation-test:
name: Mutation Testing
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install mutation testing tool
run: |
go install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest
sudo apt-get update && sudo apt-get install -y etcd
- name: Run mutation tests on changed files
run: |
echo "# Mutation Testing Report" > mutation-report.md
echo "" >> mutation-report.md
# Get changed Go files
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep '\.go$' | grep -v '_test\.go$' || true)
if [ -z "$CHANGED_FILES" ]; then
echo "ℹ️ No Go source files changed" >> mutation-report.md
else
echo "## Mutation Testing Results" >> mutation-report.md
echo "" >> mutation-report.md
TOTAL_MUTANTS=0
KILLED_MUTANTS=0
for file in $CHANGED_FILES; do
if [ -f "$file" ]; then
echo "Testing mutations in $file..." >> mutation-report.md
# Run mutation testing with timeout
timeout 300 go-mutesting -verbose -include="$file" . 2>&1 | tee mutation-output.txt || true
# Parse results
KILLED=$(grep -c "PASS" mutation-output.txt || echo "0")
TOTAL=$(grep -c "MUTATION" mutation-output.txt || echo "0")
TOTAL_MUTANTS=$((TOTAL_MUTANTS + TOTAL))
KILLED_MUTANTS=$((KILLED_MUTANTS + KILLED))
if [ "$TOTAL" -gt 0 ]; then
SCORE=$((KILLED * 100 / TOTAL))
echo "- $file: $KILLED/$TOTAL mutants killed (${SCORE}%)" >> mutation-report.md
fi
fi
done
if [ "$TOTAL_MUTANTS" -gt 0 ]; then
TOTAL_SCORE=$((KILLED_MUTANTS * 100 / TOTAL_MUTANTS))
echo "" >> mutation-report.md
echo "**Total: $KILLED_MUTANTS/$TOTAL_MUTANTS mutants killed (${TOTAL_SCORE}%)**" >> mutation-report.md
fi
fi
- name: Upload mutation artifacts
uses: actions/upload-artifact@v4
with:
name: mutation-test-results
path: mutation-report.md
# Dependency audit
dependency-audit:
name: Dependency Audit
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install audit tools
run: |
go install github.com/sonatype-nexus-community/nancy@latest
go install github.com/Zxilly/go-size-analyzer/cmd/gsa@latest
- name: Run dependency audit
run: |
echo "# Dependency Audit Report" > dependency-report.md
echo "" >> dependency-report.md
# Check for outdated dependencies
echo "## Outdated Dependencies" >> dependency-report.md
go list -u -m all | grep '\[' > outdated.txt || true
if [ -s outdated.txt ]; then
echo "⚠️ Found outdated dependencies:" >> dependency-report.md
echo '```' >> dependency-report.md
cat outdated.txt >> dependency-report.md
echo '```' >> dependency-report.md
else
echo "✅ All dependencies are up to date" >> dependency-report.md
fi
echo "" >> dependency-report.md
# Vulnerability scan
echo "## Vulnerability Scan (Nancy)" >> dependency-report.md
go list -json -m all | nancy sleuth 2>&1 | tee nancy.txt || true
if grep -q "Vulnerable" nancy.txt; then
echo "⚠️ Vulnerabilities found:" >> dependency-report.md
echo '```' >> dependency-report.md
grep -A5 "Vulnerable" nancy.txt >> dependency-report.md
echo '```' >> dependency-report.md
else
echo "✅ No known vulnerabilities" >> dependency-report.md
fi
echo "" >> dependency-report.md
# Dependency graph
echo "## Dependency Statistics" >> dependency-report.md
DIRECT_DEPS=$(go list -m all | grep -v "^github.com/aptly-dev/aptly" | wc -l)
echo "- Direct dependencies: $DIRECT_DEPS" >> dependency-report.md
# Binary size analysis
echo "" >> dependency-report.md
echo "## Binary Size Analysis" >> dependency-report.md
go build -o aptly-binary || true
if [ -f aptly-binary ]; then
SIZE=$(du -h aptly-binary | cut -f1)
echo "- Binary size: $SIZE" >> dependency-report.md
# Analyze size
gsa aptly-binary -o size-analysis.txt || true
if [ -f size-analysis.txt ]; then
echo "### Top packages by size:" >> dependency-report.md
echo '```' >> dependency-report.md
head -20 size-analysis.txt >> dependency-report.md
echo '```' >> dependency-report.md
fi
fi
- name: Upload dependency artifacts
uses: actions/upload-artifact@v4
with:
name: dependency-audit-results
path: |
dependency-report.md
*.txt
# Stress testing
stress-test:
name: Stress Testing
runs-on: ubuntu-latest
needs: quick-checks
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y etcd
- name: Run stress tests
run: |
echo "# Stress Testing Report" > stress-report.md
echo "" >> stress-report.md
# Run race detection with multiple iterations
echo "## Race Detection (100 iterations)" >> stress-report.md
if go test -race -count=100 -timeout=30m ./utils ./database/goleveldb ./context 2>&1 | tee race-stress.txt; then
echo "✅ No races detected in 100 iterations" >> stress-report.md
else
echo "❌ Race conditions detected:" >> stress-report.md
echo '```' >> stress-report.md
grep -A10 "WARNING: DATA RACE" race-stress.txt | head -50 >> stress-report.md
echo '```' >> stress-report.md
fi
echo "" >> stress-report.md
# Parallel stress test
echo "## Parallel Execution Test" >> stress-report.md
if go test -parallel=10 -count=10 ./deb 2>&1 | tee parallel-stress.txt; then
echo "✅ Parallel tests passed" >> stress-report.md
else
echo "❌ Parallel test failures" >> stress-report.md
tail -50 parallel-stress.txt >> stress-report.md
fi
echo "" >> stress-report.md
# Memory stress test
echo "## Memory Stress Test" >> stress-report.md
GOGC=10 go test -run=TestPackageCollection -count=50 ./deb 2>&1 | tee memory-stress.txt || true
echo "✅ Memory stress test completed" >> stress-report.md
- name: Upload stress test artifacts
uses: actions/upload-artifact@v4
with:
name: stress-test-results
path: |
stress-report.md
*-stress.txt
# Aggregate all reports into a single PR comment
test-report-summary:
name: Test Report Summary
runs-on: ubuntu-latest
needs: [advanced-coverage, performance-profile, fuzz-test, deep-analysis, mutation-test, dependency-audit, stress-test]
if: always() && github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: reports
- name: Create combined report
run: |
echo "# 📊 Comprehensive Test Report for PR #${{ github.event.pull_request.number }}" > combined-report.md
echo "" >> combined-report.md
echo "*Generated at $(date -u)*" >> combined-report.md
echo "" >> combined-report.md
# Coverage section
if [ -f reports/coverage-report/coverage-report.md ]; then
cat reports/coverage-report/coverage-report.md >> combined-report.md
echo "" >> combined-report.md
echo "[📄 Full HTML Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> combined-report.md
echo "" >> combined-report.md
fi
# Performance section
if [ -f reports/performance-profiles/performance-report.md ]; then
echo "" >> combined-report.md
echo "🔥 Performance Profile
" >> combined-report.md
echo "" >> combined-report.md
cat reports/performance-profiles/performance-report.md >> combined-report.md
echo " " >> combined-report.md
echo "" >> combined-report.md
fi
# Fuzz testing section
if [ -f reports/fuzz-test-results/fuzz-report.md ]; then
echo "" >> combined-report.md
echo "🧬 Fuzz Testing
" >> combined-report.md
echo "" >> combined-report.md
cat reports/fuzz-test-results/fuzz-report.md >> combined-report.md
echo " " >> combined-report.md
echo "" >> combined-report.md
fi
# Static analysis section
if [ -f reports/static-analysis-results/analysis-report.md ]; then
echo "" >> combined-report.md
echo "🔍 Static Analysis
" >> combined-report.md
echo "" >> combined-report.md
cat reports/static-analysis-results/analysis-report.md >> combined-report.md
echo " " >> combined-report.md
echo "" >> combined-report.md
fi
# Mutation testing section
if [ -f reports/mutation-test-results/mutation-report.md ]; then
echo "" >> combined-report.md
echo "🦠 Mutation Testing
" >> combined-report.md
echo "" >> combined-report.md
cat reports/mutation-test-results/mutation-report.md >> combined-report.md
echo " " >> combined-report.md
echo "" >> combined-report.md
fi
# Dependency audit section
if [ -f reports/dependency-audit-results/dependency-report.md ]; then
echo "" >> combined-report.md
echo "📦 Dependency Audit
" >> combined-report.md
echo "" >> combined-report.md
cat reports/dependency-audit-results/dependency-report.md >> combined-report.md
echo " " >> combined-report.md
echo "" >> combined-report.md
fi
# Stress test section
if [ -f reports/stress-test-results/stress-report.md ]; then
echo "" >> combined-report.md
echo "💪 Stress Testing
" >> combined-report.md
echo "" >> combined-report.md
cat reports/stress-test-results/stress-report.md >> combined-report.md
echo " " >> combined-report.md
echo "" >> combined-report.md
fi
echo "---" >> combined-report.md
echo "*View detailed artifacts in the [Actions run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})*" >> combined-report.md
- name: Find Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Comprehensive Test Report
- name: Create or Update Comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: combined-report.md
edit-mode: replace
# Build artifacts
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, test-unit]
strategy:
matrix:
include:
- os: linux
arch: amd64
- os: linux
arch: arm64
- os: darwin
arch: amd64
- os: darwin
arch: arm64
- os: windows
arch: amd64
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Build
run: |
GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build \
-ldflags "-X main.Version=${GITHUB_SHA::8}" \
-o aptly-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.os == 'windows' && '.exe' || '' }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: aptly-${{ matrix.os }}-${{ matrix.arch }}
path: aptly-${{ matrix.os }}-${{ matrix.arch }}*
# Docker build
docker:
name: Docker Build
runs-on: ubuntu-latest
needs: [lint, test-unit]
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: false
tags: |
aptly/aptly:latest
aptly/aptly:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Debian package building
debian-packages:
name: Build Debian Packages
runs-on: ubuntu-22.04
needs: [lint, test-unit]
if: github.event_name == 'push' || github.event_name == 'pull_request'
strategy:
matrix:
include:
- distribution: debian
release: buster
arch: amd64
- distribution: debian
release: bullseye
arch: amd64
- distribution: debian
release: bookworm
arch: amd64
- distribution: debian
release: trixie
arch: amd64
- distribution: ubuntu
release: focal
arch: amd64
- distribution: ubuntu
release: jammy
arch: amd64
- distribution: ubuntu
release: noble
arch: amd64
# ARM builds
- distribution: debian
release: bullseye
arch: arm64
- distribution: debian
release: bookworm
arch: arm64
- distribution: ubuntu
release: jammy
arch: arm64
- distribution: ubuntu
release: noble
arch: arm64
# 32-bit builds
- distribution: debian
release: bullseye
arch: i386
- distribution: debian
release: bookworm
arch: i386
- distribution: debian
release: bullseye
arch: armhf
- distribution: debian
release: bookworm
arch: armhf
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
if: matrix.arch != 'amd64' && matrix.arch != 'i386'
uses: docker/setup-qemu-action@v3
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
- name: Setup CI version suffix
if: github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/')
run: |
echo "CI_VERSION_SUFFIX=+ci" >> $GITHUB_ENV
- name: Build Debian package
run: |
export DEBIAN_FRONTEND=noninteractive
# Install build dependencies
sudo apt-get update
sudo apt-get install -y devscripts dpkg-dev
# Install cross-compilation tools if needed
if [[ "${{ matrix.arch }}" != "amd64" && "${{ matrix.arch }}" != "i386" ]]; then
sudo apt-get install -y gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf
fi
# Set up environment for cross-compilation
case "${{ matrix.arch }}" in
i386)
export GOARCH=386
;;
arm64)
export GOARCH=arm64
export CC=aarch64-linux-gnu-gcc
;;
armhf)
export GOARCH=arm
export GOARM=7
export CC=arm-linux-gnueabihf-gcc
;;
*)
export GOARCH=amd64
;;
esac
# Build the package
make dpkg DEBIAN_RELEASE=${{ matrix.release }} ARCHITECTURE=${{ matrix.arch }}
- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: debian-${{ matrix.distribution }}-${{ matrix.release }}-${{ matrix.arch }}
path: build/*.deb
retention-days: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && 90 || 7 }}
# Binary builds with expanded platform support
binary-builds:
name: Build Binaries
runs-on: ubuntu-latest
needs: [lint, test-unit]
strategy:
matrix:
include:
# 64-bit builds
- os: linux
arch: amd64
- os: linux
arch: arm64
- os: darwin
arch: amd64
- os: darwin
arch: arm64
- os: windows
arch: amd64
- os: freebsd
arch: amd64
# 32-bit builds
- os: linux
arch: 386
- os: linux
arch: arm
- os: windows
arch: 386
- os: freebsd
arch: 386
- os: freebsd
arch: arm
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
- name: Setup version
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/}
else
VERSION=$(make version)
fi
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Build binary
run: |
# Set architecture-specific variables
case "${{ matrix.arch }}" in
386)
export GOARCH=386
;;
arm)
export GOARCH=arm
export GOARM=7
;;
*)
export GOARCH=${{ matrix.arch }}
;;
esac
export GOOS=${{ matrix.os }}
# Build with static linking for Linux
if [[ "${{ matrix.os }}" == "linux" ]]; then
export CGO_ENABLED=0
LDFLAGS="-extldflags=-static"
else
LDFLAGS=""
fi
# Build the binary
go build -ldflags "$LDFLAGS -X main.Version=$VERSION" -o aptly${{ matrix.os == 'windows' && '.exe' || '' }}
- name: Build man pages
if: matrix.os == 'linux' && matrix.arch == 'amd64'
run: |
make man
- name: Build completion files
if: matrix.os == 'linux' && matrix.arch == 'amd64'
run: |
make completion
- name: Create archive
run: |
ARCHIVE_NAME="aptly_${VERSION}_${{ matrix.os }}_${{ matrix.arch }}.zip"
# Include binary
FILES="aptly${{ matrix.os == 'windows' && '.exe' || '' }}"
# Include man pages and completions for main Linux build
if [[ "${{ matrix.os }}" == "linux" && "${{ matrix.arch }}" == "amd64" ]]; then
FILES="$FILES man completion"
fi
zip -r $ARCHIVE_NAME $FILES
echo "ARCHIVE_NAME=$ARCHIVE_NAME" >> $GITHUB_ENV
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.os }}-${{ matrix.arch }}
path: ${{ env.ARCHIVE_NAME }}
retention-days: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && 90 || 7 }}
# Dependency check
dependencies:
name: Check Dependencies
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
steps:
- uses: actions/checkout@v4
- name: Read Go Version
run: |
gover=$(sed -n 's/^go \(.*\)/\1/p' go.mod)
echo "Go Version: $gover"
echo "GOVER=$gover" >> $GITHUB_OUTPUT
id: goversion
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ steps.goversion.outputs.GOVER }}
cache: true
- name: Check for updates
run: |
echo "## Outdated Dependencies" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
go list -u -m all | grep '\[' >> $GITHUB_STEP_SUMMARY || echo "All dependencies are up to date!" >> $GITHUB_STEP_SUMMARY