@wipcomputer/wip-ai-devops-toolbox 1.9.62 → 1.9.63
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/CHANGELOG.md +26 -0
- package/SKILL.md +1 -1
- package/package.json +1 -1
- package/tools/deploy-public/package.json +1 -1
- package/tools/post-merge-rename/package.json +1 -1
- package/tools/wip-branch-guard/package.json +1 -1
- package/tools/wip-file-guard/package.json +1 -1
- package/tools/wip-license-guard/package.json +1 -1
- package/tools/wip-license-hook/package.json +1 -1
- package/tools/wip-readme-format/package.json +1 -1
- package/tools/wip-release/core.mjs +18 -15
- package/tools/wip-release/package.json +1 -1
- package/tools/wip-repo-init/package.json +1 -1
- package/tools/wip-repo-permissions-hook/package.json +1 -1
- package/tools/wip-repos/package.json +1 -1
- package/tools/wip-universal-installer/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -31,6 +31,32 @@
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
|
|
35
|
+
## 1.9.63 (2026-03-29)
|
|
36
|
+
|
|
37
|
+
# Release Notes: wip-ai-devops-toolbox v1.9.63
|
|
38
|
+
|
|
39
|
+
**Fix all wip-release errors: branch cleanup crashes, shell injection, stale remote refs.**
|
|
40
|
+
|
|
41
|
+
## The story
|
|
42
|
+
|
|
43
|
+
Every wip-release run produced errors: "fatal: Not a valid object name +", "remote ref does not exist", and shell injection risks from branch names passed through execSync template strings. These were dismissed as "non-blocking" but they cluttered every release output and masked real problems.
|
|
44
|
+
|
|
45
|
+
Root cause: branch cleanup code (sections 10 and 11) used `execSync` with template strings, which breaks on branch names with special characters and allows shell injection. Also tried to delete remote branches that GitHub already deleted during PR merge.
|
|
46
|
+
|
|
47
|
+
Fix: replaced all `execSync` template strings with `execFileSync` array args (safe from injection). Added character validation to skip branches with special chars. Wrapped remote delete in try/catch since GitHub PR merge already handles deletion.
|
|
48
|
+
|
|
49
|
+
## Issues closed
|
|
50
|
+
|
|
51
|
+
- #231 (continued: release pipeline reliability)
|
|
52
|
+
|
|
53
|
+
## How to verify
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
wip-release patch --dry-run
|
|
57
|
+
# Should show no "fatal" or "Not a valid object name" errors
|
|
58
|
+
# Guard tests: cd tools/wip-branch-guard && bash test.sh
|
|
59
|
+
```
|
|
34
60
|
|
|
35
61
|
## 1.9.62 (2026-03-29)
|
|
36
62
|
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
interface: [cli, module, mcp, skill, hook, plugin]
|
|
6
6
|
metadata:
|
|
7
7
|
display-name: "WIP AI DevOps Toolbox"
|
|
8
|
-
version: "1.9.
|
|
8
|
+
version: "1.9.63"
|
|
9
9
|
homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
|
|
10
10
|
author: "Parker Todd Brooks"
|
|
11
11
|
category: dev-tools
|
package/package.json
CHANGED
|
@@ -1569,31 +1569,33 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1569
1569
|
.filter(b => b && b !== 'main' && b !== 'master' && !b.startsWith('*') && !b.includes('--merged-'));
|
|
1570
1570
|
|
|
1571
1571
|
if (merged.length > 0) {
|
|
1572
|
+
const current = execSync('git branch --show-current', { cwd: repoPath, encoding: 'utf8' }).trim();
|
|
1572
1573
|
console.log(` Scanning ${merged.length} merged branch(es) for rename...`);
|
|
1573
1574
|
for (const branch of merged) {
|
|
1574
|
-
const current = execSync('git branch --show-current', { cwd: repoPath, encoding: 'utf8' }).trim();
|
|
1575
1575
|
if (branch === current) continue;
|
|
1576
|
+
// Skip branches with characters that break git commands
|
|
1577
|
+
if (/[+\s~^:?*\[\]]/.test(branch)) continue;
|
|
1576
1578
|
|
|
1577
1579
|
let mergeDate;
|
|
1578
1580
|
try {
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
).trim().split('\n').pop().split(' ')[0];
|
|
1581
|
+
// Use execFileSync (array args) instead of execSync (shell string) to avoid injection
|
|
1582
|
+
const mergeBase = execFileSync('git', ['merge-base', 'main', branch], { cwd: repoPath, encoding: 'utf8' }).trim();
|
|
1583
|
+
const logOutput = execFileSync('git', ['log', 'main', '--format=%ai', '--ancestry-path', `${mergeBase}..main`], { cwd: repoPath, encoding: 'utf8' }).trim();
|
|
1584
|
+
if (logOutput) mergeDate = logOutput.split('\n').pop().split(' ')[0];
|
|
1584
1585
|
} catch {}
|
|
1585
1586
|
if (!mergeDate) {
|
|
1586
1587
|
try {
|
|
1587
|
-
mergeDate =
|
|
1588
|
+
mergeDate = execFileSync('git', ['log', branch, '-1', '--format=%ai'], { cwd: repoPath, encoding: 'utf8' }).trim().split(' ')[0];
|
|
1588
1589
|
} catch {}
|
|
1589
1590
|
}
|
|
1590
1591
|
if (!mergeDate) continue;
|
|
1591
1592
|
|
|
1592
1593
|
const newName = `${branch}--merged-${mergeDate}`;
|
|
1593
1594
|
try {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1595
|
+
execFileSync('git', ['branch', '-m', branch, newName], { cwd: repoPath, stdio: 'pipe' });
|
|
1596
|
+
execFileSync('git', ['push', 'origin', newName], { cwd: repoPath, stdio: 'pipe' });
|
|
1597
|
+
// Remote branch may already be deleted by GitHub PR merge. That's fine.
|
|
1598
|
+
try { execFileSync('git', ['push', 'origin', '--delete', branch], { cwd: repoPath, stdio: 'pipe' }); } catch {}
|
|
1597
1599
|
console.log(` ✓ Renamed: ${branch} -> ${newName}`);
|
|
1598
1600
|
} catch (e) {
|
|
1599
1601
|
console.log(` ! Could not rename ${branch}: ${e.message}`);
|
|
@@ -1635,8 +1637,8 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1635
1637
|
|
|
1636
1638
|
for (let i = KEEP_COUNT; i < branches.length; i++) {
|
|
1637
1639
|
try {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
+
execFileSync('git', ['push', 'origin', '--delete', branches[i]], { cwd: repoPath, stdio: 'pipe' });
|
|
1641
|
+
try { execFileSync('git', ['branch', '-d', branches[i]], { cwd: repoPath, stdio: 'pipe' }); } catch {}
|
|
1640
1642
|
pruned++;
|
|
1641
1643
|
} catch {}
|
|
1642
1644
|
}
|
|
@@ -1659,11 +1661,12 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1659
1661
|
let staleCleaned = 0;
|
|
1660
1662
|
for (const branch of allRemote) {
|
|
1661
1663
|
if (branch === current) continue;
|
|
1664
|
+
if (/[+\s~^:?*\[\]]/.test(branch)) continue;
|
|
1662
1665
|
try {
|
|
1663
|
-
|
|
1666
|
+
execFileSync('git', ['merge-base', '--is-ancestor', `origin/${branch}`, 'origin/main'], { cwd: repoPath, stdio: 'pipe' });
|
|
1664
1667
|
// If we get here, branch is fully merged
|
|
1665
|
-
|
|
1666
|
-
|
|
1668
|
+
try { execFileSync('git', ['push', 'origin', '--delete', branch], { cwd: repoPath, stdio: 'pipe' }); } catch {}
|
|
1669
|
+
try { execFileSync('git', ['branch', '-d', branch], { cwd: repoPath, stdio: 'pipe' }); } catch {}
|
|
1667
1670
|
staleCleaned++;
|
|
1668
1671
|
} catch {}
|
|
1669
1672
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wipcomputer/universal-installer",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.63",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Universal Interface specification for agent-native software. Teaches your AI how to build repos with every interface: CLI, Module, MCP Server, OpenClaw Plugin, Skill, Claude Code Hook.",
|
|
6
6
|
"main": "detect.mjs",
|