-
Notifications
You must be signed in to change notification settings - Fork 1
fix(security): remediation bundle — AES-256-GCM, DOMPurify, WebSocket auth, 4 CVEs, SonarCloud #198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6624995
f08fe6e
957e149
68b49b8
95c672f
ce4404d
a88864e
5286f72
8f98e92
3e7bf2d
c6790e2
773a8f1
b396cbc
a67ea0b
357606f
09991f1
863ca0b
8439c5e
094f9f7
981b638
05cbbe8
708b123
e94289f
54d9f73
0b7770e
4276418
d6368ed
d842934
65011c5
b66c76f
0f6105a
e447d4d
8b73818
dc3f923
d94ccbc
e386337
13fcf33
d7fc987
b51771b
1a70685
3c188b1
e6db1dd
f3036a3
e230477
15dfccb
64c798f
d22bfcc
f6d2a89
5744e9a
1bf1de1
4434134
6195dd4
26eff2c
6398cd3
9ae8225
a7af29b
e43cc06
18c18d7
d19ee96
53421be
8d84124
3513ee1
6529265
cf9a0a8
ea4087a
a25c0b3
fe32cb4
9a9696f
d388307
ba7ddee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,8 +9,8 @@ comment: | |
| coverage: | ||
| precision: 2 | ||
| range: | ||
| - 60.0 | ||
| - 80.0 | ||
| - 60.0 | ||
| - 80.0 | ||
| round: down | ||
| status: | ||
| changes: false | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -264,11 +264,10 @@ jobs: | |||||||
| name: SonarCloud PR Comment | ||||||||
| runs-on: ubuntu-latest | ||||||||
| needs: sonar | ||||||||
| # Only run when the scan succeeded — otherwise the API has no findings | ||||||||
| # to comment on and the script would post a confusing empty/errored | ||||||||
| # comment. Branch protection enforces SonarCloud Scan as required, so | ||||||||
| # a scan failure correctly blocks the merge regardless. | ||||||||
| if: github.event_name == 'pull_request' && needs.sonar.result == 'success' | ||||||||
| # The scan uploads findings to SonarCloud API before quality gate check. | ||||||||
| # Run even when quality gate fails — the API still has data to comment. | ||||||||
| # always() overrides needs dependency failure; !cancelled() alone does not. | ||||||||
| if: github.event_name == 'pull_request' && always() && !cancelled() | ||||||||
|
Comment on lines
+269
to
+270
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The condition change looks reasonable and the inline comments explain the motivation clearly. However, there is a subtle edge case to consider: If the Suggestion: Consider adding a guard in the script to detect whether the scan actually ran — e.g., by checking the sonar-scan step outcome if feasible, or by querying the API for a baseline check before posting a "clean" comment. Suggestion:
Suggested change
|
||||||||
| permissions: | ||||||||
| contents: read | ||||||||
| pull-requests: write | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,21 +1,36 @@ | ||||||||||||||||||||||||||||||||||||||||||
| # Fallow audit gate | ||||||||||||||||||||||||||||||||||||||||||
| # Base pinned to https://github.com/BillyOutlast/drop/tree/rebuild so the gate | ||||||||||||||||||||||||||||||||||||||||||
| # only fails on findings introduced since the remote rebuild branch. | ||||||||||||||||||||||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||||||||||||||||||||||
| # Fallow audit gate — every finding in changed files blocks the commit. | ||||||||||||||||||||||||||||||||||||||||||
| # `gate = "all"` is configured in fallow.toml so inherited findings also gate. | ||||||||||||||||||||||||||||||||||||||||||
| # Base pinned to https://github.com/BillyOutlast/drop/tree/rebuild. | ||||||||||||||||||||||||||||||||||||||||||
| FALLOW_AUDIT_BASE=origin/rebuild fallow audit --format json --quiet --explain --gate-marker agent || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Run prisma generate when schema or proto files changed (needed for typecheck) | ||||||||||||||||||||||||||||||||||||||||||
| # Prisma generate trigger — only when schema or proto files change (build-dep, | ||||||||||||||||||||||||||||||||||||||||||
| # not a content check). | ||||||||||||||||||||||||||||||||||||||||||
| changed_schema=$(git diff --cached --name-only --diff-filter=ACM -- 'server/prisma/schema.prisma' 'server/**/*.proto') | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$changed_schema" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| echo "Prisma schema or proto changed — running prisma generate..." | ||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop exec prisma generate || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop lint-staged && pnpm --filter drop typecheck | ||||||||||||||||||||||||||||||||||||||||||
| # Whole-repo type-safety + style gate (prettier --check + eslint, no auto-fix). | ||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop lint || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switching from Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop typecheck || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Check test files for bare .toBeDefined() / .not.toBeNull() without companion assertions | ||||||||||||||||||||||||||||||||||||||||||
| changed_tests=$(git diff --cached --name-only --diff-filter=ACM -- '*.test.ts' '*.spec.ts') | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$changed_tests" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| bare_assertions=$(grep -n '\.toBeDefined()\|\.not\.toBeNull()' $changed_tests 2>/dev/null | grep -v '\.toEqual\|\.toMatchSnapshot\|\.toStrictEqual\|\.toBe(' || true) | ||||||||||||||||||||||||||||||||||||||||||
| # Whole-repo shellcheck — all git-tracked .sh files, not just staged ones. | ||||||||||||||||||||||||||||||||||||||||||
| if command -v shellcheck >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||
| tracked_sh=$(git ls-files '*.sh') | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$tracked_sh" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| echo "$tracked_sh" | xargs shellcheck --severity=warning || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The shellcheck and bare-assertion scans use Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||
| echo "shellcheck not installed — skipping shell script checks" | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Whole-repo bare-assertion scan — flag toBeDefined / .not.toBeNull() without | ||||||||||||||||||||||||||||||||||||||||||
| # a companion meaningful assertion across every test file in the repo. | ||||||||||||||||||||||||||||||||||||||||||
| tracked_tests=$(git ls-files '*.test.ts' '*.spec.ts') | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$tracked_tests" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| bare_assertions=$(echo "$tracked_tests" | xargs grep -n '\.toBeDefined()\|\.not\.toBeNull()' 2>/dev/null | grep -v '\.toEqual\|\.toMatchSnapshot\|\.toStrictEqual\|\.toBe(' || true) | ||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bare assertion scan also uses Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$bare_assertions" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| echo "ERROR: Bare .toBeDefined() or .not.toBeNull() without companion assertion:" | ||||||||||||||||||||||||||||||||||||||||||
| echo "$bare_assertions" | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -24,30 +39,12 @@ if [ -n "$changed_tests" ]; then | |||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Check shell scripts | ||||||||||||||||||||||||||||||||||||||||||
| changed_sh=$(git diff --cached --name-only --diff-filter=ACM -- '*.sh') | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$changed_sh" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| if command -v shellcheck >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||
| echo "$changed_sh" | xargs shellcheck --severity=warning || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||
| echo "shellcheck not installed — skipping shell script checks" | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Check Rust formatting on changed .rs files, filtered by workspace | ||||||||||||||||||||||||||||||||||||||||||
| changed_rs=$(git diff --cached --name-only --diff-filter=ACM -- '*.rs') | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$changed_rs" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| torrential_rs=$(echo "$changed_rs" | grep '^torrential/' || true) | ||||||||||||||||||||||||||||||||||||||||||
| cli_rs=$(echo "$changed_rs" | grep '^cli/' || true) | ||||||||||||||||||||||||||||||||||||||||||
| desktop_rs=$(echo "$changed_rs" | grep '^desktop/' || true) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$torrential_rs" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| cargo fmt --manifest-path torrential/Cargo.toml -- --check $torrential_rs || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$cli_rs" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| cargo fmt --manifest-path cli/Cargo.toml -- --check $cli_rs || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| if [ -n "$desktop_rs" ]; then | ||||||||||||||||||||||||||||||||||||||||||
| cargo fmt --manifest-path desktop/src-tauri/Cargo.toml -- --check $desktop_rs || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
| # Whole-repo cargo fmt --check across all three rust workspaces (no auto-fix; | ||||||||||||||||||||||||||||||||||||||||||
| # forces developer to format manually if any .rs file drifts). | ||||||||||||||||||||||||||||||||||||||||||
| if command -v cargo >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||
| cargo fmt --all --manifest-path torrential/Cargo.toml -- --check || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| cargo fmt --all --manifest-path cli/Cargo.toml -- --check || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| cargo fmt --all --manifest-path desktop/src-tauri/Cargo.toml -- --check || exit 1 | ||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||
| echo "cargo not installed — skipping rust format checks" | ||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+42
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Running Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,69 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Pre-push gate: incremental tests, fallow audit, optional full suite, PR thread check. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -u | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Incremental test run: only tests affected by pushed changes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Full suite still runs in CI. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop test:changed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if command -v pnpm >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop test:changed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: pnpm not found — cannot run incremental tests" >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The script uses Compare with
Suggestion:
Suggested change
Comment on lines
+8
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exit code from incremental tests is silently discarded — pushes with failing tests succeed. The original single-line As a result, developers can push code with failing incremental tests without ever being blocked, defeating the primary purpose of the pre-push gate. Fix: Capture the test exit code and propagate it, e.g.: if command -v pnpm >/dev/null 2>&1; then
pnpm --filter drop test:changed || exit 1
else
echo ":: pnpm not found — cannot run incremental tests" >&2
exit 1
fiSuggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Detect base from upstream tracking. If upstream matches current branch | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (PR branch self-tracking) or unset, fall back to origin/rebuild. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UPSTREAM="$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null || true)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REMOTE_BASE="${UPSTREAM}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -z "${REMOTE_BASE}" ] || [ "${REMOTE_BASE##*/}" = "${CURRENT_BRANCH}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
BillyOutlast marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REMOTE_BASE="origin/rebuild" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
BillyOutlast marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # fallow audit gate: block on 'fail' verdict, tolerate JSON parse errors. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if command -v fallow >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FALLOW_STDERR="$(mktemp)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FALLOW_JSON="$(FALLOW_AUDIT_BASE="${REMOTE_BASE}" fallow audit --format json --quiet --explain --gate-marker agent 2>"${FALLOW_STDERR}" || echo '{"verdict":"error","error":true}')" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FALLOW_STDERR_CONTENT="$(cat "${FALLOW_STDERR}")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rm -f "${FALLOW_STDERR}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The temporary file created via Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -n "${FALLOW_STDERR_CONTENT}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: fallow audit stderr: ${FALLOW_STDERR_CONTENT}" >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if command -v jq >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FALLOW_VERDICT="$(echo "${FALLOW_JSON}" | jq -r '.verdict // "error"' 2>/dev/null || echo "error")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Fallback: parse verdict without jq so the gate still works | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FALLOW_VERDICT="$(echo "${FALLOW_JSON}" | grep -oE '"verdict":"[a-z]+"' | cut -d'"' -f4 || echo "error")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+33
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fallback JSON verdict parser using More importantly, the project's other shell scripts ( Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "${FALLOW_VERDICT}" = "fail" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: fallow audit verdict: fail. Fix findings or use --no-verify." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [ "${FALLOW_VERDICT}" = "error" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: fallow audit returned errors (non-blocking) — continuing" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: fallow CLI not found — skipping audit gate" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The full test suite ( Suggestion:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No While Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Optional full test suite gate: verify no regressions before push. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Disabled by default — CI runs the full suite. Set FULL_TEST=1 to enable. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -n "${FULL_TEST:-}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pnpm --filter drop test | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Check for PR review threads. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Quick count only — use /pull-review-comments skill for full details + resolution. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if command -v gh >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lines inside the Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| REPO="$(git remote get-url origin | sed -nE 's#.*github.com[:/]([^/]+/[^/.]+)(\.git)?$#\1#p' 2>/dev/null)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -z "${REPO}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: unable to parse GitHub repository from remote URL — skipping review thread check" >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When If the intent is to skip the PR thread check when the repo can't be determined, the script should fall through to the implicit exit code of previous commands rather than explicitly exiting 0. Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PR_NUMBER="$(gh pr view --json number -q .number 2>/dev/null || true)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -n "${PR_NUMBER}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: PR #${PR_NUMBER} open — run /pull-review-comments for unresolved review threads before pushing" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo ":: gh or jq not found — skipping review thread check" >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+67
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The file is missing a trailing newline at the end of the file ( Suggestion:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| --- | ||
| name: ci-format-guard | ||
| description: Use when pre-commit hooks pass locally but CI fails on formatting (prettier --check, cargo fmt --check), or when adding/modifying git hooks that enforce code style. Also use when a formatting failure in one CI job cascades to skip downstream lint/test jobs. | ||
| --- | ||
|
|
||
| # CI Format Guard | ||
|
|
||
| Pre-commit hooks that pass locally but CI fails on formatting mean the hook checks but doesn't fix. CI formatting is the last line of defense — it should never be the first. | ||
|
|
||
| ## Core Principle | ||
|
|
||
| **Hooks auto-fix, not just check.** If a hook detects a formatting issue it can fix, it should fix it — not report it and block. | ||
|
|
||
| ## Pattern: Auto-Fix + Re-Stage | ||
|
|
||
| ```bash | ||
| # ❌ BAD: Checks but doesn't fix — CI will fail | ||
| cargo fmt --manifest-path desktop/src-tauri/Cargo.toml -- --check $changed_rs || exit 1 | ||
|
|
||
| # ✅ GOOD: Auto-fixes AND propagates failure for unfixable errors | ||
| cargo fmt --manifest-path desktop/src-tauri/Cargo.toml -- $changed_rs || exit 1 | ||
| echo "$changed_rs" | xargs -r git add | ||
| ``` | ||
|
|
||
| The `|| exit 1` stays — cargo fmt can fail for unfixable reasons (missing toolchain, malformed syntax). But formatting issues get auto-fixed and re-staged. | ||
|
|
||
| ## Quick Reference | ||
|
|
||
| | Workspace | Format command (auto-fix) | Verify command (CI) | | ||
| |-----------|--------------------------|---------------------| | ||
| | server (TS/Vue) | `pnpm --filter drop exec prettier --write <file>` | `pnpm --filter drop exec prettier --check .` | | ||
| | Rust (any) | `cargo fmt -- <file>` | `cargo fmt -- --check` | | ||
| | YAML/MD/JSON | `pnpm --filter drop exec prettier --write <file>` | Same as server | | ||
|
|
||
| ## jq Defensive Patterns | ||
|
|
||
| jq in CI scripts crashes on null/non-numeric values. Always guard: | ||
|
|
||
| ```bash | ||
| # ❌ BAD: crashes on null | ||
| .value | tonumber > 0 | ||
|
|
||
| # ✅ GOOD: fallback + try/catch guards against non-numeric | ||
| (.value // "0") | (try tonumber catch 0) > 0 | ||
| ``` | ||
|
BillyOutlast marked this conversation as resolved.
|
||
|
|
||
| ## Bash Pipeline Traps | ||
|
|
||
| ```bash | ||
| # ❌ BAD: while loop runs in subshell — COMMENT_BODY mutations lost | ||
| echo "$data" | jq -c '.[]' | while read -r entry; do | ||
| COMMENT_BODY+="processed" | ||
| done | ||
|
|
||
| # ✅ GOOD: process substitution keeps while in parent shell | ||
| while read -r entry; do | ||
| COMMENT_BODY+="processed" | ||
| done < <(echo "$data" | jq -c '.[]') | ||
| ``` | ||
|
|
||
| ## SonarCloud Coverage Disconnect | ||
|
|
||
| `new_uncovered_lines` = 0 but `new_coverage` = 0% happens when changed lines aren't classified as "coverable" (imports, type annotations, comments). Include BOTH metrics in coverage gap detection: | ||
|
|
||
| ```jq | ||
| select( | ||
| (.measures[]? | select(.metric == "new_uncovered_lines") | (.value // "0") | tonumber > 0) | ||
| or | ||
| (.measures[]? | select(.metric == "new_coverage") | .value // "100") == "0.0" | ||
| ) | ||
| ``` | ||
|
|
||
| ## Pre-commit Hook Structure | ||
|
|
||
| ```text | ||
| 1. lint-staged (prettier --write, eslint --fix) → auto-fixes JS/TS/Vue → re-stages | ||
| 2. cargo fmt → auto-fixes Rust → re-stages | ||
| 3. typecheck → validates (read-only, no fixes) | ||
| 4. shellcheck → validates (read-only) | ||
| 5. fallow audit → validates (read-only, gate-only) | ||
| ``` | ||
|
|
||
| Auto-fixers first, validators last. Nothing unfixed leaves the hook. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| --- | ||
| name: pr-review-cleanup | ||
| description: Use when PR has 10+ open review threads from automated bots (OCR, CodeRabbit, Sourcery) that need batch evaluation and resolution, or when pre-push hook warnings show unresolved threads accumulating across commits. | ||
| --- | ||
|
|
||
| # PR Review Cleanup | ||
|
|
||
| Automated review bots (OCR, CodeRabbit, Sourcery) fire on every push. Each push creates new threads. Without cleanup, threads accumulate exponentially — every new scan finds existing threads plus new ones. | ||
|
|
||
| ## Core Pattern | ||
|
|
||
| ``` | ||
| push → new review run → new threads → unresolved count grows | ||
| ↓ | ||
| hook warns (advisory) → agent reads JSON → batch evaluate → resolve all | ||
| ``` | ||
|
|
||
| ## Quick Reference | ||
|
|
||
| | Step | Command | | ||
| |------|---------| | ||
| | Pull unresolved threads | `gh api graphql -f query='...reviewThreads...' -F pr=N` | | ||
| | Filter for JSON | `jq 'select(.isResolved == false)'` | | ||
| | Resolve thread | `gh api --method PATCH repos/:owner/:repo/pulls/:pr/comments/:id -f state=CLOSED` (or use MCP `resolve_thread` with PRRT_xxx ID) | | ||
| | Post summary | `gh pr comment N --body "All N threads evaluated: ..."` | | ||
|
BillyOutlast marked this conversation as resolved.
|
||
|
|
||
| ## GraphQL Query (get PRRT_xxx IDs — cursor-paginated) | ||
|
|
||
| ```graphql | ||
| query($owner:String!, $repo:String!, $pr:Int!, $cursor:String) { | ||
| repository(owner:$owner, name:$repo) { | ||
| pullRequest(number:$pr) { | ||
| reviewThreads(first:100, after:$cursor) { | ||
| nodes { | ||
| id isResolved isOutdated | ||
| comments(first:1) { | ||
| nodes { author { login } body path line } | ||
| } | ||
| } | ||
| pageInfo { hasNextPage endCursor } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
BillyOutlast marked this conversation as resolved.
|
||
|
|
||
| Loop until `hasNextPage` is false. The `first:100` cap silently drops threads on large PRs; cursor pagination is mandatory. | ||
|
|
||
| The returned `id` is the PRRT_xxx GraphQL node ID used for resolution. | ||
|
|
||
| ## Batch Resolution Pattern | ||
|
|
||
| 1. Pull all threads into JSON | ||
| 2. Filter `isResolved == false` | ||
| 3. Categorize: *fix now* (bug/security), *skip with reason* (nitpick/false-positive), *already fixed* (commit addressed) | ||
| 4. Post one summary comment explaining disposition of all threads | ||
| 5. Resolve all threads via `resolve_thread` API (one call each) | ||
| 6. Verify with `pre-push` hook — count should be 0 | ||
|
|
||
| ## Common Thread Categories | ||
|
|
||
| | Category | Action | | ||
| |----------|--------| | ||
| | Bug / security / functional | Fix in code, resolve | | ||
| | OCR false positive (Nitro auto-imports, hadolint suppression) | Skip with explanation, resolve | | ||
| | Duplicate from multiple OCR runs | Resolve (one run's copy), other auto-resolved | | ||
| | Nitpick / cosmetic | Skip with reason, resolve | | ||
| | Positive feedback ("good improvement") | Acknowledge, resolve | | ||
| | Pre-existing (not introduced by PR) | Skip, resolve | | ||
| | Stale / outdated (code already changed) | Resolve as outdated | | ||
|
|
||
| ## Red Flags | ||
|
|
||
| - Resolving threads without reading them — OCR finds real bugs | ||
| - Leaving "good improvement" comments unresolved — clutter | ||
| - Not posting a summary comment — future reviewers need context on why threads were closed | ||
| - Using `--no-verify` to skip the hook instead of cleaning threads | ||
|
|
||
| ## Pre-push Hook Integration | ||
|
|
||
| The pre-push hook at `.husky/pre-push` queries unresolved threads. JSON output between `## PR_REVIEW_THREADS_START` / `## PR_REVIEW_THREADS_END` markers. Agent should parse this and act BEFORE pushing — clean threads first, then push clean. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
always()causes this job to run even when thesonarjob is skipped (not just failed). If the sonar scan was never executed, the script may still post a "No issues found" comment on the PR, which is misleading since no actual analysis occurred. Consider narrowing the condition to only run when sonar succeeded or failed, but not when skipped.Suggestion: