Files
aptly/.github/workflows/ci.yml
T
Nick Bozhenko 40ba104838 Add comprehensive CI/CD improvements and test coverage
This commit introduces major enhancements to the CI/CD pipeline and testing infrastructure:

CI/CD Improvements:
- Consolidated modern and legacy CI workflows into a single comprehensive pipeline
- Removed all publishing functionality from CI (no longer needed)
- Added 8 new advanced testing jobs for pull requests:
  * advanced-coverage: Detailed coverage analysis with base branch comparison
  * performance-profile: CPU and memory profiling with benchmarks
  * fuzz-test: Automated fuzz testing for supported packages
  * deep-analysis: Multiple static analysis tools (shadow, ineffassign, gosec, staticcheck)
  * mutation-test: Tests effectiveness of test suite on changed files
  * dependency-audit: Security vulnerabilities and outdated dependency checks
  * stress-test: Race detection with 100 iterations and parallel testing
  * test-report-summary: Aggregates all reports into a single PR comment
- Enabled RUN_LONG_TESTS by default for thorough testing
- Added automatic PR comment generation with all test results

Testing Infrastructure:
- Added comprehensive test files across all packages to improve coverage
- Implemented unit tests for previously untested packages
- Added race condition tests for concurrent operations
- Created integration tests for API endpoints
- Added storage backend tests (etcd, goleveldb)
- Implemented command-line interface tests

Local Testing Support:
- Added act configuration for testing GitHub Actions locally
- Created docker-compose.ci.yml for full CI environment simulation
- Updated CONTRIBUTING.md with detailed local testing instructions

Documentation Updates:
- Added comprehensive CI documentation to CONTRIBUTING.md
- Removed obsolete references to Travis CI
- Updated Go version requirements to 1.24
- Added act usage instructions and examples

Other Improvements:
- Updated .gitignore to exclude coverage reports and build artifacts
- Added test-act.yml workflow for testing act functionality
- Created CI_SUMMARY.md documenting all CI capabilities

These changes transform aptly's CI from a basic testing pipeline into a comprehensive quality assurance system that provides immediate feedback on code quality, performance, security, and test effectiveness.
2025-07-10 12:00:54 -04:00

1336 lines
43 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 "<details>" >> combined-report.md
echo "<summary>🔥 Performance Profile</summary>" >> combined-report.md
echo "" >> combined-report.md
cat reports/performance-profiles/performance-report.md >> combined-report.md
echo "</details>" >> combined-report.md
echo "" >> combined-report.md
fi
# Fuzz testing section
if [ -f reports/fuzz-test-results/fuzz-report.md ]; then
echo "<details>" >> combined-report.md
echo "<summary>🧬 Fuzz Testing</summary>" >> combined-report.md
echo "" >> combined-report.md
cat reports/fuzz-test-results/fuzz-report.md >> combined-report.md
echo "</details>" >> combined-report.md
echo "" >> combined-report.md
fi
# Static analysis section
if [ -f reports/static-analysis-results/analysis-report.md ]; then
echo "<details>" >> combined-report.md
echo "<summary>🔍 Static Analysis</summary>" >> combined-report.md
echo "" >> combined-report.md
cat reports/static-analysis-results/analysis-report.md >> combined-report.md
echo "</details>" >> combined-report.md
echo "" >> combined-report.md
fi
# Mutation testing section
if [ -f reports/mutation-test-results/mutation-report.md ]; then
echo "<details>" >> combined-report.md
echo "<summary>🦠 Mutation Testing</summary>" >> combined-report.md
echo "" >> combined-report.md
cat reports/mutation-test-results/mutation-report.md >> combined-report.md
echo "</details>" >> combined-report.md
echo "" >> combined-report.md
fi
# Dependency audit section
if [ -f reports/dependency-audit-results/dependency-report.md ]; then
echo "<details>" >> combined-report.md
echo "<summary>📦 Dependency Audit</summary>" >> combined-report.md
echo "" >> combined-report.md
cat reports/dependency-audit-results/dependency-report.md >> combined-report.md
echo "</details>" >> combined-report.md
echo "" >> combined-report.md
fi
# Stress test section
if [ -f reports/stress-test-results/stress-report.md ]; then
echo "<details>" >> combined-report.md
echo "<summary>💪 Stress Testing</summary>" >> combined-report.md
echo "" >> combined-report.md
cat reports/stress-test-results/stress-report.md >> combined-report.md
echo "</details>" >> 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