From 836936cad0455bf28c1e6515d61ddad585f881a1 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:06:50 -0400 Subject: [PATCH] Add `sfw` aggregator gate to enforce required CI checks (#89) * ci: add Socket Firewall aggregator gate; bump 3.2.0 -> 3.2.1 Add a single sfw-gate job (if: always(), needs the conditional inspect + free/enterprise smoke + workflow-notice jobs) that fails only when an upstream job failed or was cancelled -- success and skipped both pass. This is the check intended to become the required status check on main: the smoke jobs are conditional (deps-changed gates them, and exactly one of free/enterprise runs per PR), so none can be required directly -- a required check whose job is if-skipped is never created and blocks merge forever. The gate is green when no deps change and is satisfied by whichever smoke path actually ran. NOT yet wired into branch protection -- added during a soak period so the check is visible before it becomes blocking, and so requiring it doesn't strand other open PRs. Pattern adapted from SocketDev/socket-python-cli #224. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * ci: spell out 'iff' as 'if and only if' in gate comment Review feedback: 'iff' read as a typo. It is the logic shorthand for 'if and only if', but the comment exists to communicate, so spell it out. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --------- Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 49 +++++++++++++++++++++++++ pyproject.toml | 2 +- socketdev/version.py | 2 +- uv.lock | 2 +- 4 files changed, 52 insertions(+), 3 deletions(-) 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" },