@eduardbar/drift 1.3.0 → 1.4.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.
- package/.gga +50 -0
- package/.github/actions/drift-review/README.md +60 -0
- package/.github/actions/drift-review/action.yml +131 -0
- package/.github/actions/drift-scan/README.md +28 -32
- package/.github/actions/drift-scan/action.yml +78 -14
- package/.github/workflows/review-pr.yml +34 -41
- package/AGENTS.md +75 -251
- package/CHANGELOG.md +28 -0
- package/README.md +148 -41
- package/dist/benchmark.d.ts +1 -1
- package/dist/benchmark.js +71 -52
- package/dist/cli.js +243 -8
- package/dist/config.js +16 -2
- package/dist/diff.js +42 -50
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +133 -0
- package/dist/format.d.ts +17 -0
- package/dist/format.js +45 -0
- package/dist/guard-types.d.ts +57 -0
- package/dist/guard-types.js +2 -0
- package/dist/guard.d.ts +14 -0
- package/dist/guard.js +239 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +4 -1
- package/dist/init.d.ts +15 -0
- package/dist/init.js +273 -0
- package/dist/map-cycles.d.ts +2 -0
- package/dist/map-cycles.js +34 -0
- package/dist/map-svg.d.ts +19 -0
- package/dist/map-svg.js +97 -0
- package/dist/map.js +78 -138
- package/dist/metrics.js +70 -55
- package/dist/output-metadata.d.ts +13 -0
- package/dist/output-metadata.js +17 -0
- package/dist/plugins-capabilities.d.ts +4 -0
- package/dist/plugins-capabilities.js +21 -0
- package/dist/plugins-messages.d.ts +10 -0
- package/dist/plugins-messages.js +16 -0
- package/dist/plugins-rules.d.ts +9 -0
- package/dist/plugins-rules.js +137 -0
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +45 -142
- package/dist/reporter-constants.d.ts +16 -0
- package/dist/reporter-constants.js +39 -0
- package/dist/reporter.d.ts +3 -3
- package/dist/reporter.js +35 -55
- package/dist/review.d.ts +2 -1
- package/dist/review.js +2 -1
- package/dist/rules/phase3-configurable.js +23 -15
- package/dist/saas/constants.d.ts +15 -0
- package/dist/saas/constants.js +48 -0
- package/dist/saas/dashboard.d.ts +8 -0
- package/dist/saas/dashboard.js +132 -0
- package/dist/saas/errors.d.ts +19 -0
- package/dist/saas/errors.js +37 -0
- package/dist/saas/helpers.d.ts +21 -0
- package/dist/saas/helpers.js +110 -0
- package/dist/saas/ingest.d.ts +3 -0
- package/dist/saas/ingest.js +249 -0
- package/dist/saas/organization.d.ts +5 -0
- package/dist/saas/organization.js +82 -0
- package/dist/saas/plan-change.d.ts +10 -0
- package/dist/saas/plan-change.js +15 -0
- package/dist/saas/store.d.ts +21 -0
- package/dist/saas/store.js +159 -0
- package/dist/saas/types.d.ts +191 -0
- package/dist/saas/types.js +2 -0
- package/dist/saas.d.ts +8 -218
- package/dist/saas.js +7 -761
- package/dist/sarif.d.ts +74 -0
- package/dist/sarif.js +122 -0
- package/dist/trust-advanced.d.ts +14 -0
- package/dist/trust-advanced.js +65 -0
- package/dist/trust-kpi-fs.d.ts +3 -0
- package/dist/trust-kpi-fs.js +141 -0
- package/dist/trust-kpi-parse.d.ts +7 -0
- package/dist/trust-kpi-parse.js +186 -0
- package/dist/trust-kpi-types.d.ts +16 -0
- package/dist/trust-kpi-types.js +2 -0
- package/dist/trust-kpi.d.ts +1 -3
- package/dist/trust-kpi.js +6 -266
- package/dist/trust-policy.d.ts +32 -0
- package/dist/trust-policy.js +160 -0
- package/dist/trust-render.d.ts +9 -0
- package/dist/trust-render.js +54 -0
- package/dist/trust-scoring.d.ts +9 -0
- package/dist/trust-scoring.js +208 -0
- package/dist/trust.d.ts +4 -32
- package/dist/trust.js +29 -432
- package/dist/types/app.d.ts +30 -0
- package/dist/types/app.js +2 -0
- package/dist/types/config.d.ts +25 -0
- package/dist/types/config.js +2 -0
- package/dist/types/core.d.ts +100 -0
- package/dist/types/core.js +2 -0
- package/dist/types/diff.d.ts +55 -0
- package/dist/types/diff.js +2 -0
- package/dist/types/plugin.d.ts +41 -0
- package/dist/types/plugin.js +2 -0
- package/dist/types/trust.d.ts +120 -0
- package/dist/types/trust.js +2 -0
- package/dist/types.d.ts +8 -365
- package/docs/release-notes-draft.md +40 -0
- package/docs/rules-catalog.md +49 -0
- package/docs/trust-core-release-checklist.md +37 -5
- package/package.json +3 -2
- package/packages/vscode-drift/src/code-actions.ts +1 -1
- package/schemas/drift-ai-output.v1.json +162 -0
- package/schemas/drift-report.v1.json +151 -0
- package/schemas/drift-trust.v1.json +131 -0
- package/scripts/smoke-repo.mjs +394 -0
- package/src/benchmark.ts +75 -53
- package/src/cli.ts +285 -13
- package/src/config.ts +19 -2
- package/src/diff.ts +57 -48
- package/src/doctor.ts +173 -0
- package/src/format.ts +81 -0
- package/src/guard-types.ts +64 -0
- package/src/guard.ts +324 -0
- package/src/index.ts +35 -0
- package/src/init.ts +298 -0
- package/src/map-cycles.ts +38 -0
- package/src/map-svg.ts +124 -0
- package/src/map.ts +111 -142
- package/src/metrics.ts +78 -59
- package/src/output-metadata.ts +30 -0
- package/src/plugins-capabilities.ts +36 -0
- package/src/plugins-messages.ts +35 -0
- package/src/plugins-rules.ts +296 -0
- package/src/plugins.ts +76 -283
- package/src/reporter-constants.ts +46 -0
- package/src/reporter.ts +64 -65
- package/src/review.ts +4 -2
- package/src/rules/phase3-configurable.ts +39 -26
- package/src/saas/constants.ts +56 -0
- package/src/saas/dashboard.ts +172 -0
- package/src/saas/errors.ts +45 -0
- package/src/saas/helpers.ts +140 -0
- package/src/saas/ingest.ts +278 -0
- package/src/saas/organization.ts +99 -0
- package/src/saas/plan-change.ts +19 -0
- package/src/saas/store.ts +172 -0
- package/src/saas/types.ts +216 -0
- package/src/saas.ts +49 -1031
- package/src/sarif.ts +232 -0
- package/src/trust-advanced.ts +99 -0
- package/src/trust-kpi-fs.ts +169 -0
- package/src/trust-kpi-parse.ts +219 -0
- package/src/trust-kpi-types.ts +19 -0
- package/src/trust-kpi.ts +8 -316
- package/src/trust-policy.ts +246 -0
- package/src/trust-render.ts +61 -0
- package/src/trust-scoring.ts +231 -0
- package/src/trust.ts +62 -576
- package/src/types/app.ts +30 -0
- package/src/types/config.ts +27 -0
- package/src/types/core.ts +105 -0
- package/src/types/diff.ts +61 -0
- package/src/types/plugin.ts +46 -0
- package/src/types/trust.ts +134 -0
- package/src/types.ts +78 -409
- package/tests/cli-sarif.test.ts +92 -0
- package/tests/format.test.ts +157 -0
- package/tests/new-features.test.ts +10 -2
- package/tests/phase1-init-doctor-guard.test.ts +199 -0
- package/tests/sarif.test.ts +160 -0
- package/tests/trust-kpi.test.ts +31 -4
- package/tests/trust.test.ts +18 -0
package/README.md
CHANGED
|
@@ -51,12 +51,61 @@ npm install --save-dev @eduardbar/drift
|
|
|
51
51
|
|
|
52
52
|
- Product requirements and roadmap: [`docs/PRD.md`](./docs/PRD.md)
|
|
53
53
|
- Trust core release checklist: [`docs/trust-core-release-checklist.md`](./docs/trust-core-release-checklist.md)
|
|
54
|
+
- Rules catalog (source-of-truth snapshot): [`docs/rules-catalog.md`](./docs/rules-catalog.md)
|
|
54
55
|
- Contributor/agent workflow guide: [`docs/AGENTS.md`](./docs/AGENTS.md)
|
|
55
56
|
|
|
56
57
|
---
|
|
57
58
|
|
|
58
59
|
## Commands
|
|
59
60
|
|
|
61
|
+
### `drift init`
|
|
62
|
+
|
|
63
|
+
Scaffold first-run setup for drift in an existing repository.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
drift init
|
|
67
|
+
drift init --preset node-backend
|
|
68
|
+
drift init --preset react-app --ci --baseline
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Flag | Description |
|
|
72
|
+
|------|-------------|
|
|
73
|
+
| `--preset <type>` | Generate `drift.config.ts` using one of: `node-backend`, `react-app`, `hexagonal`, `monorepo` |
|
|
74
|
+
| `--ci` | Generate `.github/workflows/drift-review.yml` |
|
|
75
|
+
| `--baseline` | Generate `drift-baseline.json` from the current scan |
|
|
76
|
+
|
|
77
|
+
`drift init` is non-destructive for pre-existing targets: drift skips generation when an output file already exists and prints a warning.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### `drift doctor`
|
|
82
|
+
|
|
83
|
+
Run environment diagnostics for Node/runtime and project readiness.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
drift doctor
|
|
87
|
+
drift doctor --json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Flag | Description |
|
|
91
|
+
|------|-------------|
|
|
92
|
+
| `--json` | Output structured doctor report JSON |
|
|
93
|
+
|
|
94
|
+
Checks include Node major version support (`>=18`), `package.json`, ESM mode, `tsconfig.json`, source file count, optional `--low-memory` recommendation, and `drift.config.*` detection.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### Repository smoke (local source CLI)
|
|
99
|
+
|
|
100
|
+
Run a non-destructive end-to-end smoke suite against any repository path using local source commands (`node --import tsx ./src/cli.ts ...`).
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm run smoke:repo -- ../target-repo --base origin/main
|
|
104
|
+
npm run smoke:repo -- ../target-repo --dry-run
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The script writes JSON + Markdown summaries and command logs under `.drift-smoke/<repo>-<timestamp>/`.
|
|
108
|
+
|
|
60
109
|
### `drift scan [path]`
|
|
61
110
|
|
|
62
111
|
Scan a directory and print a scored report to stdout.
|
|
@@ -77,16 +126,19 @@ drift scan ./src --low-memory --max-file-size-kb 1024
|
|
|
77
126
|
| Flag | Description |
|
|
78
127
|
|------|-------------|
|
|
79
128
|
| `--output <file>` | Write Markdown report to a file instead of stdout |
|
|
129
|
+
| `--format <type>` | Output format: `console\|json\|markdown\|ai\|sarif` |
|
|
80
130
|
| `--json` | Output raw `DriftReport` JSON |
|
|
81
131
|
| `--ai` | Output structured JSON optimized for LLM consumption (Claude, GPT, etc.) |
|
|
82
132
|
| `--fix` | Print inline fix suggestions for each detected issue |
|
|
83
|
-
| `--min-score <n>` | Exit with code 1 if the overall score
|
|
133
|
+
| `--min-score <n>` | Exit with code 1 if the overall score strictly exceeds this threshold |
|
|
84
134
|
| `--low-memory` | Analyze files in bounded chunks to reduce peak RAM |
|
|
85
135
|
| `--chunk-size <n>` | Files per chunk in low-memory mode (default: 40) |
|
|
86
136
|
| `--max-files <n>` | Soft cap on analyzed files; extra files are skipped with diagnostics |
|
|
87
137
|
| `--max-file-size-kb <n>` | Skip oversized files with diagnostics to avoid OOM |
|
|
88
138
|
| `--with-semantic-duplication` | Keep semantic duplication rule enabled in low-memory mode |
|
|
89
139
|
|
|
140
|
+
`--min-score` currently fails when `totalScore > n` (strictly greater than), matching CLI behavior.
|
|
141
|
+
|
|
90
142
|
**Example output:**
|
|
91
143
|
|
|
92
144
|
```
|
|
@@ -113,6 +165,33 @@ drift scan ./src --low-memory --max-file-size-kb 1024
|
|
|
113
165
|
|
|
114
166
|
---
|
|
115
167
|
|
|
168
|
+
### `drift guard [path]`
|
|
169
|
+
|
|
170
|
+
Evaluate drift regression guardrails against a git diff (`--base`) or a baseline file/object.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
drift guard ./src --base origin/main
|
|
174
|
+
drift guard ./src --baseline drift-baseline.json
|
|
175
|
+
drift guard ./src --base origin/main --budget 3 --by-severity error=0,warning=2,info=5
|
|
176
|
+
drift guard ./src --base origin/main --json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
| Flag | Description |
|
|
180
|
+
|------|-------------|
|
|
181
|
+
| `--base <ref>` | Diff mode: compare current state vs git ref |
|
|
182
|
+
| `--baseline <file>` | Baseline mode: compare against baseline JSON (default file name: `drift-baseline.json`) |
|
|
183
|
+
| `--budget <n>` | Maximum allowed score delta |
|
|
184
|
+
| `--by-severity <spec>` | Severity thresholds as CSV `key=value` pairs (`error`, `warning`, `info`) |
|
|
185
|
+
| `--json` | Output full `GuardResult` JSON |
|
|
186
|
+
| `--low-memory` / `--chunk-size <n>` / `--max-files <n>` / `--max-file-size-kb <n>` / `--with-semantic-duplication` | Analysis resource controls shared with scan/diff/report/badge/ci/trust |
|
|
187
|
+
|
|
188
|
+
Behavior:
|
|
189
|
+
- With `--base`, guard enforces no-regression checks for score and total issues, then applies optional budget/severity thresholds.
|
|
190
|
+
- Without `--base`, guard requires a baseline (inline or file) and compares only against available baseline anchors.
|
|
191
|
+
- Exit code is `1` when any guard check fails.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
116
195
|
### `drift diff [ref]`
|
|
117
196
|
|
|
118
197
|
Compare the current project state against any git ref. Defaults to `HEAD~1`.
|
|
@@ -122,6 +201,7 @@ drift diff # HEAD vs HEAD~1
|
|
|
122
201
|
drift diff HEAD~3 # HEAD vs 3 commits ago
|
|
123
202
|
drift diff main # HEAD vs branch main
|
|
124
203
|
drift diff abc1234 # HEAD vs a specific commit
|
|
204
|
+
drift diff --format sarif # Output SARIF for code scanning
|
|
125
205
|
drift diff --json # Output raw JSON diff
|
|
126
206
|
```
|
|
127
207
|
|
|
@@ -129,6 +209,7 @@ drift diff --json # Output raw JSON diff
|
|
|
129
209
|
|
|
130
210
|
| Flag | Description |
|
|
131
211
|
|------|-------------|
|
|
212
|
+
| `--format <type>` | Output format: `console\|json\|sarif` |
|
|
132
213
|
| `--json` | Output raw JSON diff |
|
|
133
214
|
|
|
134
215
|
Shows score delta, issues introduced, and issues resolved since the given ref.
|
|
@@ -143,12 +224,14 @@ Review drift against a git base ref and output a PR-ready markdown comment.
|
|
|
143
224
|
drift review --base origin/main
|
|
144
225
|
drift review --base main --comment
|
|
145
226
|
drift review --base HEAD~3 --json
|
|
227
|
+
drift review --base origin/main --format sarif
|
|
146
228
|
drift review --base origin/main --fail-on 5
|
|
147
229
|
```
|
|
148
230
|
|
|
149
231
|
| Flag | Description |
|
|
150
232
|
|------|-------------|
|
|
151
233
|
| `--base <ref>` | Git base ref to compare against (default: `origin/main`) |
|
|
234
|
+
| `--format <type>` | Output format: `console\|json\|markdown\|sarif` |
|
|
152
235
|
| `--json` | Output structured review JSON |
|
|
153
236
|
| `--comment` | Print only the markdown body for PR comments |
|
|
154
237
|
| `--fail-on <n>` | Exit code 1 when score delta is greater than or equal to `n` |
|
|
@@ -167,6 +250,7 @@ drift trust ./src
|
|
|
167
250
|
drift trust ./src --json
|
|
168
251
|
drift trust ./src --base origin/main
|
|
169
252
|
drift trust ./src --base origin/main --markdown
|
|
253
|
+
drift trust ./src --format sarif
|
|
170
254
|
drift trust ./src --markdown --output trust.md
|
|
171
255
|
drift trust ./src --min-trust 45
|
|
172
256
|
drift trust ./src --max-risk HIGH
|
|
@@ -180,6 +264,7 @@ drift trust ./src --advanced-trust --history-file ./drift-history.json --markdow
|
|
|
180
264
|
| Flag | Description |
|
|
181
265
|
|------|-------------|
|
|
182
266
|
| `--base <ref>` | Compare against a git base ref and include deterministic diff-aware penalties/bonuses |
|
|
267
|
+
| `--format <type>` | Output format: `console\|json\|markdown\|sarif` |
|
|
183
268
|
| `--json` | Output structured trust JSON (`trust_score`, `merge_risk`, `top_reasons`, `fix_priorities`, optional `diff_context`) |
|
|
184
269
|
| `--markdown` | Output PR-ready markdown trust summary |
|
|
185
270
|
| `--output <file>` | Write selected trust output format to file |
|
|
@@ -308,6 +393,7 @@ Emit GitHub Actions annotations and a step summary. Designed to run inside a CI
|
|
|
308
393
|
```bash
|
|
309
394
|
drift ci # scan current directory
|
|
310
395
|
drift ci ./src
|
|
396
|
+
drift ci ./src --format sarif
|
|
311
397
|
drift ci ./src --min-score 60
|
|
312
398
|
```
|
|
313
399
|
|
|
@@ -315,7 +401,8 @@ drift ci ./src --min-score 60
|
|
|
315
401
|
|
|
316
402
|
| Flag | Description |
|
|
317
403
|
|------|-------------|
|
|
318
|
-
| `--
|
|
404
|
+
| `--format <type>` | Output format: `console\|json\|sarif` |
|
|
405
|
+
| `--min-score <n>` | Exit with code 1 if the overall score strictly exceeds this threshold |
|
|
319
406
|
|
|
320
407
|
Outputs `::error` and `::warning` annotations visible in the PR diff. Writes a markdown summary to `$GITHUB_STEP_SUMMARY`.
|
|
321
408
|
|
|
@@ -427,36 +514,28 @@ export default {
|
|
|
427
514
|
|
|
428
515
|
## Rules
|
|
429
516
|
|
|
430
|
-
|
|
517
|
+
drift currently defines **35 rule IDs** in `RULE_WEIGHTS` (`src/analyzer.ts`): core detections, configurable architecture checks, AI/meta aggregation, plugin diagnostics, and analysis guardrail diagnostics.
|
|
518
|
+
|
|
519
|
+
For the complete up-to-date catalog (id, severity, weight, phase/origin, note), see [`docs/rules-catalog.md`](./docs/rules-catalog.md).
|
|
520
|
+
|
|
521
|
+
Highlights:
|
|
431
522
|
|
|
432
523
|
| Rule | Severity | Weight | What it detects |
|
|
433
524
|
|------|----------|--------|-----------------|
|
|
434
|
-
| `large-file` | error | 20 | Files exceeding 300 lines
|
|
435
|
-
| `
|
|
436
|
-
| `
|
|
437
|
-
| `
|
|
438
|
-
| `
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
443
|
-
| `
|
|
444
|
-
| `
|
|
445
|
-
| `
|
|
446
|
-
| `
|
|
447
|
-
| `
|
|
448
|
-
| `promise-style-mix` | warning | 7 | `async/await` and `.then()` / `.catch()` used together in the same file — AI combines styles inconsistently |
|
|
449
|
-
| `unused-export` | warning | 8 | Named exports that are never imported anywhere in the project — cross-file dead code ESLint cannot detect |
|
|
450
|
-
| `dead-file` | warning | 10 | Files never imported by any other file in the project — invisible dead code |
|
|
451
|
-
| `unused-dependency` | warning | 6 | Packages listed in `package.json` with no corresponding import in source files |
|
|
452
|
-
| `cross-boundary-import` | warning | 10 | Imports that cross module boundaries outside the allowed list — requires `drift.config.ts` |
|
|
453
|
-
| `hardcoded-config` | warning | 10 | Hardcoded URLs, IP addresses, secrets, or connection strings — AI skips environment variable abstraction |
|
|
454
|
-
| `inconsistent-error-handling` | warning | 8 | Mixed `try/catch` and `.catch()` patterns in the same file — AI combines approaches without a consistent strategy |
|
|
455
|
-
| `unnecessary-abstraction` | warning | 7 | Wrapper functions or helpers that add no logic over what they wrap — AI over-engineers simple calls |
|
|
456
|
-
| `naming-inconsistency` | warning | 6 | Mixed `camelCase` and `snake_case` in the same module — AI forgets project conventions mid-generation |
|
|
457
|
-
| `semantic-duplication` | warning | 12 | Functions with structurally identical logic despite different names — detected via AST fingerprinting, not text comparison |
|
|
458
|
-
| `no-return-type` | info | 5 | Functions missing an explicit return type annotation |
|
|
459
|
-
| `magic-number` | info | 3 | Numeric literals used directly in logic without a named constant |
|
|
525
|
+
| `large-file` | error | 20 | Files exceeding 300 lines |
|
|
526
|
+
| `duplicate-function-name` | error | 18 | Same function name repeated across a file |
|
|
527
|
+
| `high-complexity` | error | 15 | Cyclomatic complexity above threshold |
|
|
528
|
+
| `layer-violation` | error | 16 | Invalid layer import direction (requires `layers` config) |
|
|
529
|
+
| `cross-boundary-import` | warning | 10 | Invalid module boundary import (requires `modules`/legacy boundaries config) |
|
|
530
|
+
| `controller-no-db` | warning | 11 | Controller touching DB directly (configurable architecture rule) |
|
|
531
|
+
| `service-no-http` | warning | 11 | Service layer coupled to HTTP concerns (configurable architecture rule) |
|
|
532
|
+
| `max-function-lines` | warning | 9 | Function line cap enforced by `architectureRules.maxFunctionLines` |
|
|
533
|
+
| `semantic-duplication` | warning | 12 | AST-level semantic function duplication |
|
|
534
|
+
| `ai-code-smell` | warning | 12 | Aggregated AI-smell signal from multiple heuristics in a file |
|
|
535
|
+
| `plugin-error` | warning | 4 | Plugin load/contract/runtime failure surfaced as issue |
|
|
536
|
+
| `plugin-warning` | info | 0 | Non-fatal plugin validation warning |
|
|
537
|
+
| `analysis-skip-max-files` | info | 0 | File skipped by `maxFiles` guardrail |
|
|
538
|
+
| `analysis-skip-file-size` | info | 0 | File skipped by `maxFileSizeKb` guardrail |
|
|
460
539
|
|
|
461
540
|
---
|
|
462
541
|
|
|
@@ -476,7 +555,7 @@ export default {
|
|
|
476
555
|
|
|
477
556
|
## Configuration
|
|
478
557
|
|
|
479
|
-
drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`)
|
|
558
|
+
drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`) and configurable architecture checks use `drift.config.ts` (or `.js` / `.json`) at project root:
|
|
480
559
|
|
|
481
560
|
```typescript
|
|
482
561
|
import type { DriftConfig } from '@eduardbar/drift'
|
|
@@ -509,22 +588,16 @@ export default {
|
|
|
509
588
|
{ name: 'app', patterns: ['src/app/**'], canImportFrom: ['domain'] },
|
|
510
589
|
{ name: 'infra', patterns: ['src/infra/**'], canImportFrom: ['domain', 'app'] },
|
|
511
590
|
],
|
|
512
|
-
|
|
591
|
+
modules: [
|
|
513
592
|
{ name: 'auth', root: 'src/modules/auth', allowedExternalImports: ['src/shared'] },
|
|
514
593
|
{ name: 'billing', root: 'src/modules/billing', allowedExternalImports: ['src/shared'] },
|
|
515
594
|
],
|
|
516
|
-
exclude: [
|
|
517
|
-
'src/generated/**',
|
|
518
|
-
'**/*.spec.ts',
|
|
519
|
-
],
|
|
520
|
-
rules: {
|
|
521
|
-
'large-file': { threshold: 400 }, // override default 300
|
|
522
|
-
'magic-number': 'off', // disable a rule
|
|
523
|
-
},
|
|
524
595
|
} satisfies DriftConfig
|
|
525
596
|
```
|
|
526
597
|
|
|
527
|
-
|
|
598
|
+
For backwards compatibility, `moduleBoundaries` and `boundaries` are normalized to `modules`.
|
|
599
|
+
|
|
600
|
+
Without a config file, `layer-violation`, `cross-boundary-import`, and configurable architecture checks (`controller-no-db`, `service-no-http`, `max-function-lines`) are skipped. All other rules run with defaults.
|
|
528
601
|
|
|
529
602
|
### Memory guardrails (recommended for large repos)
|
|
530
603
|
|
|
@@ -563,7 +636,7 @@ jobs:
|
|
|
563
636
|
run: npx @eduardbar/drift scan ./src --min-score 60
|
|
564
637
|
```
|
|
565
638
|
|
|
566
|
-
Exit code is `1` if the score
|
|
639
|
+
Exit code is `1` if the score is strictly greater than `--min-score`. Exit code `0` otherwise.
|
|
567
640
|
|
|
568
641
|
### Annotations and step summary with `drift ci`
|
|
569
642
|
|
|
@@ -595,11 +668,45 @@ The repository includes `.github/workflows/review-pr.yml`, which:
|
|
|
595
668
|
- enforces a trust baseline gate with `drift trust-gate drift-trust.json --min-trust 45 --max-risk HIGH`
|
|
596
669
|
- uploads `drift trust --json` as `drift-trust-json-pr-<PR_NUMBER>-run-<RUN_ATTEMPT>` for manual KPI tracking
|
|
597
670
|
- publishes a compact trust KPI summary in `$GITHUB_STEP_SUMMARY` (score, merge risk, new/resolved issues when diff context is available)
|
|
671
|
+
- generates `drift.sarif` via `drift scan --format sarif`, uploads it to GitHub Code Scanning on non-fork PRs, and stores the SARIF file as workflow artifact for traceability
|
|
672
|
+
|
|
673
|
+
Quick local SARIF export:
|
|
674
|
+
|
|
675
|
+
```bash
|
|
676
|
+
drift scan ./src --format sarif > drift.sarif
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
Example GitHub Code Scanning upload with SARIF:
|
|
680
|
+
|
|
681
|
+
```yaml
|
|
682
|
+
- name: Generate drift SARIF
|
|
683
|
+
run: npx @eduardbar/drift scan ./src --format sarif > drift.sarif
|
|
684
|
+
|
|
685
|
+
- name: Upload SARIF to Code Scanning
|
|
686
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
687
|
+
with:
|
|
688
|
+
sarif_file: drift.sarif
|
|
689
|
+
```
|
|
598
690
|
|
|
599
691
|
Default gate behavior in this repo:
|
|
600
692
|
- fail when trust is below 45
|
|
601
693
|
- fail when merge risk is above `HIGH` (that means `CRITICAL` is blocked)
|
|
602
694
|
|
|
695
|
+
### GitHub Action contract (v2)
|
|
696
|
+
|
|
697
|
+
This repo also ships reusable local composite actions:
|
|
698
|
+
|
|
699
|
+
- `./.github/actions/drift-scan`: score-focused scan gate using `npx @eduardbar/drift@<version>` (no global install).
|
|
700
|
+
- `./.github/actions/drift-review`: trust/review/gate flow for PRs, aligned with `.github/workflows/review-pr.yml`.
|
|
701
|
+
|
|
702
|
+
`drift-scan` exposes machine-friendly outputs for CI composition:
|
|
703
|
+
- `score`, `grade`, `errors`, `warnings`, `infos`, `total-issues`, `files-affected`, `top-rules`
|
|
704
|
+
|
|
705
|
+
`drift-review` exposes:
|
|
706
|
+
- `trust-score`, `merge-risk`, `new-issues`, `resolved-issues`, `trust-json`, `trust-markdown`, `review-markdown`
|
|
707
|
+
|
|
708
|
+
See `.github/actions/drift-scan/README.md` and `.github/actions/drift-review/README.md` for usage details.
|
|
709
|
+
|
|
603
710
|
---
|
|
604
711
|
|
|
605
712
|
## drift-ignore
|
package/dist/benchmark.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare function runBenchmarkCli(argv?: string[]): Promise<void>;
|
|
2
2
|
//# sourceMappingURL=benchmark.d.ts.map
|
package/dist/benchmark.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { analyzeProject } from './analyzer.js';
|
|
4
5
|
import { loadConfig } from './config.js';
|
|
5
6
|
import { buildReport } from './reporter.js';
|
|
@@ -7,6 +8,30 @@ import { generateReview } from './review.js';
|
|
|
7
8
|
import { buildTrustReport } from './trust.js';
|
|
8
9
|
import { cleanupTempDir, extractFilesAtRef } from './git.js';
|
|
9
10
|
import { computeDiff } from './diff.js';
|
|
11
|
+
const DEFAULT_SCAN_PATH = '.';
|
|
12
|
+
const DEFAULT_REVIEW_PATH = '.';
|
|
13
|
+
const DEFAULT_TRUST_PATH = '.';
|
|
14
|
+
const DEFAULT_BASE_REF = 'HEAD~1';
|
|
15
|
+
const DEFAULT_WARMUP_RUNS = 1;
|
|
16
|
+
const DEFAULT_MEASURED_RUNS = 5;
|
|
17
|
+
const TABLE_WIDTHS = {
|
|
18
|
+
task: 10,
|
|
19
|
+
warmup: 8,
|
|
20
|
+
runs: 6,
|
|
21
|
+
median: 13,
|
|
22
|
+
mean: 11,
|
|
23
|
+
min: 10,
|
|
24
|
+
max: 10,
|
|
25
|
+
};
|
|
26
|
+
const TABLE_COLUMNS = [
|
|
27
|
+
{ key: 'task', header: 'task' },
|
|
28
|
+
{ key: 'warmup', header: 'warmup' },
|
|
29
|
+
{ key: 'runs', header: 'runs' },
|
|
30
|
+
{ key: 'median', header: 'median(ms)' },
|
|
31
|
+
{ key: 'mean', header: 'mean(ms)' },
|
|
32
|
+
{ key: 'min', header: 'min(ms)' },
|
|
33
|
+
{ key: 'max', header: 'max(ms)' },
|
|
34
|
+
];
|
|
10
35
|
function parseNumberFlag(value, flagName) {
|
|
11
36
|
const parsed = Number(value);
|
|
12
37
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
@@ -16,52 +41,29 @@ function parseNumberFlag(value, flagName) {
|
|
|
16
41
|
}
|
|
17
42
|
function parseOptions(argv) {
|
|
18
43
|
const options = {
|
|
19
|
-
scanPath:
|
|
20
|
-
reviewPath:
|
|
21
|
-
trustPath:
|
|
22
|
-
baseRef:
|
|
23
|
-
warmupRuns:
|
|
24
|
-
measuredRuns:
|
|
44
|
+
scanPath: DEFAULT_SCAN_PATH,
|
|
45
|
+
reviewPath: DEFAULT_REVIEW_PATH,
|
|
46
|
+
trustPath: DEFAULT_TRUST_PATH,
|
|
47
|
+
baseRef: DEFAULT_BASE_REF,
|
|
48
|
+
warmupRuns: DEFAULT_WARMUP_RUNS,
|
|
49
|
+
measuredRuns: DEFAULT_MEASURED_RUNS,
|
|
25
50
|
};
|
|
26
|
-
|
|
51
|
+
const handlers = {
|
|
52
|
+
'--scan-path': (value) => { options.scanPath = value; },
|
|
53
|
+
'--review-path': (value) => { options.reviewPath = value; },
|
|
54
|
+
'--trust-path': (value) => { options.trustPath = value; },
|
|
55
|
+
'--base': (value) => { options.baseRef = value; },
|
|
56
|
+
'--warmup': (value) => { options.warmupRuns = parseNumberFlag(value, '--warmup'); },
|
|
57
|
+
'--runs': (value) => { options.measuredRuns = parseNumberFlag(value, '--runs'); },
|
|
58
|
+
'--json-out': (value) => { options.jsonOut = value; },
|
|
59
|
+
};
|
|
60
|
+
for (let i = 0; i < argv.length; i += 2) {
|
|
27
61
|
const arg = argv[i];
|
|
28
62
|
const next = argv[i + 1];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
if (arg === '--review-path' && next) {
|
|
35
|
-
options.reviewPath = next;
|
|
36
|
-
i += 1;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (arg === '--trust-path' && next) {
|
|
40
|
-
options.trustPath = next;
|
|
41
|
-
i += 1;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (arg === '--base' && next) {
|
|
45
|
-
options.baseRef = next;
|
|
46
|
-
i += 1;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (arg === '--warmup' && next) {
|
|
50
|
-
options.warmupRuns = parseNumberFlag(next, '--warmup');
|
|
51
|
-
i += 1;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (arg === '--runs' && next) {
|
|
55
|
-
options.measuredRuns = parseNumberFlag(next, '--runs');
|
|
56
|
-
i += 1;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (arg === '--json-out' && next) {
|
|
60
|
-
options.jsonOut = next;
|
|
61
|
-
i += 1;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
throw new Error(`Unknown or incomplete argument: ${arg}`);
|
|
63
|
+
const handler = handlers[arg];
|
|
64
|
+
if (!handler || !next)
|
|
65
|
+
throw new Error(`Unknown or incomplete argument: ${arg}`);
|
|
66
|
+
handler(next);
|
|
65
67
|
}
|
|
66
68
|
if (options.measuredRuns < 1) {
|
|
67
69
|
throw new Error('--runs must be at least 1');
|
|
@@ -107,8 +109,8 @@ async function runTask(name, warmupRuns, measuredRuns, task) {
|
|
|
107
109
|
};
|
|
108
110
|
}
|
|
109
111
|
function printTable(results) {
|
|
110
|
-
const headers =
|
|
111
|
-
const widths =
|
|
112
|
+
const headers = TABLE_COLUMNS.map((column) => column.header);
|
|
113
|
+
const widths = TABLE_COLUMNS.map((column) => TABLE_WIDTHS[column.key]);
|
|
112
114
|
const row = (values) => values
|
|
113
115
|
.map((value, index) => value.padEnd(widths[index], ' '))
|
|
114
116
|
.join(' ');
|
|
@@ -153,11 +155,14 @@ async function runTrust(trustPath, baseRef) {
|
|
|
153
155
|
}
|
|
154
156
|
buildTrustReport(report, { diff });
|
|
155
157
|
}
|
|
156
|
-
async function
|
|
157
|
-
|
|
158
|
+
async function runReview(reviewPath, baseRef) {
|
|
159
|
+
await generateReview(reviewPath, baseRef);
|
|
160
|
+
}
|
|
161
|
+
async function main(argv) {
|
|
162
|
+
const options = parseOptions(argv);
|
|
158
163
|
const results = [
|
|
159
164
|
await runTask('scan', options.warmupRuns, options.measuredRuns, () => runScan(options.scanPath)),
|
|
160
|
-
await runTask('review', options.warmupRuns, options.measuredRuns, () =>
|
|
165
|
+
await runTask('review', options.warmupRuns, options.measuredRuns, () => runReview(options.reviewPath, options.baseRef)),
|
|
161
166
|
await runTask('trust', options.warmupRuns, options.measuredRuns, () => runTrust(options.trustPath, options.baseRef)),
|
|
162
167
|
];
|
|
163
168
|
const output = {
|
|
@@ -178,8 +183,22 @@ async function main() {
|
|
|
178
183
|
process.stdout.write(`\nSaved benchmark JSON to ${options.jsonOut}\n`);
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
function isExecutedAsEntryPoint() {
|
|
187
|
+
const entryArg = process.argv[1];
|
|
188
|
+
if (!entryArg)
|
|
189
|
+
return false;
|
|
190
|
+
return import.meta.url === pathToFileURL(path.resolve(entryArg)).href;
|
|
191
|
+
}
|
|
192
|
+
export async function runBenchmarkCli(argv = process.argv.slice(2)) {
|
|
193
|
+
try {
|
|
194
|
+
await main(argv);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (isExecutedAsEntryPoint()) {
|
|
202
|
+
void runBenchmarkCli();
|
|
203
|
+
}
|
|
185
204
|
//# sourceMappingURL=benchmark.js.map
|