diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index ea0acee..fd1d8fc 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -279,3 +279,52 @@ jobs: echo "This PR changes workflow, composite-action, or dependabot config files." echo "Require explicit human review before merge." } >> "$GITHUB_STEP_SUMMARY" + + # Aggregator gate -- the single check intended to become the required status + # check on main. The Socket Firewall smoke jobs are conditional (deps-changed + # gates them, and exactly one of free/enterprise runs per PR), so neither can + # be required directly: a required check whose job is `if:`-skipped is never + # created and sits at "Expected -- Waiting for status to be reported" + # forever, permanently blocking merge (this hits every Dependabot/fork PR and + # every PR that doesn't touch deps). + # + # This job runs unconditionally (`if: always()`), depends on all the + # conditional jobs, and fails ONLY when one of them actually failed or was + # cancelled. A `skipped` dependency passes -- so the gate is green when no + # deps changed, and otherwise satisfied by whichever smoke path ran (free for + # Dependabot/forks, enterprise for trusted maintainers). A real Socket + # Firewall block surfaces as a smoke-job failure and thus a gate failure. + # + # NOT YET wired into branch protection -- added during a soak period so the + # check is visible before it becomes blocking. Requiring it before it lands + # on main would strand every other open PR on the trap above. + sfw-gate: + name: Socket Firewall Gate + needs: [inspect, python-sfw-smoke-free, python-sfw-smoke-enterprise, workflow-notice] + if: always() + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Evaluate dependency-review results + env: + NEEDS_JSON: ${{ toJSON(needs) }} + run: | + echo "$NEEDS_JSON" + # Fail if and only if a needed job reported failure or cancelled; + # success and skipped both pass. jq returns the count of offending + # results. + bad="$(printf '%s' "$NEEDS_JSON" \ + | jq '[to_entries[] | select(.value.result == "failure" or .value.result == "cancelled")] | length')" + + { + echo "## Socket Firewall Gate" + printf '%s\n' "$NEEDS_JSON" | jq -r 'to_entries[] | "- \(.key): \(.value.result)"' + } >> "$GITHUB_STEP_SUMMARY" + + if [ "$bad" -ne 0 ]; then + echo "Gate failed: $bad upstream job(s) failed or were cancelled." >> "$GITHUB_STEP_SUMMARY" + echo "::error::Socket Firewall Gate failed -- $bad upstream job(s) failed or were cancelled." + exit 1 + fi + + echo "Gate passed." >> "$GITHUB_STEP_SUMMARY" diff --git a/pyproject.toml b/pyproject.toml index d6e5f17..7c30914 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "socketdev" -version = "3.2.0" +version = "3.2.1" requires-python = ">= 3.9" dependencies = [ 'requests', diff --git a/socketdev/version.py b/socketdev/version.py index 1173108..1da6a55 100644 --- a/socketdev/version.py +++ b/socketdev/version.py @@ -1 +1 @@ -__version__ = "3.2.0" +__version__ = "3.2.1" diff --git a/uv.lock b/uv.lock index 98f3daf..6331649 100644 --- a/uv.lock +++ b/uv.lock @@ -1353,7 +1353,7 @@ wheels = [ [[package]] name = "socketdev" -version = "3.2.0" +version = "3.2.1" source = { editable = "." } dependencies = [ { name = "requests" },