diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9a6b8987..9f5e5c9a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -5,18 +5,14 @@ on: branches: - main - development - - feature/** - - hotfix/** - paths: - - 'backend/**' + - 'feature/**' + - 'hotfix/**' pull_request: branches: - main - development - - feature/** - - hotfix/** - paths: - - 'backend/**' + - 'feature/**' + - 'hotfix/**' workflow_dispatch: concurrency: diff --git a/.github/workflows/cerberus-integration.yml b/.github/workflows/cerberus-integration.yml index 59a7ab15..2d58d62d 100644 --- a/.github/workflows/cerberus-integration.yml +++ b/.github/workflows/cerberus-integration.yml @@ -30,8 +30,8 @@ jobs: name: Cerberus Security Stack Integration runs-on: ubuntu-latest timeout-minutes: 20 - # Only run if docker-build.yml succeeded, or if manually triggered - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + # Only run if docker-build.yml succeeded, or if manually triggered, OR on direct push/PR + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event_name == 'pull_request' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -100,10 +100,19 @@ jobs: echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT echo "Determined image tag: $(cat $GITHUB_OUTPUT | grep tag=)" + # Build image locally for Push/PR events to ensure immediate feedback + - name: Build Docker image (Local) + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + run: | + echo "Building image locally for integration test..." + docker build -t charon:local . + echo "✅ Successfully built charon:local" + # Pull image from registry with retry logic (dual-source strategy) # Try registry first (fast), fallback to artifact if registry fails - name: Pull Docker image from registry id: pull_image + if: ${{ github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch' }} uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3 with: timeout_minutes: 5 @@ -118,8 +127,9 @@ jobs: continue-on-error: true # Fallback: Download artifact if registry pull failed + # Only runs if pull_image failed AND we are in a workflow_run context - name: Fallback to artifact download - if: steps.pull_image.outcome == 'failure' + if: steps.pull_image.outcome == 'failure' && github.event_name == 'workflow_run' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SHA: ${{ steps.determine-tag.outputs.sha }} diff --git a/.github/workflows/codecov-upload.yml b/.github/workflows/codecov-upload.yml index 1722f302..b671e8db 100644 --- a/.github/workflows/codecov-upload.yml +++ b/.github/workflows/codecov-upload.yml @@ -6,6 +6,13 @@ on: - main - development - 'feature/**' + - 'hotfix/**' + pull_request: + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8e4e8246..f7d9619d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,17 @@ name: CodeQL - Analyze on: push: - branches: [ main, development, 'feature/**' ] + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' pull_request: - branches: [ main, development ] + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' schedule: - cron: '0 3 * * 1' diff --git a/.github/workflows/crowdsec-integration.yml b/.github/workflows/crowdsec-integration.yml index e4d2e8c8..b899def2 100644 --- a/.github/workflows/crowdsec-integration.yml +++ b/.github/workflows/crowdsec-integration.yml @@ -30,8 +30,8 @@ jobs: name: CrowdSec Bouncer Integration runs-on: ubuntu-latest timeout-minutes: 15 - # Only run if docker-build.yml succeeded, or if manually triggered - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + # Only run if docker-build.yml succeeded, or if manually triggered, OR on direct push/PR + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event_name == 'pull_request' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -100,10 +100,19 @@ jobs: echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT echo "Determined image tag: $(cat $GITHUB_OUTPUT | grep tag=)" + # Build image locally for Push/PR events to ensure immediate feedback + - name: Build Docker image (Local) + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + run: | + echo "Building image locally for integration test..." + docker build -t charon:local . + echo "✅ Successfully built charon:local" + # Pull image from registry with retry logic (dual-source strategy) # Try registry first (fast), fallback to artifact if registry fails - name: Pull Docker image from registry id: pull_image + if: ${{ github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch' }} uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3 with: timeout_minutes: 5 @@ -118,8 +127,9 @@ jobs: continue-on-error: true # Fallback: Download artifact if registry pull failed + # Only runs if pull_image failed AND we are in a workflow_run context - name: Fallback to artifact download - if: steps.pull_image.outcome == 'failure' + if: steps.pull_image.outcome == 'failure' && github.event_name == 'workflow_run' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SHA: ${{ steps.determine-tag.outputs.sha }} diff --git a/.github/workflows/docker-lint.yml b/.github/workflows/docker-lint.yml index acfb6fa5..8b89d96d 100644 --- a/.github/workflows/docker-lint.yml +++ b/.github/workflows/docker-lint.yml @@ -2,11 +2,11 @@ name: Docker Lint on: push: - branches: [ main, development, 'feature/**' ] + branches: [ main, development, 'feature/**', 'hotfix/**' ] paths: - 'Dockerfile' pull_request: - branches: [ main, development ] + branches: [ main, development, 'feature/**', 'hotfix/**' ] paths: - 'Dockerfile' diff --git a/.github/workflows/dry-run-history-rewrite.yml b/.github/workflows/dry-run-history-rewrite.yml index c964f910..7a27880e 100644 --- a/.github/workflows/dry-run-history-rewrite.yml +++ b/.github/workflows/dry-run-history-rewrite.yml @@ -1,6 +1,8 @@ name: History Rewrite Dry-Run on: + push: + branches: [main, development, 'feature/**', 'hotfix/**'] pull_request: types: [opened, synchronize, reopened] schedule: diff --git a/.github/workflows/e2e-tests-split.yml b/.github/workflows/e2e-tests-split.yml index ac291e26..6e7c40b1 100644 --- a/.github/workflows/e2e-tests-split.yml +++ b/.github/workflows/e2e-tests-split.yml @@ -13,10 +13,14 @@ name: 'E2E Tests' on: - workflow_run: - workflows: ["Docker Build, Publish & Test"] - types: [completed] + push: branches: [main, development, 'feature/**', 'hotfix/**'] + paths: + - 'frontend/**' + - 'backend/**' + - 'tests/**' + - 'playwright.config.js' + - '.github/workflows/e2e-tests-split.yml' pull_request: branches: [main, development, 'feature/**', 'hotfix/**'] paths: diff --git a/.github/workflows/history-rewrite-tests.yml b/.github/workflows/history-rewrite-tests.yml index 9d6a5a15..96c964ec 100644 --- a/.github/workflows/history-rewrite-tests.yml +++ b/.github/workflows/history-rewrite-tests.yml @@ -2,12 +2,17 @@ name: History Rewrite Tests on: push: - paths: - - 'scripts/history-rewrite/**' - - '.github/workflows/history-rewrite-tests.yml' + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' pull_request: - paths: - - 'scripts/history-rewrite/**' + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index d911c461..1189a034 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -2,9 +2,17 @@ name: Quality Checks on: push: - branches: [ main, development, 'feature/**' ] + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' pull_request: - branches: [ main, development ] + branches: + - main + - development + - 'feature/**' + - 'hotfix/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/rate-limit-integration.yml b/.github/workflows/rate-limit-integration.yml index 1a25e6ff..0d3375d0 100644 --- a/.github/workflows/rate-limit-integration.yml +++ b/.github/workflows/rate-limit-integration.yml @@ -30,8 +30,8 @@ jobs: name: Rate Limiting Integration runs-on: ubuntu-latest timeout-minutes: 15 - # Only run if docker-build.yml succeeded, or if manually triggered - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + # Only run if docker-build.yml succeeded, or if manually triggered, OR on direct push/PR + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event_name == 'pull_request' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -100,10 +100,19 @@ jobs: echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT echo "Determined image tag: $(cat $GITHUB_OUTPUT | grep tag=)" + # Build image locally for Push/PR events to ensure immediate feedback + - name: Build Docker image (Local) + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + run: | + echo "Building image locally for integration test..." + docker build -t charon:local . + echo "✅ Successfully built charon:local" + # Pull image from registry with retry logic (dual-source strategy) # Try registry first (fast), fallback to artifact if registry fails - name: Pull Docker image from registry id: pull_image + if: ${{ github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch' }} uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3 with: timeout_minutes: 5 @@ -118,8 +127,9 @@ jobs: continue-on-error: true # Fallback: Download artifact if registry pull failed + # Only runs if pull_image failed AND we are in a workflow_run context - name: Fallback to artifact download - if: steps.pull_image.outcome == 'failure' + if: steps.pull_image.outcome == 'failure' && github.event_name == 'workflow_run' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SHA: ${{ steps.determine-tag.outputs.sha }} diff --git a/.github/workflows/security-pr.yml b/.github/workflows/security-pr.yml index 6707fa96..c3d5cda4 100644 --- a/.github/workflows/security-pr.yml +++ b/.github/workflows/security-pr.yml @@ -33,6 +33,8 @@ jobs: # Run for: manual dispatch, PR builds, or any push builds from docker-build if: >- github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + github.event_name == 'pull_request' || ((github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'push') && github.event.workflow_run.conclusion == 'success') @@ -92,9 +94,16 @@ jobs: echo "is_push=false" >> "$GITHUB_OUTPUT" fi + - name: Build Docker image (Local) + if: github.event_name == 'push' || github.event_name == 'pull_request' + run: | + echo "Building image locally for security scan..." + docker build -t charon:local . + echo "✅ Successfully built charon:local" + - name: Check for PR image artifact id: check-artifact - if: steps.pr-info.outputs.pr_number != '' || steps.pr-info.outputs.is_push == 'true' + if: (steps.pr-info.outputs.pr_number != '' || steps.pr-info.outputs.is_push == 'true') && github.event_name != 'push' && github.event_name != 'pull_request' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -159,7 +168,7 @@ jobs: fi - name: Skip if no artifact - if: (steps.pr-info.outputs.pr_number == '' && steps.pr-info.outputs.is_push != 'true') || 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') && github.event_name != 'push' && github.event_name != 'pull_request' run: | echo "ℹ️ Skipping security scan - no PR image artifact available" echo "This is expected for:" @@ -186,9 +195,31 @@ jobs: docker images | grep charon - name: Extract charon binary from container - if: steps.check-artifact.outputs.artifact_exists == 'true' + if: steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' id: extract run: | + # Use local image for Push/PR events + if [[ "${{ github.event_name }}" == "push" || "${{ github.event_name }}" == "pull_request" ]]; then + echo "Using local image: charon:local" + CONTAINER_ID=$(docker create "charon:local") + echo "container_id=${CONTAINER_ID}" >> "$GITHUB_OUTPUT" + + # Extract the charon binary + mkdir -p ./scan-target + docker cp "${CONTAINER_ID}:/app/charon" ./scan-target/charon + docker rm "${CONTAINER_ID}" + + if [[ -f "./scan-target/charon" ]]; then + echo "✅ Binary extracted successfully" + ls -lh ./scan-target/charon + echo "binary_path=./scan-target" >> "$GITHUB_OUTPUT" + else + echo "❌ Failed to extract binary" + exit 1 + fi + exit 0 + fi + # Normalize image name for reference IMAGE_NAME=$(echo "${{ github.repository_owner }}/charon" | tr '[:upper:]' '[:lower:]') if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then @@ -241,7 +272,7 @@ jobs: fi - name: Run Trivy filesystem scan (SARIF output) - if: steps.check-artifact.outputs.artifact_exists == 'true' + if: steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' # aquasecurity/trivy-action v0.33.1 uses: aquasecurity/trivy-action@22438a435773de8c97dc0958cc0b823c45b064ac with: @@ -253,7 +284,7 @@ jobs: continue-on-error: true - name: Upload Trivy SARIF to GitHub Security - if: steps.check-artifact.outputs.artifact_exists == 'true' + if: steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' # github/codeql-action v4 uses: github/codeql-action/upload-sarif@f959778b39f110f7919139e242fa5ac47393c877 with: @@ -262,7 +293,7 @@ jobs: continue-on-error: true - name: Run Trivy filesystem scan (fail on CRITICAL/HIGH) - if: steps.check-artifact.outputs.artifact_exists == 'true' + if: steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' # aquasecurity/trivy-action v0.33.1 uses: aquasecurity/trivy-action@22438a435773de8c97dc0958cc0b823c45b064ac with: @@ -273,7 +304,7 @@ jobs: exit-code: '1' - name: Upload scan artifacts - if: always() && steps.check-artifact.outputs.artifact_exists == 'true' + if: always() && (steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request') # actions/upload-artifact v4.4.3 uses: actions/upload-artifact@47309c993abb98030a35d55ef7ff34b7fa1074b5 with: @@ -283,7 +314,7 @@ jobs: retention-days: 14 - name: Create job summary - if: always() && steps.check-artifact.outputs.artifact_exists == 'true' + if: always() && (steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request') run: | if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then echo "## 🔒 Security Scan Results - Branch: ${{ github.event.workflow_run.head_branch }}" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/supply-chain-pr.yml b/.github/workflows/supply-chain-pr.yml index 5faaa628..8cfbb7ed 100644 --- a/.github/workflows/supply-chain-pr.yml +++ b/.github/workflows/supply-chain-pr.yml @@ -119,7 +119,7 @@ jobs: - name: Check for PR image artifact id: check-artifact - if: steps.pr-number.outputs.pr_number != '' || steps.pr-number.outputs.is_push == 'true' + if: github.event_name == 'workflow_run' && (steps.pr-number.outputs.pr_number != '' || steps.pr-number.outputs.is_push == 'true') env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -191,14 +191,14 @@ jobs: echo "✅ Found artifact: ${ARTIFACT_NAME} (ID: ${ARTIFACT_ID})" - name: Skip if no artifact - if: (steps.pr-number.outputs.pr_number == '' && steps.pr-number.outputs.is_push != 'true') || steps.check-artifact.outputs.artifact_found != 'true' + if: github.event_name == 'workflow_run' && ((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" exit 0 - name: Download PR image artifact - if: steps.check-artifact.outputs.artifact_found == 'true' + if: github.event_name == 'workflow_run' && steps.set-target.outputs.image_name != '' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -216,9 +216,9 @@ jobs: unzip -o artifact.zip echo "✅ Artifact downloaded and extracted" - - name: Load Docker image - if: steps.check-artifact.outputs.artifact_found == 'true' - id: load-image + - name: Load Docker image (Artifact) + if: github.event_name == 'workflow_run' && steps.set-target.outputs.image_name != '' + id: load-image-artifact run: | if [[ ! -f "charon-pr-image.tar" ]]; then echo "❌ charon-pr-image.tar not found in artifact" @@ -246,18 +246,36 @@ jobs: echo "image_name=${IMAGE_NAME}" >> "$GITHUB_OUTPUT" echo "✅ Loaded image: ${IMAGE_NAME}" + - name: Build Docker image (Local) + if: github.event_name != 'workflow_run' + id: build-image-local + run: | + echo "🐳 Building Docker image locally..." + docker build -t charon:local . + echo "image_name=charon:local" >> "$GITHUB_OUTPUT" + echo "✅ Built image: charon:local" + + - name: Set Target Image + id: set-target + run: | + if [[ "${{ github.event_name }}" == "workflow_run" ]]; then + echo "image_name=${{ steps.load-image-artifact.outputs.image_name }}" >> "$GITHUB_OUTPUT" + else + echo "image_name=${{ steps.build-image-local.outputs.image_name }}" >> "$GITHUB_OUTPUT" + fi + # Generate SBOM using official Anchore action (auto-updated by Renovate) - name: Generate SBOM - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' uses: anchore/sbom-action@28d71544de8eaf1b958d335707167c5f783590ad # v0.22.2 id: sbom with: - image: ${{ steps.load-image.outputs.image_name }} + image: ${{ steps.set-target.outputs.image_name }} format: cyclonedx-json output-file: sbom.cyclonedx.json - name: Count SBOM components - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' id: sbom-count run: | COMPONENT_COUNT=$(jq '.components | length' sbom.cyclonedx.json 2>/dev/null || echo "0") @@ -266,7 +284,7 @@ jobs: # Scan for vulnerabilities using official Anchore action (auto-updated by Renovate) - name: Scan for vulnerabilities - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2 id: grype-scan with: @@ -275,7 +293,7 @@ jobs: output-format: json - name: Process vulnerability results - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' id: vuln-summary run: | # The scan-action outputs results.json and results.sarif @@ -316,7 +334,7 @@ jobs: echo " Total: ${TOTAL_COUNT}" - name: Upload SARIF to GitHub Security - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' uses: github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4 continue-on-error: true with: @@ -324,7 +342,7 @@ jobs: category: supply-chain-pr - name: Upload supply chain artifacts - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' # actions/upload-artifact v4.6.0 uses: actions/upload-artifact@47309c993abb98030a35d55ef7ff34b7fa1074b5 with: @@ -335,7 +353,7 @@ jobs: retention-days: 14 - name: Comment on PR - if: steps.check-artifact.outputs.artifact_found == 'true' && steps.pr-number.outputs.is_push != 'true' + if: steps.set-target.outputs.image_name != '' && steps.pr-number.outputs.is_push != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -412,7 +430,7 @@ jobs: echo "✅ PR comment posted" - name: Fail on critical vulnerabilities - if: steps.check-artifact.outputs.artifact_found == 'true' + if: steps.set-target.outputs.image_name != '' run: | CRITICAL_COUNT="${{ steps.grype-scan.outputs.critical_count }}" diff --git a/.github/workflows/waf-integration.yml b/.github/workflows/waf-integration.yml index 1badab28..bda6b8d2 100644 --- a/.github/workflows/waf-integration.yml +++ b/.github/workflows/waf-integration.yml @@ -30,8 +30,8 @@ jobs: name: Coraza WAF Integration runs-on: ubuntu-latest timeout-minutes: 15 - # Only run if docker-build.yml succeeded, or if manually triggered - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + # Only run if docker-build.yml succeeded, or if manually triggered, OR on direct push/PR + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event_name == 'pull_request' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -100,10 +100,19 @@ jobs: echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT echo "Determined image tag: $(cat $GITHUB_OUTPUT | grep tag=)" + # Build image locally for Push/PR events to ensure immediate feedback + - name: Build Docker image (Local) + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + run: | + echo "Building image locally for integration test..." + docker build -t charon:local . + echo "✅ Successfully built charon:local" + # Pull image from registry with retry logic (dual-source strategy) # Try registry first (fast), fallback to artifact if registry fails - name: Pull Docker image from registry id: pull_image + if: ${{ github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch' }} uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3 with: timeout_minutes: 5 @@ -118,8 +127,9 @@ jobs: continue-on-error: true # Fallback: Download artifact if registry pull failed + # Only runs if pull_image failed AND we are in a workflow_run context - name: Fallback to artifact download - if: steps.pull_image.outcome == 'failure' + if: steps.pull_image.outcome == 'failure' && github.event_name == 'workflow_run' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SHA: ${{ steps.determine-tag.outputs.sha }} diff --git a/docs/issues/manual_test_workflow_triggers.md b/docs/issues/manual_test_workflow_triggers.md new file mode 100644 index 00000000..3053f70c --- /dev/null +++ b/docs/issues/manual_test_workflow_triggers.md @@ -0,0 +1,49 @@ +--- +title: Manual Test Plan - Workflow Trigger Verification +status: Open +priority: Normal +assignee: DevOps +labels: testing, workflows, ci/cd +--- + +# Test Objectives +Verify that all CI/CD workflows trigger correctly on feature branches and provide immediate feedback without waiting for the `docker-build` workflow (except where intended for release verification). + +# Scope +- `dry-run-history-rewrite.yml` (Modified) +- `cerberus-integration.yml` +- `crowdsec-integration.yml` +- `waf-integration.yml` +- `rate-limit-integration.yml` +- `e2e-tests-split.yml` + +# Test Steps + +## 1. Dry Run Workflow (Modified) +- [ ] Create a new branch `feature/test-workflow-triggers`. +- [ ] Make a dummy change to a file (e.g., `README.md`). +- [ ] Push the branch. +- [ ] Go to Actions tab. +- [ ] Verify `Dry Run History Rewrite` workflow starts immediately. + +## 2. Integration Tests (Dual Mode Verification) +- [ ] Using the same branch `feature/test-workflow-triggers`. +- [ ] Verify the following workflows start immediately (building locally): + - [ ] `Cerberus Integration` + - [ ] `CrowdSec Integration` + - [ ] `Coraza WAF Integration` + - [ ] `Rate Limiting Integration` +- [ ] Inspect the logs of one of them. +- [ ] Confirm it executes the "Build Docker image (Local)" step and *skips* the "Pull Docker image from registry" step. + +## 3. Supply Chain (Split Verification) +- [ ] Verify `Supply Chain Security (PR)` starts on the feature branch push. +- [ ] Verify `Supply Chain Verify (Release)` does **NOT** start (it should wait for `docker-build` on main/release). + +## 4. E2E Tests +- [ ] Verify `E2E Tests` workflow starts immediately and builds its own image. + +# Success Criteria +- All "Validation" workflows trigger on `push` to `feature/*`. +- Integration tests build locally instead of failing/waiting for registry. +- No "Resource not accessible" errors for secrets on the feature branch. diff --git a/playwright.config.js b/playwright.config.js index 724ce1e8..2d16bc28 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -5,8 +5,6 @@ import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; /** - * Read environment variables from file (local development only). - * In CI, environment variables are provided by GitHub secrets. * Read environment variables from file (local development only). * In CI, environment variables are provided by GitHub secrets. * https://github.com/motdotla/dotenv @@ -15,9 +13,6 @@ import dotenv from 'dotenv'; if (!process.env.CI) { dotenv.config({ path: join(dirname(fileURLToPath(import.meta.url)), '.env') }); } -if (!process.env.CI) { - dotenv.config({ path: join(dirname(fileURLToPath(import.meta.url)), '.env') }); -} /** * Auth state storage path - shared across all browser projects @@ -29,13 +24,9 @@ const STORAGE_STATE = join(__dirname, 'playwright/.auth/user.json'); /** * Coverage reporter configuration for E2E tests * Only loaded when PLAYWRIGHT_COVERAGE=1 - * Only loaded when PLAYWRIGHT_COVERAGE=1 */ const enableCoverage = process.env.PLAYWRIGHT_COVERAGE === '1'; -const coverageReporterConfig = enableCoverage ? defineCoverageReporterConfig({ -const enableCoverage = process.env.PLAYWRIGHT_COVERAGE === '1'; - const coverageReporterConfig = enableCoverage ? defineCoverageReporterConfig({ sourceRoot: __dirname, exclude: [ @@ -62,7 +53,6 @@ const coverageReporterConfig = enableCoverage ? defineCoverageReporterConfig({ functions: [50, 80], lines: [50, 80], }, - rewritePath: ({ absolutePath }) => { rewritePath: ({ absolutePath }) => { if (absolutePath.startsWith('/app/')) { return absolutePath.replace('/app/', `${__dirname}/`); @@ -76,7 +66,6 @@ const coverageReporterConfig = enableCoverage ? defineCoverageReporterConfig({ return absolutePath; }, }) : null; -}) : null; /** * @see https://playwright.dev/docs/test-configuration @@ -85,8 +74,6 @@ export default defineConfig({ testDir: './tests', testIgnore: ['**/frontend/**', '**/node_modules/**', '**/backend/**'], - /* Standard globalSetup - runs once before all tests */ - /* Standard globalSetup - runs once before all tests */ globalSetup: './tests/global-setup.ts', @@ -94,28 +81,16 @@ export default defineConfig({ timeout: process.env.CI ? 60000 : 90000, expect: { timeout: 5000 }, - /* Parallelization */ - - /* Timeouts */ - timeout: process.env.CI ? 60000 : 90000, - expect: { timeout: 5000 }, - /* Parallelization */ fullyParallel: true, workers: process.env.CI ? 1 : undefined, - /* CI settings */ - workers: process.env.CI ? 1 : undefined, - /* CI settings */ forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, - /* Reporters - simplified for CI */ - /* Reporters - simplified for CI */ reporter: [ - process.env.CI ? ['github'] : ['list'], process.env.CI ? ['github'] : ['list'], ['html', { open: process.env.CI ? 'never' : 'on-failure' }], ...(enableCoverage ? [['@bgotink/playwright-coverage', coverageReporterConfig]] : []), @@ -135,13 +110,8 @@ export default defineConfig({ * IMPORTANT: Using 127.0.0.1 (IPv4 loopback) instead of localhost to avoid * IPv6/IPv4 resolution issues where Node.js/Playwright might prefer ::1 (IPv6) * but the Docker container binds to 0.0.0.0 (IPv4). - * - * IMPORTANT: Using 127.0.0.1 (IPv4 loopback) instead of localhost to avoid - * IPv6/IPv4 resolution issues where Node.js/Playwright might prefer ::1 (IPv6) - * but the Docker container binds to 0.0.0.0 (IPv4). */ baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080', - baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:8080', /* Traces: Capture execution traces for debugging * @@ -174,14 +144,12 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ - // Setup project - authentication (runs FIRST) // Setup project - authentication (runs FIRST) { name: 'setup', testMatch: /auth\.setup\.ts/, }, - // Security Tests - Run WITH security enabled (SEQUENTIAL, Chromium only) // Security Tests - Run WITH security enabled (SEQUENTIAL, Chromium only) { name: 'security-tests', @@ -194,24 +162,19 @@ export default defineConfig({ teardown: 'security-teardown', fullyParallel: false, workers: 1, - fullyParallel: false, - workers: 1, use: { ...devices['Desktop Chrome'], headless: true, - headless: true, storageState: STORAGE_STATE, }, }, - // Security Teardown - Disable ALL security modules // Security Teardown - Disable ALL security modules { name: 'security-teardown', testMatch: /security-teardown\.setup\.ts/, }, - // Browser projects - standard Playwright pattern // Browser projects - standard Playwright pattern { name: 'chromium', @@ -220,7 +183,6 @@ export default defineConfig({ storageState: STORAGE_STATE, }, dependencies: ['setup'], - dependencies: ['setup'], }, { @@ -270,7 +232,5 @@ export default defineConfig({ // timeout: 120000, // stdout: 'pipe', // PHASE 1: Enable log visibility // stderr: 'pipe', // PHASE 1: Enable log visibility - // stdout: 'pipe', // PHASE 1: Enable log visibility - // stderr: 'pipe', // PHASE 1: Enable log visibility // }, });