@blundergoat/gruff-ts 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.
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTING.md +87 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/SECURITY.md +45 -0
- package/bin/gruff-ts +25 -0
- package/docs/CONFIGURATION.md +220 -0
- package/docs/RELEASING.md +103 -0
- package/docs/REPORTS_AND_CI.md +156 -0
- package/fixtures/sample.ts +21 -0
- package/package.json +56 -0
- package/scripts/bump-version.sh +145 -0
- package/scripts/check.sh +4 -0
- package/scripts/npm-publish.sh +258 -0
- package/scripts/preflight-checks.sh +357 -0
- package/scripts/start-dev.sh +8 -0
- package/scripts/test-performance.sh +695 -0
- package/src/analyser.ts +461 -0
- package/src/baseline.ts +90 -0
- package/src/blocks.ts +687 -0
- package/src/class-rules.ts +326 -0
- package/src/cli-program.ts +326 -0
- package/src/cli.ts +19 -0
- package/src/comment-rules.ts +605 -0
- package/src/comment-scanner.ts +357 -0
- package/src/config.ts +622 -0
- package/src/constants.ts +4 -0
- package/src/context-doc-rules.ts +241 -0
- package/src/dashboard.ts +114 -0
- package/src/dead-code-rules.ts +183 -0
- package/src/discovery.ts +508 -0
- package/src/doc-rules.ts +368 -0
- package/src/findings-helpers.ts +108 -0
- package/src/findings.ts +45 -0
- package/src/fixture-purpose-rules.ts +334 -0
- package/src/fixtures/rule-catalogue-security-doctrine.ts +132 -0
- package/src/github-actions-rules.ts +413 -0
- package/src/line-rules.ts +538 -0
- package/src/naming-pushers.ts +191 -0
- package/src/project-config-rules.ts +555 -0
- package/src/project-rules.ts +545 -0
- package/src/report-renderers.ts +691 -0
- package/src/rule-list.ts +179 -0
- package/src/rules.ts +135 -0
- package/src/safety-rules.ts +355 -0
- package/src/scoring.ts +74 -0
- package/src/security-flow-rules.ts +112 -0
- package/src/sensitive-data-rules.ts +288 -0
- package/src/source-text.ts +722 -0
- package/src/test-block-rules.ts +347 -0
- package/src/test-fixtures.ts +621 -0
- package/src/text-scans.ts +193 -0
- package/src/types.ts +113 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
`gruff-ts` can run without config. A config file is useful when adopting the
|
|
4
|
+
tool in a real project with generated files, local naming conventions, or rule
|
|
5
|
+
thresholds that need tuning.
|
|
6
|
+
|
|
7
|
+
## Discovery Order
|
|
8
|
+
|
|
9
|
+
`analyse` auto-loads the first supported config file it finds in the project root:
|
|
10
|
+
|
|
11
|
+
1. `.gruff-ts.yaml`
|
|
12
|
+
2. `.gruff.json`
|
|
13
|
+
3. `.gruff.yaml`
|
|
14
|
+
4. `.gruff.yml`
|
|
15
|
+
|
|
16
|
+
Use an explicit path:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
gruff-ts analyse . --config .gruff-ts.yaml
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Skip config for a run:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
gruff-ts analyse . --no-config
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Shape
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
paths:
|
|
32
|
+
ignore:
|
|
33
|
+
- "generated/**"
|
|
34
|
+
|
|
35
|
+
allowlists:
|
|
36
|
+
acceptedAbbreviations:
|
|
37
|
+
- api
|
|
38
|
+
- cli
|
|
39
|
+
secretPreviews: []
|
|
40
|
+
bannedGenericNames: [process, handle, doit, run, execute, manage]
|
|
41
|
+
booleanPrefixes: [is, has, can, should, does, did, was, will, may, in, scan, supports, requires]
|
|
42
|
+
hungarianPrefixes: [str, obj, arr, bool, int, num]
|
|
43
|
+
placeholderNames: [foo, bar, baz, tmp, temp, thing, stuff, data, value, item]
|
|
44
|
+
abbreviationDenylist: [ctx, pkg, opts, fn, idx, cb]
|
|
45
|
+
negativeBooleanAllowed: [nostore, nofollow, noreferrer, noscript, noindex]
|
|
46
|
+
knownAcronyms: [url, http, https, id, xml, json, html, css, api, sql, db, io, ui, uuid, ip, tcp, udp, ast, cli, npm]
|
|
47
|
+
|
|
48
|
+
rules:
|
|
49
|
+
rule.id:
|
|
50
|
+
enabled: true
|
|
51
|
+
threshold: 10
|
|
52
|
+
severity: warning
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Ignored Paths
|
|
56
|
+
|
|
57
|
+
Recursive directory scans respect root and nested `.gitignore` files before
|
|
58
|
+
adding supported source and config files to a run. `paths.ignore` is an extra
|
|
59
|
+
project policy layer for paths that should remain out of normal scans even when
|
|
60
|
+
they are not ignored by Git.
|
|
61
|
+
|
|
62
|
+
`paths.ignore` accepts exact paths, prefix-style paths, and simple glob
|
|
63
|
+
patterns. Examples:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
paths:
|
|
67
|
+
ignore:
|
|
68
|
+
- "generated/**"
|
|
69
|
+
- "fixtures/vendor/**"
|
|
70
|
+
- "src/generated-client.ts"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Default ignored directories are matched by first path segment:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
.git, .hg, .svn, .idea, .vscode, build, cache, coverage, dist,
|
|
77
|
+
generated, node_modules, target, tmp, vendor
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use `--include-ignored` when you intentionally want to scan default ignored
|
|
81
|
+
directories and Git-ignored paths. Configured `paths.ignore` entries still
|
|
82
|
+
apply.
|
|
83
|
+
|
|
84
|
+
## Allowlists
|
|
85
|
+
|
|
86
|
+
`allowlists.acceptedAbbreviations` lowers naming-rule noise for project-specific
|
|
87
|
+
short terms:
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
allowlists:
|
|
91
|
+
acceptedAbbreviations:
|
|
92
|
+
- api
|
|
93
|
+
- cli
|
|
94
|
+
- env
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`allowlists.secretPreviews` accepts redacted secret previews that are known false
|
|
98
|
+
positives:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
allowlists:
|
|
102
|
+
secretPreviews:
|
|
103
|
+
- "abcd...wxyz (redacted, 32 chars)"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Prefer fixing false positives with a narrow config entry instead of disabling an
|
|
107
|
+
entire sensitive-data rule.
|
|
108
|
+
|
|
109
|
+
Naming allowlists tune the 0.1.0 naming pack without changing rule ids or
|
|
110
|
+
fingerprints:
|
|
111
|
+
|
|
112
|
+
| Key | Used by | Default behavior |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `acceptedAbbreviations` | `naming.short-variable` | Adds short names that should not be flagged. |
|
|
115
|
+
| `bannedGenericNames` | `naming.generic-function` | Replaces the built-in generic function-name denylist. |
|
|
116
|
+
| `booleanPrefixes` | `naming.boolean-prefix` | Replaces the accepted boolean-name prefixes such as `is`, `has`, `should`, `may`, `supports`, and `requires`. |
|
|
117
|
+
| `hungarianPrefixes` | `naming.hungarian-notation` | Replaces type-style prefixes to flag. |
|
|
118
|
+
| `placeholderNames` | `naming.identifier-quality`, `naming.generic-parameter` | Replaces placeholder words; numbered suffix checks stay active. |
|
|
119
|
+
| `abbreviationDenylist` | `naming.abbreviation` | Replaces the opt-in abbreviation denylist. |
|
|
120
|
+
| `negativeBooleanAllowed` | `naming.negative-boolean` | Replaces domain terms allowed to start with `no`. |
|
|
121
|
+
| `knownAcronyms` | `naming.acronym-case` | Replaces acronyms checked for mixed casing. |
|
|
122
|
+
|
|
123
|
+
For replace-style allowlists, use an empty list (`[]`) when you intentionally
|
|
124
|
+
want no entries.
|
|
125
|
+
|
|
126
|
+
## Rule Controls
|
|
127
|
+
|
|
128
|
+
Disable a rule:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
rules:
|
|
132
|
+
docs.missing-public-doc:
|
|
133
|
+
enabled: false
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Set one threshold and one emitted severity for a metric rule:
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
rules:
|
|
140
|
+
complexity.cyclomatic:
|
|
141
|
+
threshold: 10
|
|
142
|
+
severity: warning
|
|
143
|
+
size.file-length:
|
|
144
|
+
threshold: 400
|
|
145
|
+
severity: error
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Rules with extra tuning knobs use `options` for those knobs while the primary
|
|
149
|
+
metric still uses `threshold` and `severity`:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
rules:
|
|
153
|
+
design.large-module-concentration:
|
|
154
|
+
threshold: 55
|
|
155
|
+
severity: advisory
|
|
156
|
+
options:
|
|
157
|
+
minFiles: 4
|
|
158
|
+
minLines: 80
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
List supported thresholds and options:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
gruff-ts list-rules
|
|
165
|
+
gruff-ts list-rules --format=json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Example Project Config
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
paths:
|
|
172
|
+
ignore:
|
|
173
|
+
- "generated/**"
|
|
174
|
+
- "fixtures/**"
|
|
175
|
+
|
|
176
|
+
allowlists:
|
|
177
|
+
acceptedAbbreviations:
|
|
178
|
+
- api
|
|
179
|
+
- cli
|
|
180
|
+
- env
|
|
181
|
+
- id
|
|
182
|
+
secretPreviews: []
|
|
183
|
+
|
|
184
|
+
rules:
|
|
185
|
+
complexity.cognitive:
|
|
186
|
+
threshold: 15
|
|
187
|
+
severity: warning
|
|
188
|
+
complexity.cyclomatic:
|
|
189
|
+
threshold: 10
|
|
190
|
+
severity: warning
|
|
191
|
+
design.deep-relative-import:
|
|
192
|
+
threshold: 2
|
|
193
|
+
severity: advisory
|
|
194
|
+
sensitive-data.high-entropy-string:
|
|
195
|
+
threshold: 32
|
|
196
|
+
severity: error
|
|
197
|
+
size.function-length:
|
|
198
|
+
threshold: 30
|
|
199
|
+
severity: warning
|
|
200
|
+
test-quality.setup-bloat:
|
|
201
|
+
threshold: 12
|
|
202
|
+
severity: advisory
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Adoption Defaults
|
|
206
|
+
|
|
207
|
+
Two noisy-by-nature rules are present in the public catalogue but disabled by
|
|
208
|
+
default in this repo config:
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
rules:
|
|
212
|
+
docs.todo-density:
|
|
213
|
+
enabled: false
|
|
214
|
+
naming.abbreviation:
|
|
215
|
+
enabled: false
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`docs.todo-without-tracking` remains enabled because it checks whether a TODO
|
|
219
|
+
has owner, issue, date, ADR, or task context instead of counting raw TODO
|
|
220
|
+
markers.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
This checklist prepares the public `@blundergoat/gruff-ts@0.1.0` release and
|
|
4
|
+
subsequent `0.1.x` patch releases.
|
|
5
|
+
|
|
6
|
+
## Bump The Version
|
|
7
|
+
|
|
8
|
+
`scripts/bump-version.sh <semver>` updates `package.json` and
|
|
9
|
+
`src/constants.ts` together so the CLI `--version` output and the published
|
|
10
|
+
`@blundergoat/gruff-ts` package version cannot drift apart. For the initial
|
|
11
|
+
`0.1.0` release, the version should already be `0.1.0`; use `--check` instead
|
|
12
|
+
of bumping unless the release version changes.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
scripts/bump-version.sh --check
|
|
16
|
+
scripts/bump-version.sh 0.1.1
|
|
17
|
+
scripts/bump-version.sh --check
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The script edits files in place and does not commit or tag. After running it,
|
|
21
|
+
update `CHANGELOG.md` and run `npm run check`.
|
|
22
|
+
|
|
23
|
+
## Before Publishing
|
|
24
|
+
|
|
25
|
+
- [ ] `scripts/bump-version.sh --check` reports the intended version.
|
|
26
|
+
- [ ] `CHANGELOG.md` has an entry for the new version with today's date.
|
|
27
|
+
- [ ] `README.md`, `CONTRIBUTING.md`, `SECURITY.md`, and docs under `docs/`
|
|
28
|
+
reflect any user-visible changes.
|
|
29
|
+
- [ ] `.goat-flow/tasks/0.1/` has no unresolved release blockers beyond
|
|
30
|
+
explicitly accepted `human-verification-pending` milestones.
|
|
31
|
+
- [ ] `LICENSE` is present and `package.json` `license` field matches.
|
|
32
|
+
- [ ] `npm run check` passes.
|
|
33
|
+
- [ ] `scripts/preflight-checks.sh` passes (runs `npm run check`, a full
|
|
34
|
+
`gruff-ts` self-scan, and `shellcheck` on `scripts/*.sh` when
|
|
35
|
+
`shellcheck` is installed).
|
|
36
|
+
- [ ] `npm pack --dry-run` shows only publishable runtime, docs, scripts, and
|
|
37
|
+
metadata files.
|
|
38
|
+
- [ ] Local smoke scan succeeds:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
./bin/gruff-ts
|
|
42
|
+
./bin/gruff-ts analyse fixtures/sample.ts --fail-on=none
|
|
43
|
+
./bin/gruff-ts summary fixtures/sample.ts --fail-on=none
|
|
44
|
+
./bin/gruff-ts report fixtures/sample.ts --output /tmp/gruff-ts-report.html
|
|
45
|
+
./bin/gruff-ts list-rules
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Package Review
|
|
49
|
+
|
|
50
|
+
Preview `@blundergoat/gruff-ts` package contents:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm pack --dry-run
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The package should include:
|
|
57
|
+
|
|
58
|
+
- `bin/gruff-ts`
|
|
59
|
+
- `src/` (all runtime `.ts` files; `src/**/*.test.ts` files are excluded by
|
|
60
|
+
`.npmignore`)
|
|
61
|
+
- `scripts/` (`bump-version.sh`, `check.sh`, `preflight-checks.sh`,
|
|
62
|
+
`npm-publish.sh`, `start-dev.sh`, `test-performance.sh`)
|
|
63
|
+
- `fixtures/sample.ts`
|
|
64
|
+
- `README.md`
|
|
65
|
+
- `CHANGELOG.md`
|
|
66
|
+
- `CONTRIBUTING.md`
|
|
67
|
+
- `SECURITY.md`
|
|
68
|
+
- `LICENSE`
|
|
69
|
+
- `docs/`
|
|
70
|
+
- `package.json`
|
|
71
|
+
- `tsconfig.json`
|
|
72
|
+
|
|
73
|
+
The package must exclude:
|
|
74
|
+
|
|
75
|
+
- `node_modules/`
|
|
76
|
+
- `coverage/`
|
|
77
|
+
- `.agents/`, `.claude/`, `.codex/`, `.github/`, and `.goat-flow/`
|
|
78
|
+
- `AGENTS.md` and `CLAUDE.md`
|
|
79
|
+
- `.gruff-ts.yaml` (this repo's local config)
|
|
80
|
+
- env and secret files (`.env`, `.env.*` except `.env.example`)
|
|
81
|
+
- local scratchpad or log artifacts
|
|
82
|
+
- `src/**/*.test.ts`
|
|
83
|
+
|
|
84
|
+
## Publish
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bash scripts/npm-publish.sh
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The script verifies npm auth, checks version lockstep, runs
|
|
91
|
+
`scripts/preflight-checks.sh`, prints an `npm publish --dry-run` summary, and
|
|
92
|
+
prompts before publishing.
|
|
93
|
+
|
|
94
|
+
## After Publishing
|
|
95
|
+
|
|
96
|
+
- [ ] Install `@blundergoat/gruff-ts` in a temporary project.
|
|
97
|
+
- [ ] Run `gruff-ts --help`.
|
|
98
|
+
- [ ] Run `gruff-ts analyse . --fail-on=none`.
|
|
99
|
+
- [ ] Run `gruff-ts summary . --fail-on=none`.
|
|
100
|
+
- [ ] Run `gruff-ts list-rules`.
|
|
101
|
+
- [ ] Verify `README.md` install instructions from a clean checkout.
|
|
102
|
+
- [ ] Tag the release in git (`git tag v0.1.0 && git push --tags`) and create
|
|
103
|
+
or update public release notes from `CHANGELOG.md`.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Reports And CI
|
|
2
|
+
|
|
3
|
+
This guide covers output formats, exit codes, baselines, GitHub annotations, and
|
|
4
|
+
the local dashboard.
|
|
5
|
+
|
|
6
|
+
## Exit Codes
|
|
7
|
+
|
|
8
|
+
`analyse` exits with:
|
|
9
|
+
|
|
10
|
+
- `0` when the scan completed and no finding met `--fail-on`.
|
|
11
|
+
- `1` when at least one finding met `--fail-on`.
|
|
12
|
+
- `2` when diagnostics were produced, such as missing inputs or parse/config
|
|
13
|
+
diagnostics.
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
gruff-ts analyse . --fail-on=none
|
|
19
|
+
gruff-ts analyse . --fail-on=error
|
|
20
|
+
gruff-ts analyse . --fail-on=warning
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Machine Output
|
|
24
|
+
|
|
25
|
+
Full JSON report:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
gruff-ts analyse . --format=json --fail-on=none > gruff-report.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Hotspot summary:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
gruff-ts analyse . --format=hotspot --fail-on=none > gruff-hotspots.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Human scan digest:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
gruff-ts summary . --fail-on=none
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The summary output includes the scanned path, elapsed duration, total findings,
|
|
44
|
+
per-pillar counts, top rules, and top file offenders.
|
|
45
|
+
|
|
46
|
+
Schema strings:
|
|
47
|
+
|
|
48
|
+
- `gruff.analysis.v1` for full analysis reports.
|
|
49
|
+
- `gruff.baseline.v1` for baselines.
|
|
50
|
+
- `gruff.hotspot.v1` for hotspot output.
|
|
51
|
+
|
|
52
|
+
## GitHub Actions
|
|
53
|
+
|
|
54
|
+
Use GitHub annotation output in a workflow step:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
gruff-ts analyse . --format=github --fail-on=warning
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Changed-file modes:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
gruff-ts analyse . --diff=working-tree --format=github --fail-on=warning
|
|
64
|
+
gruff-ts analyse . --diff=staged --format=github --fail-on=warning
|
|
65
|
+
gruff-ts analyse . --diff=origin/main --format=github --fail-on=warning
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`--diff` filters findings to changed files after analysis.
|
|
69
|
+
|
|
70
|
+
For SARIF consumers, write SARIF output from `analyse` and upload the generated
|
|
71
|
+
file with your platform's code-scanning upload step:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
gruff-ts analyse . --format=sarif --fail-on=none > gruff.sarif
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For a strict security-oriented gate, bypass baselines and fail on error-severity
|
|
78
|
+
findings:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
gruff-ts analyse . --no-baseline --fail-on=error
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This is useful when an adoption baseline exists for general quality debt but
|
|
85
|
+
security and sensitive-data errors should still break CI.
|
|
86
|
+
|
|
87
|
+
## Baselines
|
|
88
|
+
|
|
89
|
+
Generate an adoption baseline:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
gruff-ts analyse . --generate-baseline gruff-baseline.json --fail-on=none
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Apply it in CI:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
gruff-ts analyse . --baseline gruff-baseline.json --fail-on=warning
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Skip automatic baseline discovery:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
gruff-ts analyse . --no-baseline --fail-on=none
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Review baseline diffs carefully. A baseline suppresses matching fingerprints, so
|
|
108
|
+
unexpected additions can hide findings.
|
|
109
|
+
|
|
110
|
+
`report` intentionally renders raw scan results and does not accept a
|
|
111
|
+
`--baseline` option. Use `analyse` for baseline-aware machine output.
|
|
112
|
+
|
|
113
|
+
## HTML Reports
|
|
114
|
+
|
|
115
|
+
Write a dark self-contained report:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
gruff-ts report . --output gruff-report.html
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Write report JSON:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
gruff-ts report . --format=json --output gruff-report.json
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`report` defaults to `--fail-on none`, making it suitable for local inspection
|
|
128
|
+
and scheduled reporting.
|
|
129
|
+
|
|
130
|
+
## Dashboard
|
|
131
|
+
|
|
132
|
+
Start the dashboard:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
gruff-ts dashboard --host 127.0.0.1 --port 8767 --project-root .
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The dashboard serves:
|
|
139
|
+
|
|
140
|
+
- `/` - iframe shell and controls panel.
|
|
141
|
+
- `/health` - plain `ok`.
|
|
142
|
+
- `/scan?projectRoot=<path>&path=<path>` - report HTML for the selected scan.
|
|
143
|
+
|
|
144
|
+
Keep the dashboard on loopback unless the network is trusted. The scan endpoint
|
|
145
|
+
accepts filesystem paths through request parameters.
|
|
146
|
+
|
|
147
|
+
## Score History
|
|
148
|
+
|
|
149
|
+
Append score history to a JSON file:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
gruff-ts analyse . --history-file .gruff-history.json --fail-on=none
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
History files are local artifacts. Commit them only if your project explicitly
|
|
156
|
+
wants trend data in version control.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class SampleAnalyzer {
|
|
2
|
+
public name = "demo";
|
|
3
|
+
private secretUrl = "mysql://demo:password123@example.test/app";
|
|
4
|
+
|
|
5
|
+
public process(a: boolean, b: string[], c: string, d: string, e: string, f: string): void {
|
|
6
|
+
if (a) {
|
|
7
|
+
for (const item of b) {
|
|
8
|
+
if (item === c) {
|
|
9
|
+
eval(item);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const apiKey = "AKIA1111111111111111";
|
|
15
|
+
console.log(apiKey, this.secretUrl, d, e, f);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test("sleeps without assertion", async () => {
|
|
20
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
21
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blundergoat/gruff-ts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Static analyzer for TypeScript and JavaScript projects - 121 rules across 11 quality pillars, SARIF output, baselines, and a local dashboard.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Matthew Hansen (https://www.blundergoat.com/about)",
|
|
7
|
+
"homepage": "https://github.com/blundergoat/gruff-ts#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/blundergoat/gruff-ts.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/blundergoat/gruff-ts/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"typescript",
|
|
17
|
+
"javascript",
|
|
18
|
+
"static-analysis",
|
|
19
|
+
"code-quality",
|
|
20
|
+
"linter",
|
|
21
|
+
"analyzer",
|
|
22
|
+
"cli",
|
|
23
|
+
"sarif",
|
|
24
|
+
"baseline",
|
|
25
|
+
"fingerprint",
|
|
26
|
+
"complexity",
|
|
27
|
+
"dead-code",
|
|
28
|
+
"security",
|
|
29
|
+
"naming",
|
|
30
|
+
"dashboard",
|
|
31
|
+
"ci",
|
|
32
|
+
"github-actions",
|
|
33
|
+
"nodejs"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"type": "module",
|
|
39
|
+
"bin": {
|
|
40
|
+
"gruff-ts": "bin/gruff-ts"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"check": "tsc --noEmit && npm test",
|
|
44
|
+
"test": "node --import tsx --test src/**/*.test.ts",
|
|
45
|
+
"start-dev": "tsx src/cli.ts dashboard"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"commander": "^14.0.2",
|
|
49
|
+
"tsx": "^4.21.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@blundergoat/goat-flow": "^1.6.4",
|
|
53
|
+
"@types/node": "^25.0.0",
|
|
54
|
+
"typescript": "^5.9.3"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Bump gruff-ts to a new semver in package.json and src/constants.ts in one step.
|
|
5
|
+
# The CLI surfaces VERSION from src/constants.ts; package.json drives `npm publish`.
|
|
6
|
+
# Keeping them in lockstep is a release invariant.
|
|
7
|
+
|
|
8
|
+
usage() {
|
|
9
|
+
cat <<'USAGE'
|
|
10
|
+
Usage:
|
|
11
|
+
scripts/bump-version.sh <new-version>
|
|
12
|
+
scripts/bump-version.sh --check
|
|
13
|
+
|
|
14
|
+
Arguments:
|
|
15
|
+
<new-version> Target semver, e.g. 0.1.1, 0.2.0, 1.0.0-rc.1.
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--check Verify package.json and src/constants.ts already agree.
|
|
19
|
+
--help, -h Show this help.
|
|
20
|
+
|
|
21
|
+
Notes:
|
|
22
|
+
Edits files in place. Does not commit or tag. Run `npm run check` afterwards.
|
|
23
|
+
USAGE
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
die() {
|
|
27
|
+
printf 'bump-version: %s\n' "$*" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
repo_root() {
|
|
32
|
+
local script_dir
|
|
33
|
+
script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
34
|
+
CDPATH='' cd -- "$script_dir/.." && pwd
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
read_package_version() {
|
|
38
|
+
awk -F'"' '/^[[:space:]]*"version"[[:space:]]*:/ { print $4; exit }' package.json
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
read_constants_version() {
|
|
42
|
+
awk -F'"' '/^const VERSION = "/ { print $2; exit }' src/constants.ts
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
write_package_version() {
|
|
46
|
+
local next_version="$1"
|
|
47
|
+
awk -v target="$next_version" '
|
|
48
|
+
BEGIN { done = 0 }
|
|
49
|
+
/^[[:space:]]*"version"[[:space:]]*:/ && !done {
|
|
50
|
+
sub(/"version"[[:space:]]*:[[:space:]]*"[^"]+"/, "\"version\": \"" target "\"")
|
|
51
|
+
done = 1
|
|
52
|
+
}
|
|
53
|
+
{ print }
|
|
54
|
+
' package.json > package.json.tmp
|
|
55
|
+
mv package.json.tmp package.json
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
write_constants_version() {
|
|
59
|
+
local next_version="$1"
|
|
60
|
+
awk -v target="$next_version" '
|
|
61
|
+
BEGIN { done = 0 }
|
|
62
|
+
/^const VERSION = "/ && !done {
|
|
63
|
+
print "const VERSION = \"" target "\";"
|
|
64
|
+
done = 1
|
|
65
|
+
next
|
|
66
|
+
}
|
|
67
|
+
{ print }
|
|
68
|
+
' src/constants.ts > src/constants.ts.tmp
|
|
69
|
+
mv src/constants.ts.tmp src/constants.ts
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
validate_semver() {
|
|
73
|
+
local value="$1"
|
|
74
|
+
# Standard semver: MAJOR.MINOR.PATCH with optional prerelease/build metadata.
|
|
75
|
+
local pattern='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'
|
|
76
|
+
[[ "$value" =~ $pattern ]] || die "not a valid semver: $value"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
main() {
|
|
80
|
+
if [[ "$#" -eq 0 ]]; then
|
|
81
|
+
usage >&2
|
|
82
|
+
exit 2
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
cd "$(repo_root)"
|
|
86
|
+
[[ -f package.json ]] || die "package.json not found"
|
|
87
|
+
[[ -f src/constants.ts ]] || die "src/constants.ts not found"
|
|
88
|
+
|
|
89
|
+
case "$1" in
|
|
90
|
+
-h|--help)
|
|
91
|
+
usage
|
|
92
|
+
exit 0
|
|
93
|
+
;;
|
|
94
|
+
--check)
|
|
95
|
+
local pkg const_
|
|
96
|
+
pkg="$(read_package_version)" || die "failed to read package.json version"
|
|
97
|
+
const_="$(read_constants_version)" || die "failed to read src/constants.ts VERSION"
|
|
98
|
+
[[ -n "$pkg" ]] || die "package.json has no \"version\" field"
|
|
99
|
+
[[ -n "$const_" ]] || die "src/constants.ts has no VERSION constant"
|
|
100
|
+
if [[ "$pkg" != "$const_" ]]; then
|
|
101
|
+
printf 'package.json (%s) and src/constants.ts (%s) disagree\n' "$pkg" "$const_" >&2
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
printf 'package.json and src/constants.ts agree on %s\n' "$pkg"
|
|
105
|
+
exit 0
|
|
106
|
+
;;
|
|
107
|
+
esac
|
|
108
|
+
|
|
109
|
+
local next="$1"
|
|
110
|
+
validate_semver "$next"
|
|
111
|
+
|
|
112
|
+
local current
|
|
113
|
+
current="$(read_package_version)" || die "failed to read package.json version"
|
|
114
|
+
[[ -n "$current" ]] || die "package.json has no \"version\" field"
|
|
115
|
+
|
|
116
|
+
local current_const
|
|
117
|
+
current_const="$(read_constants_version)" || die "failed to read src/constants.ts VERSION"
|
|
118
|
+
[[ -n "$current_const" ]] || die "src/constants.ts has no VERSION constant"
|
|
119
|
+
|
|
120
|
+
if [[ "$current" != "$current_const" ]]; then
|
|
121
|
+
die "current versions diverge: package.json=$current src/constants.ts=$current_const (resolve manually first)"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [[ "$current" == "$next" ]]; then
|
|
125
|
+
printf 'already at %s; nothing to do\n' "$next"
|
|
126
|
+
exit 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
write_package_version "$next"
|
|
130
|
+
write_constants_version "$next"
|
|
131
|
+
|
|
132
|
+
local check_pkg check_const
|
|
133
|
+
check_pkg="$(read_package_version)"
|
|
134
|
+
check_const="$(read_constants_version)"
|
|
135
|
+
[[ "$check_pkg" == "$next" ]] || die "package.json did not update cleanly (read back: ${check_pkg:-empty})"
|
|
136
|
+
[[ "$check_const" == "$next" ]] || die "src/constants.ts did not update cleanly (read back: ${check_const:-empty})"
|
|
137
|
+
|
|
138
|
+
printf 'bumped %s -> %s\n' "$current" "$next"
|
|
139
|
+
printf ' package.json\n'
|
|
140
|
+
printf ' src/constants.ts\n'
|
|
141
|
+
# shellcheck disable=SC2016
|
|
142
|
+
printf 'next: update CHANGELOG.md and run `npm run check`\n'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main "$@"
|
package/scripts/check.sh
ADDED