[feat] Remove Additions section and add hyperlinks to API changelog

- Remove Additions section from changelog output (no longer needed)
- Add line number tracking in API snapshots
- Generate GitHub permalinks to referenced code in changelog
- Update workflows to pass git reference for link generation
- Breaking changes and modifications now link to source code

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
snomiao
2025-11-05 10:15:37 +00:00
parent 779f539b0e
commit 9c94a4818f
4 changed files with 87 additions and 41 deletions

View File

@@ -149,12 +149,18 @@ jobs:
- name: Compare API snapshots and generate changelog
id: generate_changelog
run: |
# Get git ref for TO version
GIT_REF=$(git rev-parse ${{ steps.validate_versions.outputs.to_tag }})
# Run the comparison script
CHANGELOG_OUTPUT=$(node scripts/compare-api-snapshots.js \
.api-snapshots/from.json \
.api-snapshots/to.json \
${{ steps.validate_versions.outputs.from_version }} \
${{ steps.validate_versions.outputs.to_version }})
${{ steps.validate_versions.outputs.to_version }} \
Comfy-Org \
ComfyUI_frontend \
"$GIT_REF")
# Save changelog to file for artifact
echo "$CHANGELOG_OUTPUT" > .api-snapshots/CHANGELOG-${{ steps.validate_versions.outputs.from_version }}-to-${{ steps.validate_versions.outputs.to_version }}.md

View File

@@ -141,6 +141,9 @@ jobs:
# Create docs directory if it doesn't exist
mkdir -p docs
# Get current git ref (commit SHA)
GIT_REF=$(git rev-parse HEAD)
# Run the comparison script
if [ -f .api-snapshots/previous.json ]; then
node scripts/compare-api-snapshots.js \
@@ -148,6 +151,9 @@ jobs:
.api-snapshots/current.json \
${{ steps.previous_version.outputs.version }} \
${{ steps.current_version.outputs.version }} \
Comfy-Org \
ComfyUI_frontend \
"$GIT_REF" \
>> docs/API-CHANGELOG.md
else
# First release - just document the initial API surface

View File

@@ -10,12 +10,20 @@ import * as fs from 'fs'
const args = process.argv.slice(2)
if (args.length < 4) {
console.error(
'Usage: compare-api-snapshots.js <previous.json> <current.json> <previous-version> <current-version>'
'Usage: compare-api-snapshots.js <previous.json> <current.json> <previous-version> <current-version> [repo-owner] [repo-name] [git-ref]'
)
process.exit(1)
}
const [previousPath, currentPath, previousVersion, currentVersion] = args
const [
previousPath,
currentPath,
previousVersion,
currentVersion,
repoOwner = 'Comfy-Org',
repoName = 'ComfyUI_frontend',
gitRef = 'main'
] = args
if (!fs.existsSync(previousPath)) {
console.error(`Previous snapshot not found: ${previousPath}`)
@@ -30,6 +38,15 @@ if (!fs.existsSync(currentPath)) {
const previousApi = JSON.parse(fs.readFileSync(previousPath, 'utf-8'))
const currentApi = JSON.parse(fs.readFileSync(currentPath, 'utf-8'))
/**
* Generate GitHub permalink to source code
*/
function generateGitHubLink(name, line) {
if (!line) return name
// Format: https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/dist/index.d.ts#L123
return `[\`${name}\`](https://github.com/${repoOwner}/${repoName}/blob/${gitRef}/dist/index.d.ts#L${line})`
}
/**
* Compare two API snapshots and generate changelog
*/
@@ -263,37 +280,40 @@ function formatChangelog(changes, prevVersion, currVersion) {
lines.push(`**${categoryToTitle(category)}**`)
lines.push('')
for (const item of items) {
lines.push(`- **Removed**: \`${item.name}\``)
const displayName = item.item?.line
? generateGitHubLink(item.name, item.item.line)
: `\`${item.name}\``
lines.push(`- **Removed**: ${displayName}`)
}
lines.push('')
}
}
// Additions
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('')
}
}
// 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) {
@@ -314,7 +334,13 @@ function formatChangelog(changes, prevVersion, currVersion) {
lines.push(`**${categoryToTitle(category)}**`)
lines.push('')
for (const item of items) {
lines.push(`- \`${item.name}\``)
// Get the current item to access line number
const currItem =
currentApi[item.category] && currentApi[item.category][item.name]
const displayName = currItem?.line
? generateGitHubLink(item.name, currItem.line)
: `\`${item.name}\``
lines.push(`- ${displayName}`)
for (const change of item.changes) {
const formatted = formatChange(change)
if (formatted) {
@@ -326,11 +352,7 @@ function formatChangelog(changes, prevVersion, currVersion) {
}
}
if (
changes.breaking.length === 0 &&
changes.additions.length === 0 &&
changes.modifications.length === 0
) {
if (changes.breaking.length === 0 && changes.modifications.length === 0) {
lines.push('_No API changes detected._')
lines.push('')
}

View File

@@ -38,17 +38,20 @@ function extractApiSurface(sourceFile) {
// Extract type aliases
if (ts.isTypeAliasDeclaration(node) && node.name) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
api.types[name] = {
kind: 'type',
name,
text: node.getText(sourceFile),
exported: hasExportModifier(node)
exported: hasExportModifier(node),
line: line + 1 // Convert to 1-indexed
}
}
// Extract interfaces
if (ts.isInterfaceDeclaration(node) && node.name) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const members = []
node.members.forEach((member) => {
@@ -83,13 +86,15 @@ function extractApiSurface(sourceFile) {
clause.types.map((type) => type.getText(sourceFile))
)
.flat()
: []
: [],
line: line + 1 // Convert to 1-indexed
}
}
// Extract enums
if (ts.isEnumDeclaration(node) && node.name) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const members = node.members.map((member) => ({
name: member.name.getText(sourceFile),
value: member.initializer
@@ -101,13 +106,15 @@ function extractApiSurface(sourceFile) {
kind: 'enum',
name,
members,
exported: hasExportModifier(node)
exported: hasExportModifier(node),
line: line + 1 // Convert to 1-indexed
}
}
// Extract functions
if (ts.isFunctionDeclaration(node) && node.name) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
api.functions[name] = {
kind: 'function',
name,
@@ -117,13 +124,15 @@ function extractApiSurface(sourceFile) {
optional: !!p.questionToken
})),
returnType: node.type ? node.type.getText(sourceFile) : 'any',
exported: hasExportModifier(node)
exported: hasExportModifier(node),
line: line + 1 // Convert to 1-indexed
}
}
// Extract classes
if (ts.isClassDeclaration(node) && node.name) {
const name = node.name.text
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
const members = []
const methods = []
@@ -162,12 +171,14 @@ function extractApiSurface(sourceFile) {
clause.types.map((type) => type.getText(sourceFile))
)
.flat()
: []
: [],
line: line + 1 // Convert to 1-indexed
}
}
// Extract variable declarations (constants)
if (ts.isVariableStatement(node)) {
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
node.declarationList.declarations.forEach((decl) => {
if (decl.name && ts.isIdentifier(decl.name)) {
const name = decl.name.text
@@ -175,7 +186,8 @@ function extractApiSurface(sourceFile) {
kind: 'constant',
name,
type: decl.type ? decl.type.getText(sourceFile) : 'unknown',
exported: hasExportModifier(node)
exported: hasExportModifier(node),
line: line + 1 // Convert to 1-indexed
}
}
})