updating workflows #123
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Test MarchProxy | ||
| on: | ||
| push: | ||
| branches: [ main, develop, 'feature/*', 'release/*' ] | ||
| tags: [ 'v*' ] | ||
| pull_request: | ||
| branches: [ main, develop ] | ||
| 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 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - name: Set up Go | ||
| uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | ||
| with: | ||
| go-version: '1.22' | ||
| - name: Cache Go modules | ||
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | ||
| 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: 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: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 | ||
| 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | ||
| with: | ||
| python-version: '3.12' | ||
| - name: Cache Python packages | ||
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | ||
| 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 | ||
| # Build single arch per platform (truly parallel) | ||
| build-platform: | ||
| needs: [test-proxy, test-manager] | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| component: [manager, proxy] | ||
| platform: [linux/amd64, linux/arm64] | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 | ||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | ||
| - name: Log in to Container Registry | ||
| if: github.event_name != 'pull_request' | ||
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Generate platform tag | ||
| id: platform-tag | ||
| env: | ||
| PLATFORM: ${{ matrix.platform }} | ||
| run: | | ||
| ARCH=$(echo "${PLATFORM}" | cut -d'/' -f2) | ||
| echo "arch=${ARCH}" >> $GITHUB_OUTPUT | ||
| - name: Extract image name | ||
| id: image-name | ||
| run: | | ||
| if [ "${{ matrix.component }}" = "manager" ]; then | ||
| echo "name=${{ env.IMAGE_NAME_MANAGER }}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "name=${{ env.IMAGE_NAME_PROXY }}" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Extract version and build tag | ||
| id: version | ||
| env: | ||
| REF: ${{ github.ref }} | ||
| REF_NAME: ${{ github.ref_name }} | ||
| run: | | ||
| VERSION="${REF_NAME}" | ||
| EPOCH=$(date +%s) | ||
| if [[ "${REF}" == "refs/tags/v"* ]]; then | ||
| # Release tag: v1.2.3 | ||
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | ||
| elif [[ "${REF}" == "refs/heads/release/"* ]]; then | ||
| # Release branch: release/v1.x.x -> v1.x.x-beta | ||
| echo "version=${VERSION}-beta" >> $GITHUB_OUTPUT | ||
| echo "epoch-tag=beta-${EPOCH}" >> $GITHUB_OUTPUT | ||
| elif [[ "${REF}" == "refs/heads/main" ]]; then | ||
| # Main branch: gamma | ||
| echo "version=gamma-${EPOCH}" >> $GITHUB_OUTPUT | ||
| else | ||
| # Feature/develop branches: alpha | ||
| echo "version=alpha-${EPOCH}" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Build and push Docker image (platform-specific) | ||
| uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 | ||
| with: | ||
| context: . | ||
| file: ./docker/${{ matrix.component }}/Dockerfile | ||
| platforms: ${{ matrix.platform }} | ||
| push: ${{ github.event_name != 'pull_request' }} | ||
| tags: ${{ env.REGISTRY }}/${{ steps.image-name.outputs.name }}:${{ steps.version.outputs.version }}-${{ steps.platform-tag.outputs.arch }} | ||
| labels: | | ||
| org.opencontainers.image.version=${{ steps.version.outputs.version }} | ||
| org.opencontainers.image.revision=${{ github.sha }} | ||
| target: production | ||
| cache-from: type=gha,scope=${{ matrix.component }}-${{ matrix.platform }} | ||
| cache-to: type=gha,scope=${{ matrix.component }}-${{ matrix.platform }},mode=max | ||
| build-args: | | ||
| VERSION=${{ github.ref_name }} | ||
| VCS_REF=${{ github.sha }} | ||
| # Merge multi-arch manifests | ||
| merge-manifests: | ||
| needs: build-platform | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| component: [manager, proxy] | ||
| if: github.event_name != 'pull_request' | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - name: Log in to Container Registry | ||
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | ||
| - name: Extract image name | ||
| id: image-name | ||
| run: | | ||
| if [ "${{ matrix.component }}" = "manager" ]; then | ||
| echo "name=${{ env.IMAGE_NAME_MANAGER }}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "name=${{ env.IMAGE_NAME_PROXY }}" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Determine manifest tags and source version | ||
| id: manifest | ||
| env: | ||
| REF: ${{ github.ref }} | ||
| REF_NAME: ${{ github.ref_name }} | ||
| REGISTRY: ${{ env.REGISTRY }} | ||
| IMAGE_NAME: ${{ steps.image-name.outputs.name }} | ||
| run: | | ||
| VERSION="${REF_NAME}" | ||
| EPOCH=$(date +%s) | ||
| TAGS="" | ||
| SOURCE_VERSION="" | ||
| if [[ "${REF}" == "refs/tags/v"* ]]; then | ||
| # Release tag: v1.2.3 -> tag as v1.2.3 and latest | ||
| SOURCE_VERSION="${VERSION}" | ||
| TAGS="${REGISTRY}/${IMAGE_NAME}:${VERSION} ${REGISTRY}/${IMAGE_NAME}:latest" | ||
| elif [[ "${REF}" == "refs/heads/release/"* ]]; then | ||
| # Release branch: release/v1.x.x -> tag as v1.x.x-beta and beta-<epoch> | ||
| SOURCE_VERSION="${VERSION}-beta" | ||
| TAGS="${REGISTRY}/${IMAGE_NAME}:${VERSION}-beta ${REGISTRY}/${IMAGE_NAME}:beta-${EPOCH}" | ||
| elif [[ "${REF}" == "refs/heads/main" ]]; then | ||
| # Main branch: gamma-<epoch> | ||
| SOURCE_VERSION="gamma-${EPOCH}" | ||
| TAGS="${REGISTRY}/${IMAGE_NAME}:gamma-${EPOCH}" | ||
| else | ||
| # Feature/develop branches: alpha-<epoch> | ||
| SOURCE_VERSION="alpha-${EPOCH}" | ||
| TAGS="${REGISTRY}/${IMAGE_NAME}:alpha-${EPOCH}" | ||
| fi | ||
| echo "source-version=${SOURCE_VERSION}" >> $GITHUB_OUTPUT | ||
| echo "tags=${TAGS}" >> $GITHUB_OUTPUT | ||
| - name: Create and push manifest | ||
| env: | ||
| REGISTRY: ${{ env.REGISTRY }} | ||
| IMAGE_NAME: ${{ steps.image-name.outputs.name }} | ||
| SOURCE_VERSION: ${{ steps.manifest.outputs.source-version }} | ||
| TAGS: ${{ steps.manifest.outputs.tags }} | ||
| run: | | ||
| # Create manifest list from amd64 and arm64 images | ||
| docker buildx imagetools create -t "${REGISTRY}/${IMAGE_NAME}:${SOURCE_VERSION}" \ | ||
| "${REGISTRY}/${IMAGE_NAME}:${SOURCE_VERSION}-amd64" \ | ||
| "${REGISTRY}/${IMAGE_NAME}:${SOURCE_VERSION}-arm64" | ||
| # Add additional tags | ||
| for TAG in ${TAGS}; do | ||
| if [ "${TAG}" != "${REGISTRY}/${IMAGE_NAME}:${SOURCE_VERSION}" ]; then | ||
| docker buildx imagetools create -t "${TAG}" \ | ||
| "${REGISTRY}/${IMAGE_NAME}:${SOURCE_VERSION}-amd64" \ | ||
| "${REGISTRY}/${IMAGE_NAME}:${SOURCE_VERSION}-arm64" | ||
| fi | ||
| done | ||
| # Integration testing with multi-arch images | ||
| integration-test: | ||
| needs: [merge-manifests] | ||
| runs-on: ubuntu-latest | ||
| if: github.event_name != 'pull_request' | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | ||
| - name: Log in to Container Registry | ||
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | ||
| 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - name: Run Trivy vulnerability scanner | ||
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.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@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 | ||
| 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| - 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@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3 | ||
| 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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 | ||
| 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 | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| # Schedule cleanup weekly | ||
| schedule: | ||
| - cron: '0 2 * * 0' # Sunday at 2 AM UTC | ||