@wipcomputer/wip-release 1.9.74 → 1.9.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core.mjs +266 -26
- package/package.json +1 -1
package/core.mjs
CHANGED
|
@@ -1408,6 +1408,67 @@ function enforceMainBranchGuard(repoPath, skipWorktreeCheck) {
|
|
|
1408
1408
|
* Related: `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md`
|
|
1409
1409
|
* Phase 8.
|
|
1410
1410
|
*/
|
|
1411
|
+
/**
|
|
1412
|
+
* Phase 5: Auto-bump sub-tool patch versions when their files changed since last tag.
|
|
1413
|
+
* Called before validateSubToolVersions so drift is fixed before validation runs.
|
|
1414
|
+
* Returns the number of sub-tools bumped.
|
|
1415
|
+
*/
|
|
1416
|
+
function autoFixSubToolVersions(repoPath) {
|
|
1417
|
+
const toolsDir = join(repoPath, 'tools');
|
|
1418
|
+
if (!existsSync(toolsDir)) return 0;
|
|
1419
|
+
let lastTag = null;
|
|
1420
|
+
try {
|
|
1421
|
+
lastTag = execFileSync('git', ['describe', '--tags', '--abbrev=0'], {
|
|
1422
|
+
cwd: repoPath, encoding: 'utf8'
|
|
1423
|
+
}).trim();
|
|
1424
|
+
} catch {
|
|
1425
|
+
return 0;
|
|
1426
|
+
}
|
|
1427
|
+
if (!lastTag) return 0;
|
|
1428
|
+
|
|
1429
|
+
let bumped = 0;
|
|
1430
|
+
let entries;
|
|
1431
|
+
try {
|
|
1432
|
+
entries = readdirSync(toolsDir, { withFileTypes: true });
|
|
1433
|
+
} catch {
|
|
1434
|
+
return 0;
|
|
1435
|
+
}
|
|
1436
|
+
for (const entry of entries) {
|
|
1437
|
+
if (!entry.isDirectory()) continue;
|
|
1438
|
+
const subDir = `tools/${entry.name}`;
|
|
1439
|
+
const subPkgPath = join(toolsDir, entry.name, 'package.json');
|
|
1440
|
+
if (!existsSync(subPkgPath)) continue;
|
|
1441
|
+
try {
|
|
1442
|
+
const diff = execFileSync('git', ['diff', '--name-only', lastTag, 'HEAD', '--', subDir], {
|
|
1443
|
+
cwd: repoPath, encoding: 'utf8'
|
|
1444
|
+
}).trim();
|
|
1445
|
+
if (!diff) continue;
|
|
1446
|
+
const subPkg = JSON.parse(readFileSync(subPkgPath, 'utf8'));
|
|
1447
|
+
const currentSubVersion = subPkg.version;
|
|
1448
|
+
let oldSubVersion = null;
|
|
1449
|
+
try {
|
|
1450
|
+
oldSubVersion = JSON.parse(
|
|
1451
|
+
execFileSync('git', ['show', `${lastTag}:${subDir}/package.json`], {
|
|
1452
|
+
cwd: repoPath, encoding: 'utf8'
|
|
1453
|
+
})
|
|
1454
|
+
).version;
|
|
1455
|
+
} catch {}
|
|
1456
|
+
if (currentSubVersion === oldSubVersion) {
|
|
1457
|
+
const parts = currentSubVersion.split('.');
|
|
1458
|
+
parts[2] = String(Number(parts[2]) + 1);
|
|
1459
|
+
const newSubVersion = parts.join('.');
|
|
1460
|
+
subPkg.version = newSubVersion;
|
|
1461
|
+
writeFileSync(subPkgPath, JSON.stringify(subPkg, null, 2) + '\n');
|
|
1462
|
+
console.log(` ✓ Auto-bumped ${entry.name}: ${currentSubVersion} -> ${newSubVersion}`);
|
|
1463
|
+
bumped++;
|
|
1464
|
+
}
|
|
1465
|
+
} catch (err) {
|
|
1466
|
+
console.log(` ! Auto-bump failed for ${entry.name}: ${err.message}`);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return bumped;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1411
1472
|
function validateSubToolVersions(repoPath, allowSubToolDrift) {
|
|
1412
1473
|
const toolsDir = join(repoPath, 'tools');
|
|
1413
1474
|
if (!existsSync(toolsDir)) {
|
|
@@ -1740,9 +1801,17 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1740
1801
|
}
|
|
1741
1802
|
}
|
|
1742
1803
|
|
|
1743
|
-
// 0. License compliance gate
|
|
1804
|
+
// 0. License compliance gate (MANDATORY: blocks release if .license-guard.json missing)
|
|
1744
1805
|
const configPath = join(repoPath, '.license-guard.json');
|
|
1745
|
-
if (existsSync(configPath)) {
|
|
1806
|
+
if (!existsSync(configPath)) {
|
|
1807
|
+
console.log(` ✗ .license-guard.json not found.`);
|
|
1808
|
+
console.log(` Every repo must have .license-guard.json to release.`);
|
|
1809
|
+
console.log(` Run: wip-repo-init (scaffolds ai/, .license-guard.json, CLA.md)`);
|
|
1810
|
+
console.log(` Or: wip-license-guard init`);
|
|
1811
|
+
console.log('');
|
|
1812
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
1813
|
+
}
|
|
1814
|
+
{
|
|
1746
1815
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
1747
1816
|
const licenseIssues = [];
|
|
1748
1817
|
|
|
@@ -1770,6 +1839,24 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1770
1839
|
if (config.license === 'MIT+AGPL' && !readme.includes('AGPL')) licenseIssues.push('README.md License section missing AGPL reference');
|
|
1771
1840
|
}
|
|
1772
1841
|
|
|
1842
|
+
// .npmignore must exclude ai/ if repo has ai/ directory
|
|
1843
|
+
const aiDir = join(repoPath, 'ai');
|
|
1844
|
+
if (existsSync(aiDir)) {
|
|
1845
|
+
const npmignorePath = join(repoPath, '.npmignore');
|
|
1846
|
+
const pkgJson = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf8'));
|
|
1847
|
+
const hasFilesWhitelist = Array.isArray(pkgJson.files);
|
|
1848
|
+
if (!hasFilesWhitelist) {
|
|
1849
|
+
if (!existsSync(npmignorePath)) {
|
|
1850
|
+
licenseIssues.push('.npmignore is missing (ai/ directory exists and could leak to npm)');
|
|
1851
|
+
} else {
|
|
1852
|
+
const npmignore = readFileSync(npmignorePath, 'utf8');
|
|
1853
|
+
if (!npmignore.includes('ai/')) {
|
|
1854
|
+
licenseIssues.push('.npmignore does not exclude ai/ (plans and bugs could leak to npm)');
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1773
1860
|
if (licenseIssues.length > 0) {
|
|
1774
1861
|
console.log(` ✗ License compliance failed:`);
|
|
1775
1862
|
for (const issue of licenseIssues) console.log(` - ${issue}`);
|
|
@@ -2057,6 +2144,14 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
2057
2144
|
writePackageVersion(repoPath, newVersion);
|
|
2058
2145
|
console.log(` ✓ package.json -> ${newVersion}`);
|
|
2059
2146
|
|
|
2147
|
+
// 1.25. Auto-bump sub-tool versions (Phase 5: auto-fix before validation)
|
|
2148
|
+
{
|
|
2149
|
+
const autoBumped = autoFixSubToolVersions(repoPath);
|
|
2150
|
+
if (autoBumped > 0) {
|
|
2151
|
+
console.log(` ✓ Auto-bumped ${autoBumped} sub-tool(s)`);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2060
2155
|
// 1.5. Validate sub-tool version bumps (Phase 8: error by default)
|
|
2061
2156
|
{
|
|
2062
2157
|
const subToolResult = validateSubToolVersions(repoPath, allowSubToolDrift);
|
|
@@ -2086,35 +2181,76 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
2086
2181
|
console.log(` ✓ Product docs synced to v${newVersion} (${docsUpdated} file(s))`);
|
|
2087
2182
|
}
|
|
2088
2183
|
|
|
2089
|
-
// 4. Git commit + tag
|
|
2090
|
-
gitCommitAndTag(repoPath, newVersion, notes);
|
|
2091
|
-
console.log(` ✓ Committed and tagged v${newVersion}`);
|
|
2092
|
-
|
|
2093
|
-
// 5. Push commit + tag (with auto-PR fallback on protected main, Phase 4)
|
|
2094
|
-
{
|
|
2095
|
-
const pushResult = pushReleaseWithAutoPr(repoPath, newVersion, level);
|
|
2096
|
-
if (pushResult.ok) {
|
|
2097
|
-
console.log(` ✓ Pushed to remote (${pushResult.via})`);
|
|
2098
|
-
} else {
|
|
2099
|
-
logPushFailure(pushResult, `v${newVersion}`);
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
2184
|
// Distribution results collector (#104)
|
|
2104
2185
|
const distResults = [];
|
|
2105
2186
|
|
|
2187
|
+
// 4. npm publish BEFORE commit (Phase 3: true publish-before-commit)
|
|
2188
|
+
// Files are bumped and staged but NOT committed. If npm fails, we just
|
|
2189
|
+
// revert the file changes. No commit, no tag, no remote state to clean up.
|
|
2106
2190
|
if (!noPublish) {
|
|
2107
|
-
// 6. npm publish
|
|
2108
2191
|
try {
|
|
2109
2192
|
publishNpm(repoPath);
|
|
2110
2193
|
const pkg = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf8'));
|
|
2111
2194
|
distResults.push({ target: 'npm', status: 'ok', detail: `${pkg.name}@${newVersion}` });
|
|
2112
2195
|
console.log(` ✓ Published to npm`);
|
|
2113
2196
|
} catch (e) {
|
|
2114
|
-
distResults.push({ target: 'npm', status: 'failed', detail: e.message });
|
|
2115
2197
|
console.log(` ✗ npm publish failed: ${e.message}`);
|
|
2198
|
+
console.log(` Reverting file changes (no commit was made)...`);
|
|
2199
|
+
try {
|
|
2200
|
+
execSync('git checkout -- .', { cwd: repoPath, stdio: 'pipe' });
|
|
2201
|
+
// Clean up any new files (like trashed release notes)
|
|
2202
|
+
execSync('git clean -fd _trash/', { cwd: repoPath, stdio: 'pipe' });
|
|
2203
|
+
console.log(` ✓ Reverted. Working tree is clean. Fix the issue and try again.`);
|
|
2204
|
+
} catch (revertErr) {
|
|
2205
|
+
console.log(` ✗ Revert failed: ${revertErr.message}`);
|
|
2206
|
+
console.log(` Manual cleanup: git checkout -- .`);
|
|
2207
|
+
}
|
|
2208
|
+
console.log('');
|
|
2209
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
2116
2210
|
}
|
|
2117
2211
|
|
|
2212
|
+
// Phase 5: Auto-publish changed sub-tools to npm
|
|
2213
|
+
const toolsDir = join(repoPath, 'tools');
|
|
2214
|
+
if (existsSync(toolsDir)) {
|
|
2215
|
+
for (const tool of readdirSync(toolsDir)) {
|
|
2216
|
+
const toolPath = join(toolsDir, tool);
|
|
2217
|
+
const toolPkg = join(toolPath, 'package.json');
|
|
2218
|
+
if (!existsSync(toolPkg)) continue;
|
|
2219
|
+
const pkg = JSON.parse(readFileSync(toolPkg, 'utf8'));
|
|
2220
|
+
if (!pkg.name || pkg.private) continue;
|
|
2221
|
+
try {
|
|
2222
|
+
publishNpm(toolPath);
|
|
2223
|
+
distResults.push({ target: 'npm', status: 'ok', detail: `${pkg.name}@${pkg.version}` });
|
|
2224
|
+
console.log(` ✓ Published sub-tool: ${pkg.name}@${pkg.version}`);
|
|
2225
|
+
} catch (e) {
|
|
2226
|
+
// Sub-tool publish failure is non-fatal but loud (Phase 7)
|
|
2227
|
+
const msg = e.message || '';
|
|
2228
|
+
if (msg.includes('previously published') || msg.includes('cannot publish over')) {
|
|
2229
|
+
// Already published at this version. Not an error.
|
|
2230
|
+
} else {
|
|
2231
|
+
distResults.push({ target: 'npm', status: 'failed', detail: `${pkg.name}: ${msg}` });
|
|
2232
|
+
console.log(` ✗ Sub-tool ${pkg.name} publish failed: ${msg}`);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
// 5. Git commit + tag (AFTER npm publish succeeds)
|
|
2240
|
+
gitCommitAndTag(repoPath, newVersion, notes);
|
|
2241
|
+
console.log(` ✓ Committed and tagged v${newVersion}`);
|
|
2242
|
+
|
|
2243
|
+
// 5.5. Push commit + tag
|
|
2244
|
+
{
|
|
2245
|
+
const pushResult = pushReleaseWithAutoPr(repoPath, newVersion, level);
|
|
2246
|
+
if (pushResult.ok) {
|
|
2247
|
+
console.log(` ✓ Pushed to remote (${pushResult.via})`);
|
|
2248
|
+
} else {
|
|
2249
|
+
logPushFailure(pushResult, `v${newVersion}`);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
if (!noPublish) {
|
|
2118
2254
|
// 7. GitHub Packages ... SKIPPED from private repos.
|
|
2119
2255
|
// deploy-public.sh publishes to GitHub Packages from the public repo clone.
|
|
2120
2256
|
// Publishing from private ties the package to the private repo, making it
|
|
@@ -2426,6 +2562,66 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
|
|
|
2426
2562
|
}
|
|
2427
2563
|
}
|
|
2428
2564
|
|
|
2565
|
+
// License compliance gate (MANDATORY: blocks release if .license-guard.json missing)
|
|
2566
|
+
{
|
|
2567
|
+
const configPath = join(repoPath, '.license-guard.json');
|
|
2568
|
+
if (!existsSync(configPath)) {
|
|
2569
|
+
console.log(` \u2717 .license-guard.json not found.`);
|
|
2570
|
+
console.log(` Every repo must have .license-guard.json to release.`);
|
|
2571
|
+
console.log(` Run: wip-repo-init (scaffolds ai/, .license-guard.json, CLA.md)`);
|
|
2572
|
+
console.log(` Or: wip-license-guard init`);
|
|
2573
|
+
console.log('');
|
|
2574
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
2575
|
+
}
|
|
2576
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
2577
|
+
const licenseIssues = [];
|
|
2578
|
+
const licensePath = join(repoPath, 'LICENSE');
|
|
2579
|
+
if (!existsSync(licensePath)) {
|
|
2580
|
+
licenseIssues.push('LICENSE file is missing');
|
|
2581
|
+
} else {
|
|
2582
|
+
const licenseText = readFileSync(licensePath, 'utf8');
|
|
2583
|
+
if (!licenseText.includes(config.copyright)) {
|
|
2584
|
+
licenseIssues.push(`LICENSE copyright does not match "${config.copyright}"`);
|
|
2585
|
+
}
|
|
2586
|
+
if (config.license === 'MIT+AGPL' && !licenseText.includes('AGPL') && !licenseText.includes('GNU Affero')) {
|
|
2587
|
+
licenseIssues.push('LICENSE is MIT-only but config requires MIT+AGPL');
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
if (!existsSync(join(repoPath, 'CLA.md'))) {
|
|
2591
|
+
licenseIssues.push('CLA.md is missing');
|
|
2592
|
+
}
|
|
2593
|
+
const readmePath = join(repoPath, 'README.md');
|
|
2594
|
+
if (existsSync(readmePath)) {
|
|
2595
|
+
const readme = readFileSync(readmePath, 'utf8');
|
|
2596
|
+
if (!readme.includes('## License')) licenseIssues.push('README.md missing ## License section');
|
|
2597
|
+
if (config.license === 'MIT+AGPL' && !readme.includes('AGPL')) licenseIssues.push('README.md License section missing AGPL reference');
|
|
2598
|
+
}
|
|
2599
|
+
const aiDir = join(repoPath, 'ai');
|
|
2600
|
+
if (existsSync(aiDir)) {
|
|
2601
|
+
const npmignorePath = join(repoPath, '.npmignore');
|
|
2602
|
+
const pkgJson = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf8'));
|
|
2603
|
+
const hasFilesWhitelist = Array.isArray(pkgJson.files);
|
|
2604
|
+
if (!hasFilesWhitelist) {
|
|
2605
|
+
if (!existsSync(npmignorePath)) {
|
|
2606
|
+
licenseIssues.push('.npmignore is missing (ai/ directory exists and could leak to npm)');
|
|
2607
|
+
} else {
|
|
2608
|
+
const npmignore = readFileSync(npmignorePath, 'utf8');
|
|
2609
|
+
if (!npmignore.includes('ai/')) {
|
|
2610
|
+
licenseIssues.push('.npmignore does not exclude ai/ (plans and bugs could leak to npm)');
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
if (licenseIssues.length > 0) {
|
|
2616
|
+
console.log(` \u2717 License compliance failed:`);
|
|
2617
|
+
for (const issue of licenseIssues) console.log(` - ${issue}`);
|
|
2618
|
+
console.log(`\n Run \`wip-license-guard check --fix\` to auto-repair, then try again.`);
|
|
2619
|
+
console.log('');
|
|
2620
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
2621
|
+
}
|
|
2622
|
+
console.log(` \u2713 License compliance passed`);
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2429
2625
|
if (dryRun) {
|
|
2430
2626
|
console.log(` [dry run] Would bump package.json to ${newVersion}`);
|
|
2431
2627
|
if (!noPublish) {
|
|
@@ -2454,6 +2650,14 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
|
|
|
2454
2650
|
writePackageVersion(repoPath, newVersion);
|
|
2455
2651
|
console.log(` \u2713 package.json -> ${newVersion}`);
|
|
2456
2652
|
|
|
2653
|
+
// 1.25. Auto-bump sub-tool versions (Phase 5)
|
|
2654
|
+
{
|
|
2655
|
+
const autoBumped = autoFixSubToolVersions(repoPath);
|
|
2656
|
+
if (autoBumped > 0) {
|
|
2657
|
+
console.log(` \u2713 Auto-bumped ${autoBumped} sub-tool(s)`);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2457
2661
|
// 1.5. Validate sub-tool version bumps (Phase 8: error by default)
|
|
2458
2662
|
{
|
|
2459
2663
|
const subToolResult = validateSubToolVersions(repoPath, allowSubToolDrift);
|
|
@@ -2501,6 +2705,31 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
|
|
|
2501
2705
|
console.log(` \u2717 npm publish failed: ${e.message}`);
|
|
2502
2706
|
}
|
|
2503
2707
|
|
|
2708
|
+
// 5.5. Auto-publish changed sub-tools to npm (Phase 5)
|
|
2709
|
+
const toolsDir = join(repoPath, 'tools');
|
|
2710
|
+
if (existsSync(toolsDir)) {
|
|
2711
|
+
for (const tool of readdirSync(toolsDir)) {
|
|
2712
|
+
const toolPath = join(toolsDir, tool);
|
|
2713
|
+
const toolPkg = join(toolPath, 'package.json');
|
|
2714
|
+
if (!existsSync(toolPkg)) continue;
|
|
2715
|
+
const pkg = JSON.parse(readFileSync(toolPkg, 'utf8'));
|
|
2716
|
+
if (!pkg.name || pkg.private) continue;
|
|
2717
|
+
try {
|
|
2718
|
+
publishNpm(toolPath);
|
|
2719
|
+
distResults.push({ target: 'npm', status: 'ok', detail: `${pkg.name}@${pkg.version}` });
|
|
2720
|
+
console.log(` \u2713 Published sub-tool: ${pkg.name}@${pkg.version}`);
|
|
2721
|
+
} catch (e) {
|
|
2722
|
+
const msg = e.message || '';
|
|
2723
|
+
if (msg.includes('previously published') || msg.includes('cannot publish over')) {
|
|
2724
|
+
// Already published at this version. Not an error.
|
|
2725
|
+
} else {
|
|
2726
|
+
distResults.push({ target: 'npm', status: 'failed', detail: `${pkg.name}: ${msg}` });
|
|
2727
|
+
console.log(` \u2717 Sub-tool ${pkg.name} publish failed: ${msg}`);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2504
2733
|
// 6. GitHub prerelease on public repo (if opted in)
|
|
2505
2734
|
if (publishReleaseNotes) {
|
|
2506
2735
|
try {
|
|
@@ -2527,11 +2756,11 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
|
|
|
2527
2756
|
}
|
|
2528
2757
|
|
|
2529
2758
|
// deploy-public: sync private -> public mirror (Phase 6)
|
|
2530
|
-
//
|
|
2531
|
-
//
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
{
|
|
2759
|
+
// Beta: deploy to public (testing distribution pipeline)
|
|
2760
|
+
// Alpha: NEVER deploy to public (dev-only)
|
|
2761
|
+
if (track === 'alpha') {
|
|
2762
|
+
console.log(` - deploy-public: skipped (alpha is dev-only, never goes to public)`);
|
|
2763
|
+
} else {
|
|
2535
2764
|
const dp = runDeployPublic(repoPath, { skip: noDeployPublic });
|
|
2536
2765
|
if (dp.skipped) {
|
|
2537
2766
|
if (dp.reason === 'flag') {
|
|
@@ -2590,9 +2819,17 @@ export async function releaseHotfix({ repoPath, notes, notesSource, dryRun, noPu
|
|
|
2590
2819
|
}
|
|
2591
2820
|
}
|
|
2592
2821
|
|
|
2593
|
-
// License compliance gate
|
|
2594
|
-
|
|
2595
|
-
|
|
2822
|
+
// License compliance gate (MANDATORY: blocks release if .license-guard.json missing)
|
|
2823
|
+
{
|
|
2824
|
+
const configPath = join(repoPath, '.license-guard.json');
|
|
2825
|
+
if (!existsSync(configPath)) {
|
|
2826
|
+
console.log(` \u2717 .license-guard.json not found.`);
|
|
2827
|
+
console.log(` Every repo must have .license-guard.json to release.`);
|
|
2828
|
+
console.log(` Run: wip-repo-init (scaffolds ai/, .license-guard.json, CLA.md)`);
|
|
2829
|
+
console.log(` Or: wip-license-guard init`);
|
|
2830
|
+
console.log('');
|
|
2831
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
2832
|
+
}
|
|
2596
2833
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
2597
2834
|
const licenseIssues = [];
|
|
2598
2835
|
const licensePath = join(repoPath, 'LICENSE');
|
|
@@ -2604,6 +2841,9 @@ export async function releaseHotfix({ repoPath, notes, notesSource, dryRun, noPu
|
|
|
2604
2841
|
licenseIssues.push(`LICENSE copyright does not match "${config.copyright}"`);
|
|
2605
2842
|
}
|
|
2606
2843
|
}
|
|
2844
|
+
if (!existsSync(join(repoPath, 'CLA.md'))) {
|
|
2845
|
+
licenseIssues.push('CLA.md is missing');
|
|
2846
|
+
}
|
|
2607
2847
|
if (licenseIssues.length > 0) {
|
|
2608
2848
|
console.log(` \u2717 License compliance failed:`);
|
|
2609
2849
|
for (const issue of licenseIssues) console.log(` - ${issue}`);
|
package/package.json
CHANGED