cuke-dedup 0.1.1 → 0.2.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 +93 -0
- package/README.md +110 -273
- package/SECURITY.md +29 -1
- package/docs/ci-and-baselines.md +119 -0
- package/docs/configuration.md +199 -0
- package/docs/discovery-and-frameworks.md +97 -0
- package/docs/reports.md +83 -0
- package/docs/rules.md +113 -0
- package/docs/safety-and-limitations.md +97 -0
- package/npm/lib/launcher.mjs +4 -14
- package/npm/lib/targets.mjs +134 -0
- package/package.json +10 -9
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,98 @@
|
|
|
3
3
|
All notable changes to CukeDedup are documented in this file. The project follows
|
|
4
4
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## [0.2.0] - 2026-09-10
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Reorganized the user documentation around a concise quick-start README and focused reference
|
|
11
|
+
guides for configuration, discovery, rules, reports, CI adoption, and safety limits.
|
|
12
|
+
- **Breaking (reports and baselines):** JSON and JSONL reports now use schema version `2`, and
|
|
13
|
+
semantic baselines use version `2`. Large exact-equivalence groups are represented by bounded
|
|
14
|
+
cluster evidence instead of many pair records. Existing version `1` baselines are rejected for
|
|
15
|
+
normal comparison but can be replaced directly with `--update-baseline`; no manual deletion is
|
|
16
|
+
required.
|
|
17
|
+
- **Breaking (CLI):** analysis that cannot cover the complete corpus now warns and exits on
|
|
18
|
+
finding severity instead of returning operational exit code `2`. Candidate-comparison
|
|
19
|
+
truncation, resolution resource limits, and registration imports that cannot be resolved
|
|
20
|
+
statically are all reported as incomplete coverage rather than analyzer failure. Set
|
|
21
|
+
`failOnIncomplete: true`, pass `--fail-on-incomplete`, or set the Action's
|
|
22
|
+
`fail-on-incomplete` input to restore exit code `2`.
|
|
23
|
+
- **Breaking (SARIF):** an incomplete run now reports `executionSuccessful: false` so code-scanning
|
|
24
|
+
consumers can distinguish partial coverage from a complete scan.
|
|
25
|
+
- **Breaking (Rust API):** `AnalysisOutcome::operational_errors` is replaced by
|
|
26
|
+
`AnalysisOutcome::incomplete`. Bounded work that could not finish is a completeness signal, not
|
|
27
|
+
an operational failure; `analyze` still refuses an incomplete result.
|
|
28
|
+
- **Breaking (Rust API):** report renderers and `write_reports` now accept a shared
|
|
29
|
+
`ReportContext` instead of separate result, root, threshold, and metrics arguments. Construct it
|
|
30
|
+
with `ReportContext::new` or `ReportContext::with_metrics`; the redundant
|
|
31
|
+
`*_with_threshold` and `write_reports_with_metrics` wrappers were removed.
|
|
32
|
+
- **Breaking (Rust API):** public result, diagnostic, reporter, and extensible enum types are now
|
|
33
|
+
`#[non_exhaustive]`. Use `AnalysisResult::new`, `ExecutionMetrics::new`, existing constructors,
|
|
34
|
+
and wildcard enum match arms so future fields and variants do not require another breaking
|
|
35
|
+
release.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- An `overlapping-matcher` rule that reports two matchers accepting the same step text without
|
|
40
|
+
needing a feature corpus to prove it.
|
|
41
|
+
- `registrations` project configuration plus inference for local wrappers that forward their own
|
|
42
|
+
leading parameters to a known registration.
|
|
43
|
+
- `parameterTypes` project configuration, which restores exact usage and ambiguity analysis for
|
|
44
|
+
matchers that use project-defined Cucumber Expression parameter types.
|
|
45
|
+
- `failOnIncomplete` project configuration, the `--fail-on-incomplete` flag, and the Action's
|
|
46
|
+
`fail-on-incomplete` input.
|
|
47
|
+
- A `corpus.incomplete` flag in machine reports and a matching HTML alert when a registration
|
|
48
|
+
import could not be resolved statically.
|
|
49
|
+
- Legacy Cucumber.js and Cypress Cucumber registration entrypoints, plus Playwright-BDD
|
|
50
|
+
class-method decorator registrations.
|
|
51
|
+
- A `corpus.featureFilesWithoutSteps` census field for converted Gherkin Markdown inputs that
|
|
52
|
+
yielded no concrete steps.
|
|
53
|
+
|
|
54
|
+
### Security
|
|
55
|
+
|
|
56
|
+
- Repository-controlled configuration can lower analysis candidate budgets but can no longer
|
|
57
|
+
raise their immutable memory and work ceilings.
|
|
58
|
+
- npm publishing now uses short-lived OIDC trusted-publisher credentials exclusively; the release
|
|
59
|
+
workflow no longer reads a long-lived `NPM_TOKEN` secret and fails closed on unsupported npm
|
|
60
|
+
clients.
|
|
61
|
+
- A second fuzz target covers configuration discovery, project module metadata, and baseline
|
|
62
|
+
parsing, the untrusted inputs that are read outside the source parsers.
|
|
63
|
+
- Release jobs are bound to the immutable workflow commit instead of accepting a free-form checkout
|
|
64
|
+
ref, and the GitHub Action no longer derives outbound download URLs from runtime file contents.
|
|
65
|
+
|
|
66
|
+
### Performance
|
|
67
|
+
|
|
68
|
+
- Feature-step usage analysis evaluates every matcher in one pass with a shared regular-expression
|
|
69
|
+
set and matches each distinct step text once, roughly halving usage-dominated analysis time.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- Repository-owned CodeQL analysis excludes the intentionally malformed JavaScript parser fixture
|
|
74
|
+
from extraction while preserving extended security queries for every supported project language.
|
|
75
|
+
- Analyzing a single package whose imports resolve outside it no longer fails the run; the
|
|
76
|
+
unresolved specifier is reported as a source-localized warning naming its cause.
|
|
77
|
+
- Registration modules the filesystem refuses to read remain operational failures, matching how
|
|
78
|
+
unreadable definition sources are already treated.
|
|
79
|
+
- Semantic baselines are no longer updated from an incomplete corpus.
|
|
80
|
+
- Wrapper inference no longer promotes deferred callbacks, nested declarations, generators, async
|
|
81
|
+
functions, conditional calls, or multi-statement helpers into file-wide registration aliases.
|
|
82
|
+
- Ambiguity evidence uses linear membership storage, and static overlap work shares the configured
|
|
83
|
+
candidate budget and caps retained findings instead of allocating an unbounded quadratic pair
|
|
84
|
+
or finding set.
|
|
85
|
+
- Oversized combined matcher indexes are split into bounded regular-expression sets instead of
|
|
86
|
+
silently restoring the definitions-by-steps fallback loop.
|
|
87
|
+
- npm release validation now covers `package-lock.json`, including the root package, platform
|
|
88
|
+
workspaces, and optional dependency versions.
|
|
89
|
+
- Gherkin Markdown that converts to an empty feature document now marks the corpus incomplete and
|
|
90
|
+
disables `unused-definition` instead of producing false non-use findings.
|
|
91
|
+
- Non-authoritative Cucumber Expression fallbacks and JavaScript regular expressions using the
|
|
92
|
+
Unicode-sets `v` flag can no longer produce false unused or ambiguity conclusions.
|
|
93
|
+
- Playwright-BDD factory aliases now require trusted ESM or CommonJS import evidence, and local
|
|
94
|
+
registration barrels preserve imported framework bindings across recoverable resolver errors.
|
|
95
|
+
- Static overlap witness scans are bounded before index evaluation, handler behavior signatures
|
|
96
|
+
normalize renamed receivers, and SARIF preserves pre-truncated cluster metadata.
|
|
97
|
+
|
|
6
98
|
## [0.1.1] - 2026-09-08
|
|
7
99
|
|
|
8
100
|
### Changed
|
|
@@ -28,5 +120,6 @@ Initial public release.
|
|
|
28
120
|
- Native Cargo and npm distributions for eight supported targets.
|
|
29
121
|
- A checksum-verified GitHub Action and an agent-oriented CukeDedup skill.
|
|
30
122
|
|
|
123
|
+
[0.2.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.1.1...v0.2.0
|
|
31
124
|
[0.1.1]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.1.0...v0.1.1
|
|
32
125
|
[0.1.0]: https://github.com/figueiredoluiz/cuke-dedup/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -3,41 +3,54 @@
|
|
|
3
3
|
[](https://github.com/figueiredoluiz/cuke-dedup/actions/workflows/ci.yml)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
|
|
6
|
-
CukeDedup
|
|
6
|
+
CukeDedup is a fast static analyzer for duplicate, ambiguous, reusable, and unused
|
|
7
|
+
Cucumber/Gherkin step definitions. It reads project configuration and test code without executing
|
|
8
|
+
either one.
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
It supports:
|
|
9
11
|
|
|
10
|
-
- JavaScript
|
|
11
|
-
- Cucumber.js, Playwright
|
|
12
|
+
- JavaScript, JSX, TypeScript, and TSX step definitions.
|
|
13
|
+
- Cucumber.js, Playwright-BDD, and Cypress Cucumber projects.
|
|
12
14
|
- Classic `.feature` files and Gherkin Markdown `.feature.md` files.
|
|
13
15
|
- Terminal, JSON, JSON Lines, HTML, and SARIF reports.
|
|
14
|
-
-
|
|
16
|
+
- Thresholds, baselines, changed-file analysis, suppressions, and ignore files.
|
|
15
17
|
|
|
16
18
|
## Install
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
With Cargo:
|
|
19
21
|
|
|
20
22
|
```sh
|
|
21
23
|
cargo install cuke-dedup
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
Or
|
|
26
|
+
Or in a JavaScript project:
|
|
25
27
|
|
|
26
28
|
```sh
|
|
27
29
|
npm install --save-dev cuke-dedup
|
|
28
30
|
npx cuke-dedup .
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
`cargo build --release --locked`.
|
|
33
|
+
Building from source requires Rust 1.90 or newer:
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
```sh
|
|
36
|
+
cargo build --release --locked
|
|
37
|
+
```
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
Run CukeDedup from the root of a test project:
|
|
37
42
|
|
|
38
43
|
```sh
|
|
39
44
|
cuke-dedup .
|
|
40
|
-
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
No configuration is required. CukeDedup respects `.gitignore` and `.cuke-dedupignore`, skips
|
|
48
|
+
common generated directories, discovers conventional Gherkin and JavaScript/TypeScript files, and
|
|
49
|
+
recognizes common `Given`, `When`, `Then`, and `defineStep` registrations and aliases.
|
|
50
|
+
|
|
51
|
+
Useful commands:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
41
54
|
cuke-dedup . --threshold 5
|
|
42
55
|
cuke-dedup . --print-config
|
|
43
56
|
cuke-dedup . --reporters terminal,json,html,sarif
|
|
@@ -45,228 +58,74 @@ cuke-dedup . --reporters jsonl
|
|
|
45
58
|
cuke-dedup . --output reports/cuke-dedup
|
|
46
59
|
```
|
|
47
60
|
|
|
48
|
-
`check
|
|
49
|
-
|
|
50
|
-
With no configuration, CukeDedup respects `.gitignore` and `.cuke-dedupignore`, excludes common generated directories, discovers classic `.feature` files and Gherkin Markdown `.feature.md` files, and inspects conventional JavaScript/TypeScript step registrations such as `Given`, `When`, `Then`, and `defineStep`, including common aliases.
|
|
51
|
-
|
|
52
|
-
## Configuration
|
|
61
|
+
`cuke-dedup check .` is equivalent to `cuke-dedup .`. The explicit `check` form requires a path;
|
|
62
|
+
use `cuke-dedup ./check` to analyze a directory literally named `check`.
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
## Framework support
|
|
55
65
|
|
|
56
|
-
|
|
66
|
+
| Workflow | Recognized registrations |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| Cucumber.js | `@cucumber/cucumber` and legacy `cucumber`; ESM, CJS, aliases, namespaces, and static local re-exports. |
|
|
69
|
+
| Playwright-BDD | `createBdd()` registrations and `playwright-bdd/decorators` class-method decorators. |
|
|
70
|
+
| Cypress Cucumber | `@badeball/cypress-cucumber-preprocessor` and legacy `cypress-cucumber-preprocessor/steps`. |
|
|
57
71
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```
|
|
72
|
+
Package entrypoints are matched exactly. Plain Playwright projects are supported when their
|
|
73
|
+
Gherkin bindings use Cucumber.js or Playwright-BDD.
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
See [Discovery and frameworks](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/discovery-and-frameworks.md)
|
|
76
|
+
for configuration detection, monorepo boundaries, module resolution, and incomplete-corpus
|
|
77
|
+
behavior.
|
|
63
78
|
|
|
64
|
-
|
|
65
|
-
.cuke-dedup.json
|
|
66
|
-
.config/cuke-dedup.json
|
|
67
|
-
.config/.cuke-dedup.json
|
|
68
|
-
cuke-dedup.config.json (legacy compatibility)
|
|
69
|
-
package.json#cukeDedup
|
|
70
|
-
```
|
|
79
|
+
## Configuration
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
CukeDedup reads an explicit `--config` file, an auto-discovered CukeDedup configuration, or
|
|
82
|
+
supported framework configuration. CLI flags always have the highest precedence.
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
A minimal `.cuke-dedup.json` might be:
|
|
75
85
|
|
|
76
86
|
```json
|
|
77
87
|
{
|
|
78
88
|
"definitions": ["features/steps/**/*.ts"],
|
|
79
89
|
"features": ["features/**/*.{feature,feature.md}"],
|
|
80
90
|
"exclude": ["dist/**"],
|
|
81
|
-
"excludeDefaults": true,
|
|
82
|
-
"includeHidden": false,
|
|
83
91
|
"threshold": 5,
|
|
84
|
-
"
|
|
85
|
-
"noMetrics": false,
|
|
86
|
-
"reporters": ["terminal", "json", "html", "sarif"],
|
|
87
|
-
"output": "reports/cuke-dedup",
|
|
88
|
-
"rules": {
|
|
89
|
-
"duplicate-matcher": "error",
|
|
90
|
-
"duplicate-handler": "error",
|
|
91
|
-
"near-duplicate-step": "warning",
|
|
92
|
-
"unused-definition": "off"
|
|
93
|
-
},
|
|
94
|
-
"suppressions": [
|
|
95
|
-
{
|
|
96
|
-
"rule": "duplicate-handler",
|
|
97
|
-
"path": "features/steps/legacy.ts",
|
|
98
|
-
"matcher": "the legacy flow is complete",
|
|
99
|
-
"reason": "Kept distinct while the legacy flow is retired"
|
|
100
|
-
}
|
|
101
|
-
]
|
|
92
|
+
"reporters": ["terminal", "html"]
|
|
102
93
|
}
|
|
103
94
|
```
|
|
104
95
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Every suppression must include a non-empty `reason` and select at least a `path` or `matcher`:
|
|
108
|
-
|
|
109
|
-
- When both selectors are present, both must match.
|
|
110
|
-
- For a pair finding, a configured `path` must contain every involved definition.
|
|
111
|
-
- A matcher selector must select at least one involved definition.
|
|
112
|
-
|
|
113
|
-
These rules prevent a directory-scoped exception from hiding a conflict that crosses into maintained code.
|
|
114
|
-
|
|
115
|
-
One definition can instead carry an auditable source-local suppression. The directive must be immediately above the registration, select one rule, and include a reason:
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
// cuke-dedup:ignore duplicate-handler -- retained for an external compatibility contract
|
|
119
|
-
Given("the legacy flow completes", legacyHandler);
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Malformed directives are operational errors rather than silently ignored comments. A directive suppresses findings involving its attached definition only.
|
|
123
|
-
|
|
124
|
-
### Framework discovery and corpus boundaries
|
|
125
|
-
|
|
126
|
-
When `features` is not explicitly configured, CukeDedup statically reads literal paths from the project's BDD setup: Playwright-BDD's `defineBddConfig`, Cypress `e2e.specPattern` when the Badeball Cucumber preprocessor is installed, and Cucumber.js configuration. It never executes project configuration. Cucumber directories include both `.feature` and `.feature.md`; Playwright-BDD directories follow that framework's `.feature` default. Explicit file or glob paths are preserved, so a project can select names such as `*.spec`. Dynamic paths produce a warning and can be made deterministic by setting `features` in CukeDedup's own configuration.
|
|
127
|
-
|
|
128
|
-
Analysis is intentionally scoped to the supplied root: every discovered definition and feature under that root belongs to one comparison corpus. In a monorepo whose packages use independent step registries, run CukeDedup once per package (for example, `cuke-dedup packages/accounts`) instead of treating the monorepo root as one suite. Framework detection selects the appropriate registration patterns but does not silently split the corpus because mixed-framework projects can deliberately share definitions.
|
|
129
|
-
|
|
130
|
-
`.feature.md` selects the Gherkin Markdown parser. Other extensions selected by a custom or framework glob are treated as classic Gherkin. This prevents ordinary Markdown files from being scanned while still allowing conventions such as `.spec`.
|
|
131
|
-
|
|
132
|
-
### Exclusions and ignore files
|
|
133
|
-
|
|
134
|
-
Configured `exclude` patterns replace custom patterns from lower-precedence layers, while built-in generated-directory exclusions remain independently enabled. An exclusion may name one file, one directory, or a glob.
|
|
135
|
-
|
|
136
|
-
- Set `excludeDefaults` to `false` to analyze an explicitly selected workspace under `node_modules`, `target`, or another protected directory.
|
|
137
|
-
- Set `includeHidden` to `true` to descend into dot-directories.
|
|
138
|
-
- Definitions, features, excludes, reporters, and suppressions replace the corresponding lower-precedence value.
|
|
139
|
-
- Scalar values and individual rule severities merge by precedence and rule name.
|
|
140
|
-
|
|
141
|
-
Configured `definitions`, `features`, `exclude`, and suppression-path values use `globset` syntax against root-relative paths normalized with `/`. `*` and `?` may cross `/`; use `**` when a recursive directory boundary should be obvious to readers. Character classes such as `[ab]`, brace alternatives such as `{js,ts}`, and backslash escaping are supported. These configuration globs are distinct from `.gitignore` and `.cuke-dedupignore`, which use directory-scoped gitignore semantics and support negation.
|
|
142
|
-
|
|
143
|
-
Repositories may also place a `.cuke-dedupignore` file at the analyzed root or in any descendant directory. It uses gitignore syntax: blank lines and `#` comments are ignored, `/` anchors a rule to the ignore file's directory, a trailing `/` selects directories, and `!` negates an earlier matching rule. Nested files apply only to their directory subtree. These rules are applied in addition to `.gitignore`, built-in exclusions, and configured `exclude` patterns. `--no-default-excludes` disables only the built-in generated-directory list; it does not disable either ignore file. Configured `exclude` patterns remain hard exclusions and cannot be negated from `.cuke-dedupignore`.
|
|
144
|
-
|
|
145
|
-
Example `.cuke-dedupignore`:
|
|
96
|
+
Use `.cuke-dedupignore` for repository-specific exclusions:
|
|
146
97
|
|
|
147
98
|
```gitignore
|
|
148
|
-
# Generated feature sources
|
|
149
99
|
features/generated/*
|
|
150
100
|
**/*.generated.ts
|
|
151
|
-
|
|
152
|
-
# Keep one reviewed generated definition
|
|
153
101
|
!features/generated/reviewed.generated.ts
|
|
154
102
|
```
|
|
155
103
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Equivalent discovery and rule overrides are available from the CLI:
|
|
159
|
-
|
|
160
|
-
```sh
|
|
161
|
-
cuke-dedup . \
|
|
162
|
-
--config .cuke-dedup.json \
|
|
163
|
-
--definitions 'features/steps/**/*.ts' \
|
|
164
|
-
--features 'features/**/*.feature' \
|
|
165
|
-
--exclude 'generated/legacy.ts' \
|
|
166
|
-
--exclude 'vendor' \
|
|
167
|
-
--exclude 'dist/**/*.ts' \
|
|
168
|
-
--threshold 5 \
|
|
169
|
-
--rule duplicate-matcher=warning
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
`--exclude` may be repeated or receive comma-separated patterns. Patterns are resolved relative to the analyzed root. The CLI equivalents for the discovery escape hatches are `--no-default-excludes` and `--include-hidden`. Explicit definition globs do not implicitly weaken either safety default.
|
|
173
|
-
|
|
174
|
-
Use `--explain-discovery` to print the effective pattern origin, selected parser, matching pattern, and definition inputs without changing report output. Unmatched feature patterns are warnings. If definitions exist but no feature files match, CukeDedup warns and disables `unused-definition` findings rather than presenting an incomplete corpus as proof that every definition is unused. Set `requireFeatures: true` or pass `--require-features` to make that condition an operational failure (exit code `2`).
|
|
175
|
-
|
|
176
|
-
Malformed discovered JavaScript or TypeScript remains a fail-closed operational error because partial extraction could make a duplication gate pass incorrectly. Fix the syntax, use a `.tsx` extension for JSX-bearing TypeScript, or narrow `definitions` to the actual step-definition sources.
|
|
177
|
-
|
|
178
|
-
Use `--print-config` to serialize the fully merged and validated configuration as JSON and exit without discovery. The output includes the selected CukeDedup configuration source, framework-derived feature-pattern origin, CLI overrides, effective default exclusions, rule severities, and configuration warnings.
|
|
179
|
-
|
|
180
|
-
## Rules
|
|
181
|
-
|
|
182
|
-
| Rule | Default | Meaning |
|
|
183
|
-
| --- | --- | --- |
|
|
184
|
-
| `duplicate-matcher` | Error | Definitions use the same effective matcher. |
|
|
185
|
-
| `normalized-matcher` | Error | Matchers become equivalent after normalization. |
|
|
186
|
-
| `ambiguous-step` | Error | A concrete feature step matches multiple definitions. |
|
|
187
|
-
| `duplicate-handler` | Error | Different matchers have the same alpha-normalized handler. |
|
|
188
|
-
| `near-duplicate-step` | Warning | Matcher wording is close and handler structure agrees. |
|
|
189
|
-
| `parameterization-candidate` | Warning | Handler structures differ primarily in literal values. |
|
|
190
|
-
| `unused-definition` | Warning | No discovered feature step uses the definition. |
|
|
191
|
-
|
|
192
|
-
Set a rule to `off`, `warning`, or `error` in configuration. The CLI also accepts `warn` as an alias for `warning`.
|
|
193
|
-
|
|
194
|
-
Exact handler equivalence is always reported as `duplicate-handler` when matchers are not equivalent. If the matcher wording is also close, the same retained pair may carry the advisory `near-duplicate-step` finding as additional evidence.
|
|
195
|
-
|
|
196
|
-
## Duplication threshold
|
|
197
|
-
|
|
198
|
-
`threshold` controls the percentage of discovered definitions that may participate in active, error-level duplication findings. It accepts a value from `0` through `100` and defaults to `0`.
|
|
199
|
-
|
|
200
|
-
```sh
|
|
201
|
-
cuke-dedup . --threshold 5
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
The numerator contains unique definition locations involved in `duplicate-matcher`, `normalized-matcher`, `duplicate-handler`, and any `near-duplicate-step` or `parameterization-candidate` rule configured as an error. A definition is counted once even when it participates in several pairs. Suppressed and baselined findings do not count. The denominator is every discovered definition, including definitions without findings.
|
|
104
|
+
See the [configuration reference](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/configuration.md)
|
|
105
|
+
for precedence, every setting, glob semantics, suppressions, and discovery diagnostics.
|
|
205
106
|
|
|
206
|
-
|
|
107
|
+
## Findings and reports
|
|
207
108
|
|
|
208
|
-
|
|
109
|
+
CukeDedup detects eight classes of duplication, ambiguity, reuse, and unused definitions. Exact
|
|
110
|
+
collisions default to errors; heuristic findings default to warnings. See the
|
|
111
|
+
[rule reference](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/rules.md) for the
|
|
112
|
+
meaning, default severity, and remediation guidance for every rule.
|
|
209
113
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `html` | File | Human review with search, filters, and theme switching. |
|
|
218
|
-
| `sarif` | File | Code-scanning platforms such as GitHub. |
|
|
219
|
-
|
|
220
|
-
The `jsonl` reporter emits one compact object per finding followed by a final summary object. Because `terminal` and `jsonl` both own stdout, they cannot be selected together. JSONL can be combined with file reporters without contaminating the stream.
|
|
221
|
-
|
|
222
|
-
JSON, HTML, and SARIF reports default to `reports/cuke-dedup/` and can be redirected with `--output`. Relative output paths are resolved from the analyzed root, not the shell's current directory. File-only reporter runs print the generated report paths to stdout.
|
|
223
|
-
|
|
224
|
-
### JSON and HTML
|
|
225
|
-
|
|
226
|
-
The JSON report uses schema version `1`. It includes relative source spans, severity, suppressions, similarity scores, suggested actions, structured matcher/handler evidence, threshold calculations, input counts, and execution metrics. The self-contained HTML report presents the same result with search, severity and rule filters, a light/dark theme switch, matcher differences, and side-by-side handler snippets.
|
|
227
|
-
|
|
228
|
-
Set `noMetrics: true`, pass `--no-metrics`, or use the Action's `no-metrics: true` input to omit timing data when byte-reproducible artifacts matter.
|
|
229
|
-
|
|
230
|
-
### JSON Lines
|
|
231
|
-
|
|
232
|
-
JSONL schema version `1` is intended for streaming agent and shell consumption:
|
|
233
|
-
|
|
234
|
-
```sh
|
|
235
|
-
cuke-dedup . --reporters jsonl \
|
|
236
|
-
| jq -c 'select(.type == "finding" and .active)'
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
Each finding record is self-contained and carries a location-independent semantic fingerprint, active/suppressed state, threshold contribution, relative source spans, suggested action, and structured evidence. Free-text fields are capped at 2,000 Unicode characters; `truncatedFields` names every shortened field.
|
|
240
|
-
|
|
241
|
-
The final `type: "summary"` record declares `recordCount` and `truncated: false`, and includes aggregate counts, threshold outcome, and execution metrics. Operational warnings and errors remain on stderr, so stdout can be parsed incrementally.
|
|
242
|
-
|
|
243
|
-
### SARIF, safety, and scale
|
|
244
|
-
|
|
245
|
-
SARIF 2.1.0 contains active findings, portable relative Unicode-aware locations, stable partial fingerprints, severity, similarity properties, suggested actions, and the threshold outcome for GitHub code scanning or another compatible consumer. Handler snippets are bounded before being embedded. HTML-visible text is escaped, bidirectional and invisible-format controls—including Unicode Tag characters—are removed, and embedded JSON characters that could close a script element are encoded.
|
|
246
|
-
|
|
247
|
-
Terminal, JSON, HTML, and SARIF output retain at most 10,000 findings, prioritizing active errors before warnings and clearly reporting truncation. Their summaries still describe the complete analysis. JSONL remains the uncapped finding stream.
|
|
248
|
-
|
|
249
|
-
## Agent Skill
|
|
250
|
-
|
|
251
|
-
CukeDedup ships a portable Agent Skill that teaches coding assistants how to run the JSONL reporter, interpret its records, choose safe step-definition remediations, and verify the result without weakening the configured quality gate.
|
|
252
|
-
|
|
253
|
-
Install it from a local checkout:
|
|
254
|
-
|
|
255
|
-
```sh
|
|
256
|
-
npx skills add ./skills --skill cuke-dedup
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Install it directly from GitHub:
|
|
260
|
-
|
|
261
|
-
```sh
|
|
262
|
-
npx skills add figueiredoluiz/cuke-dedup --skill cuke-dedup
|
|
263
|
-
```
|
|
114
|
+
| Reporter | Best for |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| `terminal` | Interactive use and concise CI logs. |
|
|
117
|
+
| `json` | Structured integrations and complete result metadata. |
|
|
118
|
+
| `jsonl` | Streaming shell and agent workflows. |
|
|
119
|
+
| `html` | Searchable human review with light and dark themes. |
|
|
120
|
+
| `sarif` | GitHub code scanning and compatible platforms. |
|
|
264
121
|
|
|
265
|
-
|
|
122
|
+
JSON, HTML, and SARIF files default to `reports/cuke-dedup/`. See
|
|
123
|
+
[Reports](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/reports.md) for schemas,
|
|
124
|
+
destinations, reproducibility, truncation signals, and reporter-specific behavior.
|
|
266
125
|
|
|
267
126
|
## GitHub Action
|
|
268
127
|
|
|
269
|
-
|
|
128
|
+
The Action downloads a native binary and verifies its checksum and build provenance:
|
|
270
129
|
|
|
271
130
|
```yaml
|
|
272
131
|
permissions:
|
|
@@ -277,101 +136,81 @@ steps:
|
|
|
277
136
|
with:
|
|
278
137
|
fetch-depth: 0
|
|
279
138
|
persist-credentials: false
|
|
280
|
-
- uses: figueiredoluiz/cuke-dedup@v0.
|
|
281
|
-
id: cuke-dedup
|
|
139
|
+
- uses: figueiredoluiz/cuke-dedup@v0.2.0
|
|
282
140
|
with:
|
|
283
141
|
path: .
|
|
284
142
|
threshold: 5
|
|
285
|
-
config: .cuke-dedup.json
|
|
286
|
-
exclude: |
|
|
287
|
-
generated/**
|
|
288
|
-
fixtures/vendor/**
|
|
289
143
|
reporters: terminal,json,html,sarif
|
|
290
|
-
changed-since: ${{ github.event.pull_request.base.sha }}
|
|
291
|
-
no-metrics: true
|
|
292
|
-
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
293
|
-
if: always()
|
|
294
|
-
with:
|
|
295
|
-
name: cuke-dedup-reports
|
|
296
|
-
path: reports/cuke-dedup/
|
|
297
144
|
```
|
|
298
145
|
|
|
299
|
-
|
|
146
|
+
For changed-file checks, baselines, report artifacts, outputs, and hardened pinning, see
|
|
147
|
+
[CI and baselines](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/ci-and-baselines.md).
|
|
300
148
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
## Incremental CI adoption
|
|
304
|
-
|
|
305
|
-
Only report findings involving files changed from a Git revision while still comparing those files against the complete definition corpus. Paths remain correct when analyzing a repository subdirectory, and untracked files are included:
|
|
149
|
+
## Exit codes
|
|
306
150
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
151
|
+
| Code | Meaning |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| `0` | The configured quality gate passed. |
|
|
154
|
+
| `1` | The duplication threshold, an error rule, or the new-finding allowance failed. |
|
|
155
|
+
| `2` | Discovery, parsing, configuration, or another operational error prevented a valid run. |
|
|
310
156
|
|
|
311
|
-
|
|
157
|
+
Warnings alone do not produce exit code `1`.
|
|
312
158
|
|
|
313
|
-
|
|
314
|
-
cuke-dedup . \
|
|
315
|
-
--baseline .cuke-dedup-baseline.json \
|
|
316
|
-
--update-baseline
|
|
317
|
-
```
|
|
159
|
+
## Agent Skill
|
|
318
160
|
|
|
319
|
-
|
|
161
|
+
CukeDedup includes an Agent Skill for safely interpreting and remediating JSONL findings:
|
|
320
162
|
|
|
321
163
|
```sh
|
|
322
|
-
cuke-dedup
|
|
323
|
-
cuke-dedup . --baseline .cuke-dedup-baseline.json --fail-on-new 3
|
|
164
|
+
npx skills add figueiredoluiz/cuke-dedup --skill cuke-dedup
|
|
324
165
|
```
|
|
325
166
|
|
|
326
|
-
|
|
167
|
+
From a local checkout, use `npx skills add ./skills --skill cuke-dedup`.
|
|
327
168
|
|
|
328
|
-
|
|
169
|
+
The installer can target supported coding agents or install globally. Run
|
|
170
|
+
`npx skills add --help` for agent and scope options.
|
|
329
171
|
|
|
330
|
-
|
|
172
|
+
Invoke it as `$cuke-dedup` where supported, or ask the agent to analyze and safely fix duplicate
|
|
173
|
+
Cucumber step definitions. The skill lives in
|
|
174
|
+
[`skills/cuke-dedup`](https://github.com/figueiredoluiz/cuke-dedup/blob/main/skills/cuke-dedup/SKILL.md)
|
|
175
|
+
and is distributed independently from the Cargo and npm packages.
|
|
331
176
|
|
|
332
|
-
##
|
|
177
|
+
## Documentation
|
|
333
178
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
Warnings do not produce exit code `1`.
|
|
179
|
+
- [Configuration](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/configuration.md)
|
|
180
|
+
- [Discovery and frameworks](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/discovery-and-frameworks.md)
|
|
181
|
+
- [Rules and duplication threshold](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/rules.md)
|
|
182
|
+
- [Reports](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/reports.md)
|
|
183
|
+
- [CI and baselines](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/ci-and-baselines.md)
|
|
184
|
+
- [Safety and limitations](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/safety-and-limitations.md)
|
|
341
185
|
|
|
342
|
-
## Compatibility
|
|
186
|
+
## Compatibility
|
|
343
187
|
|
|
344
|
-
-
|
|
345
|
-
-
|
|
346
|
-
|
|
347
|
-
-
|
|
348
|
-
|
|
349
|
-
-
|
|
350
|
-
- Exact matcher and handler equivalence groups produce a linear spanning set of findings. Fuzzy structural comparisons are bounded at two million candidates and fail closed with exit code `2`; split independent suites or narrow the root if that limit is reached.
|
|
351
|
-
- Before version 1.0, configuration and machine-report schemas may evolve between minor releases. Schema changes will be explicit and versioned.
|
|
188
|
+
- Rust 1.90 or newer is required. The npm launcher is tested on Node.js 20 and 24.
|
|
189
|
+
- Definition extraction currently supports JavaScript and TypeScript, including JSX and common
|
|
190
|
+
module variants.
|
|
191
|
+
- Static analysis cannot safely resolve every dynamic configuration, matcher, wrapper, or imported
|
|
192
|
+
handler. CukeDedup reports incomplete analysis instead of treating missing evidence as clean.
|
|
193
|
+
- Before version 1.0, configuration and machine-report schemas may change between minor releases.
|
|
352
194
|
|
|
353
|
-
|
|
195
|
+
See [Safety and limitations](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/safety-and-limitations.md)
|
|
196
|
+
for the complete compatibility contract and resource limits.
|
|
354
197
|
|
|
355
|
-
##
|
|
198
|
+
## Contributing and support
|
|
356
199
|
|
|
357
|
-
|
|
200
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development commands, corpus guidance, and pull-request
|
|
201
|
+
expectations. Use the issue templates for reproducible bugs and focused feature requests. Report
|
|
202
|
+
vulnerabilities privately according to [SECURITY.md](SECURITY.md). Bug reports should include a
|
|
203
|
+
minimal sanitized fixture with application-specific names, credentials, and source removed.
|
|
358
204
|
|
|
359
|
-
|
|
205
|
+
CukeDedup is an independent project. It is not affiliated with or endorsed by the Cucumber project
|
|
206
|
+
or its maintainers.
|
|
360
207
|
|
|
361
|
-
|
|
208
|
+
## Releases
|
|
362
209
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
scripts/check/check.sh
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for prerequisites, focused commands, corpus and benchmark guidance, pull-request expectations, and the project decision process. Public Rust items carry rustdoc, and CI treats missing API documentation as an error.
|
|
371
|
-
|
|
372
|
-
## Releases and verification
|
|
373
|
-
|
|
374
|
-
Release notes and native archives are published on [GitHub Releases](https://github.com/figueiredoluiz/cuke-dedup/releases). Each archive has an adjacent SHA-256 checksum, a keyless Sigstore bundle, and GitHub build provenance. Verify provenance with:
|
|
210
|
+
Release archives include SHA-256 checksums, keyless Sigstore bundles, and GitHub build provenance.
|
|
211
|
+
See [GitHub Releases](https://github.com/figueiredoluiz/cuke-dedup/releases) and
|
|
212
|
+
[CHANGELOG.md](CHANGELOG.md) for published versions and release notes. Runtime dependency licenses
|
|
213
|
+
are listed in [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md).
|
|
375
214
|
|
|
376
215
|
```sh
|
|
377
216
|
gh attestation verify <archive> \
|
|
@@ -379,8 +218,6 @@ gh attestation verify <archive> \
|
|
|
379
218
|
--signer-workflow figueiredoluiz/cuke-dedup/.github/workflows/release.yml
|
|
380
219
|
```
|
|
381
220
|
|
|
382
|
-
Cargo, npm, the Git tag, and the GitHub release use the same version. See [CHANGELOG.md](CHANGELOG.md) for release history and [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md) for the runtime dependency license inventory.
|
|
383
|
-
|
|
384
221
|
## License
|
|
385
222
|
|
|
386
223
|
[MIT](LICENSE)
|