feat: add pr_comment_enabled and pr_comment_collapse_all PR comment controls - #97
feat: add pr_comment_enabled and pr_comment_collapse_all PR comment controls#97John-David Dalton (jdalton) wants to merge 3 commits into
Conversation
The Socket Basics PR comment could not be turned off, and
pr_comment_collapse_non_critical deliberately leaves critical findings
expanded, so a single critical finding always forced the comment open.
Teams evaluating finding quality in the Socket dashboard had no way to
keep the scan running without the comment appearing on every PR.
Add two independent switches:
pr_comment_enabled (default true) - post/update the PR comment
pr_comment_collapse_all (default false) - collapse every section
Notifiers run last, after the scan and after the Socket dashboard
upload, so suppressing the comment cannot suppress either. Severity
labels stay under the separate pr_labels_enabled switch.
Feature flags are now read through coerce_bool so a dashboard config
that supplies them as strings is honored; bool("false") is True, which
previously read a disabled flag as enabled.
Refs: SURF-1451
Bugbot caught that pr_labels_enabled was still a raw truthiness read, so a Socket dashboard config supplying 'false' as a string kept applying severity labels -- including on the new comments-disabled path, where a user who turned both off would still get labels.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 8322efa. Configure here.
The environment loader compared the raw input against the single string 'true', so a flag arrived off unless it was spelled exactly that way. An action input forwarded from an unset workflow variable arrives as an empty string, which meant pr_comment_enabled read as false and the comment disappeared from a workflow that never asked for that. '1', 'yes' and 'on' did not turn it on either, even though the dashboard path already accepted them. coerce_bool now lives in the config layer and both paths use it, so a value that says nothing falls back to the flag's documented default and every spelling behaves the same from an action input, a JSON config and a Socket dashboard config. Documents the truth table in the PR comment guide, and adds the two new switches to the README feature list.
|
[agent] One more string-vs-boolean hole, in the layer underneath the one this PR already fixed, and it pointed the wrong way for exactly the switch this PR adds.
Action inputs are always strings, and I probed the real loader before and after, which is the difference in one table:
Fixed in afba61a. That fallback is why 11 new tests in The other three things you asked me to confirm all hold: Suppressing the comment does not suppress the dashboard.
Defaults are right in |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit afba61a. Configure here.
lelia
left a comment
There was a problem hiding this comment.
The separation between comment suppression, labels, and the scan/upload path looks good. I found two user-facing gaps that should be resolved before merge: the newly exposed CLI switches are not applied to the effective config, and the documented collapse-all contract is broader than the formatter behavior. Details are inline; the action/env/dashboard behavior and test coverage otherwise look solid.
| default: true | ||
| description: "Auto-collapse non-critical findings (critical stays expanded)" | ||
| - name: pr_comment_collapse_all | ||
| option: --pr-comment-collapse-all |
There was a problem hiding this comment.
Could we wire notification options through create_config_from_args() before exposing this as a CLI flag? add_dynamic_cli_args() registers options from notifications.yaml, but create_config_from_args() only copies values defined in connectors.yaml. I verified that parsing --pr-comment-collapse-all produces True while the effective pr_comment_collapse_all config remains False. The new --pr-comment option also cannot disable a default-true flag. Please apply notifier CLI arguments, expose a negative/default-true form such as --no-pr-comment (or BooleanOptionalAction), and add an end-to-end parser/config test.
| pr_comment_collapse_all: | ||
| description: >- | ||
| Collapse every findings section, including critical ones. Use this when | ||
| you want the comment to stay small no matter what it finds. Overrides |
There was a problem hiding this comment.
Can we either implement this behavior for the flat-table formatters or narrow the contract? Only OpenGrep and Tier 1 consume pr_comment_collapse_all. TruffleHog and Trivy Dockerfile findings remain expanded tables, and even SAST leaves one visible collapsed row per file, so the guide's claim that the comment becomes one line is not accurate. Please add formatter coverage for the remaining outputs, or describe this specifically as collapsing the currently collapsible SAST/Tier 1 sections and adjust the one-line wording.
LLM Description written by CLAUDE_CODE:claude-opus-5
Summary
Right now the Socket Basics PR comment cannot be turned off. There is a setting to auto-collapse non-critical findings, but critical findings always stay expanded, so a single critical finding forces the whole comment open on every push. A team that wants to evaluate finding quality in the Socket dashboard first has no way to keep the scan running without the comment landing on every developer's PR.
This PR adds two independent switches so a team can pick how quiet they want the PR to be:
pr_comment_enabledtruefalseand no comment is posted or updated at all.pr_comment_collapse_allfalsetrueand every findings section is collapsed, including critical ones.Nothing changes for anyone until a workflow opts in. Both switches default to today's behavior, so existing users see no difference when this merges. The comment disappears only for a workflow that sets
pr_comment_enabled: 'false', and findings still reach the Socket dashboard either way. Like every change to this action, it reaches users at the next release tag — and the requesting team also needs to re-mirror onto that tag, because their current pin predates these inputs.Design & behavior
Suppressing the comment cannot suppress the dashboard, because notifiers run last
socket_basics/socket_basics.pymain()runs in a fixed order:scanner.run_all_scans()— scanners executescanner.save_results(...)— writes.socket.facts.jsonscanner.submit_socket_facts(...)— uploads to the Socket dashboardscanner.notification_manager.notify_all(results)— notifiers, including the GitHub PR commentThe new switch lives in step 5, inside
GithubPRNotifier.notify(). Everything the customer cares about keeping has already happened by then. There is no code path where the comment flag reaches the scanner or the uploader.Why "collapse all" is a separate setting rather than a change to the existing one
pr_comment_collapse_non_criticalmeans "collapse the sections that are not critical". Both formatters that build collapsible sections computed expansion the same way:has_criticalis an unconditional OR, so no value ofpr_comment_collapse_non_criticalcan collapse a critical section. That is the documented intent of the setting, and some teams rely on it, so changing its meaning would be a silent behavior change for everyone else.pr_comment_collapse_allis a new flag that wins over both:The Trivy formatter already emitted a plain
<details>with no expansion logic, so it needed no change.Labels stayed on their own switch, and a string-vs-boolean bug got fixed on the way past
Labels.
pr_labels_enabledalready exists and already defaults totrue. Folding label suppression intopr_comment_enabledwould mean a team that wants labels but no comment cannot have that. So when comments are off,notify()still reconciles labels ifpr_labels_enabledis true, and there is a test for each of the four on/off combinations.String booleans.
get_feature_flags()read every flag with a bareconfig.get(...). Flags arriving from the environment loader are real Python booleans, but a Socket dashboard config can deliver them as strings — andbool("false")isTrue, so a flag disabled in the dashboard read as enabled. All the PR comment flags now go through a newcoerce_bool()helper ingithub_pr_helpers.py, which acceptstrue/1/yes/onandfalse/0/no/offin either case and passes real booleans through untouched. This is a strict improvement for the existing flags, not just the new ones.Diagnosis
Getting a flag into the config had the same string-vs-boolean bug, pointing the wrong way
coerce_bool()handles a flag once it is in the config. Getting it there wentthrough the environment loader, which read every notifier boolean as
env_value.lower() == 'true'— so anything that was not the literal wordtruecame out
False, including an empty string.Action inputs are always strings, and
action.ymlpassesINPUT_PR_COMMENT_ENABLED: ${{ inputs.pr_comment_enabled }}on every run. Aworkflow that forwards a variable which turns out to be unset hands the
container
'', which read as "off" — so the comment vanished from a workflowthat never asked to suppress it.
pr_labels_enabledhad it too, and'1','yes'and'on'did not turn a flag on even thoughcoerce_boolaccepts allthree one layer up.
'true'/'TRUE''false''0'/'no'/'off''1'/'yes'/'on'''(forwarded unset variable)' ''maybe'coerce_boolnow lives in the config layer and both paths use it, so a valuethat says nothing falls back to the flag's declared default rather than to off,
and every spelling behaves the same from an action input, a JSON config and a
Socket dashboard config.
pr_comment_collapse_allis unaffected in the blankcase because its default is already off; only the flags that default on were
being flipped.
11 tests in
TestActionInputCoerciondrive the real loader over every spellingabove. Reverting the loader to the bare
== 'true'comparison fails 7 of them.Code, testing & review
Files changed and where each switch is read
action.ymlINPUT_*env mappingssocket_basics/notifications.yamlgithub_prnotifier parameters, matching the shape of the five existingpr_comment_*onessocket_basics/core/notification/github_pr_notifier.pynotify()when comments are disabled; labels still reconciledsocket_basics/core/notification/github_pr_helpers.pycoerce_bool();collapse_alladded to the feature-flag dictsocket_basics/core/connector/opengrep/github_pr.pycollapse_allsocket_basics/core/connector/socket_tier1/github_pr.pycollapse_allscripts/preview_pr_comments.pycollapse_allso previews and formatter tests can exercise itdocs/github-pr-comment-guide.md,docs/github-action.mdREADME.mdCHANGELOG.md[Unreleased]Ran — exit codes read directly from the harness, not through a pipe.
uv run --no-sync pytest -q tests/main; 36 new)python scripts/sync_release_version.py --checkaction.ymlandnotifications.yamlNew tests: 8 in
tests/test_github_pr_notifier.pycovering the suppression switch, 4 intests/test_pr_formatters.py::TestCollapseAll, 4 intests/test_pr_formatters.py::TestFeatureFlagCoercion, and 11 intests/test_pr_formatters.py::TestActionInputCoerciondriving the real environment loader.Mutation checks: every new behavior was broken on purpose and a named test went red
Each mutation was applied, the suite was run, the mutation was reverted, and the suite was re-run green.
comment_enabledhard-coded toTruetest_notify_posts_no_comment_when_pr_comment_disabled,test_notify_posts_no_all_clear_comment_when_pr_comment_disabled,test_notify_skips_labels_when_both_switches_are_off,test_notify_honors_string_false_from_dashboard_configtest_notify_still_applies_labels_when_pr_comment_disabledcollapse_allTestCollapseAll::test_opengrep_collapses_critical_section_when_enabledcollapse_allTestCollapseAll::test_tier1_collapses_critical_section_when_enabledcoerce_booldegraded to a plainbool()casttest_notify_honors_string_false_from_dashboard_config,TestFeatureFlagCoercion::test_string_false_does_not_enable_collapse_all,TestFeatureFlagCoercion::test_string_false_disables_linksAfter restoring all five: exit 0, 231 passed.
Review feedback addressed: pr_labels_enabled had the same string-boolean bug
Bugbot pointed out that
pr_labels_enabledwas still a rawconfig.get(...)truthiness read whilepr_comment_enabledwent throughcoerce_bool(). It was right, and it mattered for exactly the case this PR is about: a team that sets bothpr_comment_enabled: 'false'andpr_labels_enabled: 'false'in a Socket dashboard config would get no comment but would still get labels, becausebool("false")isTrue.labels_enablednow goes throughcoerce_bool()too. Two tests cover it — one on the comments-disabled path and one on the ordinary posting path. Mutation check: reverting to the raw read failstest_notify_honors_string_false_for_labelsandtest_notify_string_false_labels_are_skipped_on_the_normal_path, exit 1. Suite after the fix: exit 0, 233 passed.Did not run
_post_comment,_update_comment, and_reconcile_pr_labelsand assert on what would have been sent, so no network call is made.action.ymlstill points at the released2.2.1image; the image reference is bumped at release time, not here.pyproject.tomlandsocket_basics/version.pyare untouched.Note
Medium Risk
Changes default interpretation of env/dashboard boolean strings for existing PR comment and label flags, which could flip behavior for misconfigured workflows; comment suppression is scoped to the notifier after scan/upload.
Overview
Adds
pr_comment_enabled(default on) so scans can run without posting or updating the PR comment, while dashboard upload, job failure on high/critical, and other notifiers stay the same.pr_labels_enabledremains separate so labels can still be reconciled when comments are off.Adds
pr_comment_collapse_allso OpenGrep and Socket Tier 1 formatters can keep every<details>section collapsed, including critical findings, overriding the existing non-critical collapse behavior.Introduces
coerce_bool()in the config layer and uses it for notifier env loading (notifications.yamlbooleans), PR feature flags inget_feature_flags(), andGithubPRNotifier(pr_comment_enabled,pr_labels_enabled). Blank or unrecognized strings fall back to documented defaults instead of being treated as off; string forms like'false','0','yes', and'on'parse consistently across Action inputs, env, JSON, and dashboard config.Wires new inputs through
action.yml,notifications.yaml, docs, changelog, preview script, and expanded unit tests.Reviewed by Cursor Bugbot for commit afba61a. Configure here.