build(versioning): add public API versioning check#8016
Draft
tomdavies73 wants to merge 1 commit into
Draft
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a public API “versioning check” workflow to detect breaking changes in exported component props and public index.ts exports, integrating it into local git hooks and documenting the process for contributors.
Changes:
- Introduces a type snapshot generator/comparator for public component props (
types/carbon-react/types.json) and exposes it vianpm run versioning-check. - Adds a
commit-msggate that blocks commits with breaking public API changes unless the commit message includes a breaking marker. - Updates Husky hooks + documentation, and marks the snapshot file as generated for GitHub diff collapsing.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
scripts/types/public_types_core.mjs |
Core snapshot generation + prop/type comparison utilities (ts-morph-based). |
scripts/types/check_types.mjs |
CLI to generate/merge types.json and compare against HEAD. |
scripts/types/check_breaking_changes.mjs |
commit-msg hook gate for breaking type snapshot + export removals. |
package.json |
Adds versioning-check npm script. |
CONTRIBUTING.md |
Documents the public API versioning checks and workflow. |
.husky/pre-commit |
Regenerates/stages snapshot before committing (when triggered). |
.husky/commit-msg |
Runs breaking-change gate and (currently) additional checks. |
.github/copilot-instructions.md |
Documents versioning check behavior for Copilot guidance. |
.gitattributes |
Marks the snapshot JSON as linguist-generated. |
| # Regenerate the public API snapshot before lint-staged. | ||
| # Breaking-change validation is handled in commit-msg where the final commit | ||
| # message is available. Pre-commit only refreshes and stages the snapshot. | ||
| if git diff --cached --name-only | grep -q '^src/components/'; then |
Comment on lines
+4
to
+6
| # Regenerate the public API snapshot before lint-staged. | ||
| # Breaking-change validation is handled in commit-msg where the final commit | ||
| # message is available. Pre-commit only refreshes and stages the snapshot. |
Comment on lines
+20
to
+23
| npx lint-staged || { | ||
| unstage_types_snapshot | ||
| exit 1 | ||
| } |
| exit 1 | ||
| } | ||
|
|
||
| npx commitlint --edit $1 || { |
Comment on lines
+7
to
+10
| * npm run versioning-check — generate + compare vs committed baseline | ||
| * npm run versioning-check -- Button — same, scoped to one component | ||
| * node check_types.mjs --update-only — generate silently (used by pre-commit hook) | ||
| */ |
Comment on lines
+56
to
+70
| // Build map of new snapshots | ||
| /** @type {Record<string, ComponentTypeSnapshot>} */ | ||
| const newComponents = Object.fromEntries(snapshots.map((s) => [s.component, s])); | ||
|
|
||
| // Merge with existing types.json so a scoped run doesn't wipe other components | ||
| /** @type {Record<string, ComponentTypeSnapshot>} */ | ||
| let existingComponents = {}; | ||
| try { | ||
| const raw = await fs.readFile(typesFile, "utf8"); | ||
| existingComponents = JSON.parse(raw).components ?? {}; | ||
| } catch { | ||
| // First generation | ||
| } | ||
|
|
||
| const merged = { ...existingComponents, ...newComponents }; |
Comment on lines
+106
to
+125
| let totalBreaking = 0; | ||
|
|
||
| for (const snapshot of snapshots) { | ||
| const baseline = committedComponents[snapshot.component]; | ||
| if (!baseline) { | ||
| console.log(`[${snapshot.component}] New component.`); | ||
| continue; | ||
| } | ||
|
|
||
| const { breaking, safe: _safe } = compareSnapshots(baseline, snapshot); | ||
|
|
||
| if (breaking.length) { | ||
| console.error(`[${snapshot.component}] Breaking changes:`); | ||
| for (const msg of breaking) { | ||
| console.error(` ! ${msg}`); | ||
| } | ||
| totalBreaking += breaking.length; | ||
| } | ||
| } | ||
|
|
Comment on lines
+191
to
+195
| const typeText = propertyType.getText( | ||
| valueDeclaration ?? contextFile, | ||
| TypeFormatFlags.NoTruncation | | ||
| TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, | ||
| ); |
Comment on lines
+335
to
+338
| const tempProject = new Project({ | ||
| useInMemoryFileSystem: true, | ||
| compilerOptions: { strictNullChecks: true }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed behaviour
Current behaviour
Checklist
d.tsfile added or updated if requiredQA
Additional context
Testing instructions