diff --git a/.github/actions/build-charon-image/action.yml b/.github/actions/build-charon-image/action.yml new file mode 100644 index 000000000..6768add45 --- /dev/null +++ b/.github/actions/build-charon-image/action.yml @@ -0,0 +1,48 @@ +name: Build Charon image +description: >- + Build the Charon Docker image locally for integration / security-scan jobs, + with GitHub Actions layer caching (type=gha) so the multi-stage build is not + rebuilt cold on every run. Loads the image into the local Docker daemon. +inputs: + tag: + description: Image tag to load locally. + required: false + default: charon:local + ci: + description: Value passed as the CI build-arg. + required: false + default: 'false' + no-cache-filters: + description: >- + Comma-separated Dockerfile stages to force-rebuild (never restore from the + layer cache). Empty by default: every stage, including the expensive + caddy-builder xcaddy step, is GHA layer-cached — that is where the build + time is recovered. None of this action's callers publish a released + artifact; the release image is built by docker-build.yml, which runs its + own clean rebuild of caddy-builder / crowdsec-builder. The PR-gate scan + jobs that use this action (security-pr.yml, supply-chain-pr.yml) scan the + resulting binary / filesystem / SBOM against a scan-time vulnerability DB, + so a cached layer cannot hide a CVE in a pinned dependency; the only + staleness is the two unpinned xcaddy plugins (caddy-geoip2, + caddy-ratelimit), which refresh whenever the Dockerfile or a build-arg + changes or the cache is evicted. Pass a value to override. + required: false + default: '' +runs: + using: composite + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - name: Build image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 + with: + context: . + file: ./Dockerfile + push: false + load: true + tags: ${{ inputs.tag }} + build-args: | + CI=${{ inputs.ci }} + cache-from: type=gha,scope=charon-integration-image + cache-to: type=gha,mode=max,scope=charon-integration-image + no-cache-filters: ${{ inputs.no-cache-filters }} diff --git a/.github/skills/examples/gorm-scanner-ci-workflow.yml b/.github/skills/examples/gorm-scanner-ci-workflow.yml index dabcf72ab..31005d6ed 100644 --- a/.github/skills/examples/gorm-scanner-ci-workflow.yml +++ b/.github/skills/examples/gorm-scanner-ci-workflow.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 with: - GO_VERSION: '1.27.0' + go-version: "1.27.0" - name: Run GORM Security Scanner id: gorm-scan diff --git a/.github/workflows/cerberus-integration.yml b/.github/workflows/cerberus-integration.yml index a1a49c90a..bf47e2b9f 100644 --- a/.github/workflows/cerberus-integration.yml +++ b/.github/workflows/cerberus-integration.yml @@ -1,7 +1,6 @@ name: Cerberus Integration -# Phase 2-3: Build Once, Test Many - Use registry image instead of building -# This workflow now waits for docker-build.yml to complete and pulls the built image +# Builds the Charon image locally via the shared build-charon-image composite action (GHA layer cache), then runs the Cerberus integration tests. on: workflow_dispatch: inputs: @@ -32,10 +31,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Build Docker image (Local) - run: | - echo "Building image locally for integration tests..." - docker build -t charon:local --build-arg CI="${CI:-false}" . - echo "✅ Successfully built charon:local" + uses: ./.github/actions/build-charon-image - name: Run Cerberus integration tests id: cerberus-test diff --git a/.github/workflows/crowdsec-integration.yml b/.github/workflows/crowdsec-integration.yml index d5449022d..c7d399962 100644 --- a/.github/workflows/crowdsec-integration.yml +++ b/.github/workflows/crowdsec-integration.yml @@ -1,7 +1,6 @@ name: CrowdSec Integration -# Phase 2-3: Build Once, Test Many - Use registry image instead of building -# This workflow now waits for docker-build.yml to complete and pulls the built image +# Builds the Charon image locally via the shared build-charon-image composite action (GHA layer cache), then runs the CrowdSec bouncer integration tests. on: workflow_dispatch: inputs: @@ -27,15 +26,14 @@ jobs: crowdsec-integration: name: CrowdSec Bouncer Integration runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 20 # 20m: warm GHA cache builds in ~8-12m; first run on a fresh branch / after cache eviction is still a full cold build. steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Build Docker image (Local) - run: | - echo "Building image locally for integration tests..." - docker build -t charon:local --build-arg CI="${CI:-false}" . - echo "✅ Successfully built charon:local" + uses: ./.github/actions/build-charon-image + with: + ci: 'true' - name: Run CrowdSec integration tests id: crowdsec-test diff --git a/.github/workflows/rate-limit-integration.yml b/.github/workflows/rate-limit-integration.yml index 09a6e5c37..faa49223b 100644 --- a/.github/workflows/rate-limit-integration.yml +++ b/.github/workflows/rate-limit-integration.yml @@ -1,7 +1,6 @@ name: Rate Limit integration -# Phase 2-3: Build Once, Test Many - Use registry image instead of building -# This workflow now waits for docker-build.yml to complete and pulls the built image +# Builds the Charon image locally via the shared build-charon-image composite action (GHA layer cache), then runs the rate limiting integration tests. on: workflow_dispatch: inputs: @@ -27,15 +26,12 @@ jobs: rate-limit-integration: name: Rate Limiting Integration runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 20 # 20m: warm GHA cache builds in ~8-12m; first run on a fresh branch / after cache eviction is still a full cold build. steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Build Docker image (Local) - run: | - echo "Building image locally for integration tests..." - docker build -t charon:local --build-arg CI="${CI:-false}" . - echo "✅ Successfully built charon:local" + uses: ./.github/actions/build-charon-image - name: Run rate limit integration tests id: ratelimit-test diff --git a/.github/workflows/security-pr.yml b/.github/workflows/security-pr.yml index 4c35a6cbe..cd5ccc4ff 100644 --- a/.github/workflows/security-pr.yml +++ b/.github/workflows/security-pr.yml @@ -29,7 +29,7 @@ jobs: security-scan: name: Trivy Binary Scan runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 20 # 20m: cold GHA cache (first run / post-eviction) is a full ~10-14m image build; warm-cache builds are far quicker. # Run for manual dispatch, direct PR/push, or successful upstream workflow_run if: >- github.event_name == 'workflow_dispatch' || @@ -154,10 +154,7 @@ jobs: - 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" + uses: ./.github/actions/build-charon-image - name: Check for PR image artifact id: check-artifact diff --git a/.github/workflows/supply-chain-pr.yml b/.github/workflows/supply-chain-pr.yml index a370b5650..7deb8b587 100644 --- a/.github/workflows/supply-chain-pr.yml +++ b/.github/workflows/supply-chain-pr.yml @@ -31,7 +31,7 @@ jobs: verify-supply-chain: name: Verify Supply Chain runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 20 # 20m: cold GHA cache (first run / post-eviction) is a full ~10-14m image build; warm-cache builds are far quicker. # Run for: manual dispatch, or successful workflow_run triggered by push/PR if: > github.event_name == 'workflow_dispatch' || @@ -248,13 +248,13 @@ jobs: echo "✅ Loaded image: ${IMAGE_NAME}" - name: Build Docker image (Local) + if: github.event_name != 'workflow_run' + uses: ./.github/actions/build-charon-image + + - name: Expose local image name 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" + run: echo "image_name=charon:local" >> "$GITHUB_OUTPUT" - name: Set Target Image id: set-target diff --git a/.github/workflows/waf-integration.yml b/.github/workflows/waf-integration.yml index cfab0b9e0..231d92e33 100644 --- a/.github/workflows/waf-integration.yml +++ b/.github/workflows/waf-integration.yml @@ -1,7 +1,6 @@ name: WAF integration -# Phase 2-3: Build Once, Test Many - Use registry image instead of building -# This workflow now waits for docker-build.yml to complete and pulls the built image +# Builds the Charon image locally via the shared build-charon-image composite action (GHA layer cache), then runs the Coraza WAF integration tests. on: workflow_dispatch: inputs: @@ -27,15 +26,12 @@ jobs: waf-integration: name: Coraza WAF Integration runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 20 # 20m: warm GHA cache builds in ~8-12m; first run on a fresh branch / after cache eviction is still a full cold build. steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Build Docker image (Local) - run: | - echo "Building image locally for integration tests..." - docker build -t charon:local --build-arg CI="${CI:-false}" . - echo "✅ Successfully built charon:local" + uses: ./.github/actions/build-charon-image - name: Run WAF integration tests id: waf-test diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 18ec56b0d..bc86e4a2f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.37.1" + ".": "0.38.0" } diff --git a/package-lock.json b/package-lock.json index dc303dbf3..5c8aec3dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "Charon", "dependencies": { "@typescript/analyze-trace": "^0.11.1", "tldts": "^7.4.11", diff --git a/release-please-config.json b/release-please-config.json index 773c0c298..7f56f8078 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,7 +3,6 @@ "release-type": "go", "include-component-in-tag": false, "bump-minor-pre-major": true, - "bump-patch-for-minor-pre-major": true, "pull-request-header": "Here's what's new in Charon", "pull-request-footer": "Merge this PR to cut the release.", "packages": {