name: Build Pipeline on: push: branches: - main - develop tags: - 'v*.*.*' pull_request: branches: - main - develop env: REGISTRY: ghcr.io IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }} jobs: docker-build: name: Docker Build (${{ matrix.service }}) runs-on: ubuntu-latest timeout-minutes: 30 strategy: matrix: service: - api_gateway - trading_service - backtesting_service - ml_training_service permissions: contents: read packages: write id-token: write steps: - name: Checkout Code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract Docker Metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.IMAGE_PREFIX }}/${{ matrix.service }} tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix={{branch}}- type=raw,value=latest,enable={{is_default_branch}} - name: Build and Push Docker Image uses: docker/build-push-action@v5 with: context: . file: services/${{ matrix.service }}/Dockerfile push: ${{ github.event_name != 'pull_request' }} 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 build-args: | SERVICE_NAME=${{ matrix.service }} GIT_COMMIT=${{ github.sha }} GIT_BRANCH=${{ github.ref_name }} BUILD_DATE=${{ github.event.head_commit.timestamp }} - name: Scan Docker Image (Trivy) uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }} format: 'sarif' output: 'trivy-results-${{ matrix.service }}.sarif' severity: 'CRITICAL,HIGH' exit-code: '0' # Don't fail build, just report - name: Upload Trivy Results to GitHub Security uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: 'trivy-results-${{ matrix.service }}.sarif' category: 'docker-${{ matrix.service }}' - name: Check for Critical Vulnerabilities run: | CRITICAL_COUNT=$(grep -c "CRITICAL" trivy-results-${{ matrix.service }}.sarif || true) HIGH_COUNT=$(grep -c "HIGH" trivy-results-${{ matrix.service }}.sarif || true) echo "::notice::Critical vulnerabilities: $CRITICAL_COUNT" echo "::notice::High vulnerabilities: $HIGH_COUNT" if [ "$CRITICAL_COUNT" -gt 0 ]; then echo "::error::Critical vulnerabilities found in ${{ matrix.service }}" exit 1 fi build-tli: name: Build TLI Client runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu artifact_name: tli asset_name: tli-linux-amd64 - os: macos-latest target: x86_64-apple-darwin artifact_name: tli asset_name: tli-macos-amd64 - os: windows-latest target: x86_64-pc-windows-msvc artifact_name: tli.exe asset_name: tli-windows-amd64.exe steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Rust Toolchain uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} - name: Cache Cargo Registry uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Build TLI run: cargo build --release -p tli --target ${{ matrix.target }} - name: Strip Binary (Linux/macOS) if: matrix.os != 'windows-latest' run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }} - name: Upload Binary uses: actions/upload-artifact@v4 with: name: ${{ matrix.asset_name }} path: target/${{ matrix.target }}/release/${{ matrix.artifact_name }} retention-days: 30 create-release: name: Create GitHub Release runs-on: ubuntu-latest needs: [docker-build, build-tli] if: startsWith(github.ref, 'refs/tags/v') timeout-minutes: 10 permissions: contents: write steps: - name: Checkout Code uses: actions/checkout@v4 - name: Download All Artifacts uses: actions/download-artifact@v4 with: path: artifacts/ - name: Create Release uses: softprops/action-gh-release@v1 with: files: artifacts/**/* generate_release_notes: true draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} push-to-ecr: name: Push to AWS ECR (Production) runs-on: ubuntu-latest needs: docker-build if: github.ref == 'refs/heads/main' && github.event_name == 'push' timeout-minutes: 15 strategy: matrix: service: - api_gateway - trading_service - backtesting_service - ml_training_service permissions: id-token: write contents: read steps: - name: Checkout Code uses: actions/checkout@v4 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Pull Image from GHCR run: | docker pull ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }} - name: Tag Image for ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} run: | docker tag \ ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }} \ $ECR_REGISTRY/${{ matrix.service }}:${{ github.sha }} docker tag \ ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }} \ $ECR_REGISTRY/${{ matrix.service }}:latest - name: Push Image to ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} run: | docker push $ECR_REGISTRY/${{ matrix.service }}:${{ github.sha }} docker push $ECR_REGISTRY/${{ matrix.service }}:latest summary: name: Build Summary runs-on: ubuntu-latest needs: [docker-build, build-tli] if: always() steps: - name: Generate Summary run: | echo "## Build Pipeline Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY echo "| Docker Build | ${{ needs.docker-build.result }} |" >> $GITHUB_STEP_SUMMARY echo "| TLI Build | ${{ needs.build-tli.result }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ "${{ github.ref }}" = "refs/heads/main" ]; then echo "### 🚀 Production Images Built" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Images available at:" >> $GITHUB_STEP_SUMMARY echo "- \`ghcr.io/${{ github.repository_owner }}/api_gateway:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY echo "- \`ghcr.io/${{ github.repository_owner }}/trading_service:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY echo "- \`ghcr.io/${{ github.repository_owner }}/backtesting_service:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY echo "- \`ghcr.io/${{ github.repository_owner }}/ml_training_service:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY fi - name: Check Overall Status if: contains(needs.*.result, 'failure') run: | echo "::error::Build pipeline failed - check job results above" exit 1