diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f798c762..8288bbc4 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -12,13 +12,13 @@ on: branches: - main - development - - feature/beta-release + - 'feature/**' # Note: Tags are handled by release-goreleaser.yml to avoid duplicate builds pull_request: branches: - main - development - - feature/beta-release + - 'feature/**' workflow_dispatch: workflow_call: @@ -74,12 +74,12 @@ jobs: if echo "$HEAD_MSG" | grep -Ei '^chore:' >/dev/null 2>&1; then should_skip=true; fi if echo "$pr_title" | grep -Ei '^chore\(deps' >/dev/null 2>&1; then should_skip=true; fi if echo "$pr_title" | grep -Ei '^chore:' >/dev/null 2>&1; then should_skip=true; fi - # Always build on beta-release branch to ensure artifacts for testing + # Always build on feature branches to ensure artifacts for testing # For PRs: github.ref is refs/pull/N/merge, so check github.head_ref instead # For pushes: github.ref is refs/heads/branch-name - if [[ "$REF" == "refs/heads/feature/beta-release" ]] || [[ "$HEAD_REF" == "feature/beta-release" ]]; then + if [[ "$REF" == refs/heads/feature/* ]] || [[ "$HEAD_REF" == feature/* ]]; then should_skip=false - echo "Force building on beta-release branch" + echo "Force building on feature branch" fi echo "skip_build=$should_skip" >> $GITHUB_OUTPUT @@ -115,7 +115,7 @@ jobs: tags: | type=raw,value=latest,enable={{is_default_branch}} type=raw,value=dev,enable=${{ github.ref == 'refs/heads/development' }} - type=raw,value=beta,enable=${{ github.ref == 'refs/heads/feature/beta-release' }} + type=ref,event=branch,enable=${{ startsWith(github.ref, 'refs/heads/feature/') }} type=raw,value=pr-${{ github.event.pull_request.number }},enable=${{ github.event_name == 'pull_request' }} type=sha,format=short,enable=${{ github.event_name != 'pull_request' }} - name: Build and push Docker image @@ -153,7 +153,7 @@ jobs: # 2. Image doesn't exist locally after build # 3. Artifact creation fails - name: Save Docker Image as Artifact - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || github.event_name == 'push' run: | # Extract the first tag from metadata action (PR tag) IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n 1) @@ -184,10 +184,10 @@ jobs: ls -lh /tmp/charon-pr-image.tar - name: Upload Image Artifact - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || github.event_name == 'push' uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: - name: pr-image-${{ github.event.pull_request.number }} + name: ${{ github.event_name == 'pull_request' && format('pr-image-{0}', github.event.pull_request.number) || 'push-image' }} path: /tmp/charon-pr-image.tar retention-days: 1 # Only needed for workflow duration diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index b1003a54..4cc9f046 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -24,10 +24,11 @@ jobs: name: E2E Tests runs-on: ubuntu-latest timeout-minutes: 20 - # Only run for PRs or manual dispatch + # Run for: manual dispatch, PR builds, or any push builds from docker-build if: >- github.event_name == 'workflow_dispatch' || - (github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success') + ((github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'push') && + github.event.workflow_run.conclusion == 'success') env: CHARON_ENV: development @@ -75,14 +76,27 @@ jobs: echo "pr_number=" >> "$GITHUB_OUTPUT" fi + # Check if this is a push event (not a PR) + if [[ "${{ github.event.workflow_run.event }}" == "push" ]]; then + echo "is_push=true" >> "$GITHUB_OUTPUT" + echo "✅ Detected push build from branch: ${{ github.event.workflow_run.head_branch }}" + else + echo "is_push=false" >> "$GITHUB_OUTPUT" + fi + - name: Check for PR image artifact id: check-artifact - if: steps.pr-info.outputs.pr_number != '' + if: steps.pr-info.outputs.pr_number != '' || steps.pr-info.outputs.is_push == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}" - ARTIFACT_NAME="pr-image-${PR_NUMBER}" + # Determine artifact name based on event type + if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then + ARTIFACT_NAME="push-image" + else + PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}" + ARTIFACT_NAME="pr-image-${PR_NUMBER}" + fi RUN_ID="${{ github.event.workflow_run.id }}" echo "🔍 Checking for artifact: ${ARTIFACT_NAME}" @@ -122,7 +136,7 @@ jobs: fi - name: Skip if no artifact - if: steps.pr-info.outputs.pr_number == '' || steps.check-artifact.outputs.artifact_exists != 'true' + if: (steps.pr-info.outputs.pr_number == '' && steps.pr-info.outputs.is_push != 'true') || steps.check-artifact.outputs.artifact_exists != 'true' run: | echo "â„šī¸ Skipping Playwright tests - no PR image artifact available" echo "This is expected for:" @@ -136,7 +150,7 @@ jobs: # actions/download-artifact v4.1.8 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: pr-image-${{ steps.pr-info.outputs.pr_number }} + name: ${{ steps.pr-info.outputs.is_push == 'true' && 'push-image' || format('pr-image-{0}', steps.pr-info.outputs.pr_number) }} run-id: ${{ steps.check-artifact.outputs.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} @@ -152,13 +166,23 @@ jobs: if: steps.check-artifact.outputs.artifact_exists == 'true' run: | echo "🚀 Starting Charon container..." + + # Normalize image name (GitHub lowercases repository owner names in GHCR) + IMAGE_NAME=$(echo "${{ github.repository_owner }}/charon" | tr '[:upper:]' '[:lower:]') + if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then + IMAGE_REF="ghcr.io/${IMAGE_NAME}:${{ github.event.workflow_run.head_branch }}" + else + IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}" + fi + + echo "đŸ“Ļ Starting container with image: ${IMAGE_REF}" docker run -d \ --name charon-test \ -p 8080:8080 \ -e CHARON_ENV="${CHARON_ENV}" \ -e CHARON_DEBUG="${CHARON_DEBUG}" \ -e CHARON_ENCRYPTION_KEY="${CHARON_ENCRYPTION_KEY}" \ - charon:pr-${{ steps.pr-info.outputs.pr_number }} + "${IMAGE_REF}" echo "✅ Container started" @@ -213,7 +237,7 @@ jobs: # actions/upload-artifact v4.4.3 uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 with: - name: playwright-report-pr-${{ steps.pr-info.outputs.pr_number }} + name: ${{ steps.pr-info.outputs.is_push == 'true' && format('playwright-report-{0}', github.event.workflow_run.head_branch) || format('playwright-report-pr-{0}', steps.pr-info.outputs.pr_number) }} path: playwright-report/ retention-days: 14 diff --git a/.github/workflows/security-pr.yml b/.github/workflows/security-pr.yml index 95f8b159..712b5eea 100644 --- a/.github/workflows/security-pr.yml +++ b/.github/workflows/security-pr.yml @@ -25,10 +25,11 @@ jobs: name: Trivy Binary Scan runs-on: ubuntu-latest timeout-minutes: 10 - # Only run for PRs or manual dispatch + # Run for: manual dispatch, PR builds, or any push builds from docker-build if: >- github.event_name == 'workflow_dispatch' || - (github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success') + ((github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'push') && + github.event.workflow_run.conclusion == 'success') permissions: contents: read @@ -77,14 +78,27 @@ jobs: echo "pr_number=" >> "$GITHUB_OUTPUT" fi + # Check if this is a push event (not a PR) + if [[ "${{ github.event.workflow_run.event }}" == "push" ]]; then + echo "is_push=true" >> "$GITHUB_OUTPUT" + echo "✅ Detected push build from branch: ${{ github.event.workflow_run.head_branch }}" + else + echo "is_push=false" >> "$GITHUB_OUTPUT" + fi + - name: Check for PR image artifact id: check-artifact - if: steps.pr-info.outputs.pr_number != '' + if: steps.pr-info.outputs.pr_number != '' || steps.pr-info.outputs.is_push == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}" - ARTIFACT_NAME="pr-image-${PR_NUMBER}" + # Determine artifact name based on event type + if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then + ARTIFACT_NAME="push-image" + else + PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}" + ARTIFACT_NAME="pr-image-${PR_NUMBER}" + fi RUN_ID="${{ github.event.workflow_run.id }}" echo "🔍 Checking for artifact: ${ARTIFACT_NAME}" @@ -124,7 +138,7 @@ jobs: fi - name: Skip if no artifact - if: steps.pr-info.outputs.pr_number == '' || steps.check-artifact.outputs.artifact_exists != 'true' + if: (steps.pr-info.outputs.pr_number == '' && steps.pr-info.outputs.is_push != 'true') || steps.check-artifact.outputs.artifact_exists != 'true' run: | echo "â„šī¸ Skipping security scan - no PR image artifact available" echo "This is expected for:" @@ -138,7 +152,7 @@ jobs: # actions/download-artifact v4.1.8 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: pr-image-${{ steps.pr-info.outputs.pr_number }} + name: ${{ steps.pr-info.outputs.is_push == 'true' && 'push-image' || format('pr-image-{0}', steps.pr-info.outputs.pr_number) }} run-id: ${{ steps.check-artifact.outputs.run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} @@ -156,7 +170,11 @@ jobs: run: | # Normalize image name for reference IMAGE_NAME=$(echo "${{ github.repository_owner }}/charon" | tr '[:upper:]' '[:lower:]') - IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}" + if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then + IMAGE_REF="ghcr.io/${IMAGE_NAME}:${{ github.event.workflow_run.head_branch }}" + else + IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}" + fi echo "🔍 Extracting binary from: ${IMAGE_REF}" @@ -199,7 +217,7 @@ jobs: uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 with: sarif_file: 'trivy-binary-results.sarif' - category: security-scan-pr-${{ steps.pr-info.outputs.pr_number }} + category: ${{ steps.pr-info.outputs.is_push == 'true' && format('security-scan-{0}', github.event.workflow_run.head_branch) || format('security-scan-pr-{0}', steps.pr-info.outputs.pr_number) }} continue-on-error: true - name: Run Trivy filesystem scan (fail on CRITICAL/HIGH) @@ -218,7 +236,7 @@ jobs: # actions/upload-artifact v4.4.3 uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 with: - name: security-scan-pr-${{ steps.pr-info.outputs.pr_number }} + name: ${{ steps.pr-info.outputs.is_push == 'true' && format('security-scan-{0}', github.event.workflow_run.head_branch) || format('security-scan-pr-{0}', steps.pr-info.outputs.pr_number) }} path: | trivy-binary-results.sarif retention-days: 14 @@ -226,7 +244,11 @@ jobs: - name: Create job summary if: always() && steps.check-artifact.outputs.artifact_exists == 'true' run: | - echo "## 🔒 Security Scan Results - PR #${{ steps.pr-info.outputs.pr_number }}" >> $GITHUB_STEP_SUMMARY + if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then + echo "## 🔒 Security Scan Results - Branch: ${{ github.event.workflow_run.head_branch }}" >> $GITHUB_STEP_SUMMARY + else + echo "## 🔒 Security Scan Results - PR #${{ steps.pr-info.outputs.pr_number }}" >> $GITHUB_STEP_SUMMARY + fi echo "" >> $GITHUB_STEP_SUMMARY echo "**Scan Type**: Trivy Filesystem Scan" >> $GITHUB_STEP_SUMMARY echo "**Target**: \`/app/charon\` binary" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/supply-chain-pr.yml b/.github/workflows/supply-chain-pr.yml index 90875d1a..2fb99027 100644 --- a/.github/workflows/supply-chain-pr.yml +++ b/.github/workflows/supply-chain-pr.yml @@ -34,10 +34,10 @@ jobs: name: Verify Supply Chain runs-on: ubuntu-latest timeout-minutes: 15 - # Only run for PRs (workflow_run from PR or manual dispatch with PR number) + # Run for: manual dispatch, PR builds, or any push builds from docker-build if: > github.event_name == 'workflow_dispatch' || - (github.event.workflow_run.event == 'pull_request' && + ((github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'push') && github.event.workflow_run.conclusion == 'success') steps: @@ -92,20 +92,32 @@ jobs: if [[ -z "${PR_NUMBER}" ]]; then echo "âš ī¸ Could not find PR number for this workflow run" echo "pr_number=" >> "$GITHUB_OUTPUT" - exit 0 + else + echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" + echo "✅ Found PR number: ${PR_NUMBER}" fi - echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" - echo "✅ Found PR number: ${PR_NUMBER}" + # Check if this is a push event (not a PR) + if [[ "${{ github.event.workflow_run.event }}" == "push" ]]; then + echo "is_push=true" >> "$GITHUB_OUTPUT" + echo "✅ Detected push build from branch: ${{ github.event.workflow_run.head_branch }}" + else + echo "is_push=false" >> "$GITHUB_OUTPUT" + fi - name: Check for PR image artifact id: check-artifact - if: steps.pr-number.outputs.pr_number != '' + if: steps.pr-number.outputs.pr_number != '' || steps.pr-number.outputs.is_push == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR_NUMBER="${{ steps.pr-number.outputs.pr_number }}" - ARTIFACT_NAME="pr-image-${PR_NUMBER}" + # Determine artifact name based on event type + if [[ "${{ steps.pr-number.outputs.is_push }}" == "true" ]]; then + ARTIFACT_NAME="push-image" + else + PR_NUMBER="${{ steps.pr-number.outputs.pr_number }}" + ARTIFACT_NAME="pr-image-${PR_NUMBER}" + fi RUN_ID="${{ github.event.workflow_run.id }}" echo "🔍 Looking for artifact: ${ARTIFACT_NAME}" @@ -140,7 +152,7 @@ jobs: echo "✅ Found artifact: ${ARTIFACT_NAME} (ID: ${ARTIFACT_ID})" - name: Skip if no artifact - if: steps.check-artifact.outputs.artifact_found != 'true' + if: (steps.pr-number.outputs.pr_number == '' && steps.pr-number.outputs.is_push != 'true') || steps.check-artifact.outputs.artifact_found != 'true' run: | echo "â„šī¸ No PR image artifact found - skipping supply chain verification" echo "This is expected if the Docker build did not produce an artifact for this PR" @@ -285,14 +297,14 @@ jobs: # actions/upload-artifact v4.6.0 uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 with: - name: supply-chain-pr-${{ steps.pr-number.outputs.pr_number }} + name: ${{ steps.pr-number.outputs.is_push == 'true' && format('supply-chain-{0}', github.event.workflow_run.head_branch) || format('supply-chain-pr-{0}', steps.pr-number.outputs.pr_number) }} path: | sbom.cyclonedx.json grype-results.json retention-days: 14 - name: Comment on PR - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.check-artifact.outputs.artifact_found == 'true' && steps.pr-number.outputs.is_push != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: |