fix(web-bundles): harden release script per PR review
- Verify the zip CLI is on PATH up front with a clear install hint, instead of crashing mid-zip with an opaque execSync error. - Wrap JSON.parse in try/catch; validate the manifest shape (bundles array non-empty, releaseTag present, slug present per entry) before trying to package, so config errors fail with a targeted message. - Catch zip failures per-bundle and surface the failing slug. - Refuse to print the gh release command when zero bundles were packaged (would otherwise mislead the user into creating an empty release). - Derive --title from manifest.releaseTag so the printed command can never drift from the actual tag (was previously hardcoded "Web Bundles v1" while the tag had moved to v1.0.0). - Remove the stale `web-bundles-v1` example from the file header. Addresses augmentcode bot review comments on PR #2424.
This commit is contained in:
parent
d7ac83e9cd
commit
c4cdbdc4c0
|
|
@ -7,10 +7,8 @@
|
|||
* Usage:
|
||||
* node tools/bundle-web-bundles.js
|
||||
*
|
||||
* Then upload the resulting zips to a GitHub Release:
|
||||
* gh release create web-bundles-v1 dist/web-bundles/*.zip \
|
||||
* --title "Web Bundles v1" \
|
||||
* --notes "BMad web bundles for Gemini Gems and ChatGPT Custom GPTs"
|
||||
* After running, the script prints the exact `gh release create` command
|
||||
* (with the correct tag from bundles.json) for you to copy.
|
||||
*/
|
||||
|
||||
const fs = require('node:fs');
|
||||
|
|
@ -22,14 +20,42 @@ const BUNDLES_DIR = path.join(REPO_ROOT, 'web-bundles');
|
|||
const DIST_DIR = path.join(REPO_ROOT, 'dist', 'web-bundles');
|
||||
const MANIFEST = path.join(BUNDLES_DIR, 'bundles.json');
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(MANIFEST)) {
|
||||
console.error(`Error: bundles.json not found at ${MANIFEST}`);
|
||||
process.exit(1);
|
||||
}
|
||||
function fail(msg) {
|
||||
console.error(`[ERROR] ${msg}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf-8'));
|
||||
const releaseTag = manifest.releaseTag || 'web-bundles-v1';
|
||||
function requireZipCli() {
|
||||
try {
|
||||
execSync('zip -v', { stdio: 'ignore' });
|
||||
} catch {
|
||||
fail("'zip' CLI not found on PATH. Install zip (macOS: preinstalled; Debian/Ubuntu: apt install zip; Alpine: apk add zip) and re-run.");
|
||||
}
|
||||
}
|
||||
|
||||
function loadManifest() {
|
||||
if (!fs.existsSync(MANIFEST)) {
|
||||
fail(`bundles.json not found at ${MANIFEST}`);
|
||||
}
|
||||
let manifest;
|
||||
try {
|
||||
manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf-8'));
|
||||
} catch (error) {
|
||||
fail(`bundles.json is not valid JSON: ${error.message}`);
|
||||
}
|
||||
if (!Array.isArray(manifest.bundles) || manifest.bundles.length === 0) {
|
||||
fail('bundles.json is missing a non-empty "bundles" array.');
|
||||
}
|
||||
if (typeof manifest.releaseTag !== 'string' || !manifest.releaseTag) {
|
||||
fail('bundles.json is missing "releaseTag".');
|
||||
}
|
||||
return manifest;
|
||||
}
|
||||
|
||||
function main() {
|
||||
requireZipCli();
|
||||
const manifest = loadManifest();
|
||||
const releaseTag = manifest.releaseTag;
|
||||
|
||||
fs.mkdirSync(DIST_DIR, { recursive: true });
|
||||
|
||||
|
|
@ -37,6 +63,10 @@ function main() {
|
|||
|
||||
const zipped = [];
|
||||
for (const bundle of manifest.bundles) {
|
||||
if (!bundle.slug) {
|
||||
console.warn(` [SKIP] bundle entry missing slug — ${JSON.stringify(bundle).slice(0, 80)}`);
|
||||
continue;
|
||||
}
|
||||
const src = path.join(BUNDLES_DIR, bundle.slug);
|
||||
if (!fs.existsSync(src)) {
|
||||
console.warn(` [SKIP] ${bundle.slug} — directory not found`);
|
||||
|
|
@ -46,20 +76,28 @@ function main() {
|
|||
const out = path.join(DIST_DIR, `${bundle.slug}.zip`);
|
||||
if (fs.existsSync(out)) fs.unlinkSync(out);
|
||||
|
||||
execSync(`zip -r -X -q "${out}" "${bundle.slug}" -x "*.DS_Store"`, {
|
||||
cwd: BUNDLES_DIR,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
try {
|
||||
execSync(`zip -r -X -q "${out}" "${bundle.slug}" -x "*.DS_Store"`, {
|
||||
cwd: BUNDLES_DIR,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
} catch (error) {
|
||||
fail(`zip failed for ${bundle.slug}: ${error.message}`);
|
||||
}
|
||||
|
||||
const size = (fs.statSync(out).size / 1024).toFixed(1);
|
||||
console.log(` [OK] ${bundle.slug}.zip (${size} KB)`);
|
||||
zipped.push(bundle.slug);
|
||||
}
|
||||
|
||||
if (zipped.length === 0) {
|
||||
fail('No bundles were packaged. Check bundles.json slugs against web-bundles/ subdirectories.');
|
||||
}
|
||||
|
||||
console.log(`\nWrote ${zipped.length} bundles to ${path.relative(REPO_ROOT, DIST_DIR)}/`);
|
||||
console.log('\nNext step — create or update the GitHub Release:\n');
|
||||
console.log(` gh release create ${releaseTag} dist/web-bundles/*.zip \\`);
|
||||
console.log(` --title "Web Bundles v1" \\`);
|
||||
console.log(` --title "${releaseTag}" \\`);
|
||||
console.log(` --notes "BMad web bundles for Gemini Gems and ChatGPT Custom GPTs. See https://bmadcode.com/web-bundles/"\n`);
|
||||
console.log('Or, to refresh an existing release:\n');
|
||||
console.log(` gh release upload ${releaseTag} dist/web-bundles/*.zip --clobber\n`);
|
||||
|
|
|
|||
Loading…
Reference in New Issue