Skip to content

Adding the dry-run#48

Open
tmikula-dev wants to merge 30 commits intomasterfrom
feature/22-adding-the-dry-run
Open

Adding the dry-run#48
tmikula-dev wants to merge 30 commits intomasterfrom
feature/22-adding-the-dry-run

Conversation

@tmikula-dev
Copy link
Copy Markdown
Collaborator

@tmikula-dev tmikula-dev commented Apr 30, 2026

Overview

This pull request introduces a "dry-run" capability to the AquaSec security alert-to-issue workflow, improves logging consistency, and adds better tracking of sync actions. The dry-run mode allows the workflow to simulate actions without making persistent changes, which is useful for testing and validation. Additionally, logging output is standardized and made more informative, and internal statistics are now tracked for key sync operations.

Release Notes

  • Addin dry-run to CI and update the log messages

Related

Closes #22

Summary by CodeRabbit

  • New Features

    • Dry-run option added to AquaSec workflows and scan runs to test actions without changing resources
  • Improvements

    • Security sync now records detailed sync statistics and better handles orphaned child issues
    • Alerts output files are silently overwritten by default (no force flag)
    • More consistent, clearer logging and stronger repository metadata validation
    • Teams notifications unified and support dry-run
  • Documentation

    • Testing quality gate wording clarified ("Run ALWAYS all quality gates at once: make qa")

@tmikula-dev tmikula-dev self-assigned this Apr 30, 2026
@tmikula-dev tmikula-dev added the enhancement New feature or request label Apr 30, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

Walkthrough

Adds a dry-run input to AquaSec workflows and wires it through. Implements dry-run-aware logging and prefixes across the security alerts → issues pipeline, introduces SyncStats and extended SyncContext, refactors sync orchestration (deferred parent updates, labeling/closure), updates notifications to call an external script, and adjusts CLI/file-overwrite behavior and tests.

Changes

Security sync + dry-run (single cohort)

Layer / File(s) Summary
Workflow Inputs / Wiring
.github/workflows/aquasec-night-scan.yml, docs/security/aquasec-night-scan-example.yml, .github/workflows/aquasec-scan.yml
Add boolean dry-run input (default false) to dispatch/workflow_call, forward it to downstream job, and conditionally pass --dry-run to the Python invocation.
Logging Constants
src/security/constants.py, src/core/config.py
Add LOGGING_PREFIX and DRY_RUN_PREFIX; flush stdout after logging setup.
Core Infra / GitHub
src/core/github/projects.py, src/core/github/issues.py
Parameterize GraphQL/error logs, reduce some info logs to debug, and use DRY_RUN_PREFIX in dry-run enqueue/log paths.
Data Shape / Models
src/security/issues/models.py
Add IssueBodySnapshot, ParentOriginalBodies type aliases; add SyncStats dataclass; extend SyncContext with stats, parent_sub_issues_cache, severity_changes, and parent_original_bodies.
Core Sync Implementation
src/security/issues/sync.py
Large refactor: unify lifecycle around SyncContext, record/accumulate SyncStats, add _label_adept_to_close_issues, defer parent-body updates with _flush_parent_body_updates(stats), change several function signatures to accept stats/parent_original_bodies/severity_changes, and emit a consolidated sync summary via _log_sync_summary.
Alerts Parsing & Collection / CLI
src/security/alerts/parser.py, src/security/collect_alert.py, src/security/main.py
Harden repo metadata extraction; thread LOGGING_PREFIX logs through collection and main; remove --force option and make existing output file silently overwritten (logged); move logging setup after dry_run determination.
Notifications / Delivery
src/security/notifications/teams.py
Delegate Teams posting to send_notifications.py using a temp body file and --dry-run when set; use LOGGING_PREFIX/DRY_RUN_PREFIX and cleanup temp files; update skip log messages.
Promote Alerts Path
src/security/promote_alerts.py
Add LOGGING_PREFIX startup/progress logs and explicit webhook handling; reduce automatic notification branch.
Tests & Docs
tests/security/issues/test_sync.py, tests/security/test_main.py, tests/security/notifications/test_teams.py, .github/copilot-instructions.md
Update tests to new signatures/types (SyncStats, ParentOriginalBodies, etc.), add tests for dry-run/stat counters and deferred updates, remove --force tests and one teams empty-list test, and a minor docs wording tweak.

Sequence Diagram

sequenceDiagram
    actor User
    participant Main as security/main.py
    participant Parser as alerts/parser.py
    participant Sync as issues/sync.py
    participant GitHub as GitHub API
    participant Teams as notifications/teams.py

    User->>Main: run (dry-run or live)
    Main->>Parser: load alerts file
    Parser-->>Main: parsed alerts
    Main->>Sync: sync_alerts_and_issues(alerts, SyncContext)
    Sync->>Sync: build IssueIndex and SyncStats
    Sync->>Sync: ensure_parent_issue / ensure_child_issue (accumulate state)
    Sync->>Sync: _flush_parent_body_updates(stats)
    alt live run
        Sync->>GitHub: apply updates (bodies, titles, links, labels)
        GitHub-->>Sync: responses
    else dry-run
        Sync-->>Sync: log would-be operations with DRY_RUN_PREFIX
    end
    Sync->>Sync: _label_adept_to_close_issues()
    Sync->>Sync: _log_sync_summary(stats)
    Main->>Teams: notify_teams(severity_changes)
    Teams-->>Teams: call send_notifications.py (with --dry-run when applicable)
    Main-->>User: return result / summary
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

refactoring

Suggested reviewers

  • miroslavpojer

Poem

🐇 I hopped through logs with DRY-RUN cheer,
Cached bodies and counts—everything clear.
I marked the orphans, previewed each change,
Whispered "would do" before touching the range.
Now summaries bloom, prefixed and bright.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Adding the dry-run' is vague and uses generic phrasing that doesn't convey the full scope of changes, which include logging improvements, stats tracking, and workflow modifications. Consider a more descriptive title such as 'Add dry-run support and improve logging consistency' to better reflect the comprehensive nature of the changes.
Out of Scope Changes check ❓ Inconclusive While the PR's primary focus is dry-run support (issue #22), it also includes logging standardization, stats tracking, and refactoring of sync operations that, while supportive of dry-run functionality, extend beyond the strictly minimal scope of issue #22. Clarify whether logging standardization and stats tracking enhancements are part of issue #22 scope or if they should be separated into distinct PRs for clearer change isolation.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the main objectives and uses the required template structure with Overview, Release Notes, and Related sections. However, Release Notes content appears incomplete with a typo ('Addin' instead of 'Adding').
Linked Issues check ✅ Passed The PR successfully implements all acceptance criteria from issue #22: adds dry-run boolean input to workflows, ensures no changes when dry_run=true, logs all actions clearly, and preserves existing behavior when dry_run=false.
Docstring Coverage ✅ Passed Docstring coverage is 91.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/22-adding-the-dry-run

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@tmikula-dev tmikula-dev linked an issue Apr 30, 2026 that may be closed by this pull request
Comment thread src/security/issues/sync.py Outdated
priority_sync: ProjectPrioritySync | None = None,
severity_changes: list[SeverityChange] | None = None,
parent_original_bodies: dict[int, tuple[str, str]] | None = None,
stats: SyncStats | None = None,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it is allowed to be None too? This is under our control, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right for all lines 197-199 (severity_changes, parent_original_bodies, and stats). Those are always provided. I fixed those in commit: dad4a49.

Base automatically changed from feature/39-issue-body-update to master May 6, 2026 06:02
# Conflicts:
#	.github/copilot-instructions.md
#	src/security/constants.py
#	src/security/issues/sync.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/security/aquasec-night-scan-example.yml (1)

25-46: ⚠️ Potential issue | 🟠 Major

Update the pinned commit SHA to reference a version of aquasec-scan.yml that exposes the dry-run input.

The pinned commit c1fa5b54ff24fea071415da89abc4f0506344f01 references a revision of aquasec-scan.yml that does not have a dry-run input defined. Users copying this example would get a workflow validation error when attempting to pass dry-run: ${{ inputs.dry-run || false }} to a reusable workflow that doesn't accept that input. Update the SHA to point to a commit that includes the dry-run input in the reusable workflow's input specification.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/security/aquasec-night-scan-example.yml` around lines 25 - 46, The
pinned commit SHA for the reusable workflow in jobs.scan
(AbsaOSS/organizational-workflows/.github/workflows/aquasec-scan.yml@c1fa5b54ff24fea071415da89abc4f0506344f01)
points to a revision that does not declare the dry-run input; update that SHA to
a commit where the reusable workflow aquasec-scan.yml includes an inputs.dry-run
definition so the with: dry-run: ${{ inputs.dry-run || false }} call is valid,
ensuring the jobs.scan uses the new commit hash that exposes the dry-run input.
src/security/issues/sync.py (1)

411-441: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Increment children_linked counter when linking new child to parent in live mode.

The dry-run branch increments sync.stats.children_linked when a parent exists (line 382), but the live branch at lines 429–441 calls gh_issue_add_sub_issue_by_number() without bumping the counter. This makes children_linked understate reality whenever a brand-new child is auto-linked to an existing parent, diverging from the matching dry-run number in the sync summary. The counter should be incremented on success, matching the pattern used at line 629.

🔧 Proposed fix
     if parent_issue is not None:
         maybe_reopen_parent_issue(
             ctx.repo,
             parent_issue,
             rule_id=ctx.rule_id,
             dry_run=sync.dry_run,
             context="new_child",
             child_issue_number=num,
         )
         logging.debug("Add sub-issue link parent=#%d child=#%d (alert %d)", parent_issue.number, num, ctx.alert_number)
-        gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, num)
+        if gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, num):
+            sync.stats.children_linked += 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/security/issues/sync.py` around lines 411 - 441, The live-branch is
calling gh_issue_add_sub_issue_by_number(...) to link a newly created child to a
parent but does not increment sync.stats.children_linked, causing the counter to
be understated; update the block in the new-child path (after
maybe_reopen_parent_issue(...) and after gh_issue_add_sub_issue_by_number(...))
to increment sync.stats.children_linked on successful linking (matching the
dry-run behavior and the pattern used elsewhere, e.g., line with
sync.stats.children_linked increment at the dry-run branch and at line 629),
ensuring the increment occurs only in non-dry-run/live execution and after the
gh_issue_add_sub_issue_by_number call succeeds.
🧹 Nitpick comments (5)
src/core/config.py (1)

44-44: 💤 Low value

sys.stdout.flush() immediately after basicConfig is a no-op.

logging.basicConfig only configures handlers; it does not emit any records, so there is nothing buffered to flush at this point. If the goal is real-time log visibility on GitHub Actions runners, consider one of:

  • Setting PYTHONUNBUFFERED=1 in the workflow env.
  • Re-opening stdout with sys.stdout.reconfigure(line_buffering=True) (Python 3.7+).
  • Calling flush() from a custom handler after each emit (or via StreamHandler.terminator patterns).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/config.py` at line 44, The call to sys.stdout.flush() immediately
after logging.basicConfig() in src.core.config.py is a no-op; remove the flush
and instead ensure real-time log output by either setting PYTHONUNBUFFERED=1 in
the runtime environment or reconfiguring stdout for line buffering (use
sys.stdout.reconfigure(line_buffering=True) on Python 3.7+) after
logging.basicConfig(), or implement a StreamHandler that flushes on each
emit—update the code around logging.basicConfig and the sys.stdout.flush() call
accordingly.
src/core/github/projects.py (1)

103-103: ⚡ Quick win

Use lazy % formatting in this logging.warning call.

This line is touched in the diff but still uses an f-string for the log message, which conflicts with the project-wide rule. Other places in this PR (e.g., Line 60, Lines 283–287) were converted; align this one too.

♻️ Proposed change
-        logging.warning(f"GraphQL query for project #{project_number} in org '{org}' failed")
+        logging.warning("GraphQL query for project #%d in org '%s' failed", project_number, org)

As per coding guidelines: "Use lazy % formatting in logging statements: logger.info(\"msg %s\", var)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/github/projects.py` at line 103, Replace the f-string in the logging
call so it uses lazy % formatting: change the logging.warning invocation that
references project_number and org (the call currently using f"GraphQL query for
project #{project_number} in org '{org}' failed") to logging.warning("GraphQL
query for project #%s in org '%s' failed", project_number, org) so the message
follows the project-wide lazy logging convention.
src/security/constants.py (1)

32-33: ⚡ Quick win

Consider a LoggerAdapter or helper instead of raw prefix constants.

Every call site now does LOGGING_PREFIX + "msg %s". That works (the concatenation still yields a literal format string, so lazy %-formatting is preserved), but it spreads the prefix convention across many files and is easy to forget — for example, Line 270 in src/security/collect_alert.py already omits LOGGING_PREFIX while neighboring lines use it.

A logging.LoggerAdapter (or a thin get_security_logger(dry_run: bool) factory) would centralize prefix selection (live vs. dry-run) and make it impossible to forget at a call site.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/security/constants.py` around lines 32 - 33, Replace the raw
LOGGING_PREFIX and DRY_RUN_PREFIX string constants with a centralized logger
helper: implement a get_security_logger(dry_run: bool) factory that returns a
logging.Logger or logging.LoggerAdapter which automatically prepends the
appropriate prefix, and update call sites (e.g., uses in collect_alert.py and
other modules referencing LOGGING_PREFIX/DRY_RUN_PREFIX) to call
get_security_logger(dry_run) and use logger.info/error/etc. without manual
prefix concatenation; ensure the adapter preserves lazy formatting and that
tests or callers that previously relied on the constants now import and use
get_security_logger (or the LoggerAdapter class) instead.
src/security/main.py (1)

180-180: 💤 Low value

Use the module logger for consistency.

The rest of this file uses the module-level logger = logging.getLogger(__name__), but the closing Process finished message uses the root logging API. Switching keeps the logger name consistent with other records emitted by this module.

🔧 Proposed change
-    logging.info(LOGGING_PREFIX + "Process finished")
+    logger.info(LOGGING_PREFIX + "Process finished")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/security/main.py` at line 180, The final log call uses the root logging
API instead of the module-level logger; replace the call that uses
logging.info(...) with the module logger (logger.info(...)) so the "Process
finished" message is emitted with the same logger name as the rest of
src/security/main.py; locate the standalone logging.info(LOGGING_PREFIX +
"Process finished") and switch it to use logger.info(LOGGING_PREFIX + "Process
finished").
tests/security/issues/test_sync.py (1)

821-880: ⚡ Quick win

Rename test_label_orphan_* to mirror the helper rename.

The section banner at line 817 was updated to _label_adept_to_close_issues, but the six test functions below still carry the legacy test_label_orphan_* names (and docstrings/comments referencing "orphan"). After the helper rename, these names create a discoverability gap — searching for label_adept_to_close won’t surface the tests, and the suite reads as if it’s testing something that no longer exists.

♻️ Suggested rename
-def test_label_orphan_no_orphans() -> None:
+def test_label_adept_to_close_no_unmatched() -> None:
     """No labelling when all children have matching alerts."""
@@
-def test_label_orphan_found(mocker: MockerFixture) -> None:
+def test_label_adept_to_close_found(mocker: MockerFixture) -> None:
     """Labels child issues that have no matching alert."""
@@
-def test_label_orphan_dry_run() -> None:
+def test_label_adept_to_close_dry_run() -> None:
     """Dry-run: logs but does not call gh."""
@@
-def test_label_orphan_skips_already_labelled() -> None:
+def test_label_adept_to_close_skips_already_labelled() -> None:
     """Skips if adept-to-close label is already present."""
@@
-def test_label_orphan_skips_closed_issues() -> None:
+def test_label_adept_to_close_skips_closed_issues() -> None:
     """Closed child issues are not labelled as unmatched."""
@@
-def test_label_orphan_no_repo_in_secmeta() -> None:
+def test_label_adept_to_close_no_repo_in_secmeta() -> None:
     """Skips labelling if no repo in secmeta."""
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/security/issues/test_sync.py` around lines 821 - 880, Rename the six
test functions and their docstrings/comments from the legacy test_label_orphan_*
names to names that reference the new helper _label_adept_to_close_issues (e.g.,
test_label_adept_to_close_no_orphans, test_label_adept_to_close_found,
test_label_adept_to_close_dry_run,
test_label_adept_to_close_skips_already_labelled,
test_label_adept_to_close_skips_closed_issues,
test_label_adept_to_close_no_repo_in_secmeta) and update any occurrences of
"orphan" in their docstrings to mention "adept_to_close" or "unmatched alert" so
tests are discoverable and descriptions match the helper name
(_label_adept_to_close_issues) used in the diff.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/github/projects.py`:
- Line 27: This imports DRY_RUN_PREFIX from security into core (layering
violation); remove the import and instead add a constructor parameter (e.g.,
prefix or logging_prefix) to ProjectPrioritySync and store it as an instance
attribute, update all uses inside ProjectPrioritySync to reference self.prefix,
and update all callers (including security package) to pass
security.constants.DRY_RUN_PREFIX when constructing ProjectPrioritySync;
alternatively, if you prefer the other option, move DRY_RUN_PREFIX into core
(e.g., core/config or core/logging_prefix) and re-export from security, but do
not keep the direct import from security in src/core/github/projects.py.

In `@src/security/collect_alert.py`:
- Line 234: The log message using logger.info(LOGGING_PREFIX + "Success fetching
repository metadata") is unnatural; update it to past-tense natural English
(e.g., use "Successfully fetched repository metadata") by changing the string
concatenated with LOGGING_PREFIX where logger.info is called so the message
reads correctly.
- Around line 269-270: Change the two logging calls to use the LOGGING_PREFIX on
both lines, use %d (not %i) for the numeric placeholder, and fix the
wording/pluralization—e.g., in the logger.info that references logger,
LOGGING_PREFIX, count and state replace "Successfully fetched %i %s security
alert/s" with a message that uses "%d" and a clean plural form like
"Successfully fetched %d %s security alert(s)" (or compute pluralization from
count if preferred), and in the logger.debug that references out_file and
LOGGING_PREFIX reword the tense to something like "Saved fetched security alerts
to: %s" while prefixing it with LOGGING_PREFIX so both logs are consistent.

In `@src/security/issues/sync.py`:
- Around line 879-916: The indented "Parent issues" and "Child issues" lines in
_log_sync_summary currently hardcode the "[DRY-RUN]" label; update those lines
to use the correct prefix determined earlier (DRY_RUN_PREFIX vs LOGGING_PREFIX)
or compute a small tag variable (e.g., tag = DRY_RUN_PREFIX if dry_run else "")
and prepend that tag instead of the literal "[DRY-RUN]". Ensure you adjust the
string concatenation for both the parent and child lines (refer to
_log_sync_summary, parent_parts, child_parts, and prefix) so non-dry runs do not
display the dry-run label.

In `@src/security/main.py`:
- Around line 135-138: The start-process log statements use eager "%"
interpolation; update both branches so logger.info receives the format string
and repo as a separate argument (e.g., logger.info(LOGGING_PREFIX + "Starting
the DRY-RUN process for %s", repo) and logger.info(LOGGING_PREFIX + "Starting
process for %s", repo)) to enable lazy formatting; change the two calls around
the dry_run check that reference logger, LOGGING_PREFIX, and repo accordingly.

---

Outside diff comments:
In `@docs/security/aquasec-night-scan-example.yml`:
- Around line 25-46: The pinned commit SHA for the reusable workflow in
jobs.scan
(AbsaOSS/organizational-workflows/.github/workflows/aquasec-scan.yml@c1fa5b54ff24fea071415da89abc4f0506344f01)
points to a revision that does not declare the dry-run input; update that SHA to
a commit where the reusable workflow aquasec-scan.yml includes an inputs.dry-run
definition so the with: dry-run: ${{ inputs.dry-run || false }} call is valid,
ensuring the jobs.scan uses the new commit hash that exposes the dry-run input.

In `@src/security/issues/sync.py`:
- Around line 411-441: The live-branch is calling
gh_issue_add_sub_issue_by_number(...) to link a newly created child to a parent
but does not increment sync.stats.children_linked, causing the counter to be
understated; update the block in the new-child path (after
maybe_reopen_parent_issue(...) and after gh_issue_add_sub_issue_by_number(...))
to increment sync.stats.children_linked on successful linking (matching the
dry-run behavior and the pattern used elsewhere, e.g., line with
sync.stats.children_linked increment at the dry-run branch and at line 629),
ensuring the increment occurs only in non-dry-run/live execution and after the
gh_issue_add_sub_issue_by_number call succeeds.

---

Nitpick comments:
In `@src/core/config.py`:
- Line 44: The call to sys.stdout.flush() immediately after
logging.basicConfig() in src.core.config.py is a no-op; remove the flush and
instead ensure real-time log output by either setting PYTHONUNBUFFERED=1 in the
runtime environment or reconfiguring stdout for line buffering (use
sys.stdout.reconfigure(line_buffering=True) on Python 3.7+) after
logging.basicConfig(), or implement a StreamHandler that flushes on each
emit—update the code around logging.basicConfig and the sys.stdout.flush() call
accordingly.

In `@src/core/github/projects.py`:
- Line 103: Replace the f-string in the logging call so it uses lazy %
formatting: change the logging.warning invocation that references project_number
and org (the call currently using f"GraphQL query for project #{project_number}
in org '{org}' failed") to logging.warning("GraphQL query for project #%s in org
'%s' failed", project_number, org) so the message follows the project-wide lazy
logging convention.

In `@src/security/constants.py`:
- Around line 32-33: Replace the raw LOGGING_PREFIX and DRY_RUN_PREFIX string
constants with a centralized logger helper: implement a
get_security_logger(dry_run: bool) factory that returns a logging.Logger or
logging.LoggerAdapter which automatically prepends the appropriate prefix, and
update call sites (e.g., uses in collect_alert.py and other modules referencing
LOGGING_PREFIX/DRY_RUN_PREFIX) to call get_security_logger(dry_run) and use
logger.info/error/etc. without manual prefix concatenation; ensure the adapter
preserves lazy formatting and that tests or callers that previously relied on
the constants now import and use get_security_logger (or the LoggerAdapter
class) instead.

In `@src/security/main.py`:
- Line 180: The final log call uses the root logging API instead of the
module-level logger; replace the call that uses logging.info(...) with the
module logger (logger.info(...)) so the "Process finished" message is emitted
with the same logger name as the rest of src/security/main.py; locate the
standalone logging.info(LOGGING_PREFIX + "Process finished") and switch it to
use logger.info(LOGGING_PREFIX + "Process finished").

In `@tests/security/issues/test_sync.py`:
- Around line 821-880: Rename the six test functions and their
docstrings/comments from the legacy test_label_orphan_* names to names that
reference the new helper _label_adept_to_close_issues (e.g.,
test_label_adept_to_close_no_orphans, test_label_adept_to_close_found,
test_label_adept_to_close_dry_run,
test_label_adept_to_close_skips_already_labelled,
test_label_adept_to_close_skips_closed_issues,
test_label_adept_to_close_no_repo_in_secmeta) and update any occurrences of
"orphan" in their docstrings to mention "adept_to_close" or "unmatched alert" so
tests are discoverable and descriptions match the helper name
(_label_adept_to_close_issues) used in the diff.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ec14afdd-9bbd-416c-ad8e-e38f2a81701c

📥 Commits

Reviewing files that changed from the base of the PR and between 63380d9 and dad4a49.

📒 Files selected for processing (18)
  • .github/copilot-instructions.md
  • .github/workflows/aquasec-night-scan.yml
  • .github/workflows/aquasec-scan.yml
  • docs/security/aquasec-night-scan-example.yml
  • src/core/config.py
  • src/core/github/issues.py
  • src/core/github/projects.py
  • src/security/alerts/parser.py
  • src/security/collect_alert.py
  • src/security/constants.py
  • src/security/issues/models.py
  • src/security/issues/sync.py
  • src/security/main.py
  • src/security/notifications/teams.py
  • src/security/promote_alerts.py
  • tests/security/issues/test_sync.py
  • tests/security/notifications/test_teams.py
  • tests/security/test_main.py
💤 Files with no reviewable changes (1)
  • tests/security/notifications/test_teams.py

Comment thread src/core/github/projects.py
Comment thread src/security/collect_alert.py Outdated
Comment thread src/security/collect_alert.py Outdated
Comment thread src/security/issues/sync.py
Comment thread src/security/main.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/security/issues/sync.py (1)

411-442: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

children_linked is undercounted in non-dry-run mode for newly created children.

In dry-run with a parent issue, line 382 increments sync.stats.children_linked. However, in the non-dry-run path (line 442), gh_issue_add_sub_issue_by_number is called for the new child but sync.stats.children_linked is not incremented. This causes a real-vs-dry-run stats divergence: a dry-run will report N linked children, while the actual run will report fewer (only the increments from _ensure_child_linked_to_parent for existing children).

🔧 Proposed fix
     if parent_issue is not None:
         maybe_reopen_parent_issue(
             ctx.repo,
             parent_issue,
             rule_id=ctx.rule_id,
             dry_run=sync.dry_run,
             context="new_child",
             child_issue_number=num,
         )
         logging.debug("Add sub-issue link parent=#%d child=#%d (alert %d)", parent_issue.number, num, ctx.alert_number)
-        gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, num)
+        if gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, num):
+            sync.stats.children_linked += 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/security/issues/sync.py` around lines 411 - 442, The non-dry-run path
creating a new child calls gh_issue_add_sub_issue_by_number but never increments
sync.stats.children_linked, causing stat divergence; update the block in sync.py
that handles a newly created child (after maybe_reopen_parent_issue and/or
immediately after gh_issue_add_sub_issue_by_number) to increment
sync.stats.children_linked by 1 when a link is actually created (mirror the
dry-run increment), ensuring the counter and logging reflect the real
linked-child action.
🧹 Nitpick comments (1)
tests/security/issues/test_sync.py (1)

557-566: ⚡ Quick win

Add a parallel non-dry-run test for children_linked stats with a parent.

This test validates the dry-run path increments both children_created and children_linked when a parent is provided. There is no equivalent live-path test, which is why the missing increment in the non-dry-run branch (see comment on src/security/issues/sync.py lines 411–442) went undetected. Adding a symmetric test will lock in the corrected behavior.

🔧 Suggested addition
def test_handle_new_child_real_with_parent_increments_linked(mocker: MockerFixture, sast_alert: Alert) -> None:
    """Non-dry-run with a parent increments children_linked alongside children_created."""
    mocker.patch("security.issues.sync.gh_issue_create", return_value=42)
    mocker.patch("security.issues.sync.gh_issue_add_sub_issue_by_number", return_value=True)
    parent = Issue(number=7, state="open", title="P", body="pb")
    ctx = _make_alert_context(alert=sast_alert)
    sync = _make_sync_context(notifications=[])
    _handle_new_child_issue(ctx=ctx, sync=sync, parent_issue=parent)
    assert 1 == sync.stats.children_created
    assert 1 == sync.stats.children_linked
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/security/issues/test_sync.py` around lines 557 - 566, Add a non-dry-run
counterpart to the existing dry-run test to ensure the live-path increments
children_linked when a parent is provided: create a new test (e.g.,
test_handle_new_child_real_with_parent_increments_linked) that patches
security.issues.sync.gh_issue_create and gh_issue_add_sub_issue_by_number to
return success, constructs a parent Issue and a real (non-dry-run) sync context
via _make_sync_context, calls _handle_new_child_issue(ctx=..., sync=...,
parent_issue=parent) and asserts sync.stats.children_created == 1 and
sync.stats.children_linked == 1 so the non-dry-run branch behavior is verified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/security/issues/sync.py`:
- Line 391: Replace the non-ASCII punctuation in the log messages: change the EN
DASH character (U+2013) used in the logging.debug message that contains "No
parent issue yet for rule_id=%s – link will happen on next sync" (and the two
other flagged logging.debug messages referencing ctx.rule_id) to a plain ASCII
hyphen-minus '-' and replace the ellipsis character (U+2026) found in the other
logged message with three ASCII dots "..." so all logging strings use only plain
ASCII punctuation; update the three logging.debug messages (the ones using
logging.debug and ctx.rule_id) accordingly.
- Around line 583-592: The counter sync.stats.children_title_updated is being
incremented regardless of whether the title edit actually succeeded; move the
increment so it only happens when an update occurs (mirror the parent-title
handling). Specifically, inside the else branch where
gh_issue_edit_title(ctx.repo, issue.number, expected_title) is called, increment
sync.stats.children_title_updated only after gh_issue_edit_title returns True
and you set issue.title = expected_title and log the success; do not increment
it in the outer expected_title != (issue.title or "") scope or in the dry-run
branch.

In `@src/security/main.py`:
- Around line 153-155: The debug log in the overwrite branch of the output-file
handling omits the required domain prefix; update the logger.debug call in the
block that checks os.path.exists(out_file) (around the code using out_file and
logger) to prepend LOGGING_PREFIX (e.g., LOGGING_PREFIX + " - ") to the message
so it follows the "Security - ..." convention; ensure the message content
remains "Output file is already present: overwriting" but begins with
LOGGING_PREFIX.

---

Outside diff comments:
In `@src/security/issues/sync.py`:
- Around line 411-442: The non-dry-run path creating a new child calls
gh_issue_add_sub_issue_by_number but never increments
sync.stats.children_linked, causing stat divergence; update the block in sync.py
that handles a newly created child (after maybe_reopen_parent_issue and/or
immediately after gh_issue_add_sub_issue_by_number) to increment
sync.stats.children_linked by 1 when a link is actually created (mirror the
dry-run increment), ensuring the counter and logging reflect the real
linked-child action.

---

Nitpick comments:
In `@tests/security/issues/test_sync.py`:
- Around line 557-566: Add a non-dry-run counterpart to the existing dry-run
test to ensure the live-path increments children_linked when a parent is
provided: create a new test (e.g.,
test_handle_new_child_real_with_parent_increments_linked) that patches
security.issues.sync.gh_issue_create and gh_issue_add_sub_issue_by_number to
return success, constructs a parent Issue and a real (non-dry-run) sync context
via _make_sync_context, calls _handle_new_child_issue(ctx=..., sync=...,
parent_issue=parent) and asserts sync.stats.children_created == 1 and
sync.stats.children_linked == 1 so the non-dry-run branch behavior is verified.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d65f9a8e-4837-4f10-94a6-d01ed7acc9ae

📥 Commits

Reviewing files that changed from the base of the PR and between dad4a49 and d94c4e4.

📒 Files selected for processing (4)
  • src/security/collect_alert.py
  • src/security/issues/sync.py
  • src/security/main.py
  • tests/security/issues/test_sync.py

Comment thread src/security/issues/sync.py
Comment thread src/security/issues/sync.py Outdated
Comment thread src/security/main.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/security/issues/test_sync.py (1)

1117-1140: ⚡ Quick win

Hardcode-coupling on prefix strings will silently break tests when constants change.

The parametrize values on lines 1118–1119 duplicate the literal contents of LOGGING_PREFIX and DRY_RUN_PREFIX from security.constants. If those constants are ever tweaked (rename, spacing, decoration), these tests will fail in a confusing way without protecting any real behavior. Importing the constants directly couples the tests to the production source of truth.

♻️ Proposed refactor
+from security.constants import DRY_RUN_PREFIX, LOGGING_PREFIX
@@
-@pytest.mark.parametrize("dry_run,prefix", [
-    (False, "Security Alerts to Issues - "),
-    (True, "Security Alerts to Issues [DRY-RUN] - "),
-])
+@pytest.mark.parametrize("dry_run,prefix", [
+    (False, LOGGING_PREFIX),
+    (True, DRY_RUN_PREFIX),
+])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/security/issues/test_sync.py` around lines 1117 - 1140, Replace the
hardcoded prefix strings in test_log_sync_summary with the canonical constants
from security.constants: import LOGGING_PREFIX and DRY_RUN_PREFIX and change the
parametrize tuple to use (False, LOGGING_PREFIX) and (True, DRY_RUN_PREFIX);
update references in the assertions to use the injected prefix variable so the
test adapts if LOGGING_PREFIX/DRY_RUN_PREFIX change and still validates
_log_sync_summary behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/security/issues/sync.py`:
- Around line 560-571: _in _rebuild_and_apply_child_body and
_flush_parent_body_updates the counters children_body_updated and
parents_body_updated are being incremented unconditionally even when a live
gh_issue_edit_body/gh_issue_edit_title call may have failed; change the live
branch to capture and check the return value from gh_issue_edit_body (call it
and store its result) and only increment sync.stats.children_body_updated (and
similarly sync.stats.parents_body_updated in _flush_parent_body_updates) when
that result indicates success, while leaving the unconditional increment in the
sync.dry_run path so dry-run still reports updates.

---

Nitpick comments:
In `@tests/security/issues/test_sync.py`:
- Around line 1117-1140: Replace the hardcoded prefix strings in
test_log_sync_summary with the canonical constants from security.constants:
import LOGGING_PREFIX and DRY_RUN_PREFIX and change the parametrize tuple to use
(False, LOGGING_PREFIX) and (True, DRY_RUN_PREFIX); update references in the
assertions to use the injected prefix variable so the test adapts if
LOGGING_PREFIX/DRY_RUN_PREFIX change and still validates _log_sync_summary
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60021d5c-fa21-47aa-b70b-164c2efdc2ae

📥 Commits

Reviewing files that changed from the base of the PR and between d94c4e4 and e4e9aed.

📒 Files selected for processing (3)
  • src/security/issues/models.py
  • src/security/issues/sync.py
  • tests/security/issues/test_sync.py

Comment thread src/security/issues/sync.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/security/issues/sync.py (1)

432-441: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate child-to-parent link reporting on the actual link result.

The new-child path fires gh_issue_add_sub_issue_by_number(...) without checking it, so live runs never increment children_linked there. The repair path does check the helper, but it logs a successful link before that check. Together, that makes the new sync summary undercount links and can claim a link succeeded when GitHub rejected it.

Suggested minimal fix
     if parent_issue is not None:
         maybe_reopen_parent_issue(
             ctx.repo,
             parent_issue,
             rule_id=ctx.rule_id,
             dry_run=sync.dry_run,
             stats=sync.stats,
         )
-        logging.debug("Add sub-issue link parent=#%d child=#%d (alert %d)", parent_issue.number, num, ctx.alert_number)
-        gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, num)
+        logging.debug(
+            LOGGING_PREFIX + "Adding sub-issue link parent=#%d child=#%d (alert %d)",
+            parent_issue.number,
+            num,
+            ctx.alert_number,
+        )
+        if gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, num):
+            logging.info(LOGGING_PREFIX + "Linked child issue #%d to parent #%d", num, parent_issue.number)
+            sync.stats.children_linked += 1
...
-    logging.info(
-        LOGGING_PREFIX + "Linked child issue #%d to parent #%d",
-        issue.number,
-        parent_issue.number,
-    )
     if gh_issue_add_sub_issue_by_number(ctx.repo, parent_issue.number, issue.number):
+        logging.info(
+            LOGGING_PREFIX + "Linked child issue #%d to parent #%d",
+            issue.number,
+            parent_issue.number,
+        )
         cache[parent_issue.number].add(issue.number)
         sync.stats.children_linked += 1

Also applies to: 627-634

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/security/issues/sync.py` around lines 432 - 441, The code logs and counts
a successful child→parent link before verifying gh_issue_add_sub_issue_by_number
actually succeeded; change both the "new-child" and the "repair" paths so you
call gh_issue_add_sub_issue_by_number(...) and check its return value (or catch
its failure) before logging success and incrementing sync.stats.children_linked,
and only log an error/skip increment when the helper indicates the link failed;
update the blocks around maybe_reopen_parent_issue and
gh_issue_add_sub_issue_by_number (also mirror the same fix in the similar block
around lines handling the repair path mentioned) so the logging and stats update
occur strictly after a successful helper result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/security/issues/sync.py`:
- Line 278: The new debug logs in src/security/issues/sync.py use raw "DRY-RUN:"
or missing domain prefixes; update those logging.debug calls (the ones
referencing existing.number / expected_title and the other occurrences around
lines 302-304, 395-397, 564-566, 586-592, 774-776) to prepend the standard
domain prefix "Security - " (e.g., "Security - DRY-RUN: ...") so all messages
follow the "Security - <message>" format; modify each logging.debug invocation
accordingly while preserving existing interpolation of variables like
existing.number and expected_title.
- Around line 821-832: The code logs "Marked issue ..." and increments
stats.children_marked_for_closure before confirming the label mutation
succeeded; change the flow so that for dry_run you keep the existing DRY_RUN log
only, but in the live path call gh_issue_add_labels(repo, issue.number,
[LABEL_SEC_ADEPT_TO_CLOSE]) first (wrap it to catch/handle exceptions or check
its success), and only after it returns successfully emit the LOGGING_PREFIX
"Marked issue #%d for closure..." message and increment
stats.children_marked_for_closure; on mutation failure log an error and do not
increment the stat. Ensure you reference gh_issue_add_labels,
stats.children_marked_for_closure, LABEL_SEC_ADEPT_TO_CLOSE, and the dry_run
branch when making this change.

---

Outside diff comments:
In `@src/security/issues/sync.py`:
- Around line 432-441: The code logs and counts a successful child→parent link
before verifying gh_issue_add_sub_issue_by_number actually succeeded; change
both the "new-child" and the "repair" paths so you call
gh_issue_add_sub_issue_by_number(...) and check its return value (or catch its
failure) before logging success and incrementing sync.stats.children_linked, and
only log an error/skip increment when the helper indicates the link failed;
update the blocks around maybe_reopen_parent_issue and
gh_issue_add_sub_issue_by_number (also mirror the same fix in the similar block
around lines handling the repair path mentioned) so the logging and stats update
occur strictly after a successful helper result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca8d87cd-b70a-40b5-b614-8140783af59e

📥 Commits

Reviewing files that changed from the base of the PR and between e4e9aed and 8badb4b.

📒 Files selected for processing (1)
  • src/security/issues/sync.py

Comment thread src/security/issues/sync.py
Comment thread src/security/issues/sync.py
Copy link
Copy Markdown
Contributor

@miroslavpojer miroslavpojer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add dry-run support to workflow dispatch calls

3 participants