Fix GitHub release notes workflow

This commit is contained in:
winlifes
2026-05-20 20:51:33 -07:00
parent 668898f0c6
commit be8c1d624f
4 changed files with 111 additions and 4 deletions
+4
View File
@@ -6,6 +6,10 @@ This project follows a simple changelog format inspired by [Keep a Changelog](ht
## [Unreleased]
### Fixed
- Fixed the GitHub Release workflow so release pages use generated changelog-style `RELEASE_NOTES.md` instead of the artifact installation README.
## [0.3.3] - 2026-05-20
### Added
+3 -2
View File
@@ -20,6 +20,7 @@ Use this checklist before publishing a new release of Funplay MCP for Cocos.
- [ ] `npm run registry:validate` passes when `mcp-publisher` is available
- [ ] `npm run release:package` creates `releases/<version>/`
- [ ] `shasum -a 256 -c releases/<version>/SHA256SUMS.txt` passes
- [ ] `releases/<version>/RELEASE_NOTES.md` is organized by change type, such as Added/Optimized/Changed/Fixed
## 3. Package Contents
@@ -30,7 +31,7 @@ Use this checklist before publishing a new release of Funplay MCP for Cocos.
- [ ] The zip includes docs: `README.md`, `README_CN.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, and `LICENSE`
- [ ] The zip does not contain `.git/`, `.github/`, `.DS_Store`, `node_modules/`, `Library/`, `Temp/`, `dist/`, `build/`, `test/`, or `scripts/`
- [ ] `release-manifest.json` references the correct GitHub download URL
- [ ] `SHA256SUMS.txt` includes the zip, manifest, and release README
- [ ] `SHA256SUMS.txt` includes the zip, manifest, generated release notes, and release README
## 4. Cocos Smoke Test
@@ -72,7 +73,7 @@ Use this checklist before publishing a new release of Funplay MCP for Cocos.
- [ ] Tag is `v<version>`
- [ ] GitHub Release title is `v<version>`
- [ ] GitHub Release includes the zip, manifest, checksum file, and release README
- [ ] Public GitHub Release page renders the release notes and assets correctly
- [ ] Public GitHub Release page uses `RELEASE_NOTES.md` and renders the release notes/assets correctly
## 8. Publish
+10 -2
View File
@@ -175,7 +175,7 @@ If creating a new release:
gh release create v<version> \
-R FunplayAI/funplay-cocos-mcp \
--title "v<version>" \
--notes-file /path/to/release-notes.md \
--notes-file releases/<version>/RELEASE_NOTES.md \
releases/<version>/Funplay.CocosMcp.v<version>.zip \
releases/<version>/release-manifest.json \
releases/<version>/SHA256SUMS.txt \
@@ -194,6 +194,14 @@ gh release upload v<version> \
releases/<version>/README.md
```
If the release body needs to be refreshed without replacing assets:
```bash
gh release edit v<version> \
-R FunplayAI/funplay-cocos-mcp \
--notes-file releases/<version>/RELEASE_NOTES.md
```
### 7. Verify GitHub Release
```bash
@@ -202,7 +210,7 @@ gh release view v<version> \
--json url,assets,isDraft,isPrerelease,publishedAt
```
Confirm the release has all four assets.
Confirm the release has all four assets and the public release body is organized by change type.
### 8. Publish To npm
+94
View File
@@ -101,6 +101,7 @@ function main() {
console.log(`- ${artifacts.zipName}`);
console.log('- release-manifest.json');
console.log('- SHA256SUMS.txt');
console.log('- RELEASE_NOTES.md');
console.log('- README.md');
return;
}
@@ -270,9 +271,13 @@ function packageRelease(context) {
const readmePath = path.join(releaseDir, 'README.md');
fs.writeFileSync(readmePath, buildReleaseReadme(context, manifest));
const releaseNotesPath = path.join(releaseDir, 'RELEASE_NOTES.md');
fs.writeFileSync(releaseNotesPath, buildGitHubReleaseNotes(context, manifest));
const checksums = [
checksumLine(zipPath, zipName),
checksumLine(manifestPath, 'release-manifest.json'),
checksumLine(releaseNotesPath, 'RELEASE_NOTES.md'),
checksumLine(readmePath, 'README.md')
].join('');
fs.writeFileSync(path.join(releaseDir, 'SHA256SUMS.txt'), checksums);
@@ -362,6 +367,95 @@ shasum -a 256 -c SHA256SUMS.txt
`;
}
function buildGitHubReleaseNotes(context, manifest) {
const sections = parseChangelogSections(context.changelogNotes);
const rendered = [];
const used = new Set();
const order = [
['Added', '新增'],
['Optimized', '优化'],
['Changed', '修改'],
['Fixed', '修复'],
['Security', '安全'],
['Deprecated', '弃用'],
['Removed', '移除']
];
for (const [sourceHeading, displayHeading] of order) {
const section = sections.find((item) => item.heading.toLowerCase() === sourceHeading.toLowerCase());
if (!section) {
continue;
}
used.add(section.heading);
rendered.push(`## ${displayHeading}`, '', ...section.lines, '');
}
for (const section of sections) {
if (used.has(section.heading)) {
continue;
}
rendered.push(`## ${section.heading}`, '', ...section.lines, '');
}
const zip = manifest.artifacts.extensionZip;
return [
`# Funplay MCP for Cocos ${context.tag}`,
'',
...rendered,
'## 发布资产',
'',
`- \`${zip.file}\` - Cocos Creator extension package.`,
'- `release-manifest.json` - Machine-readable release metadata.',
'- `SHA256SUMS.txt` - SHA-256 checksums for release artifacts.',
'',
'## 校验',
'',
'```bash',
'shasum -a 256 -c SHA256SUMS.txt',
'```',
''
].join('\n');
}
function parseChangelogSections(notes) {
const sections = [];
let current = null;
for (const line of String(notes || '').split(/\r?\n/)) {
const heading = /^###\s+(.+?)\s*$/.exec(line);
if (heading) {
current = {
heading: heading[1].trim(),
lines: []
};
sections.push(current);
continue;
}
if (current) {
current.lines.push(line);
}
}
return sections
.map((section) => ({
heading: section.heading,
lines: trimBlankLines(section.lines)
}))
.filter((section) => section.lines.length > 0);
}
function trimBlankLines(lines) {
const trimmed = lines.slice();
while (trimmed.length && !trimmed[0].trim()) {
trimmed.shift();
}
while (trimmed.length && !trimmed[trimmed.length - 1].trim()) {
trimmed.pop();
}
return trimmed;
}
function validateArchivePaths(paths) {
const bad = [];
const prefix = `${PACKAGE_DIR_NAME}/`;