daodou-command 1.4.6 → 1.4.7
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/.idea/workspace.xml +1 -1
- package/CHANGELOG.md +9 -0
- package/lib/commands/build.js +43 -22
- package/lib/utils/browser.js +46 -1
- package/package.json +1 -1
package/.idea/workspace.xml
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
<option name="presentableId" value="Default" />
|
|
64
64
|
<updated>1758879086844</updated>
|
|
65
65
|
<workItem from="1758879088110" duration="1336000" />
|
|
66
|
-
<workItem from="1763003382850" duration="
|
|
66
|
+
<workItem from="1763003382850" duration="5386000" />
|
|
67
67
|
</task>
|
|
68
68
|
<servers />
|
|
69
69
|
</component>
|
package/CHANGELOG.md
CHANGED
|
@@ -6,11 +6,20 @@
|
|
|
6
6
|
并且此项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [1.4.7] - 2025-12-04
|
|
10
|
+
|
|
11
|
+
### 修复
|
|
12
|
+
- 修复 `dao build` 命令中 Cookie 过期问题
|
|
13
|
+
- 实现自动捕获和更新 Jenkins 返回的 `Set-Cookie`
|
|
14
|
+
- 优化 HTTP 请求拦截器,自动管理 Cookie
|
|
15
|
+
- 增强构建过程中 Session 的稳定性
|
|
16
|
+
|
|
9
17
|
## [1.4.6] - 2025-11-13
|
|
10
18
|
|
|
11
19
|
### 修复
|
|
12
20
|
- 修复版本更新提示逻辑,当前版本已是最新版本时不再显示更新提示
|
|
13
21
|
- 优化 `getUpdateReminder()` 函数,添加当前版本与最新版本的实时比较
|
|
22
|
+
- 修正 Node.js 引擎版本要求为 `>=20.18.1`
|
|
14
23
|
|
|
15
24
|
## [1.4.5] - 2025-11-13
|
|
16
25
|
|
package/lib/commands/build.js
CHANGED
|
@@ -107,6 +107,40 @@ class BuildCommand {
|
|
|
107
107
|
baseUrl: null,
|
|
108
108
|
auth: null
|
|
109
109
|
};
|
|
110
|
+
|
|
111
|
+
// 创建 axios 实例
|
|
112
|
+
this.axios = axios.create({
|
|
113
|
+
maxRedirects: 0,
|
|
114
|
+
validateStatus: s => s < 400 || s === 401 || s === 403 || s === 302
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// 请求拦截器:自动添加 Cookie
|
|
118
|
+
this.axios.interceptors.request.use(config => {
|
|
119
|
+
const cookieString = this.browserAuth.getCookieString();
|
|
120
|
+
if (cookieString) {
|
|
121
|
+
config.headers.Cookie = cookieString;
|
|
122
|
+
}
|
|
123
|
+
// 确保有 User-Agent
|
|
124
|
+
if (!config.headers['User-Agent']) {
|
|
125
|
+
config.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36';
|
|
126
|
+
}
|
|
127
|
+
return config;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// 响应拦截器:自动更新 Cookie
|
|
131
|
+
this.axios.interceptors.response.use(async response => {
|
|
132
|
+
const setCookie = response.headers['set-cookie'];
|
|
133
|
+
if (setCookie) {
|
|
134
|
+
await this.browserAuth.updateCookiesFromSetCookie(setCookie);
|
|
135
|
+
}
|
|
136
|
+
return response;
|
|
137
|
+
}, async error => {
|
|
138
|
+
// 即使是错误响应,也可能包含 set-cookie
|
|
139
|
+
if (error.response && error.response.headers && error.response.headers['set-cookie']) {
|
|
140
|
+
await this.browserAuth.updateCookiesFromSetCookie(error.response.headers['set-cookie']);
|
|
141
|
+
}
|
|
142
|
+
return Promise.reject(error);
|
|
143
|
+
});
|
|
110
144
|
}
|
|
111
145
|
|
|
112
146
|
async execute(options = {}) {
|
|
@@ -242,19 +276,10 @@ class BuildCommand {
|
|
|
242
276
|
}
|
|
243
277
|
|
|
244
278
|
async ensureJenkinsSession() {
|
|
245
|
-
// 先用当前 cookie 访问 Jenkins 首页,确认是否有效
|
|
246
|
-
const cookieString = this.browserAuth.cookies
|
|
247
|
-
? this.browserAuth.cookies.map(c => `${c.name}=${c.value}`).join('; ')
|
|
248
|
-
: '';
|
|
249
279
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
|
|
254
|
-
},
|
|
255
|
-
maxRedirects: 0,
|
|
256
|
-
validateStatus: s => s < 400 || s === 401 || s === 403 || s === 302
|
|
257
|
-
});
|
|
280
|
+
// 使用拦截器自动带上 Cookie
|
|
281
|
+
const resp = await this.axios.get(this.jenkinsConfig.baseUrl);
|
|
282
|
+
|
|
258
283
|
// 302 跳转到登录页、401/403 都视为未登录
|
|
259
284
|
if (resp.status === 401 || resp.status === 403) throw new Error('未登录');
|
|
260
285
|
if (resp.status === 302 && resp.headers.location && resp.headers.location.includes('casdoor')) throw new Error('未登录');
|
|
@@ -286,11 +311,10 @@ class BuildCommand {
|
|
|
286
311
|
async triggerBuild(jobName, params) {
|
|
287
312
|
try {
|
|
288
313
|
const url = `${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/buildWithParameters`;
|
|
289
|
-
let headers = {
|
|
314
|
+
let headers = {};
|
|
290
315
|
// 1. 获取 crumb(防止 403)
|
|
291
316
|
try {
|
|
292
|
-
const crumbResp = await axios.get(`${this.jenkinsConfig.baseUrl}crumbIssuer/api/json`, {
|
|
293
|
-
headers,
|
|
317
|
+
const crumbResp = await this.axios.get(`${this.jenkinsConfig.baseUrl}crumbIssuer/api/json`, {
|
|
294
318
|
timeout: 10000
|
|
295
319
|
});
|
|
296
320
|
const { crumb, crumbRequestField } = crumbResp.data;
|
|
@@ -299,7 +323,7 @@ class BuildCommand {
|
|
|
299
323
|
// 如果 crumb 获取失败,继续尝试(部分 Jenkins 关闭了 CSRF 防护)
|
|
300
324
|
}
|
|
301
325
|
// 2. 触发构建
|
|
302
|
-
const response = await axios.post(url, null, {
|
|
326
|
+
const response = await this.axios.post(url, null, {
|
|
303
327
|
headers,
|
|
304
328
|
params: params,
|
|
305
329
|
timeout: 30000
|
|
@@ -328,8 +352,7 @@ class BuildCommand {
|
|
|
328
352
|
let count = 0;
|
|
329
353
|
while (!buildNumber && count < 60) { // 最多等2分钟
|
|
330
354
|
try {
|
|
331
|
-
const response = await axios.get(`${this.jenkinsConfig.baseUrl}queue/item/${queueId}/api/json`, {
|
|
332
|
-
...this.jenkinsConfig.auth,
|
|
355
|
+
const response = await this.axios.get(`${this.jenkinsConfig.baseUrl}queue/item/${queueId}/api/json`, {
|
|
333
356
|
timeout: 10000
|
|
334
357
|
});
|
|
335
358
|
const item = response.data;
|
|
@@ -363,8 +386,7 @@ class BuildCommand {
|
|
|
363
386
|
let lastLogTime = 0;
|
|
364
387
|
while (building) {
|
|
365
388
|
try {
|
|
366
|
-
const response = await axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`, {
|
|
367
|
-
...this.jenkinsConfig.auth,
|
|
389
|
+
const response = await this.axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`, {
|
|
368
390
|
timeout: 10000
|
|
369
391
|
});
|
|
370
392
|
const buildInfo = response.data;
|
|
@@ -378,8 +400,7 @@ class BuildCommand {
|
|
|
378
400
|
const now = Date.now();
|
|
379
401
|
if (now - lastLogTime > 2900) {
|
|
380
402
|
try {
|
|
381
|
-
const logResponse = await axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/consoleText`, {
|
|
382
|
-
...this.jenkinsConfig.auth,
|
|
403
|
+
const logResponse = await this.axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/consoleText`, {
|
|
383
404
|
timeout: 10000
|
|
384
405
|
});
|
|
385
406
|
const log = logResponse.data;
|
package/lib/utils/browser.js
CHANGED
|
@@ -109,8 +109,53 @@ class BrowserAuth {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
112
|
+
* 从 set-cookie 更新 cookies
|
|
113
|
+
* @param {Array<string>} setCookies 响应头中的 set-cookie 数组
|
|
113
114
|
*/
|
|
115
|
+
async updateCookiesFromSetCookie(setCookies) {
|
|
116
|
+
if (!setCookies || !Array.isArray(setCookies)) return;
|
|
117
|
+
|
|
118
|
+
if (!this.cookies) this.cookies = [];
|
|
119
|
+
|
|
120
|
+
let changed = false;
|
|
121
|
+
setCookies.forEach(str => {
|
|
122
|
+
// 简单解析:取第一个分号前的部分作为 name=value
|
|
123
|
+
const firstPart = str.split(';')[0];
|
|
124
|
+
const [name, ...valueParts] = firstPart.split('=');
|
|
125
|
+
const value = valueParts.join('=');
|
|
126
|
+
|
|
127
|
+
if (name && value) {
|
|
128
|
+
const index = this.cookies.findIndex(c => c.name === name);
|
|
129
|
+
if (index !== -1) {
|
|
130
|
+
if (this.cookies[index].value !== value) {
|
|
131
|
+
this.cookies[index].value = value;
|
|
132
|
+
changed = true;
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
this.cookies.push({
|
|
136
|
+
name,
|
|
137
|
+
value,
|
|
138
|
+
domain: new URL(this.jenkinsUrl).hostname,
|
|
139
|
+
path: '/'
|
|
140
|
+
});
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (changed) {
|
|
147
|
+
await this.saveCookies();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 获取 Cookie 字符串
|
|
153
|
+
*/
|
|
154
|
+
getCookieString() {
|
|
155
|
+
if (!this.cookies) return '';
|
|
156
|
+
return this.cookies.map(c => `${c.name}=${c.value}`).join('; ');
|
|
157
|
+
}
|
|
158
|
+
|
|
114
159
|
async ensureLogin() {
|
|
115
160
|
if (this.loadCookies()) {
|
|
116
161
|
console.log(chalk.green('✅ 已加载保存的cookies'));
|