patchdrill 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/.patchdrill.yml +33 -0
  2. package/CHANGELOG.md +150 -0
  3. package/CONTRIBUTING.md +59 -0
  4. package/LICENSE +21 -0
  5. package/README.md +601 -0
  6. package/SECURITY.md +28 -0
  7. package/action.yml +338 -0
  8. package/dist/baseline.d.ts +9 -0
  9. package/dist/baseline.js +38 -0
  10. package/dist/baseline.js.map +1 -0
  11. package/dist/cli.d.ts +19 -0
  12. package/dist/cli.js +662 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/codeowners.d.ts +14 -0
  15. package/dist/codeowners.js +104 -0
  16. package/dist/codeowners.js.map +1 -0
  17. package/dist/command-plan.d.ts +3 -0
  18. package/dist/command-plan.js +26 -0
  19. package/dist/command-plan.js.map +1 -0
  20. package/dist/demo.d.ts +5 -0
  21. package/dist/demo.js +525 -0
  22. package/dist/demo.js.map +1 -0
  23. package/dist/dependency.d.ts +4 -0
  24. package/dist/dependency.js +1424 -0
  25. package/dist/dependency.js.map +1 -0
  26. package/dist/doctor.d.ts +26 -0
  27. package/dist/doctor.js +183 -0
  28. package/dist/doctor.js.map +1 -0
  29. package/dist/evidence.d.ts +64 -0
  30. package/dist/evidence.js +352 -0
  31. package/dist/evidence.js.map +1 -0
  32. package/dist/git.d.ts +16 -0
  33. package/dist/git.js +349 -0
  34. package/dist/git.js.map +1 -0
  35. package/dist/i18n-catalog.d.ts +8 -0
  36. package/dist/i18n-catalog.js +446 -0
  37. package/dist/i18n-catalog.js.map +1 -0
  38. package/dist/i18n.d.ts +20 -0
  39. package/dist/i18n.js +67 -0
  40. package/dist/i18n.js.map +1 -0
  41. package/dist/init.d.ts +13 -0
  42. package/dist/init.js +312 -0
  43. package/dist/init.js.map +1 -0
  44. package/dist/markdown-links.d.ts +18 -0
  45. package/dist/markdown-links.js +180 -0
  46. package/dist/markdown-links.js.map +1 -0
  47. package/dist/package-scripts.d.ts +3 -0
  48. package/dist/package-scripts.js +55 -0
  49. package/dist/package-scripts.js.map +1 -0
  50. package/dist/planner.d.ts +8 -0
  51. package/dist/planner.js +2351 -0
  52. package/dist/planner.js.map +1 -0
  53. package/dist/policy.d.ts +12 -0
  54. package/dist/policy.js +255 -0
  55. package/dist/policy.js.map +1 -0
  56. package/dist/project.d.ts +2 -0
  57. package/dist/project.js +1085 -0
  58. package/dist/project.js.map +1 -0
  59. package/dist/release-readiness.d.ts +25 -0
  60. package/dist/release-readiness.js +426 -0
  61. package/dist/release-readiness.js.map +1 -0
  62. package/dist/report-annotations.d.ts +3 -0
  63. package/dist/report-annotations.js +28 -0
  64. package/dist/report-annotations.js.map +1 -0
  65. package/dist/report-contract.d.ts +2 -0
  66. package/dist/report-contract.js +82 -0
  67. package/dist/report-contract.js.map +1 -0
  68. package/dist/report-html.d.ts +7 -0
  69. package/dist/report-html.js +706 -0
  70. package/dist/report-html.js.map +1 -0
  71. package/dist/report-sarif.d.ts +2 -0
  72. package/dist/report-sarif.js +90 -0
  73. package/dist/report-sarif.js.map +1 -0
  74. package/dist/report.d.ts +14 -0
  75. package/dist/report.js +310 -0
  76. package/dist/report.js.map +1 -0
  77. package/dist/risk.d.ts +19 -0
  78. package/dist/risk.js +1226 -0
  79. package/dist/risk.js.map +1 -0
  80. package/dist/runner.d.ts +8 -0
  81. package/dist/runner.js +113 -0
  82. package/dist/runner.js.map +1 -0
  83. package/dist/scan.d.ts +2 -0
  84. package/dist/scan.js +195 -0
  85. package/dist/scan.js.map +1 -0
  86. package/dist/schema.d.ts +12 -0
  87. package/dist/schema.js +30 -0
  88. package/dist/schema.js.map +1 -0
  89. package/dist/stack-coverage.d.ts +8 -0
  90. package/dist/stack-coverage.js +94 -0
  91. package/dist/stack-coverage.js.map +1 -0
  92. package/dist/types.d.ts +206 -0
  93. package/dist/types.js +2 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/verification.d.ts +11 -0
  96. package/dist/verification.js +108 -0
  97. package/dist/verification.js.map +1 -0
  98. package/docs/ANNOTATIONS.md +34 -0
  99. package/docs/ARCHITECTURE.md +79 -0
  100. package/docs/BASELINES.md +32 -0
  101. package/docs/CASE_STUDIES.md +106 -0
  102. package/docs/CODEOWNERS.md +23 -0
  103. package/docs/DASHBOARD.md +87 -0
  104. package/docs/EVIDENCE.md +55 -0
  105. package/docs/LAUNCH_PLAYBOOK.md +103 -0
  106. package/docs/MONOREPOS.md +74 -0
  107. package/docs/POLICY.md +98 -0
  108. package/docs/PROOF_PACKS.md +57 -0
  109. package/docs/PR_COMMENTS.md +56 -0
  110. package/docs/RELEASE.md +35 -0
  111. package/docs/ROADMAP.md +152 -0
  112. package/docs/RULE_CATALOG.md +90 -0
  113. package/docs/SARIF.md +74 -0
  114. package/docs/SCHEMAS.md +49 -0
  115. package/docs/SECURITY_POSTURE.md +32 -0
  116. package/docs/STACK_COVERAGE.md +20 -0
  117. package/docs/assets/patchdrill-demo.svg +21 -0
  118. package/docs/media/patchdrill-dashboard.png +0 -0
  119. package/docs/media/patchdrill-demo.gif +0 -0
  120. package/examples/case-studies/README.md +20 -0
  121. package/examples/demo/README.md +21 -0
  122. package/examples/demo/patchdrill-demo-summary.md +35 -0
  123. package/examples/demo/patchdrill-demo.html +623 -0
  124. package/examples/demo/patchdrill-demo.json +355 -0
  125. package/examples/demo/patchdrill-demo.md +120 -0
  126. package/examples/demo/patchdrill-demo.sarif +195 -0
  127. package/examples/report.md +128 -0
  128. package/examples/risky-agent-pr/README.md +15 -0
  129. package/examples/risky-agent-pr/patchdrill-demo-summary.md +41 -0
  130. package/examples/risky-agent-pr/patchdrill-demo.html +681 -0
  131. package/examples/risky-agent-pr/patchdrill-demo.json +483 -0
  132. package/examples/risky-agent-pr/patchdrill-demo.md +140 -0
  133. package/examples/risky-agent-pr/patchdrill-demo.sarif +398 -0
  134. package/fixtures/stacks/README.md +4 -0
  135. package/fixtures/stacks/android-gradle/fixture.json +33 -0
  136. package/fixtures/stacks/aspnet-core-service/fixture.json +36 -0
  137. package/fixtures/stacks/bazel-workspace/fixture.json +30 -0
  138. package/fixtures/stacks/buck2-workspace/fixture.json +30 -0
  139. package/fixtures/stacks/cargo-workspace/fixture.json +48 -0
  140. package/fixtures/stacks/django-app/fixture.json +25 -0
  141. package/fixtures/stacks/docker-compose/fixture.json +17 -0
  142. package/fixtures/stacks/dockerfile-service/fixture.json +17 -0
  143. package/fixtures/stacks/dotnet-service/fixture.json +36 -0
  144. package/fixtures/stacks/dotnet-solution-filter/fixture.json +62 -0
  145. package/fixtures/stacks/fastapi-app/fixture.json +29 -0
  146. package/fixtures/stacks/go-workspace/fixture.json +48 -0
  147. package/fixtures/stacks/java-gradle/fixture.json +29 -0
  148. package/fixtures/stacks/java-maven/fixture.json +32 -0
  149. package/fixtures/stacks/kubernetes-helm/fixture.json +25 -0
  150. package/fixtures/stacks/kubernetes-kustomize/fixture.json +21 -0
  151. package/fixtures/stacks/nested-go-workspace/fixture.json +51 -0
  152. package/fixtures/stacks/nextjs-app/fixture.json +34 -0
  153. package/fixtures/stacks/node-turbo-workspace/fixture.json +39 -0
  154. package/fixtures/stacks/pants-python/fixture.json +33 -0
  155. package/fixtures/stacks/php-composer/fixture.json +31 -0
  156. package/fixtures/stacks/python-service/fixture.json +21 -0
  157. package/fixtures/stacks/rails-app/fixture.json +25 -0
  158. package/fixtures/stacks/spring-boot-gradle/fixture.json +29 -0
  159. package/fixtures/stacks/spring-boot-maven/fixture.json +43 -0
  160. package/fixtures/stacks/swift-package/fixture.json +21 -0
  161. package/fixtures/stacks/terraform-module/fixture.json +17 -0
  162. package/fixtures/stacks/uv-python-service/fixture.json +47 -0
  163. package/fixtures/stacks/xcode-app/fixture.json +72 -0
  164. package/package.json +80 -0
  165. package/schemas/patchdrill-doctor.schema.json +171 -0
  166. package/schemas/patchdrill-evidence.schema.json +239 -0
  167. package/schemas/patchdrill-policy.schema.json +170 -0
  168. package/schemas/patchdrill-release-check.schema.json +78 -0
  169. package/schemas/patchdrill-report.schema.json +647 -0
package/action.yml ADDED
@@ -0,0 +1,338 @@
1
+ name: PatchDrill
2
+ description: Generate deterministic patch verification evidence for pull requests.
3
+ author: PatchDrill contributors
4
+ inputs:
5
+ base:
6
+ description: Base ref to compare against.
7
+ required: false
8
+ default: origin/main
9
+ fail-on:
10
+ description: Severity threshold that should fail the action.
11
+ required: false
12
+ default: high
13
+ max-risk:
14
+ description: Maximum allowed risk score from 0 to 100.
15
+ required: false
16
+ default: "69"
17
+ max-risk-delta:
18
+ description: Maximum allowed risk increase against the optional baseline report.
19
+ required: false
20
+ default: ""
21
+ max-output-chars:
22
+ description: Maximum characters to retain from each command output stream.
23
+ required: false
24
+ default: "20000"
25
+ command-timeout-ms:
26
+ description: Optional timeout in milliseconds for each verification command.
27
+ required: false
28
+ default: ""
29
+ config:
30
+ description: Optional PatchDrill policy path.
31
+ required: false
32
+ default: ""
33
+ baseline:
34
+ description: Optional previous PatchDrill JSON report to compare against.
35
+ required: false
36
+ default: ""
37
+ run:
38
+ description: Execute required verification commands.
39
+ required: false
40
+ default: "false"
41
+ run-optional:
42
+ description: Execute optional inferred verification commands when run is true.
43
+ required: false
44
+ default: "false"
45
+ evidence:
46
+ description: Audit evidence manifest output path.
47
+ required: false
48
+ default: patchdrill-evidence.json
49
+ summary:
50
+ description: Compact Markdown summary output path for PR comments and step summaries.
51
+ required: false
52
+ default: patchdrill-summary.md
53
+ markdown:
54
+ description: Markdown report output path.
55
+ required: false
56
+ default: patchdrill-report.md
57
+ json:
58
+ description: JSON report output path.
59
+ required: false
60
+ default: patchdrill-report.json
61
+ sarif:
62
+ description: SARIF report output path.
63
+ required: false
64
+ default: patchdrill.sarif
65
+ html:
66
+ description: Static HTML dashboard output path.
67
+ required: false
68
+ default: patchdrill-dashboard.html
69
+ dashboard-history:
70
+ description: Newline-separated previous PatchDrill JSON report paths to include in the static HTML dashboard trend.
71
+ required: false
72
+ default: ""
73
+ annotations:
74
+ description: Emit GitHub Actions annotations for PatchDrill findings.
75
+ required: false
76
+ default: "true"
77
+ step-summary:
78
+ description: Append the compact Markdown summary to the GitHub Actions step summary.
79
+ required: false
80
+ default: "true"
81
+ pr-comment:
82
+ description: Upsert the compact Markdown summary as a pull request comment.
83
+ required: false
84
+ default: "false"
85
+ comment-marker:
86
+ description: Hidden marker used to find and update an existing PatchDrill PR comment.
87
+ required: false
88
+ default: "<!-- patchdrill-report -->"
89
+ outputs:
90
+ report-evidence:
91
+ description: Path to the generated audit evidence manifest.
92
+ value: ${{ steps.paths.outputs.evidence }}
93
+ report-summary:
94
+ description: Path to the generated compact Markdown summary.
95
+ value: ${{ steps.paths.outputs.summary }}
96
+ report-json:
97
+ description: Path to the generated JSON report.
98
+ value: ${{ steps.paths.outputs.json }}
99
+ report-markdown:
100
+ description: Path to the generated Markdown report.
101
+ value: ${{ steps.paths.outputs.markdown }}
102
+ report-sarif:
103
+ description: Path to the generated SARIF report.
104
+ value: ${{ steps.paths.outputs.sarif }}
105
+ report-html:
106
+ description: Path to the generated HTML dashboard.
107
+ value: ${{ steps.paths.outputs.html }}
108
+ runs:
109
+ using: composite
110
+ steps:
111
+ - uses: actions/setup-node@v6
112
+ with:
113
+ node-version: 22
114
+ cache: npm
115
+ cache-dependency-path: ${{ github.action_path }}/package-lock.json
116
+ - name: Install PatchDrill action dependencies
117
+ shell: bash
118
+ working-directory: ${{ github.action_path }}
119
+ run: npm ci --ignore-scripts
120
+ - name: Build PatchDrill action
121
+ shell: bash
122
+ working-directory: ${{ github.action_path }}
123
+ run: npm run build
124
+ - name: Run PatchDrill
125
+ id: run
126
+ shell: bash
127
+ env:
128
+ PATCHDRILL_BASE: ${{ inputs.base }}
129
+ PATCHDRILL_FAIL_ON: ${{ inputs.fail-on }}
130
+ PATCHDRILL_MAX_RISK: ${{ inputs.max-risk }}
131
+ PATCHDRILL_MAX_RISK_DELTA: ${{ inputs.max-risk-delta }}
132
+ PATCHDRILL_MAX_OUTPUT_CHARS: ${{ inputs.max-output-chars }}
133
+ PATCHDRILL_COMMAND_TIMEOUT_MS: ${{ inputs.command-timeout-ms }}
134
+ PATCHDRILL_CONFIG: ${{ inputs.config }}
135
+ PATCHDRILL_BASELINE: ${{ inputs.baseline }}
136
+ PATCHDRILL_RUN: ${{ inputs.run }}
137
+ PATCHDRILL_RUN_OPTIONAL: ${{ inputs.run-optional }}
138
+ PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
139
+ PATCHDRILL_SUMMARY: ${{ inputs.summary }}
140
+ PATCHDRILL_MARKDOWN: ${{ inputs.markdown }}
141
+ PATCHDRILL_JSON: ${{ inputs.json }}
142
+ PATCHDRILL_SARIF: ${{ inputs.sarif }}
143
+ PATCHDRILL_HTML: ${{ inputs.html }}
144
+ PATCHDRILL_ANNOTATIONS: ${{ inputs.annotations }}
145
+ run: |
146
+ args=(
147
+ scan
148
+ --base "$PATCHDRILL_BASE"
149
+ --evidence "$PATCHDRILL_EVIDENCE"
150
+ --summary-markdown "$PATCHDRILL_SUMMARY"
151
+ --markdown "$PATCHDRILL_MARKDOWN"
152
+ --json "$PATCHDRILL_JSON"
153
+ --sarif "$PATCHDRILL_SARIF"
154
+ --html "$PATCHDRILL_HTML"
155
+ --fail-on "$PATCHDRILL_FAIL_ON"
156
+ --max-risk "$PATCHDRILL_MAX_RISK"
157
+ --max-output-chars "$PATCHDRILL_MAX_OUTPUT_CHARS"
158
+ --run "${PATCHDRILL_RUN:-false}"
159
+ --run-optional "${PATCHDRILL_RUN_OPTIONAL:-false}"
160
+ --github-annotations "${PATCHDRILL_ANNOTATIONS:-false}"
161
+ )
162
+ if [ -n "$PATCHDRILL_CONFIG" ]; then
163
+ args+=(--config "$PATCHDRILL_CONFIG")
164
+ fi
165
+ if [ -n "$PATCHDRILL_BASELINE" ]; then
166
+ args+=(--baseline "$PATCHDRILL_BASELINE")
167
+ fi
168
+ if [ -n "$PATCHDRILL_MAX_RISK_DELTA" ]; then
169
+ args+=(--max-risk-delta "$PATCHDRILL_MAX_RISK_DELTA")
170
+ fi
171
+ if [ -n "$PATCHDRILL_COMMAND_TIMEOUT_MS" ]; then
172
+ args+=(--command-timeout-ms "$PATCHDRILL_COMMAND_TIMEOUT_MS")
173
+ fi
174
+ set +e
175
+ node "$GITHUB_ACTION_PATH/dist/cli.js" "${args[@]}"
176
+ STATUS=$?
177
+ echo "status=$STATUS" >> "$GITHUB_OUTPUT"
178
+ exit 0
179
+ - name: Render dashboard history
180
+ if: inputs.dashboard-history != ''
181
+ shell: bash
182
+ env:
183
+ PATCHDRILL_DASHBOARD_HISTORY: ${{ inputs.dashboard-history }}
184
+ PATCHDRILL_JSON: ${{ inputs.json }}
185
+ PATCHDRILL_HTML: ${{ inputs.html }}
186
+ run: |
187
+ args=(dashboard)
188
+ while IFS= read -r report_path; do
189
+ if [ -n "$report_path" ]; then
190
+ args+=(--json "$report_path")
191
+ fi
192
+ done <<< "$PATCHDRILL_DASHBOARD_HISTORY"
193
+ args+=(--json "$PATCHDRILL_JSON" --output "$PATCHDRILL_HTML")
194
+ node "$GITHUB_ACTION_PATH/dist/cli.js" "${args[@]}"
195
+ - name: Refresh evidence manifest
196
+ shell: bash
197
+ env:
198
+ PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
199
+ PATCHDRILL_SUMMARY: ${{ inputs.summary }}
200
+ PATCHDRILL_MARKDOWN: ${{ inputs.markdown }}
201
+ PATCHDRILL_JSON: ${{ inputs.json }}
202
+ PATCHDRILL_SARIF: ${{ inputs.sarif }}
203
+ PATCHDRILL_HTML: ${{ inputs.html }}
204
+ run: |
205
+ if [ -n "$PATCHDRILL_EVIDENCE" ] && [ -n "$PATCHDRILL_JSON" ]; then
206
+ args=(evidence --json "$PATCHDRILL_JSON" --evidence "$PATCHDRILL_EVIDENCE")
207
+ if [ -n "$PATCHDRILL_SUMMARY" ]; then
208
+ args+=(--summary-markdown "$PATCHDRILL_SUMMARY")
209
+ fi
210
+ if [ -n "$PATCHDRILL_MARKDOWN" ]; then
211
+ args+=(--markdown "$PATCHDRILL_MARKDOWN")
212
+ fi
213
+ if [ -n "$PATCHDRILL_SARIF" ]; then
214
+ args+=(--sarif "$PATCHDRILL_SARIF")
215
+ fi
216
+ if [ -n "$PATCHDRILL_HTML" ]; then
217
+ args+=(--html "$PATCHDRILL_HTML")
218
+ fi
219
+ node "$GITHUB_ACTION_PATH/dist/cli.js" "${args[@]}"
220
+ fi
221
+ - name: Verify evidence manifest
222
+ shell: bash
223
+ env:
224
+ PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
225
+ run: |
226
+ if [ -n "$PATCHDRILL_EVIDENCE" ]; then
227
+ node "$GITHUB_ACTION_PATH/dist/cli.js" verify --evidence "$PATCHDRILL_EVIDENCE"
228
+ fi
229
+ - name: Write step summary
230
+ shell: bash
231
+ env:
232
+ PATCHDRILL_STEP_SUMMARY: ${{ inputs.step-summary }}
233
+ PATCHDRILL_SUMMARY: ${{ inputs.summary }}
234
+ run: |
235
+ bool_true() {
236
+ local value="${1,,}"
237
+ case "$value" in
238
+ true|1|yes|on) return 0 ;;
239
+ false|0|no|off|"") return 1 ;;
240
+ *)
241
+ echo "Invalid boolean input for step-summary: $1" >&2
242
+ exit 1
243
+ ;;
244
+ esac
245
+ }
246
+ if ! bool_true "$PATCHDRILL_STEP_SUMMARY"; then
247
+ exit 0
248
+ fi
249
+ if [ -n "${GITHUB_STEP_SUMMARY:-}" ] && [ -f "$PATCHDRILL_SUMMARY" ]; then
250
+ cat -- "$PATCHDRILL_SUMMARY" >> "$GITHUB_STEP_SUMMARY"
251
+ fi
252
+ - name: Upsert PR comment
253
+ if: github.event_name == 'pull_request'
254
+ uses: actions/github-script@v9
255
+ env:
256
+ PATCHDRILL_PR_COMMENT: ${{ inputs.pr-comment }}
257
+ PATCHDRILL_SUMMARY: ${{ inputs.summary }}
258
+ PATCHDRILL_MARKER: ${{ inputs.comment-marker }}
259
+ with:
260
+ script: |
261
+ const fs = require("fs");
262
+ const boolTrue = (name) => {
263
+ const raw = process.env[name] || "";
264
+ const value = raw.trim().toLowerCase();
265
+ if (["true", "1", "yes", "on"].includes(value)) return true;
266
+ if (["false", "0", "no", "off", ""].includes(value)) return false;
267
+ throw new Error(`Invalid boolean input for pr-comment: ${raw}`);
268
+ };
269
+ if (!boolTrue("PATCHDRILL_PR_COMMENT")) {
270
+ return;
271
+ }
272
+ const marker = process.env.PATCHDRILL_MARKER || "<!-- patchdrill-report -->";
273
+ const summaryPath = process.env.PATCHDRILL_SUMMARY || "patchdrill-summary.md";
274
+ if (!fs.existsSync(summaryPath)) {
275
+ core.warning(`PatchDrill summary report not found: ${summaryPath}`);
276
+ return;
277
+ }
278
+ const body = `${marker}\n${fs.readFileSync(summaryPath, "utf8")}`;
279
+ const { owner, repo } = context.repo;
280
+ const issue_number = context.payload.pull_request.number;
281
+ try {
282
+ const comments = await github.paginate(github.rest.issues.listComments, {
283
+ owner,
284
+ repo,
285
+ issue_number,
286
+ per_page: 100
287
+ });
288
+ const existing = comments.find((comment) => comment.user?.type === "Bot" && comment.body?.includes(marker));
289
+ if (existing) {
290
+ await github.rest.issues.updateComment({
291
+ owner,
292
+ repo,
293
+ comment_id: existing.id,
294
+ body
295
+ });
296
+ } else {
297
+ await github.rest.issues.createComment({
298
+ owner,
299
+ repo,
300
+ issue_number,
301
+ body
302
+ });
303
+ }
304
+ } catch (error) {
305
+ const message = error instanceof Error ? error.message : String(error);
306
+ core.warning(`PatchDrill PR comment skipped: ${message}`);
307
+ }
308
+ - name: Export report paths
309
+ id: paths
310
+ shell: bash
311
+ env:
312
+ PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
313
+ PATCHDRILL_SUMMARY: ${{ inputs.summary }}
314
+ PATCHDRILL_MARKDOWN: ${{ inputs.markdown }}
315
+ PATCHDRILL_JSON: ${{ inputs.json }}
316
+ PATCHDRILL_SARIF: ${{ inputs.sarif }}
317
+ PATCHDRILL_HTML: ${{ inputs.html }}
318
+ run: |
319
+ write_output() {
320
+ local name="$1"
321
+ local value="$2"
322
+ local delimiter="patchdrill_${name}_EOF"
323
+ {
324
+ printf '%s<<%s\n' "$name" "$delimiter"
325
+ printf '%s\n' "$value"
326
+ printf '%s\n' "$delimiter"
327
+ } >> "$GITHUB_OUTPUT"
328
+ }
329
+ write_output evidence "$PATCHDRILL_EVIDENCE"
330
+ write_output summary "$PATCHDRILL_SUMMARY"
331
+ write_output json "$PATCHDRILL_JSON"
332
+ write_output markdown "$PATCHDRILL_MARKDOWN"
333
+ write_output sarif "$PATCHDRILL_SARIF"
334
+ write_output html "$PATCHDRILL_HTML"
335
+ - name: Fail on PatchDrill gate
336
+ if: steps.run.outputs.status != '0'
337
+ shell: bash
338
+ run: exit "${{ steps.run.outputs.status }}"
@@ -0,0 +1,9 @@
1
+ import type { PatchReport, PatchStatus, RiskFinding } from "./types.js";
2
+ export interface BaselineComparisonInput {
3
+ summary: {
4
+ status: PatchStatus;
5
+ riskScore: number;
6
+ };
7
+ findings: RiskFinding[];
8
+ }
9
+ export declare function compareBaseline(root: string, baselinePath: string, current: BaselineComparisonInput): NonNullable<PatchReport["baseline"]>;
@@ -0,0 +1,38 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { isAbsolute, relative, resolve } from "node:path";
3
+ export function compareBaseline(root, baselinePath, current) {
4
+ const resolved = isAbsolute(baselinePath) ? baselinePath : resolve(root, baselinePath);
5
+ if (!existsSync(resolved))
6
+ throw new Error(`PatchDrill baseline report not found: ${resolved}`);
7
+ const baseline = readBaselineReport(resolved);
8
+ const previousFindings = new Set((baseline.findings ?? []).map(findingFingerprint));
9
+ const currentFindings = new Set(current.findings.map(findingFingerprint));
10
+ const newFindingCount = [...currentFindings].filter((fingerprint) => !previousFindings.has(fingerprint)).length;
11
+ const resolvedFindingCount = [...previousFindings].filter((fingerprint) => !currentFindings.has(fingerprint)).length;
12
+ const unchangedFindingCount = [...currentFindings].filter((fingerprint) => previousFindings.has(fingerprint)).length;
13
+ return {
14
+ path: relative(root, resolved),
15
+ ...(baseline.summary?.status !== undefined ? { previousStatus: baseline.summary.status } : {}),
16
+ currentStatus: current.summary.status,
17
+ ...(baseline.summary?.riskScore !== undefined ? { previousRiskScore: baseline.summary.riskScore } : {}),
18
+ currentRiskScore: current.summary.riskScore,
19
+ riskDelta: current.summary.riskScore - (baseline.summary?.riskScore ?? 0),
20
+ newFindingCount,
21
+ resolvedFindingCount,
22
+ unchangedFindingCount
23
+ };
24
+ }
25
+ function readBaselineReport(path) {
26
+ try {
27
+ return JSON.parse(readFileSync(path, "utf8"));
28
+ }
29
+ catch (error) {
30
+ throw new Error(`Failed to read PatchDrill baseline report ${path}: ${error instanceof Error ? error.message : String(error)}`, {
31
+ cause: error
32
+ });
33
+ }
34
+ }
35
+ function findingFingerprint(finding) {
36
+ return [finding.ruleId ?? "", finding.severity, finding.title, finding.file ?? "", finding.line ?? ""].join("\0");
37
+ }
38
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { type Locale } from "./i18n.js";
3
+ import { type GateOptions } from "./report.js";
4
+ import { scan } from "./scan.js";
5
+ export interface ParsedArgs {
6
+ command: string;
7
+ flags: Record<string, string | boolean | string[]>;
8
+ positionals: string[];
9
+ }
10
+ export declare function scanCommand(parsed: ParsedArgs): Promise<void>;
11
+ export declare function dashboardCommand(parsed: ParsedArgs): void;
12
+ export declare function demoCommand(parsed: ParsedArgs): void;
13
+ export declare function doctorCommand(parsed?: ParsedArgs): void;
14
+ export declare function evidenceCommand(parsed: ParsedArgs): void;
15
+ export declare function explainCommand(): void;
16
+ export declare function renderExplainText(): string;
17
+ export declare function releaseCheckCommand(parsed?: ParsedArgs): void;
18
+ export declare function renderConsoleSummary(report: Awaited<ReturnType<typeof scan>>, gateOptions: GateOptions, locale?: Locale): string;
19
+ export declare function parseArgs(args: string[]): ParsedArgs;