corifeus-builder 2025.4.133 → 2026.4.137
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/README.md +3 -3
- package/package.json +9 -9
- package/src/task/changelog/changelog.js +263 -0
- package/src/task/changelog/github-release.js +126 -0
- package/src/task/changelog/index.js +10 -0
- package/src/task/index.js +1 -0
- package/src/task/npm/npm.js +26 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
|
-
# 🏗️ Corifeus Builder
|
|
8
|
+
# 🏗️ Corifeus Builder v2026.4.137
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
### 🛠️ Built on NodeJs version
|
|
22
22
|
|
|
23
23
|
```txt
|
|
24
|
-
|
|
24
|
+
v24.14.1
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
|
|
@@ -155,7 +155,7 @@ All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](ht
|
|
|
155
155
|
---
|
|
156
156
|
|
|
157
157
|
|
|
158
|
-
[**CORIFEUS-BUILDER**](https://corifeus.com/corifeus-builder) Build
|
|
158
|
+
[**CORIFEUS-BUILDER**](https://corifeus.com/corifeus-builder) Build v2026.4.137
|
|
159
159
|
|
|
160
160
|
[](https://www.npmjs.com/package/corifeus-builder) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [](https://www.patrikx3.com/en/front/contact) [](https://www.facebook.com/corifeus.software)
|
|
161
161
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corifeus-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2026.4.137",
|
|
4
4
|
"corifeus": {
|
|
5
5
|
"icon": "fas fa-gavel",
|
|
6
6
|
"code": "Make",
|
|
7
7
|
"publish": true,
|
|
8
|
-
"nodejs": "
|
|
8
|
+
"nodejs": "v24.14.1",
|
|
9
9
|
"reponame": "corifeus-builder",
|
|
10
10
|
"opencollective": false,
|
|
11
11
|
"build": true
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://corifeus.com/corifeus-builder",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"corifeus-utils": "^2025.4.
|
|
36
|
+
"corifeus-utils": "^2025.4.123",
|
|
37
37
|
"download": "^8.0.0",
|
|
38
38
|
"extract-zip": "^2.0.1",
|
|
39
|
-
"fs-extra": "^11.3.
|
|
39
|
+
"fs-extra": "^11.3.4",
|
|
40
40
|
"github-api": "^3.4.0",
|
|
41
41
|
"glob": "^8.0.3",
|
|
42
42
|
"glob-promise": "^6.0.7",
|
|
@@ -46,15 +46,15 @@
|
|
|
46
46
|
"grunt-contrib-htmlmin": "^3.1.0",
|
|
47
47
|
"grunt-contrib-watch": "^1.1.0",
|
|
48
48
|
"jit-grunt": "^0.10.0",
|
|
49
|
-
"lodash": "^4.
|
|
49
|
+
"lodash": "^4.18.1",
|
|
50
50
|
"mkdirp": "^3.0.1",
|
|
51
|
-
"mocha": "^11.
|
|
51
|
+
"mocha": "^11.7.5",
|
|
52
52
|
"mz": "^2.7.0",
|
|
53
|
-
"npm": "^11.
|
|
54
|
-
"npm-check-updates": "^
|
|
53
|
+
"npm": "^11.12.1",
|
|
54
|
+
"npm-check-updates": "^20.0.0",
|
|
55
55
|
"should": "^13.2.3",
|
|
56
56
|
"time-grunt": "^2.0.0",
|
|
57
|
-
"yaml": "^2.
|
|
57
|
+
"yaml": "^2.8.3"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"twemoji": "^14.0.2"
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
const fs = require('fs').promises
|
|
2
|
+
const fsSync = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const { execSync } = require('child_process')
|
|
5
|
+
|
|
6
|
+
const getCommitsSinceLastBump = (repoDir) => {
|
|
7
|
+
try {
|
|
8
|
+
const bumps = execSync(
|
|
9
|
+
'git log --oneline --grep="bump version" --grep="chore: bump" --grep="chore(release):" --format="%H" -2',
|
|
10
|
+
{ cwd: repoDir, encoding: 'utf-8' }
|
|
11
|
+
).trim().split('\n').filter(Boolean)
|
|
12
|
+
|
|
13
|
+
if (bumps.length >= 1) {
|
|
14
|
+
const afterBump = execSync(
|
|
15
|
+
`git log --oneline --no-merges ${bumps[0]}..HEAD`,
|
|
16
|
+
{ cwd: repoDir, encoding: 'utf-8' }
|
|
17
|
+
).trim()
|
|
18
|
+
if (afterBump) return afterBump
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (bumps.length >= 2) {
|
|
22
|
+
const log = execSync(
|
|
23
|
+
`git log --oneline --no-merges ${bumps[1]}..${bumps[0]}`,
|
|
24
|
+
{ cwd: repoDir, encoding: 'utf-8' }
|
|
25
|
+
).trim()
|
|
26
|
+
if (log) return log
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
return execSync('git log --oneline --no-merges -20', { cwd: repoDir, encoding: 'utf-8' }).trim()
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return ''
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getLastPublishedVersion = async (packageName, repoDir) => {
|
|
38
|
+
const nodeModulesPkgPath = path.resolve(repoDir, 'node_modules', packageName, 'package.json')
|
|
39
|
+
try {
|
|
40
|
+
const content = await fs.readFile(nodeModulesPkgPath, 'utf8')
|
|
41
|
+
return JSON.parse(content).version
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const getCommitForVersion = (repoDir, version) => {
|
|
48
|
+
try {
|
|
49
|
+
const commit = execSync(
|
|
50
|
+
`git log --all --oneline --grep="v${version}" --grep="${version}" --format="%H" -1`,
|
|
51
|
+
{ cwd: repoDir, encoding: 'utf-8' }
|
|
52
|
+
).trim()
|
|
53
|
+
if (commit) return commit
|
|
54
|
+
} catch (e) {}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const commit = execSync(
|
|
58
|
+
`git log --all --oneline -S '"version": "${version}"' --format="%H" -- package.json | tail -1`,
|
|
59
|
+
{ cwd: repoDir, encoding: 'utf-8' }
|
|
60
|
+
).trim()
|
|
61
|
+
if (commit) return commit
|
|
62
|
+
} catch (e) {}
|
|
63
|
+
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Collect git logs from multiple repos.
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} options
|
|
71
|
+
* @param {string} options.cwd - project root
|
|
72
|
+
* @param {Array<{dir: string, npmName?: string}>} [options.repos] - workspace repos (defaults to single repo)
|
|
73
|
+
*/
|
|
74
|
+
const collectLogs = async (options) => {
|
|
75
|
+
const { cwd, repos } = options
|
|
76
|
+
|
|
77
|
+
if (!repos || repos.length === 0) {
|
|
78
|
+
const log = getCommitsSinceLastBump(cwd)
|
|
79
|
+
return log ? [log] : []
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const logs = []
|
|
83
|
+
for (const repoCfg of repos) {
|
|
84
|
+
const repoDir = path.resolve(cwd, repoCfg.dir)
|
|
85
|
+
try {
|
|
86
|
+
if (repoCfg.npmName) {
|
|
87
|
+
const bumpLog = getCommitsSinceLastBump(repoDir)
|
|
88
|
+
if (bumpLog) {
|
|
89
|
+
logs.push(`## ${repoCfg.dir}\n${bumpLog}`)
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const lastPublishedVersion = await getLastPublishedVersion(repoCfg.npmName, cwd)
|
|
94
|
+
let sinceCommit = null
|
|
95
|
+
if (lastPublishedVersion) {
|
|
96
|
+
sinceCommit = getCommitForVersion(repoDir, lastPublishedVersion)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let logCmd
|
|
100
|
+
if (sinceCommit) {
|
|
101
|
+
logCmd = `git log --oneline --no-merges ${sinceCommit}..HEAD`
|
|
102
|
+
} else {
|
|
103
|
+
let lastTag = ''
|
|
104
|
+
try {
|
|
105
|
+
lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', { cwd: repoDir, encoding: 'utf-8' }).trim()
|
|
106
|
+
} catch (e) {}
|
|
107
|
+
logCmd = lastTag ? `git log --oneline --no-merges ${lastTag}..HEAD` : 'git log --oneline --no-merges -20'
|
|
108
|
+
}
|
|
109
|
+
const log = execSync(logCmd, { cwd: repoDir, encoding: 'utf-8' }).trim()
|
|
110
|
+
if (log) logs.push(`## ${repoCfg.dir}\n${log}`)
|
|
111
|
+
} else {
|
|
112
|
+
const bumpLog = getCommitsSinceLastBump(repoDir)
|
|
113
|
+
if (bumpLog) {
|
|
114
|
+
logs.push(`## ${repoCfg.dir}\n${bumpLog}`)
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
let lastTag = ''
|
|
118
|
+
try {
|
|
119
|
+
lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', { cwd: repoDir, encoding: 'utf-8' }).trim()
|
|
120
|
+
} catch (e) {}
|
|
121
|
+
const logCmd = lastTag ? `git log --oneline --no-merges ${lastTag}..HEAD` : 'git log --oneline --no-merges -20'
|
|
122
|
+
const log = execSync(logCmd, { cwd: repoDir, encoding: 'utf-8' }).trim()
|
|
123
|
+
if (log) logs.push(`## ${repoCfg.dir}\n${log}`)
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error(`Failed to get git log for ${repoCfg.dir}:`, e.message)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return logs
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generate changelog entry using Claude AI and update change-log.md
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} options
|
|
136
|
+
* @param {string} options.cwd - project root
|
|
137
|
+
* @param {string} options.projectName - e.g. "P3X Network MCP" or "P3X Redis UI"
|
|
138
|
+
* @param {Array<{dir: string, npmName?: string}>} [options.repos] - workspace repos
|
|
139
|
+
*/
|
|
140
|
+
const generateChangelog = async (options) => {
|
|
141
|
+
const { cwd, projectName, repos } = options
|
|
142
|
+
const pkg = JSON.parse(await fs.readFile(path.resolve(cwd, 'package.json'), 'utf-8'))
|
|
143
|
+
const version = pkg.version
|
|
144
|
+
const today = new Date().toLocaleDateString('en-US', { month: '2-digit', day: '2-digit', year: 'numeric' })
|
|
145
|
+
const changelogPath = path.resolve(cwd, 'change-log.md')
|
|
146
|
+
|
|
147
|
+
const logs = await collectLogs({ cwd, repos })
|
|
148
|
+
const allLogs = logs.join('\n\n')
|
|
149
|
+
if (!allLogs) {
|
|
150
|
+
console.log('No new commits for changelog')
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const existingChangelog = await fs.readFile(changelogPath, 'utf-8')
|
|
155
|
+
let previousEntry = ''
|
|
156
|
+
const prevMatch = existingChangelog.match(/### v[\d.]+\nReleased on [^\n]+\n([\s\S]*?)(?=\n### v|\n$)/)
|
|
157
|
+
if (prevMatch) {
|
|
158
|
+
previousEntry = prevMatch[0]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let changelogEntry = ''
|
|
162
|
+
try {
|
|
163
|
+
const repoNote = repos && repos.length > 1
|
|
164
|
+
? `- The commits come from MULTIPLE repos (${repos.map(r => r.dir).join(', ')}) — include significant changes from ALL repos, not just the parent`
|
|
165
|
+
: ''
|
|
166
|
+
|
|
167
|
+
const prompt = `Generate a changelog entry for version v${version} of ${projectName}.
|
|
168
|
+
Based on these recent git commits, write changelog bullet points.
|
|
169
|
+
|
|
170
|
+
${allLogs}
|
|
171
|
+
|
|
172
|
+
IMPORTANT: The following is the PREVIOUS version's changelog entry. Do NOT repeat any of these items. Only include changes that are NEW in this version:
|
|
173
|
+
|
|
174
|
+
${previousEntry}
|
|
175
|
+
|
|
176
|
+
Rules:
|
|
177
|
+
- Use this EXACT format:
|
|
178
|
+
### v${version}
|
|
179
|
+
Released on ${today}
|
|
180
|
+
* CATEGORY: Description.
|
|
181
|
+
|
|
182
|
+
- Valid categories: FEATURE, BUGFIX, PERF, REFACTOR, DOCS, CHORE
|
|
183
|
+
- Each bullet starts with "* CATEGORY: " (with the asterisk)
|
|
184
|
+
- Only include user-facing or significant changes
|
|
185
|
+
- SKIP these types of commits entirely:
|
|
186
|
+
- Version bumps, submodule updates, typo fixes, changelog updates
|
|
187
|
+
- Anything related to the secure/ folder
|
|
188
|
+
- Internal CI/CD, build pipeline, or release automation changes
|
|
189
|
+
- Co-Authored-By lines or merge commits
|
|
190
|
+
- Keep descriptions concise (one line each)
|
|
191
|
+
- Do NOT repeat anything from the previous changelog entry shown above
|
|
192
|
+
- Do NOT use markdown code fences
|
|
193
|
+
- Output ONLY the changelog entry starting with ### — absolutely NO extra text
|
|
194
|
+
${repoNote}
|
|
195
|
+
- If there are many feature commits, list each feature separately`
|
|
196
|
+
|
|
197
|
+
const tmpPrompt = path.resolve(cwd, '.changelog-prompt.tmp')
|
|
198
|
+
await fs.writeFile(tmpPrompt, prompt)
|
|
199
|
+
changelogEntry = execSync(
|
|
200
|
+
`cat ${JSON.stringify(tmpPrompt)} | claude -p - --no-session-persistence`,
|
|
201
|
+
{ encoding: 'utf-8', timeout: 60000 }
|
|
202
|
+
).trim()
|
|
203
|
+
try { await fs.unlink(tmpPrompt) } catch (e) {}
|
|
204
|
+
|
|
205
|
+
const headerIndex = changelogEntry.indexOf('### v')
|
|
206
|
+
if (headerIndex > 0) {
|
|
207
|
+
changelogEntry = changelogEntry.substring(headerIndex)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const lines = changelogEntry.split('\n')
|
|
211
|
+
const lastBulletIndex = lines.reduce((last, line, i) => line.startsWith('* ') ? i : last, -1)
|
|
212
|
+
if (lastBulletIndex >= 0) {
|
|
213
|
+
changelogEntry = lines.slice(0, lastBulletIndex + 1).join('\n')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
console.log('Claude generated changelog:\n' + changelogEntry)
|
|
217
|
+
} catch (e) {
|
|
218
|
+
console.error('Claude changelog generation failed, using fallback:', e.message)
|
|
219
|
+
changelogEntry = `### v${version}\nReleased on ${today}\n* CHORE: Release v${version}.`
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let changelog = await fs.readFile(changelogPath, 'utf-8')
|
|
223
|
+
const headerEnd = '[//]: #@corifeus-header:end'
|
|
224
|
+
const headerEndIndex = changelog.indexOf(headerEnd)
|
|
225
|
+
if (headerEndIndex !== -1) {
|
|
226
|
+
const insertPos = headerEndIndex + headerEnd.length
|
|
227
|
+
changelog = changelog.slice(0, insertPos) + '\n\n' + changelogEntry + '\n' + changelog.slice(insertPos).replace(/^\n+/, '\n')
|
|
228
|
+
await fs.writeFile(changelogPath, changelog)
|
|
229
|
+
console.log(`Changelog updated for v${version}`)
|
|
230
|
+
} else {
|
|
231
|
+
console.error('Could not find header end marker in change-log.md')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
execSync(`git add change-log.md && git commit -m "chore: update changelog for v${version}"`, {
|
|
235
|
+
cwd,
|
|
236
|
+
stdio: 'inherit',
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
return changelogEntry
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get changelog entry for a specific version
|
|
244
|
+
*/
|
|
245
|
+
const getChangelogEntry = (changelogPath, version) => {
|
|
246
|
+
try {
|
|
247
|
+
const changelog = fsSync.readFileSync(changelogPath, 'utf-8')
|
|
248
|
+
const versionEscaped = version.replace(/\./g, '\\.')
|
|
249
|
+
const regex = new RegExp(`### v${versionEscaped}\\n([\\s\\S]*?)(?=\\n### v|\\n\\[//\\]|$)`)
|
|
250
|
+
const match = changelog.match(regex)
|
|
251
|
+
if (match) {
|
|
252
|
+
return `### v${version}\n${match[1].trim()}`
|
|
253
|
+
}
|
|
254
|
+
} catch (e) {}
|
|
255
|
+
return ''
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
module.exports = {
|
|
259
|
+
generateChangelog,
|
|
260
|
+
getChangelogEntry,
|
|
261
|
+
collectLogs,
|
|
262
|
+
getCommitsSinceLastBump,
|
|
263
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const https = require('https')
|
|
2
|
+
const { getChangelogEntry } = require('./changelog')
|
|
3
|
+
|
|
4
|
+
const githubRequest = (token, method, apiPath, body) => new Promise((resolve, reject) => {
|
|
5
|
+
const options = {
|
|
6
|
+
hostname: 'api.github.com',
|
|
7
|
+
path: apiPath,
|
|
8
|
+
method,
|
|
9
|
+
headers: {
|
|
10
|
+
'Authorization': `Bearer ${token}`,
|
|
11
|
+
'Accept': 'application/vnd.github+json',
|
|
12
|
+
'User-Agent': 'corifeus-builder',
|
|
13
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
if (body) {
|
|
17
|
+
options.headers['Content-Type'] = 'application/json'
|
|
18
|
+
}
|
|
19
|
+
const req = https.request(options, (res) => {
|
|
20
|
+
let data = ''
|
|
21
|
+
res.on('data', chunk => data += chunk)
|
|
22
|
+
res.on('end', () => {
|
|
23
|
+
try { resolve({ status: res.statusCode, data: JSON.parse(data) }) }
|
|
24
|
+
catch { resolve({ status: res.statusCode, data }) }
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
req.on('error', reject)
|
|
28
|
+
if (body) req.write(JSON.stringify(body))
|
|
29
|
+
req.end()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create or update a GitHub release with changelog.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} options
|
|
36
|
+
* @param {string} options.token - GitHub token
|
|
37
|
+
* @param {string} options.owner - GitHub owner (e.g. "patrikx3")
|
|
38
|
+
* @param {string} options.repo - GitHub repo name (e.g. "network-mcp")
|
|
39
|
+
* @param {string} options.version - version string (e.g. "2026.4.105")
|
|
40
|
+
* @param {string} options.projectName - display name (e.g. "P3X Network MCP")
|
|
41
|
+
* @param {string} options.changelogPath - path to change-log.md
|
|
42
|
+
* @param {string} [options.extraBody] - extra content to append to release body
|
|
43
|
+
*/
|
|
44
|
+
const createOrUpdateRelease = async (options) => {
|
|
45
|
+
const { token, owner, repo, version, projectName, changelogPath, extraBody } = options
|
|
46
|
+
const releaseTag = `v${version}`
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const changelogEntry = getChangelogEntry(changelogPath, version)
|
|
50
|
+
const bodyParts = [changelogEntry]
|
|
51
|
+
if (extraBody) bodyParts.push(extraBody)
|
|
52
|
+
const releaseBody = bodyParts.filter(Boolean).join('\n\n')
|
|
53
|
+
|
|
54
|
+
// Check if release exists
|
|
55
|
+
const listRes = await githubRequest(token, 'GET', `/repos/${owner}/${repo}/releases?per_page=10`)
|
|
56
|
+
if (listRes.status === 200) {
|
|
57
|
+
const existing = listRes.data.find(r => r.tag_name === releaseTag)
|
|
58
|
+
if (existing) {
|
|
59
|
+
if (existing.draft) {
|
|
60
|
+
// Promote draft to published
|
|
61
|
+
const patchRes = await githubRequest(token, 'PATCH', `/repos/${owner}/${repo}/releases/${existing.id}`, {
|
|
62
|
+
tag_name: releaseTag,
|
|
63
|
+
draft: false,
|
|
64
|
+
body: releaseBody,
|
|
65
|
+
name: `${projectName} ${releaseTag}`,
|
|
66
|
+
})
|
|
67
|
+
if (patchRes.status === 200) {
|
|
68
|
+
console.log(`GitHub release ${releaseTag} promoted from draft to published`)
|
|
69
|
+
} else {
|
|
70
|
+
console.error(`Failed to promote release: ${patchRes.status}`, patchRes.data)
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// Update existing
|
|
74
|
+
const patchRes = await githubRequest(token, 'PATCH', `/repos/${owner}/${repo}/releases/${existing.id}`, {
|
|
75
|
+
body: releaseBody,
|
|
76
|
+
name: `${projectName} ${releaseTag}`,
|
|
77
|
+
})
|
|
78
|
+
if (patchRes.status === 200) {
|
|
79
|
+
console.log(`GitHub release ${releaseTag} updated with changelog`)
|
|
80
|
+
} else {
|
|
81
|
+
console.error(`Failed to update release: ${patchRes.status}`, patchRes.data)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check for untagged draft (electron-builder)
|
|
88
|
+
const untaggedDraft = listRes.data.find(r => r.draft === true && r.tag_name.startsWith('untagged'))
|
|
89
|
+
if (untaggedDraft) {
|
|
90
|
+
const patchRes = await githubRequest(token, 'PATCH', `/repos/${owner}/${repo}/releases/${untaggedDraft.id}`, {
|
|
91
|
+
tag_name: releaseTag,
|
|
92
|
+
draft: false,
|
|
93
|
+
body: releaseBody,
|
|
94
|
+
name: `${projectName} ${releaseTag}`,
|
|
95
|
+
})
|
|
96
|
+
if (patchRes.status === 200) {
|
|
97
|
+
console.log(`GitHub untagged draft promoted to ${releaseTag}`)
|
|
98
|
+
} else {
|
|
99
|
+
console.error(`Failed to promote untagged draft: ${patchRes.status}`, patchRes.data)
|
|
100
|
+
}
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Create new release
|
|
106
|
+
const createRes = await githubRequest(token, 'POST', `/repos/${owner}/${repo}/releases`, {
|
|
107
|
+
tag_name: releaseTag,
|
|
108
|
+
name: `${projectName} ${releaseTag}`,
|
|
109
|
+
body: releaseBody,
|
|
110
|
+
draft: false,
|
|
111
|
+
prerelease: false,
|
|
112
|
+
})
|
|
113
|
+
if (createRes.status === 201) {
|
|
114
|
+
console.log(`GitHub release ${releaseTag} created: ${createRes.data.html_url}`)
|
|
115
|
+
} else {
|
|
116
|
+
console.error(`Failed to create release: ${createRes.status}`, createRes.data)
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.error('Failed to manage GitHub release:', e.message)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
createOrUpdateRelease,
|
|
125
|
+
githubRequest,
|
|
126
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const { generateChangelog, getChangelogEntry } = require('./changelog')
|
|
2
|
+
const { createOrUpdateRelease, githubRequest } = require('./github-release')
|
|
3
|
+
|
|
4
|
+
// Export as a library (not a grunt task) — tokens must come from secure/ in each project
|
|
5
|
+
module.exports = {
|
|
6
|
+
generateChangelog,
|
|
7
|
+
getChangelogEntry,
|
|
8
|
+
createOrUpdateRelease,
|
|
9
|
+
githubRequest,
|
|
10
|
+
}
|
package/src/task/index.js
CHANGED
package/src/task/npm/npm.js
CHANGED
|
@@ -84,6 +84,32 @@ module.exports = async (pkgFile) => {
|
|
|
84
84
|
|
|
85
85
|
const newPkgFile = JSON.stringify(pkg, null, 4);
|
|
86
86
|
await fs.writeFile(pkgFile, newPkgFile)
|
|
87
|
+
|
|
88
|
+
// Auto-ensure .npmignore protects sensitive directories
|
|
89
|
+
const cwd = require('path').dirname(pkgFile)
|
|
90
|
+
const npmignorePath = require('path').join(cwd, '.npmignore')
|
|
91
|
+
const protectedDirs = ['secure/', 'agents/', '.claude/', '.vscode/', '.codex/']
|
|
92
|
+
const existingDirs = []
|
|
93
|
+
for (const dir of protectedDirs) {
|
|
94
|
+
try {
|
|
95
|
+
await fs.access(require('path').join(cwd, dir.replace('/', '')))
|
|
96
|
+
existingDirs.push(dir)
|
|
97
|
+
} catch (e) {}
|
|
98
|
+
}
|
|
99
|
+
if (existingDirs.length > 0) {
|
|
100
|
+
let npmignore = ''
|
|
101
|
+
try {
|
|
102
|
+
npmignore = await fs.readFile(npmignorePath, 'utf-8')
|
|
103
|
+
} catch (e) {}
|
|
104
|
+
const missing = existingDirs.filter(dir => !npmignore.split('\n').some(line => line.trim() === dir || line.trim() === dir.replace('/', '')))
|
|
105
|
+
if (missing.length > 0) {
|
|
106
|
+
const addition = missing.join('\n')
|
|
107
|
+
npmignore = npmignore ? npmignore.trimEnd() + '\n' + addition + '\n' : addition + '\n'
|
|
108
|
+
await fs.writeFile(npmignorePath, npmignore)
|
|
109
|
+
console.log(`Auto-added to .npmignore: ${missing.join(', ')}`)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
87
113
|
return pkg;
|
|
88
114
|
}
|
|
89
115
|
|