@ts-org/jenkins-cli 3.0.0 → 3.0.2

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 CHANGED
@@ -1,47 +1,66 @@
1
- ## @ts-org/jenkins-cli
1
+ ## Jenkins CLI
2
2
 
3
- 一个轻量的 Jenkins 部署命令行工具:在项目里直接运行 `jenkins-cli`,交互式选择分支和部署环境,然后触发 Jenkins 参数化构建。
3
+ 一个轻量的 Jenkins 部署工具,让你在命令行中通过交互式或非交互式的方式,轻松触发 Jenkins 参数化构建。
4
4
 
5
- ### 安装
5
+ ### ✨ 特性
6
+
7
+ - 交互式 CLI,引导你选择分支和部署环境。
8
+ - 智能触发,自动停止进行中的构建,并复用队列中相同的任务。
9
+ - 灵活的参数化构建,支持在运行时传入额外参数。
10
+ - 丰富的命令集,覆盖 Job、Builds、Queue 等常用操作。
11
+ - 支持 `jenkins-cli.yaml` 或 `package.json` 进行项目级配置。
12
+
13
+ ### 📦 安装
14
+
15
+ ```bash
16
+ # Node.js >= 16
17
+ npm install -g @ts-org/jenkins-cli
18
+
19
+ # Node.js >= 18,推荐使用pnpm
20
+ pnpm install -g @ts-org/jenkins-cli
21
+ ```
22
+
23
+ ### 🚀 使用
24
+
25
+ 在你的项目根目录下执行:
6
26
 
7
27
  ```bash
8
- pnpm i -D @ts-org/jenkins-cli
9
- # or
10
- npm add -D @ts-org/jenkins-cli
28
+ jenkins-cli
11
29
  ```
12
30
 
13
- Node 版本要求:`>= 16`。
31
+ CLI 会自动加载配置,列出 Git 分支和部署环境供你选择。
14
32
 
15
- ### 配置
33
+ ![Demo](https://unpkg.com/@ts-org/jenkins-cli@latest/docs/images/demo.png)
16
34
 
17
- 推荐在项目根目录创建 `jenkins-cli.yaml`:
35
+ ### ⚙️ 配置
36
+
37
+ 在项目根目录创建 `jenkins-cli.yaml`:
18
38
 
19
39
  ```yaml
20
- # Jenkins API Token 格式:http(s)://username:token@host:port
21
- apiToken: http://username:token@jenkins.example.com:8080
40
+ # Jenkins API 地址,格式: http(s)://username:token@host:port
41
+ apiToken: http://user:token@jenkins.example.com
22
42
 
23
- # Jenkins Job 名称
24
- job: your-job-name
43
+ # Jenkins Job 名称 (可被 -j 参数覆盖)
44
+ job: your-project-job
25
45
 
26
- # 部署环境列表(会作为交互选项;触发构建时会传给 Jenkins 参数:mode
46
+ # 部署环境列表 (将作为参数 `mode` 传入 Jenkins)
27
47
  modes:
28
48
  - dev
29
49
  - sit
30
50
  - uat
31
51
  ```
32
52
 
33
- #### `configs`:扩展交互参数(自动带到 Jenkins)
53
+ #### 扩展交互参数
34
54
 
35
- `jenkins-cli.yaml` 里加 `configs`,可以在默认交互命令里多问几项(除了 `branch` `mode` 之外),回答会自动作为 Jenkins 参数一起提交。
55
+ 通过 `configs` 字段,你可以添加自定义的交互式提问,其结果将作为参数传递给 Jenkins
36
56
 
37
- - `type` 基本就是 inquirer 的类型:`input` / `number` / `confirm` / `list` / `rawlist` / `checkbox` / `password`
38
- - `choices` / `default` / `validate` 支持引用本地 JS/TS 文件的导出:
39
- - `src/utils/abc.ts:Fun:hello`:取 `hello()` 的返回值(支持 async
40
- - `src/utils/abc.ts:Var:hello`:取 `hello` 变量值(若是 Promise 会自动 await)
41
- - 简写 `src/utils/abc.ts:hello`:等同于 `Var`
42
- - `name` 不能使用保留字段:`branch` / `modes` / `mode`
57
+ - `type`: 支持 `input`, `list`, `confirm` [Inquirer.js](https://github.com/SBoudrias/Inquirer.js/) 类型。
58
+ - `choices`, `default`, `validate`: 支持从本地 TS/JS 文件动态加载。
59
+ - `path:Fun:funcName`:调用 `funcName()` 函数获取值 (支持 async)。
60
+ - `path:Var:varName`:获取 `varName` 变量的值 (支持 Promise)。
61
+ - `name`: 参数名,注意不要使用 `branch`, `mode`, `modes` 等保留字。
43
62
 
44
- 示例:
63
+ **示例:**
45
64
 
46
65
  ```yaml
47
66
  configs:
@@ -49,223 +68,141 @@ configs:
49
68
  name: version
50
69
  message: '请输入版本号:'
51
70
  default: '1.0.0'
52
-
53
71
  - type: list
54
72
  name: service
55
73
  message: '请选择服务:'
56
- choices: src/utils/abc.ts:Fun:services
57
-
74
+ choices: src/config.ts:Fun:getServices
58
75
  - type: input
59
76
  name: ticket
60
- message: '请输入工单号:'
61
- validate: src/utils/abc.ts:Fun:validateTicket
77
+ message: '请输入关联的工单号:'
78
+ validate: src/config.ts:Fun:validateTicket
62
79
  ```
63
80
 
64
- `src/utils/abc.ts` 例子:
65
-
81
+ 对应的 `src/config.ts`:
66
82
  ```ts
67
- export async function services() {
68
- return ['service1', 'service2']
69
- }
70
-
71
- export function validateTicket(input: unknown) {
72
- const s = String(input ?? '').trim()
73
- if (!s) return 'ticket required'
74
- return true
83
+ export async function getServices() {
84
+ return ['user-center', 'order-service'];
75
85
  }
76
- ```
77
86
 
78
- 也可以写到 `package.json` 里(适合不想额外放 YAML 的项目):
79
-
80
- ```json
81
- {
82
- "jenkins-cli": {
83
- "apiToken": "http://username:token@jenkins.example.com:8080",
84
- "job": "your-job-name",
85
- "modes": ["dev", "sit", "uat"]
86
- }
87
+ export function validateTicket(input: string) {
88
+ return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || '工单号格式不正确';
87
89
  }
88
90
  ```
89
91
 
90
- ### 配置查找优先级
91
-
92
- 从高到低:
92
+ > **提示**: 配置也支持写在 `package.json` 的 `jenkins-cli` 字段里。
93
+ >
94
+ > **查找顺序**: `jenkins-cli.yaml` > `package.json` > 父目录的 `jenkins-cli.yaml`。
93
95
 
94
- 1. 项目根目录的 `jenkins-cli.yaml`
95
- 2. 项目根目录 `package.json` 里的 `jenkins-cli` 字段
96
- 3. 从项目根的父目录开始向上查找到的 `jenkins-cli.yaml`
96
+ ### 🤖 命令参考
97
97
 
98
- 高优先级会覆盖低优先级的同名字段。
98
+ #### `trigger` (非交互式触发)
99
99
 
100
- ### 使用
101
-
102
- 在项目目录执行:
103
-
104
- ```bash
105
- jenkins-cli
106
- ```
107
-
108
- 也可以在 `package.json` 加个脚本:
109
-
110
- ```json
111
- {
112
- "scripts": {
113
- "ship": "jenkins-cli"
114
- }
115
- }
116
- ```
117
-
118
- 交互示例:
119
-
120
- ```text
121
- 🚀 Jenkins CLI - Jenkins Deployment CLI
122
-
123
- ✔ Configuration loaded
124
- ✔ Found 3 branches
125
- ? 请选择要打包的分支: origin/develop
126
- ? 请选择要打包的环境: dev, sit, uat
127
-
128
- ✔ dev - Build triggered successfully
129
- ✔ sit - Build triggered successfully
130
- ✔ uat - Build triggered successfully
131
- ```
132
-
133
- 触发规则补充:
134
-
135
- - 同一个 `mode` 正在跑的构建,会先尝试停止,再触发新的构建
136
- - 如果队列里已经有完全一致的任务(`branch` + `mode` 都相同),会直接复用队列项,不重复触发
137
-
138
- ### 非交互触发(trigger)
139
-
140
- 适合脚本/CI 场景:
100
+ 适用于 CI 或其他脚本化场景。
141
101
 
142
102
  ```bash
103
+ # 触发 dev 环境构建
143
104
  jenkins-cli trigger -b origin/develop -m dev
144
- jenkins-cli trigger -b origin/develop -m dev -m uat
145
- jenkins-cli trigger -b origin/develop -m dev,uat
146
- ```
147
105
 
148
- 另外也支持附加任意参数:
106
+ # 同时触发多个环境
107
+ jenkins-cli trigger -b origin/develop -m dev,sit
149
108
 
150
- ```bash
151
- jenkins-cli trigger -b origin/develop -m dev --param foo=bar --param hello=world
109
+ # 传入额外参数
110
+ jenkins-cli trigger -b origin/develop -m dev --param version=1.2.3 --param force=true
152
111
  ```
153
112
 
154
- ### 命令
113
+ ---
155
114
 
156
- #### builds
115
+ #### `builds` - 构建管理
157
116
 
158
117
  ```bash
159
- # 最近 N 次构建(默认 20)
160
- jenkins-cli builds list -n 20
161
- jenkins-cli builds list -j your-job -n 50
118
+ # 查看最近 20 次构建
119
+ jenkins-cli builds list
162
120
 
163
- # 当前 job 的 running 构建
121
+ # 查看运行中的构建
164
122
  jenkins-cli builds running
165
- jenkins-cli builds running -j your-job
166
123
 
167
- # Jenkins 全局 running 构建(不局限 job)
124
+ # 查看 Jenkins 上所有运行中的构建
168
125
  jenkins-cli builds running -a
169
-
170
- # 停止指定构建
171
- jenkins-cli builds stop 123
172
-
173
- # 先清理该 job 的队列,再停止所有 running
174
- jenkins-cli builds stop --all
175
- jenkins-cli builds stop --all -j your-job
176
126
  ```
177
127
 
178
- #### config
128
+ #### `job` - Job 管理
179
129
 
180
130
  ```bash
181
- # 读取 job 配置(默认 xml)
182
- jenkins-cli config read
131
+ # 列出所有 Job (支持 glob 过滤)
132
+ jenkins-cli job list --search 'project-*'
183
133
 
184
- # 指定 job + 输出 json
185
- jenkins-cli config read -j your-job -f xml
186
- jenkins-cli config read -j your-job -f json
134
+ # 查看当前 Job 信息
135
+ jenkins-cli job info
187
136
  ```
188
137
 
189
- #### job
138
+ #### `log` - 查看日志
190
139
 
191
140
  ```bash
192
- # 列出所有 job(可按名称过滤)
193
- jenkins-cli job list
194
- jenkins-cli job list --search keyword
195
- # 支持 glob
196
- jenkins-cli job list --search 'remote*'
197
- jenkins-cli job list --search remote\\*
198
-
199
- # 查看 job 信息(可输出原始 JSON)
200
- jenkins-cli job info
201
- jenkins-cli job info -j your-job
202
- jenkins-cli job info -j your-job --json
203
- ```
141
+ # 查看指定构建号的日志
142
+ jenkins-cli log 1234
204
143
 
205
- #### log
144
+ # 查看最后一次构建日志
145
+ jenkins-cli log last
206
146
 
207
- ```bash
208
- # 查看构建日志
209
- jenkins-cli log 123
210
- jenkins-cli log 123 -j your-job
147
+ # 查看最后一次构建成功日志
148
+ jenkins-cli log last -s success
211
149
 
212
- # 只看最后 N 行
213
- jenkins-cli log 123 --tail 200
150
+ # 查看最后一次构建失败日志
151
+ jenkins-cli log last -s failed
214
152
 
215
- # 持续 follow
216
- jenkins-cli log 123 -f --interval 1000
153
+ # 仅输出最后 N 行
154
+ jenkins-cli log 1234 -t 200
217
155
  ```
218
156
 
219
- #### params
157
+ #### `stop` - 停止构建
220
158
 
221
159
  ```bash
222
- # 查看 job 参数定义
223
- jenkins-cli params
224
- jenkins-cli params -j your-job
160
+ # 停止一个构建
161
+ jenkins-cli stop 1234
162
+
163
+ # 清理当前 Job 的队列和所有运行中的构建
164
+ jenkins-cli stop -a
165
+
166
+ # 停止 Jenkins 上所有的构建和队列项 (慎用)
167
+ jenkins-cli stop -A
225
168
  ```
226
169
 
227
- #### queue
170
+ #### `queue` - 等待构建队列管理
228
171
 
229
172
  ```bash
230
- # 查看队列(可按 job 过滤)
173
+ # 查看等待构建的队列
231
174
  jenkins-cli queue list
232
- jenkins-cli queue list -j your-job
233
175
 
234
- # 取消队列项
235
- jenkins-cli queue cancel 456
176
+ # 取消一个等待构建队列项
177
+ jenkins-cli queue cancel 5678
236
178
  ```
237
179
 
238
- #### stop
180
+ #### `params` - Job 参数
239
181
 
240
182
  ```bash
241
- # 停止指定构建
242
- jenkins-cli stop 123
243
- jenkins-cli stop 123 -j your-job
244
-
245
- # 清理该 job 的队列 + 停止该 job 下所有 running
246
- jenkins-cli stop -a
247
- jenkins-cli stop -a -j your-job
183
+ # 查看当前 Job 的参数定义
184
+ jenkins-cli params
248
185
  ```
249
186
 
250
- #### trigger
187
+ #### `config` - Job 配置
251
188
 
252
189
  ```bash
253
- # 触发构建(-m 可重复 / 或用逗号分隔)
254
- jenkins-cli trigger -b origin/develop -m dev
255
- jenkins-cli trigger -b origin/develop -m dev,uat
190
+ # 读取当前 Job XML 配置
191
+ jenkins-cli config read
256
192
 
257
- # 额外参数
258
- jenkins-cli trigger -b origin/develop -m dev --param foo=bar
259
- jenkins-cli trigger -b origin/develop -m dev -j your-job
193
+ # 以 JSON 格式输出
194
+ jenkins-cli config read -f json
260
195
  ```
261
196
 
262
- #### whoami
197
+ #### `whoami` - 用户信息
263
198
 
264
199
  ```bash
265
- # 查看当前 Token 对应用户
200
+ # 验证 API Token 并查看当前用户
266
201
  jenkins-cli whoami
267
202
  ```
268
203
 
269
- ### 常见问题
204
+ ### ❓ FAQ
205
+
206
+ **Q: `stop` 或 `queue cancel` 命令返回 403 Forbidden?**
270
207
 
271
- - `stop` / `queue cancel` 遇到 `403 Forbidden`:通常是权限不够(常见需要 `Job -> Cancel`)。如果 Jenkins 开了 CSRF,工具会自动尝试获取 crumb + session cookie;仍失败的话,优先确认你用的是 API Token(不是密码)以及权限配置。
208
+ A: 通常是 Jenkins 用户权限不足 (需要 `Job > Cancel` 权限)。请确保你使用的是 API Token 而非密码,并检查 Jenkins CSRF 设置。工具会自动处理 CSRF,但权限问题需要手动排查。
package/dist/cli.d.ts CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -1,36 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
- import Ve from 'inquirer';
4
- import f from 'chalk';
5
- import De from 'ora';
3
+ import Gt from 'inquirer';
4
+ import g from 'chalk';
5
+ import Ot from 'ora';
6
6
  import { exec } from 'child_process';
7
- import ze, { promisify } from 'util';
8
- import $ from 'fs-extra';
9
- import Pe from 'js-yaml';
10
- import v from 'path';
11
- import qe from 'axios';
12
- import Qe from 'jiti';
7
+ import Ht, { promisify } from 'util';
8
+ import C from 'fs-extra';
9
+ import Et from 'js-yaml';
10
+ import S from 'path';
11
+ import Lt from 'axios';
12
+ import Wt from 'jiti';
13
13
  import { fileURLToPath } from 'url';
14
14
  import { XMLParser } from 'fast-xml-parser';
15
15
 
16
- var K="3.0.0",_="Jenkins deployment CLI";var G=promisify(exec);async function H(){try{let{stdout:n}=await G("git branch --show-current"),e=n.trim();if(!e)throw new Error("Not on any branch (detached HEAD state)");return e}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function V(){try{let{stdout:n}=await G('git branch -r --format="%(refname:short)"');return n.split(`
17
- `).filter(e=>e.includes("/")&&!e.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var A="jenkins-cli.yaml",T="package.json",q="jenkins-cli",Ae="jenkinsCli";function z(n,e=process.cwd()){let t=e;for(;;){let r=v.join(t,n);if($.existsSync(r))return r;let o=v.dirname(t);if(o===t)return null;t=o;}}function Be(n=process.cwd()){return z(A,n)}function Ie(n=process.cwd()){let e=z(T,n);return e?v.dirname(e):null}function Le(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function Y(n){let e=await $.readFile(n,"utf-8"),t=Pe.load(e);if(!t||typeof t!="object")throw new Error(`Invalid config in ${A}: expected a YAML object`);return t}async function Te(n){let e=v.join(n,T);if(!await $.pathExists(e))return {};let t=await $.readFile(e,"utf-8"),r=JSON.parse(t),o=r[q]??r[Ae];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${T} config: "${q}" must be an object`);return o}async function X(){let n=process.cwd(),e=Ie(n)??n,t=v.join(e,A),r={},o=v.dirname(e),s=Be(o);s&&await $.pathExists(s)&&(r=await Y(s));let i=await Te(e);r={...r,...i};let a=await $.pathExists(t)?await Y(t):{};if(r={...r,...a},!Le(r))throw new Error(`Config incomplete or not found: tried ${A} in project root, "${q}" in $
18
- {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:e}}var F={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function h(n){return `job/${n.split("/").map(t=>t.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function U(n){let e=Array.isArray(n)?n.find(o=>o?.parameters):void 0,t=Array.isArray(e?.parameters)?e.parameters:[],r={};for(let o of t)o?.name&&(r[String(o.name)]=o?.value);return r}function Fe(n){try{let t=new URL(n).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<t.length-1;o++)t[o]==="job"&&t[o+1]&&r.push(decodeURIComponent(t[o+1]));return r.join("/")}catch{return ""}}async function Ue(n,e){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",e).toString())).data??{};return {...r,parameters:U(r.actions)}}function Oe(n,e=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>e?`${r.slice(0,e)}...`:r:""}catch{return ""}}function ee(n){let e=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!e)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,t,r,o,s]=e,i=`${t}://${s.replace(/\/$/,"")}/`,a=qe.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function te(n){try{let e=await n.axios.get("crumbIssuer/api/json"),{data:t}=e;t?.crumbRequestField&&t?.crumb&&(n.axios.defaults.headers.common[t.crumbRequestField]=t.crumb,n.crumbForm={field:t.crumbRequestField,value:t.crumb});let r=e.headers["set-cookie"];if(r){let o=Array.isArray(r)?r.map(s=>s.split(";")[0].trim()):[String(r).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=o.join("; ");}}catch{}}async function k(n,e){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(e){let o=e.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function J(n,e){let t=await n.axios.get(`${h(e)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(t.data?.builds)?t.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:U(o.actions)}))}async function ne(n){let e=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),t=Array.isArray(e.data?.computer)?e.data.computer:[],r=[];for(let i of t){let a=Array.isArray(i?.executors)?i.executors:[],l=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let u of [...a,...l]){let c=u?.currentExecutable;c?.url&&r.push({number:Number(c.number),url:String(c.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await Ue(n,i.url),l=Fe(String(a?.url??i.url));return {...a,job:l}}))).filter(i=>i?.building)}async function N(n,e){return await n.axios.post("queue/cancelItem",new URLSearchParams({id:e.toString()}),F),!0}async function C(n,e,t){try{await n.axios.post(`${h(e)}/${t}/stop/`,new URLSearchParams({}),F);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(f.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(f.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${t} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function B(n,e,t){let r="branch",o="mode",s=t[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([k(n,e),J(n,e)]),l=(m,p)=>{let y=m.actions?.find(E=>E.parameters);return y?y.parameters.find(E=>E.name===p)?.value:void 0},u=t[r];if(s&&u){let m=i.find(p=>l(p,o)===s&&l(p,r)===u);if(m)return console.log(`Build already in queue (ID: ${m.id}). Skipping trigger.`),new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let c=!1;if(s)for(let m of a)l(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await C(n,e,m.number),c=!0);c&&await new Promise(m=>setTimeout(m,1e3)),s&&console.log(`Triggering new build for mode=${s}...`);try{return (await n.axios.post(`${h(e)}/buildWithParameters/`,new URLSearchParams({...t}),F)).headers.location||""}catch(m){let p=m?.response?.status,y=Oe(m?.response?.data);if(p===400){let x=[];x.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),x.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let E=y?`
16
+ var _="3.0.2",G="Jenkins deployment CLI";var V=promisify(exec);async function H(){try{let{stdout:n}=await V("git branch --show-current"),t=n.trim();if(!t)throw new Error("Not on any branch (detached HEAD state)");return t}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function Y(){try{let{stdout:n}=await V('git branch -r --format="%(refname:short)"');return n.split(`
17
+ `).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var P="jenkins-cli.yaml",T="package.json",U="jenkins-cli",vt="jenkinsCli";function X(n,t=process.cwd()){let e=t;for(;;){let r=S.join(e,n);if(C.existsSync(r))return r;let o=S.dirname(e);if(o===e)return null;e=o;}}function Nt(n=process.cwd()){return X(P,n)}function Pt(n=process.cwd()){let t=X(T,n);return t?S.dirname(t):null}function Bt(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function z(n){let t=await C.readFile(n,"utf-8"),e=Et.load(t);if(!e||typeof e!="object")throw new Error(`Invalid config in ${P}: expected a YAML object`);return e}async function It(n){let t=S.join(n,T);if(!await C.pathExists(t))return {};let e=await C.readFile(t,"utf-8"),r=JSON.parse(e),o=r[U]??r[vt];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${T} config: "${U}" must be an object`);return o}async function Z(){let n=process.cwd(),t=Pt(n)??n,e=S.join(t,P),r={},o=S.dirname(t),s=Nt(o);s&&await C.pathExists(s)&&(r=await z(s));let i=await It(t);r={...r,...i};let a=await C.pathExists(e)?await z(e):{};if(r={...r,...a},!Bt(r))throw new Error(`Config incomplete or not found: tried ${P} in project root, "${U}" in $
18
+ {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:t}}var O={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function w(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function D(n){let t=Array.isArray(n)?n.find(o=>o?.parameters):void 0,e=Array.isArray(t?.parameters)?t.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function Ft(n){try{let e=new URL(n).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<e.length-1;o++)e[o]==="job"&&e[o+1]&&r.push(decodeURIComponent(e[o+1]));return r.join("/")}catch{return ""}}async function qt(n,t){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",t).toString())).data??{};return {...r,parameters:D(r.actions)}}function Tt(n,t=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>t?`${r.slice(0,t)}...`:r:""}catch{return ""}}function et(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,e,r,o,s]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=Lt.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function nt(n){try{let t=await n.axios.get("crumbIssuer/api/json"),{data:e}=t;e?.crumbRequestField&&e?.crumb&&(n.axios.defaults.headers.common[e.crumbRequestField]=e.crumb,n.crumbForm={field:e.crumbRequestField,value:e.crumb});let r=t.headers["set-cookie"];if(r){let o=Array.isArray(r)?r.map(s=>s.split(";")[0].trim()):[String(r).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=o.join("; ");}}catch{}}async function J(n,t){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(t){let o=t.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function A(n,t){let e=await n.axios.get(`${w(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:D(o.actions)}))}async function B(n){let t=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(t.data?.computer)?t.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],u=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let l of [...a,...u]){let c=l?.currentExecutable;c?.url&&r.push({number:Number(c.number),url:String(c.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await qt(n,i.url),u=Ft(String(a?.url??i.url));return {...a,job:u}}))).filter(i=>i?.building)}async function E(n,t){return await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),O),!0}async function v(n,t,e){try{await n.axios.post(`${w(t)}/${e}/stop/`,new URLSearchParams({}),O);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(g.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(g.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function I(n,t,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([J(n,t),A(n,t)]),u=(m,d)=>{let b=m.actions?.find(R=>R.parameters);return b?b.parameters.find(R=>R.name===d)?.value:void 0},l=e[r];if(s&&l){let m=i.find(d=>u(d,o)===s&&u(d,r)===l);if(m)return console.log(`Build already in queue (ID: ${m.id}). Skipping trigger.`),new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let c=!1;if(s)for(let m of a)u(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await v(n,t,m.number),c=!0);c&&await new Promise(m=>setTimeout(m,1e3)),s&&console.log(`Triggering new build for mode=${s}...`);try{return (await n.axios.post(`${w(t)}/buildWithParameters/`,new URLSearchParams({...e}),O)).headers.location||""}catch(m){let d=m?.response?.status,b=Tt(m?.response?.data);if(d===400){let y=[];y.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),y.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let R=b?`
19
19
  Jenkins response (snippet):
20
- ${y}`:"";throw new Error(`Request failed with status code 400.${E}
21
- ${x.join(`
22
- `)}`)}if(p){let x=y?`
20
+ ${b}`:"";throw new Error(`Request failed with status code 400.${R}
21
+ ${y.join(`
22
+ `)}`)}if(d){let y=b?`
23
23
  Jenkins response (snippet):
24
- ${y}`:"";throw new Error(`Request failed with status code ${p}.${x}`)}throw m}}async function re(n){return (await n.axios.get("whoAmI/api/json")).data}async function oe(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function ie(n,e){return (await n.axios.get(`${h(e)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration]`)).data}async function se(n,e,t){let r=Math.max(1,t?.limit??20);return ((await n.axios.get(`${h(e)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:U(i.actions)}))}async function ae(n,e,t){let r=await n.axios.get(`${h(e)}/${t}/consoleText`,{responseType:"text"});return String(r.data??"")}async function ce(n,e,t,r){let o=Math.max(0,r?.start??0),s=Math.max(200,r?.intervalMs??1e3);for(;;){let i=await n.axios.get(`${h(e)}/${t}/logText/progressiveText?start=${o}`,{responseType:"text"}),a=String(i.data??"");a&&process.stdout.write(a);let l=i.headers["x-text-size"],u=i.headers["x-more-data"],c=l?Number(l):NaN;if(Number.isNaN(c)||(o=c),!(String(u??"").toLowerCase()==="true"))break;await new Promise(p=>setTimeout(p,s));}}async function le(n,e){return ((await n.axios.get(`${h(e)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function ue(n,e){let t=await n.axios.get(`${h(e)}/config.xml`,{responseType:"text"});return String(t.data??"")}async function g(){let n=De("Loading configuration...").start();try{let e=await X();n.succeed("Configuration loaded");let t=ee(e.apiToken);return await te(t),{config:e,jenkins:t}}catch(e){n.fail(f.red(e.message)),process.exit(1);}}var _e=Qe(fileURLToPath(import.meta.url),{interopDefault:!0});async function O(n){return await Promise.resolve(n)}function Ge(n){let e=String(n??"").trim();if(!e)return null;let t=e.split(":");if(t.length<2)return null;if(t.length===2){let[i,a]=t;return !i||!a?null:{file:i,kind:"Var",exportName:a}}let r=t.at(-2),o=t.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let s=t.slice(0,-2).join(":");return s?{file:s,kind:r,exportName:o}:null}function He(n,e){return v.isAbsolute(e)?e:v.join(n,e)}function pe(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function fe(n,e){let t=Ge(e);if(!t)return null;let r=He(n,t.file);if(!await $.pathExists(r))throw new Error(`configs reference not found: ${t.file}`);let o=_e(r),s=pe(o)?o:{default:o},i=t.exportName==="default"?s.default:s[t.exportName];if(i===void 0)throw new Error(`configs reference export not found: ${e}`);return {kind:t.kind,value:i}}async function de(n,e){if(typeof e!="string")return e;let t=await fe(n,e);if(!t)return e;if(t.kind==="Var"){if(typeof t.value=="function"){let o=t.value();return await O(o)}return await O(t.value)}if(typeof t.value!="function")throw new Error(`configs reference expected a function: ${e}`);let r=t.value();return await O(r)}async function ge(n,e){let t=Array.isArray(n)?n:[];if(t.length===0)return [];let r=String(e.projectRoot??"").trim()||process.cwd(),o=[];for(let s of t){if(!pe(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await de(r,i.choices);let a=String(i.type??"").toLowerCase(),l=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof l!="function"&&!Array.isArray(l))if(l==null)i.choices=[];else if(typeof l=="string"||typeof l=="number"||typeof l=="boolean")i.choices=[String(l)];else if(typeof l[Symbol.iterator]=="function")i.choices=Array.from(l).map(c=>String(c));else throw new Error(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof l})`)}if(i.default!==void 0&&(i.default=await de(r,i.default)),i.validate!==void 0){let a=i.validate;if(typeof a=="string"){let l=await fe(r,a);if(!l)i.validate=a;else if(l.kind==="Var"){if(typeof l.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=l.value;}else {if(typeof l.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=(u,c)=>l.value(u,c);}}else typeof a=="function"&&(i.validate=a);}o.push(i);}return o}function be(n,e){let t=new Set(e),r={};for(let[o,s]of Object.entries(n))if(!t.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function ye(){console.log(f.bold.blue(`
24
+ ${b}`:"";throw new Error(`Request failed with status code ${d}.${y}`)}throw m}}async function rt(n){return (await n.axios.get("whoAmI/api/json")).data}async function ot(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function L(n,t){return (await n.axios.get(`${w(t)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration],lastSuccessfulBuild[number,url,building,result,timestamp,duration],lastFailedBuild[number,url,building,result,timestamp,duration]`)).data}async function it(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${w(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:D(i.actions)}))}async function st(n,t,e){let r=await n.axios.get(`${w(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function at(n,t){return ((await n.axios.get(`${w(t)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function ct(n,t){let e=await n.axios.get(`${w(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function f(){let n=Ot("Loading configuration...").start();try{let t=await Z();n.succeed("Configuration loaded");let e=et(t.apiToken);return await nt(e),{config:t,jenkins:e}}catch(t){n.fail(g.red(t.message)),process.exit(1);}}var Kt=Wt(fileURLToPath(import.meta.url),{interopDefault:!0});async function W(n){return await Promise.resolve(n)}function Qt(n){let t=String(n??"").trim();if(!t)return null;let e=t.split(":");if(e.length<2)return null;if(e.length===2){let[i,a]=e;return !i||!a?null:{file:i,kind:"Var",exportName:a}}let r=e.at(-2),o=e.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let s=e.slice(0,-2).join(":");return s?{file:s,kind:r,exportName:o}:null}function _t(n,t){return S.isAbsolute(t)?t:S.join(n,t)}function mt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function dt(n,t){let e=Qt(t);if(!e)return null;let r=_t(n,e.file);if(!await C.pathExists(r))throw new Error(`configs reference not found: ${e.file}`);let o=Kt(r),s=mt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new Error(`configs reference export not found: ${t}`);return {kind:e.kind,value:i}}async function lt(n,t){if(typeof t!="string")return t;let e=await dt(n,t);if(!e)return t;if(e.kind==="Var"){if(typeof e.value=="function"){let o=e.value();return await W(o)}return await W(e.value)}if(typeof e.value!="function")throw new Error(`configs reference expected a function: ${t}`);let r=e.value();return await W(r)}async function pt(n,t){let e=Array.isArray(n)?n:[];if(e.length===0)return [];let r=String(t.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!mt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await lt(r,i.choices);let a=String(i.type??"").toLowerCase(),u=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof u!="function"&&!Array.isArray(u))if(u==null)i.choices=[];else if(typeof u=="string"||typeof u=="number"||typeof u=="boolean")i.choices=[String(u)];else if(typeof u[Symbol.iterator]=="function")i.choices=Array.from(u).map(c=>String(c));else throw new Error(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof u})`)}if(i.default!==void 0&&(i.default=await lt(r,i.default)),i.validate!==void 0){let a=i.validate;if(typeof a=="string"){let u=await dt(r,a);if(!u)i.validate=a;else if(u.kind==="Var"){if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=u.value;}else {if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=(l,c)=>u.value(l,c);}}else typeof a=="function"&&(i.validate=a);}o.push(i);}return o}function ft(n,t){let e=new Set(t),r={};for(let[o,s]of Object.entries(n))if(!e.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function gt(){console.log(g.bold.blue(`
25
25
  \u{1F680} Jenkins CLI - Jenkins Deployment CLI
26
- `));let{config:n,jenkins:e}=await g(),[t,r]=await Promise.all([V(),H()]),o=()=>{console.log(f.yellow(`
27
- \u{1F44B} Cancelled by user`)),process.exit(130);},s,i={};try{process.prependOnceListener("SIGINT",o);let l=await ge(n.configs,{projectRoot:n.projectRoot}),u=new Set(["branch","modes","mode"]);for(let m of l){let p=String(m?.name??"").trim();if(u.has(p))throw new Error(`configs name "${p}" is reserved. Use another name.`)}let c=await Ve.prompt([{type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:t,default:t.indexOf(r)},{type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:n.modes,validate:m=>m.length===0?"You must select at least one environment":!0},...l]);s={branch:String(c.branch??""),modes:c.modes??[]},i=be(c,["branch","modes"]);}catch(l){let u=l,c=u?.message?String(u.message):String(u);((u?.name?String(u.name):"")==="ExitPromptError"||c.toLowerCase().includes("cancelled")||c.toLowerCase().includes("canceled"))&&(console.log(f.yellow(`
28
- \u{1F44B} Cancelled by user`)),process.exit(0)),console.log(f.red(`
29
- \u274C Prompt failed: ${c}
30
- `)),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let a=s.modes.map(async l=>{let u=De(`Triggering build for ${f.yellow(l)}...`).start();try{let c=await B(e,n.job,{...i,branch:s.branch,mode:l});u.succeed(`${f.green(l)} - Triggered! Queue: ${c}`);}catch(c){throw u.fail(`${f.red(l)} - ${c.message}`),c}});try{await Promise.all(a),console.log();}catch{console.log(f.bold.red(`
26
+ `));let{config:n,jenkins:t}=await f(),[e,r]=await Promise.all([Y(),H()]),o=()=>{console.log(g.yellow(`
27
+ \u{1F44B} Cancelled by user`)),process.exit(130);},s,i={};try{process.prependOnceListener("SIGINT",o);let c=await pt(n.configs,{projectRoot:n.projectRoot}),m=new Set(["branch","modes","mode"]);for(let b of c){let y=String(b?.name??"").trim();if(m.has(y))throw new Error(`configs name "${y}" is reserved. Use another name.`)}let d=await Gt.prompt([{type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},{type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:n.modes,validate:b=>b.length===0?"You must select at least one environment":!0},...c]);s={branch:String(d.branch??""),modes:d.modes??[]},i=ft(d,["branch","modes"]);}catch(c){let m=c,d=m?.message?String(m.message):String(m);((m?.name?String(m.name):"")==="ExitPromptError"||d.toLowerCase().includes("cancelled")||d.toLowerCase().includes("canceled"))&&(console.log(g.yellow(`
28
+ \u{1F44B} Cancelled by user`)),process.exit(0)),console.log(g.red(`
29
+ \u274C Prompt failed: ${d}
30
+ `)),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let a=!1,u=()=>{a||(a=!0,console.log());},l=s.modes.map(async c=>{let m=Ot(`Triggering build for ${g.yellow(c)}...`).start();try{let d=await I(t,n.job,{...i,branch:s.branch,mode:c});u(),m.succeed(`${g.green(c)} - Triggered! Queue: ${d}`);}catch(d){throw u(),m.fail(`${g.red(c)} - ${d.message}`),d}});try{await Promise.all(l);}catch{console.log(g.bold.red(`
31
31
  \u274C Some builds failed. Check the output above.
32
- `)),process.exit(1);}}function b(n,e){return (e??"").trim()||n}function w(n,e){let t=Number(n);if(!Number.isInteger(t)||t<0)throw new Error(`${e} must be a non-negative integer`);return t}function we(n){let e=n.command("queue").description("\u961F\u5217\u76F8\u5173\u64CD\u4F5C");e.command("list").description("\u83B7\u53D6\u961F\u5217\u5217\u8868").option("-j, --job <job>","\u4EC5\u663E\u793A\u6307\u5B9A job \u7684\u961F\u5217\u9879\uFF08\u4E0D\u586B\u5219\u663E\u793A\u5168\u90E8\uFF09").action(async t=>{let{config:r,jenkins:o}=await g(),s=t.job?b(r.job,t.job):void 0,i=await k(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),e.command("cancel").description("\u53D6\u6D88\u961F\u5217\u9879").argument("<id>").action(async t=>{let{jenkins:r}=await g(),o=await N(r,w(t,"id"));console.log(o?f.green("Cancelled"):f.red("Cancel failed"));});}function d(n){return {[ze.inspect.custom]:()=>n}}function S(n){let e=String(n??""),t=e.trim();if(!t)return d(f.gray("-"));let r=s=>d(s),o=t.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(t).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",l=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||l?f.cyanBright(e):f.hex("#4EA1FF")(e))}catch{return r(f.hex("#4EA1FF")(e))}return o.startsWith("ws://")||o.startsWith("wss://")?r(f.cyan(e)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(f.magenta(e)):o.startsWith("file://")?r(f.yellow(e)):o.startsWith("mailto:")?r(f.green(e)):o.startsWith("javascript:")||o.startsWith("data:")?r(f.red(e)):t.startsWith("/")||t.startsWith("./")||t.startsWith("../")?r(f.cyan(e)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(t)?r(f.blue(e)):r(e)}function R(n){let e=new Date(n),t=r=>String(r).padStart(2,"0");return `${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}function P(n,e){if(e===!0)return d(f.cyan("BUILDING"));let t=String(n??"").toUpperCase();return d(t?t==="SUCCESS"?f.green(t):t==="ABORTED"?f.gray(t):t==="FAILURE"?f.red(t):t==="UNSTABLE"?f.yellow(t):t==="NOT_BUILT"?f.gray(t):t:"-")}function M(n){let e=String(n??"");if(!e)return d("-");let t=e.endsWith("_anime"),r=t?e.slice(0,-6):e,o=(()=>{switch(r){case"blue":return t?f.blueBright(e):f.blue(e);case"red":return t?f.redBright(e):f.red(e);case"yellow":return t?f.yellowBright(e):f.yellow(e);case"aborted":return f.gray(e);case"disabled":case"notbuilt":case"grey":case"gray":return f.gray(e);default:return e}})();return d(o)}function Xe(n){let e=Object.entries(n??{}).filter(([r])=>r);if(!e.length)return d("-");e.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"}));let t=e.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ");return d(t)}function xe(n){let e=n.command("builds").description("\u6784\u5EFA\u76F8\u5173\u64CD\u4F5C");e.command("list").description("\u83B7\u53D6\u6784\u5EFA\u5217\u8868").option("-j, --job <job>","\u6307\u5B9A job").option("-n, --limit <n>","\u6570\u91CF","20").action(async t=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job),i=await se(o,s,{limit:Number(t.limit)});console.table(i.map(a=>({number:a.number,result:P(a.result,a.building),building:a.building,durationS:Number((Number(a.duration??0)/1e3).toFixed(3)),date:d(R(Number(a.timestamp??0)))})));}),e.command("running").description("\u83B7\u53D6\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-j, --job <job>","\u6307\u5B9A job").option("-a, --all","\u67E5\u8BE2 Jenkins \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA\uFF08\u5FFD\u7565 --job\uFF09").action(async t=>{let{config:r,jenkins:o}=await g(),s=t.all?await ne(o):await J(o,b(r.job,t.job));console.table((s??[]).map(i=>({...t.all?{job:d(String(i.job??""))}:{},number:i.number,date:d(i.timestamp?R(Number(i.timestamp)):"-"),parameters:Xe(i.parameters),url:S(String(i.url??""))})));}),e.command("stop").description("\u53D6\u6D88\u961F\u5217\u5E76\u505C\u6B62\u6784\u5EFA").argument("[id]","build number\uFF08\u4E0D\u586B\u5219\u9700\u914D\u5408 --all\uFF09").option("-j, --job <job>","\u6307\u5B9A job").option("-a, --all","\u5148\u53D6\u6D88\u8BE5 job \u7684\u961F\u5217\u9879\uFF0C\u518D\u505C\u6B62\u8BE5 job \u4E0B\u6240\u6709 running \u7684\u6784\u5EFA").action(async(t,r)=>{let{config:o,jenkins:s}=await g(),i=b(o.job,r.job);if(r.all){let[a,l]=await Promise.all([k(s,i),J(s,i)]),u=(a??[]).map(m=>Number(m.id)).filter(m=>!Number.isNaN(m)),c=(l??[]).map(m=>Number(m.number)).filter(m=>!Number.isNaN(m));for(let m of u)await N(s,m);for(let m of c)console.log(`Stopping build #${m}...`),await C(s,i,m);if(!u.length&&!c.length){console.log(f.yellow("No queue items or running builds"));return}console.log(f.green(`Requested: cancelled ${u.length} queue item(s), stopped ${c.length} running build(s).`));return}if(!t){console.log(f.red("Missing build id. Provide <id> or use --all."));return}await C(s,i,w(t,"id")),console.log(f.green("Stop requested"));});}function je(n){n.command("log").description("\u83B7\u53D6\u6784\u5EFA\u63A7\u5236\u53F0\u65E5\u5FD7").argument("<id>").option("-j, --job <job>","\u6307\u5B9A job").option("--tail <n>","\u4EC5\u8F93\u51FA\u6700\u540E N \u884C").option("-f, --follow","\u6301\u7EED\u8F93\u51FA\uFF08\u76F4\u5230 Jenkins \u8868\u793A\u65E0\u66F4\u591A\u65E5\u5FD7\uFF09").option("--interval <ms>","follow \u62C9\u53D6\u95F4\u9694","1000").action(async(e,t)=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job),i=w(e,"id");if(t.follow){await ce(o,s,i,{intervalMs:Number(t.interval)});return}let a=await ae(o,s,i);if(t.tail){let l=w(t.tail,"tail"),u=a.split(`
33
- `);console.log(u.slice(Math.max(0,u.length-l)).join(`
34
- `));return}process.stdout.write(a);});}function ke(n){n.command("stop").description("\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA / \u6E05\u7406\u961F\u5217").argument("[id]","build number\uFF08\u4E0D\u586B\u5219\u9700\u914D\u5408 -a\uFF09").option("-j, --job <job>","\u6307\u5B9A job").option("-a, --all","\u53D6\u6D88\u8BE5 job \u7684\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u8BE5 job \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async(e,t)=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job);if(t.all){let[i,a]=await Promise.all([k(o,s),J(o,s)]),l=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),u=(a??[]).map(c=>Number(c.number)).filter(c=>!Number.isNaN(c));for(let c of l)await N(o,c);for(let c of u)await C(o,s,c);console.log(f.green(`Requested: cancelled ${l.length} queue item(s), stopped ${u.length} running build(s).`));return}if(!e){console.log(f.red("Missing build id. Provide <id> or use -a/--all."));return}await C(o,s,w(e,"id")),console.log(f.green("Stop requested"));});}function Ce(n){n.command("params").description("\u83B7\u53D6 job \u53C2\u6570\u5B9A\u4E49").option("-j, --job <job>","\u6307\u5B9A job").action(async e=>{let{config:t,jenkins:r}=await g(),o=b(t.job,e.job),s=await le(r,o),i=Math.max(0,...s.map(a=>String(a?.name??"").length));console.table((s??[]).map(a=>({name:d(String(a?.name??"").padEnd(i," ")),type:d(String(a?.type??"")),description:d(String(a?.description??"")),default:d(a?.default===void 0?"-":String(a.default)),choices:d(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Se(n){n.command("whoami").description("\u67E5\u770B\u5F53\u524D Token \u5BF9\u5E94\u7684 Jenkins \u7528\u6237\u4FE1\u606F").action(async()=>{let{jenkins:e}=await g(),t=await re(e),r=t&&typeof t=="object"?t:{value:t},o=(u,c)=>{let m=u.length;if(m>=c)return u;let p=c-m,y=Math.floor(p/2),x=p-y;return `${" ".repeat(y)}${u}${" ".repeat(x)}`},s=r.name??r.fullName??r.id??"",i=s==null?"":String(s),a=Math.max(20,i.length),l={};l.name=d(o(i,a));for(let u of Object.keys(r).sort((c,m)=>c.localeCompare(m,"en"))){if(u==="name")continue;let c=r[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof c=="string"||typeof c=="number")){l[u]=S(String(c));continue}let y=c==null?"":typeof c=="object"?JSON.stringify(c):String(c);l[u]=d(y);}console.table([l]);});}function $e(n){n.command("config").description("\u8BFB\u53D6 Jenkins Job \u914D\u7F6E").command("read").description("\u8BFB\u53D6 job \u914D\u7F6E").option("-j, --job <job>","\u6307\u5B9A job").option("-f, --format <format>","\u8F93\u51FA\u683C\u5F0F\uFF1Axml \u6216 json","xml").action(async t=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job),i=String(t.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new Error(`Invalid format: ${t.format}. Expected: xml or json`);let a=await ue(o,s);if(i==="xml"){process.stdout.write(a);return}let u=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(u,null,2));});}function ve(n){let e=n.command("job").description("Job \u76F8\u5173\u64CD\u4F5C"),t=(r,o)=>{let s=String(o??"").trim();if(!s)return !0;if(!/[*?]/.test(s))return r.includes(s);let l=`^${s.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(l).test(r)}catch{return r.includes(s)}};e.command("list").description("\u83B7\u53D6\u6240\u6709 job").option("--search <keyword>","\u6309\u540D\u79F0\u8FC7\u6EE4\uFF08\u652F\u6301 glob\uFF09").action(async r=>{let{jenkins:o}=await g(),s=await oe(o),i=(r.search??"").trim(),a=i?s.filter(c=>t(String(c.name??""),i)):s;a.sort((c,m)=>String(c?.name??"").localeCompare(String(m?.name??""),"en",{sensitivity:"base"}));let l=Math.max(0,...a.map(c=>String(c?.name??"").length)),u=Math.max(0,...a.map(c=>String(c?.url??"").length));console.table((a??[]).map(c=>({name:d(String(c.name??"").padEnd(l," ")),color:M(c.color),url:S(String(c.url??"").padEnd(u," "))})));}),e.command("info").description("\u83B7\u53D6 job \u4FE1\u606F").option("-j, --job <job>","\u6307\u5B9A job").option("--json","\u8F93\u51FA\u539F\u59CB JSON").action(async r=>{let{config:o,jenkins:s}=await g(),i=b(o.job,r.job),a=await ie(s,i);if(r.json){console.log(JSON.stringify(a,null,2));return}let l=a?.lastBuild,u=a?.lastCompletedBuild,c=Array.isArray(a?.healthReport)?a.healthReport:[];console.table([{name:d(String(a?.name??"")),url:S(String(a?.url??"")),color:M(a?.color),buildable:a?.buildable,inQueue:a?.inQueue,nextBuildNumber:a?.nextBuildNumber,healthScore:c[0]?.score??"-",lastBuild:d(l?.number?`#${l.number}`:"-"),lastResult:P(l?.result,l?.building),lastDurationS:l?.duration===void 0?"-":Number((Number(l.duration)/1e3).toFixed(3)),lastDate:d(l?.timestamp?R(Number(l.timestamp)):"-"),lastCompleted:d(u?.number?`#${u.number}`:"-"),lastCompletedResult:P(u?.result,!1),lastCompletedDate:d(u?.timestamp?R(Number(u.timestamp)):"-")}]);});}function tt(n){return n.split(",").map(e=>e.trim()).filter(Boolean)}function nt(n){let e=n.indexOf("=");if(e<=0)throw new Error(`Invalid --param: "${n}". Expected: key=value`);let t=n.slice(0,e).trim(),r=n.slice(e+1).trim();if(!t)throw new Error(`Invalid --param: "${n}". Key is empty`);return {key:t,value:r}}function Je(n){n.command("trigger").description("\u975E\u4EA4\u4E92\u89E6\u53D1 Jenkins \u6784\u5EFA").requiredOption("-b, --branch <branch>","\u5206\u652F\uFF08\u4F8B\u5982 origin/develop\uFF09").option("-m, --mode <mode>","\u90E8\u7F72\u73AF\u5883\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u6216\u7528\u9017\u53F7\u5206\u9694\uFF1A-m dev -m uat / -m dev,uat\uFF09",(e,t)=>t.concat(tt(e)),[]).option("--param <key=value>","\u989D\u5916\u53C2\u6570\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4F8B\u5982\uFF1A--param foo=bar\uFF09",(e,t)=>t.concat(e),[]).option("-j, --job <job>","\u6307\u5B9A job").action(async e=>{let{config:t,jenkins:r}=await g(),o=b(t.job,e.job),s=String(e.branch??"").trim();if(!s)throw new Error("branch is required");let i=(e.mode??[]).map(String).map(c=>c.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new Error("mode is required. Example: jenkins-cli trigger -b origin/develop -m dev -m uat");let l={};for(let c of e.param??[]){let{key:m,value:p}=nt(String(c));l[m]=p;}console.log();let u=a.map(async c=>{let m=De(`Triggering build for ${f.yellow(c)}...`).start();try{let p=await B(r,o,{...l,branch:s,mode:c});m.succeed(`${f.green(c)} - Triggered! Queue: ${p}`);}catch(p){throw m.fail(`${f.red(c)} - ${p.message}`),p}});try{await Promise.all(u),console.log();}catch{console.log(f.bold.red(`
32
+ `)),process.exit(1);}}function h(n,t){return (t??"").trim()||n}function x(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new Error(`${t} must be a non-negative integer`);return e}function yt(n){let t=n.command("queue").description("\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u76F8\u5173\u64CD\u4F5C");t.command("list").alias("ls").description("\u83B7\u53D6\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u5217\u8868").option("-j, --job <job>","\u4EC5\u663E\u793A\u6307\u5B9A Job \u7684\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879").action(async e=>{let{config:r,jenkins:o}=await f(),s=e.job?h(r.job,e.job):void 0,i=await J(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),t.command("cancel").description("\u53D6\u6D88\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u9879").argument("<id>").action(async e=>{let{jenkins:r}=await f(),o=await E(r,x(e,"id"));console.log(o?g.green("Cancelled"):g.red("Cancel failed"));});}function p(n){return {[Ht.inspect.custom]:()=>n}}function k(n){let t=String(n??""),e=t.trim();if(!e)return p(g.gray("-"));let r=s=>p(s),o=e.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(e).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",u=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||u?g.cyanBright(t):g.hex("#4EA1FF")(t))}catch{return r(g.hex("#4EA1FF")(t))}return o.startsWith("ws://")||o.startsWith("wss://")?r(g.cyan(t)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(g.magenta(t)):o.startsWith("file://")?r(g.yellow(t)):o.startsWith("mailto:")?r(g.green(t)):o.startsWith("javascript:")||o.startsWith("data:")?r(g.red(t)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(g.cyan(t)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(g.blue(t)):r(t)}function $(n){let t=new Date(n),e=r=>String(r).padStart(2,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function N(n,t){if(t===!0)return p(g.cyan("BUILDING"));let e=String(n??"").toUpperCase();return p(e?e==="SUCCESS"?g.green(e):e==="ABORTED"?g.gray(e):e==="FAILURE"?g.red(e):e==="UNSTABLE"?g.yellow(e):e==="NOT_BUILT"?g.gray(e):e:"-")}function M(n){let t=String(n??"");if(!t)return p("-");let e=t.endsWith("_anime"),r=e?t.slice(0,-6):t,o=(()=>{switch(r){case"blue":return e?g.blueBright(t):g.blue(t);case"red":return e?g.redBright(t):g.red(t);case"yellow":return e?g.yellowBright(t):g.yellow(t);case"aborted":return g.gray(t);case"disabled":case"notbuilt":case"grey":case"gray":return g.gray(t);default:return t}})();return p(o)}function Yt(n){let t=Object.entries(n??{}).filter(([r])=>r);if(!t.length)return p("-");t.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"}));let e=t.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ");return p(e)}function ht(n){let t=n.command("builds").description("\u6784\u5EFA\u76F8\u5173\u64CD\u4F5C");t.command("list").alias("ls").description("\u83B7\u53D6\u6784\u5EFA\u5217\u8868").option("-j, --job <job>","\u6307\u5B9A job").option("-n, --limit <n>","\u6570\u91CF","20").action(async e=>{let{config:r,jenkins:o}=await f(),s=h(r.job,e.job),i=await it(o,s,{limit:Number(e.limit)});console.table(i.map(a=>({number:a.number,result:N(a.result,a.building),building:a.building,durationS:Number((Number(a.duration??0)/1e3).toFixed(3)),date:p($(Number(a.timestamp??0)))})));}),t.command("running").description("\u83B7\u53D6\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-j, --job <job>","\u6307\u5B9A job").option("-a, --all","\u67E5\u8BE2 Jenkins \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async e=>{let{config:r,jenkins:o}=await f(),s=e.all?await B(o):await A(o,h(r.job,e.job));console.table((s??[]).map(i=>({...e.all?{job:p(String(i.job??""))}:{},number:i.number,date:p(i.timestamp?$(Number(i.timestamp)):"-"),parameters:Yt(i.parameters),url:k(String(i.url??""))})));});}function wt(n){let t=n.command("log").description("\u83B7\u53D6\u6784\u5EFA\u63A7\u5236\u53F0\u65E5\u5FD7"),e=async(r,o,s)=>{let{jenkins:i}=await f(),a=await st(i,r,o);if(s.tail){let u=x(s.tail,"tail"),l=a.split(`
33
+ `);console.log(l.slice(Math.max(0,l.length-u)).join(`
34
+ `));return}process.stdout.write(a);};t.argument("<id>").option("-j, --job <job>","\u6307\u5B9A job").option("-t, --tail <n>","\u4EC5\u8F93\u51FA\u6700\u540E N \u884C").action(async(r,o)=>{let{config:s}=await f(),i=h(s.job,o.job),a=x(r,"id");await e(i,a,o);}),t.command("last").description("\u83B7\u53D6\u6700\u540E\u4E00\u6B21\u6784\u5EFA\u65E5\u5FD7").option("-j, --job <job>","\u6307\u5B9A job").option("-s, --status <status>","success | failed | any","any").option("-t, --tail <n>","\u4EC5\u8F93\u51FA\u6700\u540E N \u884C").action(async r=>{let{config:o,jenkins:s}=await f(),i=h(o.job,r.job),a=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(a))throw new Error(`Invalid status: ${r.status}. Use success | failed | any.`);let u=await L(s,i),l=a==="success"?u?.lastSuccessfulBuild:a==="failed"?u?.lastFailedBuild:u?.lastBuild,c=Number(l?.number);if(!c){let m=a==="success"?"successful":a==="failed"?"failed":"last";throw new Error(`No ${m} build found for job: ${i}`)}await e(i,c,r);});}function jt(n){n.command("stop").description("\u6E05\u7406\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u4E2D\u7684\u4EFB\u52A1\u5E76\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").argument("[id]","\u6784\u5EFA\u7F16\u53F7").option("-j, --job <job>","\u6307\u5B9A Jenkins Job").option("-a, --all","\u53D6\u6D88\u8BE5 Job \u7684\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u8BE5 Jenkins Job \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-A, --ALL","\u53D6\u6D88 Jenkins \u4E0A\u6240\u6709\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async(t,e)=>{let{config:r,jenkins:o}=await f(),s=h(r.job,e.job);if(e.ALL){let[i,a]=await Promise.all([J(o),B(o)]),u=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),l=(a??[]).map(c=>({number:Number(c.number),job:String(c.job??"")})).filter(c=>c.job&&!Number.isNaN(c.number));for(let c of u)await E(o,c);for(let c of l)await v(o,c.job,c.number);console.log(g.green(`Requested: cancelled ${u.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([J(o,s),A(o,s)]),u=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),l=(a??[]).map(c=>Number(c.number)).filter(c=>!Number.isNaN(c));for(let c of u)await E(o,c);for(let c of l)await v(o,s,c);console.log(g.green(`Requested: cancelled ${u.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!t){console.log(g.red("Missing build id. Provide <id> or use -a/--all."));return}await v(o,s,x(t,"id")),console.log(g.green("Stop requested"));});}function xt(n){n.command("params").description("\u83B7\u53D6 job \u53C2\u6570\u5B9A\u4E49").option("-j, --job <job>","\u6307\u5B9A job").action(async t=>{let{config:e,jenkins:r}=await f(),o=h(e.job,t.job),s=await at(r,o),i=Math.max(0,...s.map(a=>String(a?.name??"").length));console.table((s??[]).map(a=>({name:p(String(a?.name??"").padEnd(i," ")),type:p(String(a?.type??"")),description:p(String(a?.description??"")),default:p(a?.default===void 0?"-":String(a.default)),choices:p(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function kt(n){n.command("whoami").description("\u67E5\u770B\u5F53\u524D Token \u5BF9\u5E94\u7684 Jenkins \u7528\u6237\u4FE1\u606F").action(async()=>{let{jenkins:t}=await f(),e=await rt(t),r=e&&typeof e=="object"?e:{value:e},o=(l,c)=>{let m=l.length;if(m>=c)return l;let d=c-m,b=Math.floor(d/2),y=d-b;return `${" ".repeat(b)}${l}${" ".repeat(y)}`},s=r.name??r.fullName??r.id??"",i=s==null?"":String(s),a=Math.max(20,i.length),u={};u.name=p(o(i,a));for(let l of Object.keys(r).sort((c,m)=>c.localeCompare(m,"en"))){if(l==="name")continue;let c=r[l],m=l.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof c=="string"||typeof c=="number")){u[l]=k(String(c));continue}let b=c==null?"":typeof c=="object"?JSON.stringify(c):String(c);u[l]=p(b);}console.table([u]);});}function Ct(n){n.command("config").description("\u8BFB\u53D6 Jenkins Job \u914D\u7F6E").command("read").description("\u8BFB\u53D6 job \u914D\u7F6E").option("-j, --job <job>","\u6307\u5B9A job").option("-f, --format <format>","\u8F93\u51FA\u683C\u5F0F\uFF1Axml \u6216 json","xml").action(async e=>{let{config:r,jenkins:o}=await f(),s=h(r.job,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new Error(`Invalid format: ${e.format}. Expected: xml or json`);let a=await ct(o,s);if(i==="xml"){process.stdout.write(a);return}let l=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(l,null,2));});}function St(n){let t=n.command("job").description("Job \u76F8\u5173\u64CD\u4F5C"),e=(r,o)=>{let s=String(o??"").trim();if(!s)return !0;if(!/[*?]/.test(s))return r.includes(s);let u=`^${s.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(u).test(r)}catch{return r.includes(s)}};t.command("list").alias("ls").description("\u83B7\u53D6\u6240\u6709 job").option("--search <keyword>","\u6309\u540D\u79F0\u8FC7\u6EE4\uFF08\u652F\u6301 glob\uFF09").action(async r=>{let{jenkins:o}=await f(),s=await ot(o),i=(r.search??"").trim(),a=i?s.filter(c=>e(String(c.name??""),i)):s;a.sort((c,m)=>String(c?.name??"").localeCompare(String(m?.name??""),"en",{sensitivity:"base"}));let u=Math.max(0,...a.map(c=>String(c?.name??"").length)),l=Math.max(0,...a.map(c=>String(c?.url??"").length));console.table((a??[]).map(c=>({name:p(String(c.name??"").padEnd(u," ")),color:M(c.color),url:k(String(c.url??"").padEnd(l," "))})));}),t.command("info").description("\u83B7\u53D6 job \u4FE1\u606F").option("-j, --job <job>","\u6307\u5B9A job").option("--json","\u8F93\u51FA\u539F\u59CB JSON").action(async r=>{let{config:o,jenkins:s}=await f(),i=h(o.job,r.job),a=await L(s,i);if(r.json){console.log(JSON.stringify(a,null,2));return}let u=a?.lastBuild,l=a?.lastCompletedBuild,c=Array.isArray(a?.healthReport)?a.healthReport:[];console.table([{name:p(String(a?.name??"")),url:k(String(a?.url??"")),color:M(a?.color),buildable:a?.buildable,inQueue:a?.inQueue,nextBuildNumber:a?.nextBuildNumber,healthScore:c[0]?.score??"-",lastBuild:p(u?.number?`#${u.number}`:"-"),lastResult:N(u?.result,u?.building),lastDurationS:u?.duration===void 0?"-":Number((Number(u.duration)/1e3).toFixed(3)),lastDate:p(u?.timestamp?$(Number(u.timestamp)):"-"),lastCompleted:p(l?.number?`#${l.number}`:"-"),lastCompletedResult:N(l?.result,!1),lastCompletedDate:p(l?.timestamp?$(Number(l.timestamp)):"-")}]);});}function Zt(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function te(n){let t=n.indexOf("=");if(t<=0)throw new Error(`Invalid --param: "${n}". Expected: key=value`);let e=n.slice(0,t).trim(),r=n.slice(t+1).trim();if(!e)throw new Error(`Invalid --param: "${n}". Key is empty`);return {key:e,value:r}}function Jt(n){n.command("trigger").description("\u975E\u4EA4\u4E92\u89E6\u53D1 Jenkins \u6784\u5EFA").requiredOption("-b, --branch <branch>","\u5206\u652F\uFF08\u4F8B\u5982 origin/develop\uFF09").option("-m, --mode <mode>","\u90E8\u7F72\u73AF\u5883\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u6216\u7528\u9017\u53F7\u5206\u9694\uFF1A-m dev -m uat / -m dev,uat\uFF09",(t,e)=>e.concat(Zt(t)),[]).option("--param <key=value>","\u989D\u5916\u53C2\u6570\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4F8B\u5982\uFF1A--param foo=bar\uFF09",(t,e)=>e.concat(t),[]).option("-j, --job <job>","\u6307\u5B9A job").action(async t=>{let{config:e,jenkins:r}=await f(),o=h(e.job,t.job),s=String(t.branch??"").trim();if(!s)throw new Error("branch is required");let i=(t.mode??[]).map(String).map(d=>d.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new Error("mode is required. Example: jenkins-cli trigger -b origin/develop -m dev -m uat");let u={};for(let d of t.param??[]){let{key:b,value:y}=te(String(d));u[b]=y;}console.log();let l=!1,c=()=>{l||(l=!0,console.log());},m=a.map(async d=>{let b=Ot(`Triggering build for ${g.yellow(d)}...`).start();try{let y=await I(r,o,{...u,branch:s,mode:d});c(),b.succeed(`${g.green(d)} - Triggered! Queue: ${y}`);}catch(y){throw c(),b.fail(`${g.red(d)} - ${y.message}`),y}});try{await Promise.all(m);}catch{console.log(g.bold.red(`
35
35
  \u274C Some builds failed. Check the output above.
36
- `)),process.exit(1);}});}function Ne(n){xe(n),$e(n),ve(n),je(n),Ce(n),we(n),Je(n),ke(n),Se(n);}program.name("jenkins-cli").description(_).version(K,"-v, --version").helpOption("-h, --help").action(ye);Ne(program);program.parse();
36
+ `)),process.exit(1);}});}function $t(n){ht(n),Ct(n),St(n),wt(n),xt(n),yt(n),jt(n),Jt(n),kt(n);}program.name("jenkins-cli").description(G).version(_,"-v, --version").helpOption("-h, --help").action(gt);$t(program);program.parse();
package/dist/index.js CHANGED
@@ -3,19 +3,19 @@ import B from 'js-yaml';
3
3
  import g from 'path';
4
4
  import { exec } from 'child_process';
5
5
  import { promisify } from 'util';
6
- import K from 'axios';
6
+ import _ from 'axios';
7
7
  import R from 'chalk';
8
8
  import Z from 'jiti';
9
9
  import { fileURLToPath } from 'url';
10
10
 
11
- var h="jenkins-cli.yaml",b="package.json",x="jenkins-cli",N="jenkinsCli";function A(e,n=process.cwd()){let t=n;for(;;){let r=g.join(t,e);if(d.existsSync(r))return r;let o=g.dirname(t);if(o===t)return null;t=o;}}function I(e=process.cwd()){return A(h,e)}function F(e=process.cwd()){let n=A(b,e);return n?g.dirname(n):null}function T(e){return !!(e.apiToken&&e.job&&Array.isArray(e.modes)&&e.modes.length>0)}async function P(e){let n=await d.readFile(e,"utf-8"),t=B.load(n);if(!t||typeof t!="object")throw new Error(`Invalid config in ${h}: expected a YAML object`);return t}async function L(e){let n=g.join(e,b);if(!await d.pathExists(n))return {};let t=await d.readFile(n,"utf-8"),r=JSON.parse(t),o=r[x]??r[N];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${b} config: "${x}" must be an object`);return o}async function U(){let e=process.cwd(),n=F(e)??e,t=g.join(n,h),r={},o=g.dirname(n),s=I(o);s&&await d.pathExists(s)&&(r=await P(s));let i=await L(n);r={...r,...i};let a=await d.pathExists(t)?await P(t):{};if(r={...r,...a},!T(r))throw new Error(`Config incomplete or not found: tried ${h} in project root, "${x}" in $
12
- {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:n}}var J=promisify(exec);async function M(){try{let{stdout:e}=await J("git branch --show-current"),n=e.trim();if(!n)throw new Error("Not on any branch (detached HEAD state)");return n}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function D(){try{let{stdout:e}=await J('git branch -r --format="%(refname:short)"');return e.split(`
13
- `).filter(n=>n.includes("/")&&!n.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var $={maxRedirects:0,validateStatus:e=>e>=200&&e<400};function k(e){return `job/${e.split("/").map(t=>t.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function H(e){let n=Array.isArray(e)?e.find(o=>o?.parameters):void 0,t=Array.isArray(n?.parameters)?n.parameters:[],r={};for(let o of t)o?.name&&(r[String(o.name)]=o?.value);return r}function _(e,n=400){try{let r=(typeof e=="string"?e:JSON.stringify(e??"",null,2)).trim();return r?r.length>n?`${r.slice(0,n)}...`:r:""}catch{return ""}}function V(e){let n=e.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!n)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,t,r,o,s]=n,i=`${t}://${s.replace(/\/$/,"")}/`,a=K.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function G(e,n){let r=(await e.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(n){let o=n.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function Y(e,n){let t=await e.axios.get(`${k(n)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(t.data?.builds)?t.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:H(o.actions)}))}async function Q(e,n,t){try{await e.axios.post(`${k(n)}/${t}/stop/`,new URLSearchParams({}),$);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(R.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(R.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${t} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function z(e,n,t){return W(e,n,{branch:t.branch,mode:t.mode})}async function W(e,n,t){let r="branch",o="mode",s=t[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([G(e,n),Y(e,n)]),u=(c,l)=>{let f=c.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===l)?.value:void 0},y=t[r];if(s&&y){let c=i.find(l=>u(l,o)===s&&u(l,r)===y);if(c)return console.log(`Build already in queue (ID: ${c.id}). Skipping trigger.`),new URL(`queue/item/${c.id}/`,e.baseURL).toString()}let p=!1;if(s)for(let c of a)u(c,o)===s&&(console.log(`Stopping running build #${c.number} (mode=${s})...`),await Q(e,n,c.number),p=!0);p&&await new Promise(c=>setTimeout(c,1e3)),s&&console.log(`Triggering new build for mode=${s}...`);try{return (await e.axios.post(`${k(n)}/buildWithParameters/`,new URLSearchParams({...t}),$)).headers.location||""}catch(c){let l=c?.response?.status,f=_(c?.response?.data);if(l===400){let m=[];m.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),m.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let w=f?`
11
+ var h="jenkins-cli.yaml",b="package.json",x="jenkins-cli",N="jenkinsCli";function P(n,e=process.cwd()){let t=e;for(;;){let r=g.join(t,n);if(d.existsSync(r))return r;let o=g.dirname(t);if(o===t)return null;t=o;}}function F(n=process.cwd()){return P(h,n)}function I(n=process.cwd()){let e=P(b,n);return e?g.dirname(e):null}function L(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function A(n){let e=await d.readFile(n,"utf-8"),t=B.load(e);if(!t||typeof t!="object")throw new Error(`Invalid config in ${h}: expected a YAML object`);return t}async function T(n){let e=g.join(n,b);if(!await d.pathExists(e))return {};let t=await d.readFile(e,"utf-8"),r=JSON.parse(t),o=r[x]??r[N];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${b} config: "${x}" must be an object`);return o}async function U(){let n=process.cwd(),e=I(n)??n,t=g.join(e,h),r={},o=g.dirname(e),i=F(o);i&&await d.pathExists(i)&&(r=await A(i));let s=await T(e);r={...r,...s};let a=await d.pathExists(t)?await A(t):{};if(r={...r,...a},!L(r))throw new Error(`Config incomplete or not found: tried ${h} in project root, "${x}" in $
12
+ {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:e}}var J=promisify(exec);async function D(){try{let{stdout:n}=await J("git branch --show-current"),e=n.trim();if(!e)throw new Error("Not on any branch (detached HEAD state)");return e}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function K(){try{let{stdout:n}=await J('git branch -r --format="%(refname:short)"');return n.split(`
13
+ `).filter(e=>e.includes("/")&&!e.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var E={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function k(n){return `job/${n.split("/").map(t=>t.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function H(n){let e=Array.isArray(n)?n.find(o=>o?.parameters):void 0,t=Array.isArray(e?.parameters)?e.parameters:[],r={};for(let o of t)o?.name&&(r[String(o.name)]=o?.value);return r}function V(n,e=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>e?`${r.slice(0,e)}...`:r:""}catch{return ""}}function M(n){let e=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!e)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,t,r,o,i]=e,s=`${t}://${i.replace(/\/$/,"")}/`,a=_.create({baseURL:s,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:s,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function G(n,e){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(e){let o=e.trim(),i=o.split("/").filter(Boolean).pop();return r.filter(s=>{let a=s.task?.name;return a===o||(i?a===i:!1)})}return r}async function Y(n,e){let t=await n.axios.get(`${k(e)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(t.data?.builds)?t.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:H(o.actions)}))}async function Q(n,e,t){try{await n.axios.post(`${k(e)}/${t}/stop/`,new URLSearchParams({}),E);}catch(r){let o=r.response?.status;if(o===403){let i=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),s=/crumb|csrf/i.test(i);console.log(R.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(R.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(s?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":i?`[Hint] Jenkins response: ${i}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${t} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function z(n,e,t){return W(n,e,{branch:t.branch,mode:t.mode})}async function W(n,e,t){let r="branch",o="mode",i=t[o];i&&console.log(`Checking for conflicting builds for mode: ${i}...`);let[s,a]=await Promise.all([G(n,e),Y(n,e)]),u=(c,l)=>{let f=c.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===l)?.value:void 0},y=t[r];if(i&&y){let c=s.find(l=>u(l,o)===i&&u(l,r)===y);if(c)return console.log(`Build already in queue (ID: ${c.id}). Skipping trigger.`),new URL(`queue/item/${c.id}/`,n.baseURL).toString()}let p=!1;if(i)for(let c of a)u(c,o)===i&&(console.log(`Stopping running build #${c.number} (mode=${i})...`),await Q(n,e,c.number),p=!0);p&&await new Promise(c=>setTimeout(c,1e3)),i&&console.log(`Triggering new build for mode=${i}...`);try{return (await n.axios.post(`${k(e)}/buildWithParameters/`,new URLSearchParams({...t}),E)).headers.location||""}catch(c){let l=c?.response?.status,f=V(c?.response?.data);if(l===400){let m=[];m.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),m.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let w=f?`
14
14
  Jenkins response (snippet):
15
15
  ${f}`:"";throw new Error(`Request failed with status code 400.${w}
16
16
  ${m.join(`
17
17
  `)}`)}if(l){let m=f?`
18
18
  Jenkins response (snippet):
19
- ${f}`:"";throw new Error(`Request failed with status code ${l}.${m}`)}throw c}}var ne=Z(fileURLToPath(import.meta.url),{interopDefault:!0});async function C(e){return await Promise.resolve(e)}function te(e){let n=String(e??"").trim();if(!n)return null;let t=n.split(":");if(t.length<2)return null;if(t.length===2){let[i,a]=t;return !i||!a?null:{file:i,kind:"Var",exportName:a}}let r=t.at(-2),o=t.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let s=t.slice(0,-2).join(":");return s?{file:s,kind:r,exportName:o}:null}function re(e,n){return g.isAbsolute(n)?n:g.join(e,n)}function j(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}async function S(e,n){let t=te(n);if(!t)return null;let r=re(e,t.file);if(!await d.pathExists(r))throw new Error(`configs reference not found: ${t.file}`);let o=ne(r),s=j(o)?o:{default:o},i=t.exportName==="default"?s.default:s[t.exportName];if(i===void 0)throw new Error(`configs reference export not found: ${n}`);return {kind:t.kind,value:i}}async function v(e,n){if(typeof n!="string")return n;let t=await S(e,n);if(!t)return n;if(t.kind==="Var"){if(typeof t.value=="function"){let o=t.value();return await C(o)}return await C(t.value)}if(typeof t.value!="function")throw new Error(`configs reference expected a function: ${n}`);let r=t.value();return await C(r)}async function oe(e,n){let t=Array.isArray(e)?e:[];if(t.length===0)return [];let r=String(n.projectRoot??"").trim()||process.cwd(),o=[];for(let s of t){if(!j(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await v(r,i.choices);let a=String(i.type??"").toLowerCase(),u=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof u!="function"&&!Array.isArray(u))if(u==null)i.choices=[];else if(typeof u=="string"||typeof u=="number"||typeof u=="boolean")i.choices=[String(u)];else if(typeof u[Symbol.iterator]=="function")i.choices=Array.from(u).map(p=>String(p));else throw new Error(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof u})`)}if(i.default!==void 0&&(i.default=await v(r,i.default)),i.validate!==void 0){let a=i.validate;if(typeof a=="string"){let u=await S(r,a);if(!u)i.validate=a;else if(u.kind==="Var"){if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=u.value;}else {if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=(y,p)=>u.value(y,p);}}else typeof a=="function"&&(i.validate=a);}o.push(i);}return o}function se(e,n){let t=new Set(n),r={};for(let[o,s]of Object.entries(e))if(!t.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}
19
+ ${f}`:"";throw new Error(`Request failed with status code ${l}.${m}`)}throw c}}var en=Z(fileURLToPath(import.meta.url),{interopDefault:!0});async function C(n){return await Promise.resolve(n)}function tn(n){let e=String(n??"").trim();if(!e)return null;let t=e.split(":");if(t.length<2)return null;if(t.length===2){let[s,a]=t;return !s||!a?null:{file:s,kind:"Var",exportName:a}}let r=t.at(-2),o=t.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let i=t.slice(0,-2).join(":");return i?{file:i,kind:r,exportName:o}:null}function rn(n,e){return g.isAbsolute(e)?e:g.join(n,e)}function v(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function S(n,e){let t=tn(e);if(!t)return null;let r=rn(n,t.file);if(!await d.pathExists(r))throw new Error(`configs reference not found: ${t.file}`);let o=en(r),i=v(o)?o:{default:o},s=t.exportName==="default"?i.default:i[t.exportName];if(s===void 0)throw new Error(`configs reference export not found: ${e}`);return {kind:t.kind,value:s}}async function j(n,e){if(typeof e!="string")return e;let t=await S(n,e);if(!t)return e;if(t.kind==="Var"){if(typeof t.value=="function"){let o=t.value();return await C(o)}return await C(t.value)}if(typeof t.value!="function")throw new Error(`configs reference expected a function: ${e}`);let r=t.value();return await C(r)}async function on(n,e){let t=Array.isArray(n)?n:[];if(t.length===0)return [];let r=String(e.projectRoot??"").trim()||process.cwd(),o=[];for(let i of t){if(!v(i))continue;let s={...i};if(s.choices!==void 0){s.choices=await j(r,s.choices);let a=String(s.type??"").toLowerCase(),u=s.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof u!="function"&&!Array.isArray(u))if(u==null)s.choices=[];else if(typeof u=="string"||typeof u=="number"||typeof u=="boolean")s.choices=[String(u)];else if(typeof u[Symbol.iterator]=="function")s.choices=Array.from(u).map(p=>String(p));else throw new Error(`configs "${String(s.name??"")}" choices must resolve to an array (got ${typeof u})`)}if(s.default!==void 0&&(s.default=await j(r,s.default)),s.validate!==void 0){let a=s.validate;if(typeof a=="string"){let u=await S(r,a);if(!u)s.validate=a;else if(u.kind==="Var"){if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);s.validate=u.value;}else {if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);s.validate=(y,p)=>u.value(y,p);}}else typeof a=="function"&&(s.validate=a);}o.push(s);}return o}function sn(n,e){let t=new Set(e),r={};for(let[o,i]of Object.entries(n))if(!t.has(o)&&i!==void 0){if(i===null){r[o]="";continue}if(Array.isArray(i)){r[o]=i.map(s=>String(s)).join(",");continue}if(typeof i=="object"){try{r[o]=JSON.stringify(i);}catch{r[o]=String(i);}continue}r[o]=String(i);}return r}
20
20
 
21
- export { se as answersToJenkinsParams, V as createJenkinsClient, D as getAllBranches, M as getCurrentBranch, U as loadConfig, oe as resolveExtraQuestions, z as triggerBuild };
21
+ export { sn as answersToJenkinsParams, M as createJenkinsClient, K as getAllBranches, D as getCurrentBranch, U as loadConfig, on as resolveExtraQuestions, z as triggerBuild };
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-org/jenkins-cli",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Jenkins deployment CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,16 +8,6 @@
8
8
  "jenkins-cli": "dist/cli.js",
9
9
  "jc": "dist/cli.js"
10
10
  },
11
- "scripts": {
12
- "build": "tsup",
13
- "dev": "NODE_ENV=development tsup --watch",
14
- "prepublishOnly": "npm run build",
15
- "link:test": "npm run build && npm link",
16
- "format": "prettier . --write",
17
- "format:check": "prettier . --check",
18
- "lint": "eslint .",
19
- "lint:fix": "eslint . --fix"
20
- },
21
11
  "keywords": [
22
12
  "jenkins",
23
13
  "cli",
@@ -34,7 +24,8 @@
34
24
  "files": [
35
25
  "dist",
36
26
  "README.md",
37
- "LICENSE"
27
+ "LICENSE",
28
+ "docs/images/demo.png"
38
29
  ],
39
30
  "dependencies": {
40
31
  "axios": "^1.13.4",
@@ -69,5 +60,14 @@
69
60
  "directories": {
70
61
  "doc": "docs"
71
62
  },
72
- "types": "./dist/index.d.ts"
73
- }
63
+ "types": "./dist/index.d.ts",
64
+ "scripts": {
65
+ "build": "tsup",
66
+ "dev": "NODE_ENV=development tsup --watch",
67
+ "link:test": "npm run build && npm link",
68
+ "format": "prettier . --write",
69
+ "format:check": "prettier . --check",
70
+ "lint": "eslint .",
71
+ "lint:fix": "eslint . --fix"
72
+ }
73
+ }