[refactor] Improve API snapshot and changelog scripts

- Filter non-exported declarations in snapshot-api.ts to only capture
  the public API surface
- Remove redundant 'exported' field from snapshot output
- Return empty string from formatChangelog when no changes detected,
  preventing empty changelog PRs
- Remove dead commented-out Additions section code
- Remove unused deprecations field
- Update README example to match actual output format
This commit is contained in:
snomiao
2026-03-14 06:14:23 +00:00
parent 2312db5a2d
commit 132539fe49
3 changed files with 36 additions and 69 deletions

View File

@@ -59,8 +59,7 @@ gh workflow run manual-api-changelog.yaml \
3. **Generates Snapshots**: Creates structured JSON snapshots of the public API surface for each version
4. **Compares APIs**: Analyzes differences and categorizes as:
- ⚠️ **Breaking changes** (removals, signature changes)
- **Additions** (new interfaces, methods, properties)
- 🔄 **Modifications** (non-breaking changes)
- 🔄 **Modifications** (member additions, type changes)
5. **Uploads Artifact**: Saves the changelog and snapshots as a workflow artifact (90-day retention)
6. **Creates PR** (optional): Generates a draft PR to update `docs/API-CHANGELOG.md`
@@ -90,18 +89,14 @@ If `create_pr` is enabled and changes are detected:
```markdown
## v1.30.2 (2025-11-04)
Comparing v1.29.0 → v1.30.2. This changelog documents changes to the public API surface.
Comparing v1.29.0 → v1.30.2. This changelog documents changes to the public
API surface that third-party extensions and custom nodes depend on.
### ✨ Additions
**Type Aliases**
- `WorkflowId`
### ⚠️ Breaking Changes
**Interfaces**
- `ExtensionMetadata`
- Members: `id`, `name`, `version`, `description`
- **Removed**: `ComfyApi.queuePrompt`
### 🔄 Modifications
@@ -112,7 +107,6 @@ Comparing v1.29.0 → v1.30.2. This changelog documents changes to the public AP
- `ComfyApi`
- ✨ Added member: `queuePromptAsync`
- ✨ Added member: `cancelPrompt`
- ⚠️ **Breaking**: Removed member: `queuePrompt`
**Enums**

View File

@@ -64,8 +64,7 @@ function compareApis(previous, current) {
const changes = {
breaking: [],
additions: [],
modifications: [],
deprecations: []
modifications: []
}
const categories = [
@@ -271,6 +270,10 @@ function compareItems(prev, curr, category) {
* Format changelog as markdown
*/
function formatChangelog(changes, prevVersion, currVersion) {
if (changes.breaking.length === 0 && changes.modifications.length === 0) {
return ''
}
const lines = []
lines.push(`## v${currVersion} (${new Date().toISOString().split('T')[0]})`)
@@ -297,32 +300,6 @@ function formatChangelog(changes, prevVersion, currVersion) {
}
}
// Additions - commented out as per feedback
// if (changes.additions.length > 0) {
// lines.push('### ✨ Additions')
// lines.push('')
//
// const grouped = groupByCategory(changes.additions)
// for (const [category, items] of Object.entries(grouped)) {
// lines.push(`**${categoryToTitle(category)}**`)
// lines.push('')
// for (const item of items) {
// lines.push(`- \`${item.name}\``)
// if (item.item.members && item.item.members.length > 0) {
// const publicMembers = item.item.members.filter(
// (m) => !m.visibility || m.visibility === 'public'
// )
// if (publicMembers.length > 0 && publicMembers.length <= 5) {
// lines.push(
// ` - Members: ${publicMembers.map((m) => `\`${m.name}\``).join(', ')}`
// )
// }
// }
// }
// lines.push('')
// }
// }
// Modifications
if (changes.modifications.length > 0) {
lines.push('### 🔄 Modifications')
@@ -358,11 +335,6 @@ function formatChangelog(changes, prevVersion, currVersion) {
}
}
if (changes.breaking.length === 0 && changes.modifications.length === 0) {
lines.push('_No API changes detected._')
lines.push('')
}
lines.push('---')
lines.push('')
@@ -441,5 +413,5 @@ function formatChange(change) {
const changes = compareApis(previousApi, currentApi)
const changelog = formatChangelog(changes, previousVersion, currentVersion)
// eslint-disable-next-line no-console
console.log(changelog)

View File

@@ -90,8 +90,12 @@ function extractApiSurface(sourceFile) {
}
function visit(node) {
// Extract type aliases
if (ts.isTypeAliasDeclaration(node) && node.name) {
// Only capture exported declarations (the public API surface)
if (
ts.isTypeAliasDeclaration(node) &&
node.name &&
hasExportModifier(node)
) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const sourceLocation = findInSourceFiles(name, 'type')
@@ -99,15 +103,17 @@ function extractApiSurface(sourceFile) {
kind: 'type',
name,
text: node.getText(sourceFile),
exported: hasExportModifier(node),
line: line + 1, // Convert to 1-indexed
line: line + 1,
sourceFile: sourceLocation?.file,
sourceLine: sourceLocation?.line
}
}
// Extract interfaces
if (ts.isInterfaceDeclaration(node) && node.name) {
if (
ts.isInterfaceDeclaration(node) &&
node.name &&
hasExportModifier(node)
) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const members = []
@@ -138,7 +144,6 @@ function extractApiSurface(sourceFile) {
kind: 'interface',
name,
members,
exported: hasExportModifier(node),
heritage: node.heritageClauses
? node.heritageClauses
.map((clause) =>
@@ -152,8 +157,7 @@ function extractApiSurface(sourceFile) {
}
}
// Extract enums
if (ts.isEnumDeclaration(node) && node.name) {
if (ts.isEnumDeclaration(node) && node.name && hasExportModifier(node)) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const members = node.members.map((member) => ({
@@ -168,15 +172,17 @@ function extractApiSurface(sourceFile) {
kind: 'enum',
name,
members,
exported: hasExportModifier(node),
line: line + 1, // Convert to 1-indexed
line: line + 1,
sourceFile: sourceLocation?.file,
sourceLine: sourceLocation?.line
}
}
// Extract functions
if (ts.isFunctionDeclaration(node) && node.name) {
if (
ts.isFunctionDeclaration(node) &&
node.name &&
hasExportModifier(node)
) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const sourceLocation = findInSourceFiles(name, 'function')
@@ -189,15 +195,13 @@ function extractApiSurface(sourceFile) {
optional: !!p.questionToken
})),
returnType: node.type ? node.type.getText(sourceFile) : 'any',
exported: hasExportModifier(node),
line: line + 1, // Convert to 1-indexed
line: line + 1,
sourceFile: sourceLocation?.file,
sourceLine: sourceLocation?.line
}
}
// Extract classes
if (ts.isClassDeclaration(node) && node.name) {
if (ts.isClassDeclaration(node) && node.name && hasExportModifier(node)) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const members = []
@@ -232,7 +236,6 @@ function extractApiSurface(sourceFile) {
name,
members,
methods,
exported: hasExportModifier(node),
heritage: node.heritageClauses
? node.heritageClauses
.map((clause) =>
@@ -240,14 +243,13 @@ function extractApiSurface(sourceFile) {
)
.flat()
: [],
line: line + 1, // Convert to 1-indexed
line: line + 1,
sourceFile: sourceLocation?.file,
sourceLine: sourceLocation?.line
}
}
// Extract variable declarations (constants)
if (ts.isVariableStatement(node)) {
if (ts.isVariableStatement(node) && hasExportModifier(node)) {
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
node.declarationList.declarations.forEach((decl) => {
if (decl.name && ts.isIdentifier(decl.name)) {
@@ -257,8 +259,7 @@ function extractApiSurface(sourceFile) {
kind: 'constant',
name,
type: decl.type ? decl.type.getText(sourceFile) : 'unknown',
exported: hasExportModifier(node),
line: line + 1, // Convert to 1-indexed
line: line + 1,
sourceFile: sourceLocation?.file,
sourceLine: sourceLocation?.line
}
@@ -310,5 +311,5 @@ const sourceFile = ts.createSourceFile(
const apiSurface = extractApiSurface(sourceFile)
// Output as JSON
// eslint-disable-next-line no-console
console.log(JSON.stringify(apiSurface, null, 2))