Skip to content
This repository was archived by the owner on Jul 8, 2026. It is now read-only.

Build and Test MarchProxy #144

Build and Test MarchProxy

Build and Test MarchProxy #144

name: Build and Test MarchProxy
on:
push:
branches: [ main, develop, 'feature/*', 'release/*' ]
tags: [ 'v*' ]
paths:
- 'proxy/**'
- 'manager/**'
- '.version'
- '.github/workflows/build-and-test.yml'
pull_request:
branches: [ main, develop ]
paths:
- 'proxy/**'
- 'manager/**'
- '.version'
schedule:
- cron: '0 2 * * 0' # Weekly cleanup on Sunday at 2 AM UTC
env:
REGISTRY: ghcr.io
IMAGE_NAME_MANAGER: ${{ github.repository }}/manager
IMAGE_NAME_PROXY: ${{ github.repository }}/proxy
jobs:
# Test Go proxy application
test-proxy:
runs-on: ubuntu-latest
outputs:
epoch64: ${{ steps.timestamp.outputs.epoch64 }}
version: ${{ steps.version.outputs.version }}
full_version: ${{ steps.version.outputs.full_version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate epoch64 timestamp
id: timestamp
run: |
EPOCH64=$(date +%s)
echo "epoch64=$EPOCH64" >> $GITHUB_OUTPUT
echo "Epoch64 timestamp: $EPOCH64"
- name: Detect version from .version file
id: version
run: |
if [ -f .version ]; then
VERSION=$(cat .version | tr -d '[:space:]')
SEMVER=$(echo "$VERSION" | cut -d'.' -f1-3)
else
VERSION="0.0.0"
SEMVER="0.0.0"
fi
echo "version=$SEMVER" >> $GITHUB_OUTPUT
echo "full_version=$VERSION" >> $GITHUB_OUTPUT
echo "Detected version: $SEMVER (full: $VERSION)"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install eBPF dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang llvm libbpf-dev linux-headers-$(uname -r) || true
- name: Install golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
working-directory: ./proxy
- name: Run golangci-lint
working-directory: ./proxy
run: |
golangci-lint run ./...
- name: Run gosec security scanner
working-directory: ./proxy
run: |
go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
gosec -fmt json -out gosec-report.json ./... || true
cat gosec-report.json
- name: Build proxy
working-directory: ./proxy
run: |
go mod tidy
go build -v ./...
- name: Test proxy
working-directory: ./proxy
run: |
go test -v ./...
- name: Run security scan
uses: securecodewarrior/github-action-add-sarif@v1
if: false # Disabled for now, enable when ready
with:
sarif-file: 'security-scan-results.sarif'
# Test Python manager application
test-manager:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: marchproxy_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Cache Python packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Python dependencies
working-directory: ./manager
run: |
pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov black flake8 mypy
- name: Lint Python code
working-directory: ./manager
run: |
black --check --diff .
flake8 . --max-line-length=100 --extend-ignore=E203,W503
mypy apps/marchproxy/ --ignore-missing-imports || true
- name: Test manager
working-directory: ./manager
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/marchproxy_test
JWT_SECRET: test-secret-key-for-ci
run: |
python -m pytest tests/ -v --cov=apps/marchproxy/ || true
# Note: Tests may not exist yet, so we allow failure
# Multi-architecture Docker builds
build-multi-arch:
needs: [test-proxy, test-manager]
runs-on: ubuntu-latest
strategy:
matrix:
component: [manager, proxy]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate build metadata
id: build_meta
run: |
EPOCH64=$(date +%s)
if [ -f .version ]; then
VERSION=$(cat .version | tr -d '[:space:]')
SEMVER=$(echo "$VERSION" | cut -d'.' -f1-3)
else
VERSION="0.0.0"
SEMVER="0.0.0"
fi
# Determine branch type for version tagging
BRANCH="${{ github.ref_name }}"
IS_MAIN=$([[ "$BRANCH" == "main" ]] && echo "true" || echo "false")
IS_RELEASE=$([[ "$BRANCH" =~ ^release/ ]] && echo "true" || echo "false")
IS_TAG=$([[ "${{ github.ref }}" =~ ^refs/tags/ ]] && echo "true" || echo "false")
IS_DEVELOP=$([[ "$BRANCH" == "develop" ]] && echo "true" || echo "false")
echo "epoch64=$EPOCH64" >> $GITHUB_OUTPUT
echo "version=$SEMVER" >> $GITHUB_OUTPUT
echo "full_version=$VERSION" >> $GITHUB_OUTPUT
echo "is_main=$IS_MAIN" >> $GITHUB_OUTPUT
echo "is_release=$IS_RELEASE" >> $GITHUB_OUTPUT
echo "is_tag=$IS_TAG" >> $GITHUB_OUTPUT
echo "is_develop=$IS_DEVELOP" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ matrix.component == 'manager' && env.IMAGE_NAME_MANAGER || env.IMAGE_NAME_PROXY }}
tags: |
# Development builds with epoch64 timestamps
type=raw,value=alpha-${{ steps.build_meta.outputs.epoch64 }},enable=${{ !fromJSON(steps.build_meta.outputs.is_main) && !fromJSON(steps.build_meta.outputs.is_tag) }}
type=raw,value=beta-${{ steps.build_meta.outputs.epoch64 }},enable=${{ fromJSON(steps.build_meta.outputs.is_main) && !fromJSON(steps.build_meta.outputs.is_tag) }}
# Version-based tags on version changes (conditional alpha/beta)
type=raw,value=v${{ steps.build_meta.outputs.version }}-alpha,enable=${{ !fromJSON(steps.build_meta.outputs.is_main) && !fromJSON(steps.build_meta.outputs.is_tag) }}
type=raw,value=v${{ steps.build_meta.outputs.version }}-beta,enable=${{ fromJSON(steps.build_meta.outputs.is_main) && !fromJSON(steps.build_meta.outputs.is_tag) }}
# Release tags for tagged versions
type=semver,pattern={{version}},enable=${{ fromJSON(steps.build_meta.outputs.is_tag) }}
type=raw,value=latest,enable=${{ fromJSON(steps.build_meta.outputs.is_tag) }}
# Additional metadata tags
type=ref,event=pr,prefix=pr-
type=sha,prefix={{branch}}-,format=short
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/${{ matrix.component }}/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
target: production
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
VCS_REF=${{ github.sha }}
# Integration testing with multi-arch images
integration-test:
needs: [build-multi-arch]
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
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: Run integration tests
run: |
# Update docker-compose to use built images
export MANAGER_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_MANAGER }}:${{ github.ref_name }}"
export PROXY_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PROXY }}:${{ github.ref_name }}"
# Start services
docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d
# Wait for services to be healthy
timeout 300s bash -c 'until docker-compose ps | grep -q "healthy"; do sleep 10; done'
# Run basic connectivity tests
curl -f http://localhost:8000/healthz || exit 1
curl -f http://localhost:8081/healthz || exit 1
# Cleanup
docker-compose down -v
# Security scanning
security-scan:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.35.0
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@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
# Release creation (only on tags)
release:
needs: [integration-test, security-scan]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate release notes
id: release_notes
run: |
# Extract version from tag
VERSION=${GITHUB_REF#refs/tags/}
echo "version=$VERSION" >> $GITHUB_OUTPUT
# Generate release notes (basic version)
cat > release_notes.md << EOF
# MarchProxy $VERSION
## Docker Images
### Manager (Multi-Architecture)
- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME_MANAGER }}:$VERSION\`
- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME_MANAGER }}:latest\`
### Proxy (Multi-Architecture)
- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PROXY }}:$VERSION\`
- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PROXY }}:latest\`
## Supported Architectures
- linux/amd64 (Intel/AMD 64-bit)
- linux/arm64 (ARM 64-bit, Apple Silicon, AWS Graviton)
- linux/arm/v7 (ARM 32-bit, Raspberry Pi)
## Quick Start
\`\`\`bash
# Download docker-compose.yml
curl -L -O https://raw.githubusercontent.com/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')/$VERSION/docker-compose.yml
# Start MarchProxy
docker-compose up -d
# Access web interface
open http://localhost:8000
\`\`\`
## What's Changed
* See commit history for detailed changes
* Full system capabilities: proxy management, clustering, authentication
* Enterprise features available with valid license
EOF
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
body_path: release_notes.md
draft: false
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Cleanup old images (runs weekly)
cleanup:
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
steps:
- name: Cleanup old container images
uses: actions/github-script@v7
with:
script: |
const packages = await github.rest.packages.listPackagesForOrganization({
org: context.repo.owner,
package_type: 'container',
visibility: 'private'
});
for (const pkg of packages.data) {
if (pkg.name.includes('marchproxy')) {
const versions = await github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg({
org: context.repo.owner,
package_type: 'container',
package_name: pkg.name
});
// Keep latest 10 versions, delete older ones
const oldVersions = versions.data.slice(10);
for (const version of oldVersions) {
await github.rest.packages.deletePackageVersionForOrg({
org: context.repo.owner,
package_type: 'container',
package_name: pkg.name,
package_version_id: version.id
});
}
}
}