Explicitly configure Docker Hub registry and Dockerfile path #91
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: QUIC Test Pipeline | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| tags: [ 'v*' ] | |
| pull_request: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to release (e.g., v1.0.0)' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| GO_VERSION: '1.25' | |
| jobs: | |
| # Stage 1: Lint | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - 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 system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libnuma-dev clang | |
| - name: Build FEC library | |
| run: | | |
| cd internal/fec | |
| make clean | |
| make | |
| cd ../.. | |
| - name: Download dependencies | |
| run: go mod download | |
| - name: Verify go.mod tidy | |
| run: | | |
| go mod tidy | |
| git diff --exit-code -- go.mod go.sum | |
| - name: Run go vet | |
| run: go vet ./... | |
| - name: Install and run golangci-lint | |
| run: | | |
| go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8 | |
| golangci-lint run --timeout=10m | |
| # Stage 2: Test | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - 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 system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libnuma-dev clang | |
| - name: Build FEC library | |
| run: | | |
| cd internal/fec | |
| make clean | |
| make | |
| cd ../.. | |
| - name: Download dependencies | |
| run: go mod download | |
| - name: Run tests | |
| run: go test -covermode=atomic -coverprofile=coverage.out ./... | |
| - name: Upload coverage | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: coverage.out | |
| # Stage 3: Security | |
| security: | |
| name: Security | |
| runs-on: ubuntu-latest | |
| needs: test | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libnuma-dev clang | |
| - name: Build FEC library | |
| run: | | |
| cd internal/fec | |
| make clean | |
| make | |
| cd ../.. | |
| - name: Download dependencies | |
| run: go mod download | |
| - name: Run govulncheck | |
| run: | | |
| go install golang.org/x/vuln/cmd/govulncheck@latest | |
| govulncheck ./... || echo "Vulnerabilities found but continuing" | |
| - name: Run gosec | |
| uses: securego/gosec@v2.22.0 | |
| continue-on-error: true | |
| with: | |
| args: '-fmt sarif -out gosec.sarif -timeout 5m ./...' | |
| - name: Upload SARIF | |
| uses: github/codeql-action/upload-sarif@v3 | |
| if: always() && hashFiles('gosec.sarif') != '' | |
| with: | |
| sarif_file: gosec.sarif | |
| # Stage 4: Build Test | |
| build-test: | |
| name: Build Test | |
| runs-on: ubuntu-latest | |
| needs: security | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libnuma-dev clang | |
| - name: Build FEC library | |
| run: | | |
| cd internal/fec | |
| make clean | |
| make | |
| cd ../.. | |
| - name: Test compilation | |
| run: | | |
| echo "Testing that code compiles successfully..." | |
| go build -v -o build/quic-test . | |
| go build -v -o build/quic-client ./cmd/quic-client | |
| go build -v -o build/quic-server ./cmd/quic-server | |
| go build -v -o build/dashboard ./cmd/dashboard | |
| echo "✅ All binaries compile successfully" | |
| - name: Test Makefile | |
| run: | | |
| make build | |
| make test | |
| echo "✅ Makefile targets work correctly" | |
| - name: Clean up test binaries | |
| run: rm -rf build/ | |
| # Stage 5: Version Check & Auto-Tagging | |
| version-check: | |
| name: Version Check & Auto-Tag | |
| runs-on: ubuntu-latest | |
| needs: build-test | |
| permissions: | |
| contents: write | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| should_release: ${{ steps.version.outputs.should_release }} | |
| is_new_version: ${{ steps.version.outputs.is_new_version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libnuma-dev clang | |
| - name: Build FEC library | |
| run: | | |
| cd internal/fec | |
| make clean | |
| make | |
| cd ../.. | |
| - name: Read version from tag.txt | |
| id: version | |
| run: | | |
| if [[ ! -f "tag.txt" ]]; then | |
| echo "❌ tag.txt file not found!" | |
| exit 1 | |
| fi | |
| VERSION=$(cat tag.txt | tr -d '\n\r' | xargs) | |
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "Version from tag.txt: ${VERSION}" | |
| # Check if this is a valid version format | |
| if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "❌ Invalid version format in tag.txt: ${VERSION}" | |
| echo "Expected format: v1.2.3" | |
| exit 1 | |
| fi | |
| # Check if this version already exists as a tag on GitHub | |
| if git ls-remote --tags origin | grep -q "refs/tags/${VERSION}$"; then | |
| echo "📌 Tag ${VERSION} already exists on GitHub" | |
| echo "should_release=false" >> $GITHUB_OUTPUT | |
| echo "is_new_version=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "🆕 New version detected: ${VERSION}" | |
| echo "should_release=true" >> $GITHUB_OUTPUT | |
| echo "is_new_version=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Build test with version | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| go mod download | |
| go build -v -o build/quic-test . | |
| echo "Testing binary with version ${VERSION}:" | |
| ./build/quic-test --version | grep "${VERSION}" | |
| rm -rf build/ | |
| - name: Create Git tag | |
| if: steps.version.outputs.is_new_version == 'true' && github.ref == 'refs/heads/main' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| echo "🏷️ Creating tag ${VERSION}..." | |
| # Configure git with GitHub Actions bot identity | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Create annotated tag | |
| git tag -a "${VERSION}" -m "Release ${VERSION} | |
| Automatic release created from tag.txt version bump | |
| This release was automatically created when tag.txt was updated to ${VERSION}. | |
| Generated by GitHub Actions pipeline." | |
| # Set up authentication for push using token | |
| git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" | |
| echo "📤 Pushing tag ${VERSION}..." | |
| git push origin "refs/tags/${VERSION}" | |
| echo "✅ Tag ${VERSION} created and pushed successfully" | |
| # Stage 6: Release Build & Publish (only for new versions) | |
| release: | |
| name: Release Build & Publish | |
| runs-on: ubuntu-latest | |
| needs: version-check | |
| if: needs.version-check.outputs.should_release == 'true' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Wait for tag (if just created) | |
| if: needs.version-check.outputs.is_new_version == 'true' | |
| run: | | |
| VERSION="${{ needs.version-check.outputs.version }}" | |
| echo "⏳ Waiting for tag ${VERSION} to be available..." | |
| # Wait up to 30 seconds for the tag to be available | |
| for i in {1..6}; do | |
| git fetch --tags | |
| if git tag -l | grep -q "^${VERSION}$"; then | |
| echo "✅ Tag ${VERSION} is now available" | |
| break | |
| fi | |
| echo "⏳ Attempt $i/6: Tag not yet available, waiting..." | |
| sleep 5 | |
| done | |
| - name: Run GoReleaser | |
| uses: goreleaser/goreleaser-action@v6 | |
| with: | |
| version: latest | |
| args: release --clean | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ needs.version-check.outputs.version }} | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-artifacts-${{ needs.version-check.outputs.version }} | |
| path: | | |
| dist/*.zip | |
| dist/*.tar.gz | |
| dist/checksums.txt | |
| retention-days: 90 | |
| # Summary job | |
| pipeline-success: | |
| name: Pipeline Success | |
| runs-on: ubuntu-latest | |
| needs: [lint, test, security, build-test, version-check, release] | |
| if: always() | |
| steps: | |
| - name: Check pipeline status | |
| run: | | |
| echo "=== Pipeline Summary ===" | |
| echo "Lint: ${{ needs.lint.result }}" | |
| echo "Test: ${{ needs.test.result }}" | |
| echo "Security: ${{ needs.security.result }}" | |
| echo "Build Test: ${{ needs.build-test.result }}" | |
| echo "Version Check: ${{ needs.version-check.result }}" | |
| # Show version info from tag.txt | |
| if [[ "${{ needs.version-check.outputs.version }}" != "" ]]; then | |
| echo "Version: ${{ needs.version-check.outputs.version }}" | |
| echo "🆕 New Version: ${{ needs.version-check.outputs.is_new_version }}" | |
| echo "Should Release: ${{ needs.version-check.outputs.should_release }}" | |
| fi | |
| if [[ "${{ needs.release.result }}" != "skipped" ]]; then | |
| echo "Release: ${{ needs.release.result }}" | |
| echo "Release pipeline completed!" | |
| else | |
| echo "✅ CI pipeline completed (no new version)!" | |
| fi | |
| # Check if any required job failed | |
| if [[ "${{ needs.lint.result }}" != "success" || | |
| "${{ needs.test.result }}" != "success" || | |
| "${{ needs.security.result }}" != "success" || | |
| "${{ needs.build-test.result }}" != "success" || | |
| "${{ needs.version-check.result }}" != "success" ]]; then | |
| echo "❌ Pipeline failed!" | |
| exit 1 | |
| fi | |
| # Check release job if it ran | |
| if [[ "${{ needs.release.result }}" != "skipped" ]]; then | |
| if [[ "${{ needs.release.result }}" != "success" ]]; then | |
| echo "❌ Release failed!" | |
| exit 1 | |
| fi | |
| fi | |
| echo "✅ Pipeline successful!" | |
| # Stage 7: Docker Build & Push (only for new versions/tags) | |
| docker-build: | |
| name: Docker Build & Push | |
| runs-on: ubuntu-latest | |
| needs: version-check | |
| if: needs.version-check.outputs.should_release == 'true' || startsWith(github.ref, 'refs/tags/') | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/quic-test | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| platforms: linux/amd64,linux/arm64 | |
| - name: Update Docker Hub description | |
| if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') | |
| uses: peter-evans/dockerhub-description@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| repository: ${{ secrets.DOCKERHUB_USERNAME }}/quic-test | |
| short-description: "Professional QUIC Protocol Testing Platform" | |
| readme-filepath: ./readme.md |