Skip to content

CI Pipeline

CI Pipeline #74

Workflow file for this run

name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
schedule:
# Run nightly tests at 2 AM UTC
- cron: '0 2 * * *'
env:
GO_VERSION: '1.21'
KUBERNETES_VERSION: 'v1.28.2'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# Static analysis and linting
lint:
name: Lint and Static Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install dependencies
run: go mod download
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout=5m
- name: Run go vet
run: go vet ./...
- name: Run go fmt check
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
echo "The following files need formatting:"
gofmt -s -l .
exit 1
fi
- name: Check for security issues
uses: securecodewarrior/github-action-gosec@master
with:
args: '-fmt sarif -out gosec.sarif ./...'
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: gosec.sarif
# Unit tests
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.20', '1.21']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go-version }}-
- name: Install dependencies
run: go mod download
- name: Run unit tests
run: |
go test -v -race -coverprofile=coverage.out -covermode=atomic ./internal/... ./pkg/...
- name: Generate coverage report
run: go tool cover -html=coverage.out -o coverage.html
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
- name: Upload coverage artifacts
uses: actions/upload-artifact@v3
with:
name: coverage-report-${{ matrix.go-version }}
path: |
coverage.out
coverage.html
# Build and test Docker images
build-images:
name: Build Docker Images
runs-on: ubuntu-latest
needs: [lint, unit-tests]
outputs:
admission-webhook-image: ${{ steps.meta-webhook.outputs.tags }}
policy-manager-image: ${{ steps.meta-manager.outputs.tags }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for admission webhook
id: meta-webhook
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/admission-webhook
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push admission webhook image
uses: docker/build-push-action@v5
with:
context: .
file: ./build/docker/admission-webhook.Dockerfile
push: true
tags: ${{ steps.meta-webhook.outputs.tags }}
labels: ${{ steps.meta-webhook.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Extract metadata for policy manager
id: meta-manager
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/policy-manager
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push policy manager image
uses: docker/build-push-action@v5
with:
context: .
file: ./build/docker/policy-manager.Dockerfile
push: true
tags: ${{ steps.meta-manager.outputs.tags }}
labels: ${{ steps.meta-manager.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Integration tests
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: [build-images]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install dependencies
run: go mod download
- name: Set up test environment
run: |
# Install test dependencies
go install github.com/onsi/ginkgo/v2/ginkgo@latest
go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
- name: Run integration tests
run: |
export KUBEBUILDER_ASSETS=$(setup-envtest use ${{ env.KUBERNETES_VERSION }} --bin-dir /tmp/envtest-bins -p path)
go test -v -race -coverprofile=coverage-integration.out ./test/integration/...
- name: Upload integration test results
uses: actions/upload-artifact@v3
with:
name: integration-test-results
path: |
coverage-integration.out
test-results/
# End-to-end tests on Kind
e2e-kind:
name: E2E Tests (Kind)
runs-on: ubuntu-latest
needs: [build-images]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Install Kind
run: |
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
- name: Install kubectl
run: |
curl -LO "https://dl.k8s.io/release/${{ env.KUBERNETES_VERSION }}/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Install Helm
run: |
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Run Kind E2E tests
run: |
export CLEANUP=false
./scripts/test/test-kind.sh
- name: Collect test artifacts
if: always()
run: |
mkdir -p artifacts/kind
cp -r test-results/kind/* artifacts/kind/ || true
kubectl get pods -A -o wide > artifacts/kind/final-pods.txt || true
kubectl get events -A --sort-by='.lastTimestamp' > artifacts/kind/final-events.txt || true
- name: Upload Kind test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: kind-test-artifacts
path: artifacts/kind/
# End-to-end tests on k3s
e2e-k3s:
name: E2E Tests (k3s)
runs-on: ubuntu-latest
needs: [build-images]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Install kubectl
run: |
curl -LO "https://dl.k8s.io/release/${{ env.KUBERNETES_VERSION }}/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Install Helm
run: |
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Run k3s E2E tests
run: |
export CLEANUP=false
sudo ./scripts/test/test-k3s.sh
- name: Collect test artifacts
if: always()
run: |
mkdir -p artifacts/k3s
sudo cp -r test-results/k3s/* artifacts/k3s/ || true
sudo kubectl get pods -A -o wide > artifacts/k3s/final-pods.txt || true
sudo kubectl get events -A --sort-by='.lastTimestamp' > artifacts/k3s/final-events.txt || true
- name: Upload k3s test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: k3s-test-artifacts
path: artifacts/k3s/
# Security scanning
security-scan:
name: Security Scanning
runs-on: ubuntu-latest
needs: [build-images]
steps:
- name: Checkout code
uses: actions/checkout@v4
- 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 to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
- name: Scan admission webhook image
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ needs.build-images.outputs.admission-webhook-image }}
format: 'sarif'
output: 'trivy-webhook.sarif'
- name: Scan policy manager image
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ needs.build-images.outputs.policy-manager-image }}
format: 'sarif'
output: 'trivy-manager.sarif'
# Performance tests
performance-tests:
name: Performance Tests
runs-on: ubuntu-latest
needs: [e2e-kind]
if: github.event_name == 'schedule' || contains(github.event.pull_request.labels.*.name, 'performance')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Install Kind
run: |
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
- name: Install kubectl
run: |
curl -LO "https://dl.k8s.io/release/${{ env.KUBERNETES_VERSION }}/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Install Helm
run: |
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Run performance tests
run: |
export CLEANUP=false
./scripts/test/test-kind.sh
# Run additional performance benchmarks
go test -bench=. -benchmem ./internal/... ./pkg/... > performance-results.txt
- name: Upload performance results
uses: actions/upload-artifact@v3
with:
name: performance-results
path: performance-results.txt
# Helm chart testing
helm-tests:
name: Helm Chart Tests
runs-on: ubuntu-latest
needs: [build-images]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
run: |
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Install chart-testing
run: |
curl -sSLo ct.tar.gz https://github.com/helm/chart-testing/releases/download/v3.9.0/chart-testing_3.9.0_linux_amd64.tar.gz
tar -xzf ct.tar.gz
sudo mv ct /usr/local/bin/
- name: Lint Helm charts
run: |
ct lint --config .github/ct.yaml
- name: Install Kind
run: |
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
- name: Create Kind cluster for chart testing
run: |
kind create cluster --name chart-testing
- name: Test Helm charts
run: |
ct install --config .github/ct.yaml
# Documentation tests
docs-tests:
name: Documentation Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for broken links
uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-quiet-mode: 'yes'
use-verbose-mode: 'yes'
config-file: '.github/mlc_config.json'
- name: Validate YAML files
run: |
find . -name "*.yaml" -o -name "*.yml" | xargs -I {} sh -c 'echo "Validating {}" && python -c "import yaml; yaml.safe_load(open(\"{}\"))"'
- name: Check Helm chart documentation
run: |
helm-docs --check
# Release preparation
prepare-release:
name: Prepare Release
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: [lint, unit-tests, integration-tests, e2e-kind, e2e-k3s, security-scan, helm-tests, docs-tests]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
run: |
# Generate changelog using conventional commits
npx conventional-changelog-cli -p angular -i CHANGELOG.md -s
- name: Create release PR
if: contains(github.event.head_commit.message, 'feat:') || contains(github.event.head_commit.message, 'fix:')
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: update changelog for release'
title: 'chore: prepare release'
body: |
This PR prepares a new release with the following changes:
- Updated changelog
- Version bump (if applicable)
Please review and merge to trigger the release process.
branch: prepare-release
delete-branch: true
# Notification
notify:
name: Notify Results
runs-on: ubuntu-latest
if: always()
needs: [lint, unit-tests, integration-tests, e2e-kind, e2e-k3s, security-scan, helm-tests, docs-tests]
steps:
- name: Notify Slack on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
channel: '#kube-policies-ci'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
fields: repo,message,commit,author,action,eventName,ref,workflow
- name: Notify Slack on success
if: success() && github.ref == 'refs/heads/main'
uses: 8398a7/action-slack@v3
with:
status: success
channel: '#kube-policies-ci'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
fields: repo,message,commit,author,action,eventName,ref,workflow