@ts-org/jenkins-cli 3.1.0 → 3.1.1
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 +15 -21
- package/dist/cli.js +22 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,10 +14,6 @@
|
|
|
14
14
|
### 📦 安装
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
# Node.js >= 16
|
|
18
|
-
npm install -g @ts-org/jenkins-cli
|
|
19
|
-
|
|
20
|
-
# Node.js >= 18,推荐使用pnpm
|
|
21
17
|
pnpm install -g @ts-org/jenkins-cli
|
|
22
18
|
```
|
|
23
19
|
|
|
@@ -74,9 +70,7 @@ export function dynamicJob(answers: Record<string, unknown>) {
|
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
export function dynamicModes(answers: Record<string, unknown>) {
|
|
77
|
-
return Number(answers.buildNumber) > 100
|
|
78
|
-
? ['dev', 'sit', 'uat', 'prod']
|
|
79
|
-
: ['dev', 'sit', 'uat']
|
|
73
|
+
return Number(answers.buildNumber) > 100 ? ['dev', 'sit', 'uat', 'prod'] : ['dev', 'sit', 'uat']
|
|
80
74
|
}
|
|
81
75
|
```
|
|
82
76
|
|
|
@@ -87,8 +81,8 @@ export function dynamicModes(answers: Record<string, unknown>) {
|
|
|
87
81
|
- `type`: 支持 `input`, `number`, `confirm`, `list`, `rawlist`, `checkbox`, `password`等类型。
|
|
88
82
|
- `choices`, `default`, `validate`, `when`, `filter`, `transformer`: 支持从本地 TS/JS 文件动态加载。
|
|
89
83
|
- `path:exportName`:自动判断并使用导出值;如果是函数则调用 (支持 async)。
|
|
90
|
-
- `offset`: 负数可调整提问顺序
|
|
91
|
-
- `-1`:插到 branch 后、modes 前
|
|
84
|
+
- `offset`: 负数可调整提问顺序
|
|
85
|
+
- `-1`:插到 branch 后、modes 前
|
|
92
86
|
- `<= -2`:插到 branch 前
|
|
93
87
|
- `name`: 参数名,注意不要使用 `branch`, `mode`, `modes` 等保留字。
|
|
94
88
|
|
|
@@ -151,24 +145,24 @@ export function validateTicket(input: unknown, answers: Record<string, unknown>)
|
|
|
151
145
|
|
|
152
146
|
**四个函数的完整入参说明**:
|
|
153
147
|
|
|
154
|
-
- `when(answers)`
|
|
155
|
-
- `answers`: 当前已回答的所有答案对象(键为问题的 `name`)。
|
|
148
|
+
- `when(answers)`
|
|
149
|
+
- `answers`: 当前已回答的所有答案对象(键为问题的 `name`)。
|
|
156
150
|
- 返回 `boolean | Promise<boolean>`,用于决定是否显示该问题。
|
|
157
151
|
|
|
158
|
-
- `validate(input, answers)`
|
|
159
|
-
- `input`: 用户当前输入的原始值。
|
|
160
|
-
- `answers`: 当前已回答的所有答案对象。
|
|
152
|
+
- `validate(input, answers)`
|
|
153
|
+
- `input`: 用户当前输入的原始值。
|
|
154
|
+
- `answers`: 当前已回答的所有答案对象。
|
|
161
155
|
- 返回 `true` 表示通过;返回 `string` 作为错误提示;也可返回 `Promise<true | string>`。
|
|
162
156
|
|
|
163
|
-
- `filter(input, answers)`
|
|
164
|
-
- `input`: 用户当前输入的原始值。
|
|
165
|
-
- `answers`: 当前已回答的所有答案对象。
|
|
157
|
+
- `filter(input, answers)`
|
|
158
|
+
- `input`: 用户当前输入的原始值。
|
|
159
|
+
- `answers`: 当前已回答的所有答案对象。
|
|
166
160
|
- 返回转换后的值(可同步或返回 Promise)。
|
|
167
161
|
|
|
168
|
-
- `transformer(input, answers, meta)`
|
|
169
|
-
- `input`: 用户当前输入的原始值。
|
|
170
|
-
- `answers`: 当前已回答的所有答案对象。
|
|
171
|
-
- `meta`: 渲染相关的元信息(例如 `isFinal`),不同版本可能略有差异。
|
|
162
|
+
- `transformer(input, answers, meta)`
|
|
163
|
+
- `input`: 用户当前输入的原始值。
|
|
164
|
+
- `answers`: 当前已回答的所有答案对象。
|
|
165
|
+
- `meta`: 渲染相关的元信息(例如 `isFinal`),不同版本可能略有差异。
|
|
172
166
|
- 返回显示用的字符串,不会修改answers的值(可同步或返回 Promise)。
|
|
173
167
|
|
|
174
168
|
> **提示**: 配置也支持写在 `package.json` 的 `jenkins-cli` 字段里。
|
package/dist/cli.js
CHANGED
|
@@ -2,41 +2,41 @@
|
|
|
2
2
|
import { program, CommanderError } from 'commander';
|
|
3
3
|
import oe from 'inquirer';
|
|
4
4
|
import y from 'chalk';
|
|
5
|
-
import
|
|
5
|
+
import en from 'ora';
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
|
-
import
|
|
7
|
+
import En, { promisify } from 'util';
|
|
8
8
|
import M from 'fs-extra';
|
|
9
|
-
import
|
|
9
|
+
import fn from 'js-yaml';
|
|
10
10
|
import z from 'path';
|
|
11
|
-
import
|
|
11
|
+
import bn from 'axios';
|
|
12
12
|
import { Buffer } from 'buffer';
|
|
13
|
-
import
|
|
13
|
+
import vn from 'jiti';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { promises } from 'fs';
|
|
16
16
|
import { XMLParser } from 'fast-xml-parser';
|
|
17
|
-
import
|
|
17
|
+
import Gn from 'os';
|
|
18
18
|
|
|
19
|
-
var
|
|
20
|
-
\u274C ${
|
|
21
|
-
`)),
|
|
22
|
-
`).filter(
|
|
19
|
+
var Ze="3.1.1",et="Jenkins deployment CLI";var d=class extends Error{hint;example;details;exitCode;constructor(t,e={}){super(t),this.name="UserError",this.hint=e.hint,this.example=e.example,this.details=e.details,this.exitCode=e.exitCode??1;}};function ln(n){return n instanceof d?n:n instanceof CommanderError?new d(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new d(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new d("Unknown error",{example:"jenkins-cli --help"})}function q(n){let t=ln(n);if(console.error(y.red(`
|
|
20
|
+
\u274C ${t.message}
|
|
21
|
+
`)),t.details&&console.error(y.gray(t.details)),t.hint&&console.error(y.yellow(`Hint: ${t.hint}`)),t.example!==""){let e=t.example??"jenkins-cli --help";console.error(y.cyan(`Example: ${e}`));}console.error(y.gray('Run "jenkins-cli --help" for usage.')),process.exitCode=t.exitCode;}var Ee=promisify(exec);async function tt(){try{let{stdout:n}=await Ee("git branch --show-current"),t=n.trim();if(!t)throw new d("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}catch{throw new d("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function nt(){try{let{stdout:n}=await Ee('git branch -r --format="%(refname:short)"');return n.split(`
|
|
22
|
+
`).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new d("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function le(){try{let{stdout:n}=await Ee("git config user.name"),t=n.trim();return t||void 0}catch{return}}var D="jenkins-cli.yaml",me="package.json",ue="jenkins-cli",pn="jenkinsCli";function ot(n,t=process.cwd()){let e=t;for(;;){let r=z.join(e,n);if(M.existsSync(r))return r;let o=z.dirname(e);if(o===e)return null;e=o;}}function dn(n=process.cwd()){return ot(D,n)}function gn(n=process.cwd()){let t=ot(me,n);return t?z.dirname(t):null}function wn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function rt(n){let t=await M.readFile(n,"utf-8"),e=fn.load(t);if(!e||typeof e!="object")throw new d(`Invalid config in ${D}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${D}:
|
|
23
23
|
apiToken: http://user:token@host
|
|
24
24
|
job: my-job
|
|
25
|
-
modes: [dev]`});return e}async function
|
|
25
|
+
modes: [dev]`});return e}async function hn(n){let t=z.join(n,me);if(!await M.pathExists(t))return {};let e=await M.readFile(t,"utf-8"),r=JSON.parse(e),o=r[ue]??r[pn];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new d(`Invalid ${me} config: "${ue}" must be an object.`,{hint:"Use an object for the config section.",example:`"${ue}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function it(){let n=process.cwd(),t=gn(n)??n,e=z.join(t,D),r={},o=z.dirname(t),s=dn(o);s&&await M.pathExists(s)&&(r=await rt(s));let i=await hn(t);r={...r,...i};let a=await M.pathExists(e)?await rt(e):{};if(r={...r,...a},!wn(r))throw new d("Config incomplete or not found.",{hint:`Tried ${D} in project root, "${ue}" in ${me}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${D}:
|
|
26
26
|
apiToken: http://user:token@host
|
|
27
27
|
job: my-job
|
|
28
28
|
modes:
|
|
29
|
-
- dev`});return {...r,projectRoot:
|
|
30
|
-
`);return _(me(s))}function hn(t){let n=[...t.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of n){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],l="",c="";for(let $ of a){let O=$[1]??"",F=_(me($[2]??"")).trim();if(!(!F||F==="..")&&!/all\s+files\s+in\s+zip/i.test(F)){l=O,c=F;break}}if(!l||!c||l==="../"||l==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||l.endsWith("/")?"dir":"file",p=_(l.split("?")[0]??"").replace(/\/+$/,"")||c,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),f=m?_(me(m[1]??"")).trim():"",g=f?Z(f):void 0,w=`${c}|${p}`;r.has(w)||(r.add(w),e.push({name:c,path:p,type:u,modified:g}));}if(e.length)return e;let o=[...t.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(me(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let l=i.endsWith("/")?"dir":"file",c=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${c}`;r.has(u)||(r.add(u),e.push({name:a,path:c,type:l}));}return e}async function Ae(t,n,e){let r=Ne(n,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await t.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),l=a.data??new ArrayBuffer(0),c=Buffer.from(l);if(!c.length)continue;let u=a.headers?.["content-type"];return {data:c,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Be(t,n,e){let r=Ne(n,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await t.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),l=typeof a.data=="string"?a.data:String(a.data??"");if(!l)continue;if(dn(l)){let c=gn(l);if(c.trim())return c;continue}return l}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function We(t,n,e){let r=Ne(n,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let c=(await t.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof c=="string")try{c=JSON.parse(c);}catch{continue}if(c&&typeof c=="object")return c}catch(l){if(i=l,l?.response?.status===404)continue;throw l}try{let a=await t.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let l=hn(a.data);if(l.length)return {children:l}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function bn(t){if(Array.isArray(t))return t;let n=[t?.children,t?.files,t?.childs,t?.items,t?.entries,t?.content,t?.tree];for(let e of n){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return t?.children&&Array.isArray(t.children?.children)?t.children.children:[]}function Ee(t){return bn(t).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,l=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),c=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,p=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:p,size:c,modified:u,type:l}})}function st(t){let n=t.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!n)throw new d("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=n,i=`${e}://${s.replace(/\/$/,"")}/`,a=pn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(c=>{let u=String(c.method??"get").toUpperCase(),p=new URL(String(c.url??""),c.baseURL??i).toString(),m=c.params,f=c.data;f instanceof URLSearchParams?f=Object.fromEntries(f):typeof f=="string"?String(c.headers?.["Content-Type"]??c.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?f=Object.fromEntries(new URLSearchParams(f)):f=f.trim()?f:void 0:Buffer.isBuffer(f)&&(f=`[Buffer ${f.length} bytes]`);let g={method:u,url:p};return m&&Object.keys(m).length>0&&(g.params=m),f!=null&&f!==""&&(g.data=f),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),c}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function at(t,n=5e3){try{let e=await t.axios.get("crumbIssuer/api/json",{timeout:n}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(t.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,t.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];t.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ue(t){return t.crumbPromise||(t.crumbPromise=at(t,5e3)),t.crumbPromise}async function E(t){await Ue(t),t.crumbForm||(t.crumbPromise=at(t,15e3),await t.crumbPromise);}async function H(t,n){let r=(await t.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 ee(t,n){let e=await t.axios.get(`${x(n)}/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:pe(o.actions)}))}async function fe(t){let n=await t.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(n.data?.computer)?n.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],l=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let c of [...a,...l]){let u=c?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.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 fn(t,i.url),l=ot(String(a?.url??i.url));return {...a,job:l}}))).filter(i=>i?.building)}async function te(t,n){return await E(t),await t.axios.post("queue/cancelItem",new URLSearchParams({id:n.toString()}),W),!0}async function ne(t,n,e){try{await E(t),await t.axios.post(`${x(n)}/${e}/stop/`,new URLSearchParams({}),W);}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(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),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 Fe(t,n,e){await E(t);let r=`view/${encodeURIComponent(n)}/addJobToView`;try{await t.axios.post(r,new URLSearchParams({name:e}),W);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function Le(t,n,e){await E(t);let r=`view/${encodeURIComponent(n)}/removeJobFromView`;try{await t.axios.post(r,new URLSearchParams({name:e}),W);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function ct(t,n){await E(t);let e=new URLSearchParams({name:n,mode:"hudson.model.ListView",json:JSON.stringify({name:n,mode:"hudson.model.ListView"})});try{await t.axios.post("createView",e,W);}catch(r){let o=r?.response?.status;throw o===400?new d(`View already exists: ${n}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new d(`Forbidden to create view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function lt(t,n){await E(t);let e=`view/${encodeURIComponent(n)}/doDelete`;try{await t.axios.post(e,new URLSearchParams({}),W);}catch(r){let o=r?.response?.status;throw o===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new d(`Forbidden to delete view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function de(t,n,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([H(t,n),ee(t,n)]),l=(p,m)=>{let f=p.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===m)?.value:void 0},c=e[r];if(s&&c){let p=i.find(m=>l(m,o)===s&&l(m,r)===c);if(p)return new URL(`queue/item/${p.id}/`,t.baseURL).toString()}let u=!1;if(s)for(let p of a)l(p,o)===s&&(console.log(`Stopping running build #${p.number} (mode=${s})...`),await ne(t,n,p.number),u=!0);u&&await new Promise(p=>setTimeout(p,1e3));try{return await E(t),(await t.axios.post(`${x(n)}/buildWithParameters/`,new URLSearchParams({...e}),W)).headers.location||""}catch(p){let m=p?.response?.status,f=it(p?.response?.data);if(m===400){let g=f?`Jenkins response (snippet):
|
|
31
|
-
${
|
|
32
|
-
${
|
|
33
|
-
${o}`:void 0})}throw e}}async function
|
|
29
|
+
- dev`});return {...r,projectRoot:t}}var B={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function x(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function pe(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 st(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 yn(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:pe(r.actions)}}function at(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 X(n){if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){let t=Number(n);if(Number.isFinite(t))return t}}function Z(n){let t=X(n);if(t!==void 0)return t;if(typeof n=="string"){let e=Date.parse(n);if(!Number.isNaN(e))return e}}function N(n){return n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/")}function We(n,t){let e=n.replace(/\/+$/,""),r=t.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function _(n){return n.replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function fe(n){return n.replace(/<[^>]*>/g,"")}function kn(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function xn(n){let t=n.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i),e=n.match(/<code[^>]*>([\s\S]*?)<\/code>/i),r=n.match(/<body[^>]*>([\s\S]*?)<\/body>/i),s=(t?.[1]??e?.[1]??r?.[1]??n).replace(/<br\s*\/?>/gi,`
|
|
30
|
+
`);return _(fe(s))}function jn(n){let t=[...n.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of t){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],c="",l="";for(let S of a){let L=S[1]??"",R=_(fe(S[2]??"")).trim();if(!(!R||R==="..")&&!/all\s+files\s+in\s+zip/i.test(R)){c=L,l=R;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",f=_(c.split("?")[0]??"").replace(/\/+$/,"")||l,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),p=m?_(fe(m[1]??"")).trim():"",g=p?Z(p):void 0,b=`${l}|${f}`;r.has(b)||(r.add(b),e.push({name:l,path:f,type:u,modified:g}));}if(e.length)return e;let o=[...n.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(fe(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let c=i.endsWith("/")?"dir":"file",l=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${l}`;r.has(u)||(r.add(u),e.push({name:a,path:l,type:c}));}return e}async function Ue(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),c=a.data??new ArrayBuffer(0),l=Buffer.from(c);if(!l.length)continue;let u=a.headers?.["content-type"];return {data:l,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Le(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),c=typeof a.data=="string"?a.data:String(a.data??"");if(!c)continue;if(kn(c)){let l=xn(c);if(l.trim())return l;continue}return c}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Te(n,t,e){let r=We(t,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let l=(await n.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof l=="string")try{l=JSON.parse(l);}catch{continue}if(l&&typeof l=="object")return l}catch(c){if(i=c,c?.response?.status===404)continue;throw c}try{let a=await n.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let c=jn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Cn(n){if(Array.isArray(n))return n;let t=[n?.children,n?.files,n?.childs,n?.items,n?.entries,n?.content,n?.tree];for(let e of t){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return n?.children&&Array.isArray(n.children?.children)?n.children.children:[]}function Oe(n){return Cn(n).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,c=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),l=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,f=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:f,size:l,modified:u,type:c}})}function ct(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new d("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=bn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(l=>{let u=String(l.method??"get").toUpperCase(),f=new URL(String(l.url??""),l.baseURL??i).toString(),m=l.params,p=l.data;p instanceof URLSearchParams?p=Object.fromEntries(p):typeof p=="string"?String(l.headers?.["Content-Type"]??l.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?p=Object.fromEntries(new URLSearchParams(p)):p=p.trim()?p:void 0:Buffer.isBuffer(p)&&(p=`[Buffer ${p.length} bytes]`);let g={method:u,url:f};return m&&Object.keys(m).length>0&&(g.params=m),p!=null&&p!==""&&(g.data=p),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),l}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function lt(n,t=5e3){try{let e=await n.axios.get("crumbIssuer/api/json",{timeout:t}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(n.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,n.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ve(n){return n.crumbPromise||(n.crumbPromise=lt(n,5e3)),n.crumbPromise}async function W(n){await Ve(n),n.crumbForm||(n.crumbPromise=lt(n,15e3),await n.crumbPromise);}async function H(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 ee(n,t){let e=await n.axios.get(`${x(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:pe(o.actions)}))}async function de(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:[],c=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let l of [...a,...c]){let u=l?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.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 yn(n,i.url),c=st(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function te(n,t){return await W(n),await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),B),!0}async function ne(n,t,e){try{await W(n),await n.axios.post(`${x(t)}/${e}/stop/`,new URLSearchParams({}),B);}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(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),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 Ie(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/addJobToView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function qe(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/removeJobFromView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function ut(n,t){await W(n);let e=new URLSearchParams({name:t,mode:"hudson.model.ListView",json:JSON.stringify({name:t,mode:"hudson.model.ListView"})});try{await n.axios.post("createView",e,B);}catch(r){let o=r?.response?.status;throw o===400?new d(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new d(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function mt(n,t){await W(n);let e=`view/${encodeURIComponent(t)}/doDelete`;try{await n.axios.post(e,new URLSearchParams({}),B);}catch(r){let o=r?.response?.status;throw o===404?new d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new d(`Forbidden to delete view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ge(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([H(n,t),ee(n,t)]),c=(f,m)=>{let p=f.actions?.find(b=>b.parameters);return p?p.parameters.find(b=>b.name===m)?.value:void 0},l=e[r];if(s&&l){let f=i.find(m=>c(m,o)===s&&c(m,r)===l);if(f)return new URL(`queue/item/${f.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let f of a)c(f,o)===s&&(console.log(`Stopping running build #${f.number} (mode=${s})...`),await ne(n,t,f.number),u=!0);u&&await new Promise(f=>setTimeout(f,1e3));try{return await W(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),B)).headers.location||""}catch(f){let m=f?.response?.status,p=at(f?.response?.data);if(m===400){let g=p?`Jenkins response (snippet):
|
|
31
|
+
${p}`:void 0;throw new d("Jenkins rejected the request (400).",{hint:`Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs). This CLI sends parameters "${r}" and "${o}".`,example:"jenkins-cli build -b origin/main -m dev",details:g})}if(m){let g=p?`Jenkins response (snippet):
|
|
32
|
+
${p}`:void 0;throw new d(`Request failed with status code ${m}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:g})}throw f}}async function ft(n){return (await n.axios.get("whoAmI/api/json")).data}async function T(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function pt(n,t,e){let r=String(t??"").trim();if(!r)return;let o=Math.max(1,e?.depth??5),s="jobs[name,url]";for(let u=0;u<o;u+=1)s=`jobs[name,url,${s}]`;let i=await n.axios.get(`api/json?tree=${encodeURIComponent(s)}`),a=Array.isArray(i.data?.jobs)?i.data.jobs:[],c=[],l=[...a];for(;l.length;){let u=l.shift();if(!u)continue;let f=String(u.name??"").trim();if(f&&f===r&&u.url){let m=st(String(u.url));m&&c.push(m);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function we(n,t){try{let r=(await n.axios.get(`${x(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??{},o=typeof r?.fullName=="string"?r.fullName:"",s=typeof r?.name=="string"?r.name:"",i=String(t??"").trim();if(!o&&!s)throw new d(`Job not found: ${i}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli job show -j my-job"});if(i&&i!==o&&i!==s)throw new d(`Job not found: ${i}`,{hint:`Jenkins returned job "${o||s}".`,example:"jenkins-cli console last -j my-job -s success"});return r}catch(e){if(e?.response?.status===404){let o=String(t??"").trim();throw new d(`Job not found: ${o}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli console last -j my-job -s success"})}throw e}}async function dt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(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:pe(i.actions)}))}async function gt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,timestamp,changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,timestamp:i.timestamp,url:i.url,changeSet:i.changeSet??{}}))}async function he(n,t,e){let o=(await n.axios.get(`${x(t)}/${e}/api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,description,actions[parameters[name,value]],changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]],culprits[fullName],executor[number,progress]`)).data??{};return {...o,parameters:pe(o.actions)}}async function wt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function ht(n,t){return ((await n.axios.get(`${x(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 De(n,t,e){let r=String(e?.path??"").trim(),o=r?`${N(r)}/`:"",s=await Te(n,x(t),o);return Oe(s??{})}async function bt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${N(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await Te(n,i,s);return Oe(a??{})}async function yt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${N(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await Te(n,i,s);return Oe(a??{})}async function kt(n,t,e){let r=N(String(e??"").trim());return await Le(n,x(t),r)}async function xt(n,t,e){let r=N(String(e??"").trim());return await Ue(n,x(t),r)}async function jt(n,t,e,r){let o=N(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Le(n,s,o)}async function Ct(n,t,e,r){let o=N(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ue(n,s,o)}async function St(n,t,e,r){let o=N(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Le(n,s,o)}async function $t(n,t,e,r){let o=N(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ue(n,s,o)}async function vt(n,t){try{return await W(n),await n.axios.post(`${x(t)}/doWipeOutWorkspace`,new URLSearchParams({}),B),!0}catch(e){let r=e?.response?.status;if(r){let o=at(e?.response?.data);throw new d(`Workspace wipe failed (HTTP ${r}).`,{hint:"Check Job/Workspace permission or CSRF settings.",example:"jenkins-cli workspace wipe -j my-job",details:o?`Jenkins response (snippet):
|
|
33
|
+
${o}`:void 0})}throw e}}async function Me(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Jt(n,t,e,r){let o=`${x(t)}/${e}/logText/progressiveText`,s=new URLSearchParams;s.append("start",String(r));let i=await n.axios.post(o,s,{responseType:"text"}),a=String(i.data??""),c=i.headers["x-text-size"],l=i.headers["x-more-data"],u=c?Number(c):r+Buffer.byteLength(a);return {text:a,newOffset:u,hasMoreData:l==="true"}}async function re(n,t){let e=t?.raw?"views[*]":"views[name,url]";return (await n.axios.get(`api/json?tree=${e}`)).data.views??[]}async function Q(n,t){let e=`view/${encodeURIComponent(t)}`;try{return (await n.axios.get(`${e}/api/json?tree=jobs[name,url,color]`)).data.jobs??[]}catch(r){throw r?.response?.status===404?new d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Rt(n,t){let e=String(t??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await re(n,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await Q(n,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function h(){let n=en("Loading configuration...").start();try{let t=await it();n.succeed("Configuration loaded");let e=ct(t.apiToken);return Ve(e),{config:t,jenkins:e}}catch(t){q(t),process.exit(1);}}var Rn=vn(fileURLToPath(import.meta.url),{interopDefault:!0});async function ye(n){return await Promise.resolve(n)}function Pn(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[s,i]=e;return !s||!i?null:{file:s,exportName:i}}let r=e.at(-1);if(!r)return null;let o=e.slice(0,-2).join(":");return o?{file:o,exportName:r}:null}function An(n,t){return z.isAbsolute(t)?t:z.join(n,t)}function Nt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function ke(n,t){let e=Pn(t);if(!e)return null;let r=An(n,e.file);if(!await M.pathExists(r))throw new d(`configs reference not found: ${e.file}`,{hint:"Check the file path (relative to project root) and file name.",example:'configs: [{ choices: "./scripts/prompts.ts:choices" }]'});let o=Rn(r),s=Nt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new d(`configs reference export not found: ${t}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function Et(n,t){if(typeof t!="string")return {isRef:!1,isFunction:!1,value:t};let e=await ke(n,t);return e?{isRef:!0,isFunction:typeof e.value=="function",value:e.value}:{isRef:!1,isFunction:!1,value:t}}async function be(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(!e.allow(r.value))throw new d(e.error(t),{hint:e.hint,example:e.example});return r.value}async function At(n,t){if(typeof t!="string")return t;let e=await ke(n,t);if(!e)return t;if(typeof e.value=="function"){let r=e.value();return await ye(r)}return await ye(e.value)}async function O(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(typeof r.value=="function"){let o=r.value(e);return await ye(o)}return await ye(r.value)}async function xe(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(!Nt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await At(r,i.choices);let a=String(i.type??"").toLowerCase(),c=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof c!="function"&&!Array.isArray(c))if(c==null)i.choices=[];else if(typeof c=="string"||typeof c=="number"||typeof c=="boolean")i.choices=[String(c)];else if(typeof c[Symbol.iterator]=="function")i.choices=Array.from(c).map(u=>String(u));else throw new d(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof c}).`,{hint:"Return an array for list/rawlist/checkbox choices.",example:'export const choices = ["dev", "uat"]'})}i.default!==void 0&&(i.default=await At(r,i.default)),i.when!==void 0&&(i.when=await be(r,i.when,{allow:a=>typeof a=="function"||typeof a=="boolean",error:a=>`when reference must be a function or boolean: ${a}`,hint:"Export a function or boolean from the referenced file.",example:'export const when = (answers) => answers.env === "dev"'})),i.validate!==void 0&&(i.validate=await be(r,i.validate,{allow:a=>typeof a=="function",error:a=>`validate reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:'export const validate = (input) => !!input || "required"'})),i.filter!==void 0&&(i.filter=await be(r,i.filter,{allow:a=>typeof a=="function",error:a=>`filter reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const filter = (input) => input.trim()"})),i.transformer!==void 0&&(i.transformer=await be(r,i.transformer,{allow:a=>typeof a=="function",error:a=>`transformer reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const transformer = (input) => input.toUpperCase()"})),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 Bt(n,t,e={}){let r=(t??"").trim();if(r)return r;let o=e.projectRoot??process.cwd(),s=await O(o,n,e.answers??{});return String(s??"").trim()||n}function E(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new d(`${t} must be a non-negative integer.`,{hint:`Provide a valid ${t}.`,example:`jenkins-cli console ${t==="id"?"123":"last -t 100"}`});return e}function je(n,t){let e=String(t??"").trim();if(!e)return !0;if(!/[*?]/.test(e))return n.includes(e);let s=`^${e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(s).test(n)}catch{return n.includes(e)}}function Ce(n){let t=n,e=t?.name?String(t.name):"",r=t?.code?String(t.code):"",o=t?.message?String(t.message):String(t??"");if(e==="ExitPromptError"||e==="AbortError"||e==="PromptAbortError"||r==="ABORT_ERR")return !0;let s=o.toLowerCase();return s.includes("cancelled")||s.includes("canceled")}async function Wt(){console.log(y.bold.blue(`
|
|
34
34
|
\u{1F680} Jenkins CLI - Jenkins Deployment CLI
|
|
35
|
-
`));let{config:
|
|
36
|
-
\u{1F44B} Cancelled by user`)),process.exit(130);},s,i=t.job,a={};try{process.prependOnceListener("SIGINT",o);let f=await Nt(t.configs,{projectRoot:t.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let S of f){let I=String(S?.name??"").trim();if(g.has(I))throw new d(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let w={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},$=[],O=[],F=[];for(let S of f){let I=Number(S.offset);if(Number.isFinite(I)&&I<=-2){$.push(S);continue}if(I===-1){O.push(S);continue}F.push(S);}let C={};Object.assign(C,await oe.prompt($,C)),Object.assign(C,await oe.prompt([w],C)),Object.assign(C,await oe.prompt(O,C));let Qe=String(t.projectRoot??"").trim()||process.cwd(),G=await Ie(Qe,t.modes,C),K=[];if(Array.isArray(G))K=G.map(S=>String(S).trim()).filter(Boolean);else if(G!=null){let S=String(G).trim();S&&(K=[S]);}K.length===0&&Array.isArray(t.modes)&&(K=t.modes);let Zt={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:S=>S.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([Zt],C)),Object.assign(C,await oe.prompt(F,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=At(C,["branch","modes"]);let en=await Ie(Qe,t.job,C),Ge=String(en??"").trim();Ge&&(i=Ge);}catch(f){let g=f,w=g?.message?String(g.message):String(g),$=g?.name?String(g.name):"";($==="ExitPromptError"||w.toLowerCase().includes("cancelled")||w.toLowerCase().includes("canceled"))&&(console.log(y.yellow(`
|
|
37
|
-
\u{1F44B} Cancelled by user`)),process.exit(0)),q(new d(`Prompt failed: ${w}`,{hint:$?`Error: ${$}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let l=!1,c=()=>{l||(l=!0,console.log());},u=await ce();u&&!("triggered_by"in a)&&(a.triggered_by=u);let m=s.modes.map(async f=>{let g=Qt(`Triggering build for ${y.yellow(f)}...`).start();try{let w=await de(n,i,{...a,branch:s.branch,mode:f});c(),g.succeed(`${y.green(f)} - Triggered! Queue: ${w}`);}catch(w){throw c(),g.fail(`${y.red(f)} - ${w.message}`),w}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
|
|
35
|
+
`));let{config:n,jenkins:t}=await h(),[e,r]=await Promise.all([nt(),tt()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let p=await xe(n.configs,{projectRoot:n.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let $ of p){let I=String($?.name??"").trim();if(g.has(I))throw new d(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let b={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},S=[],L=[],R=[];for(let $ of p){let I=Number($.offset);if(Number.isFinite(I)&&I<=-2){S.push($);continue}if(I===-1){L.push($);continue}R.push($);}let C={};Object.assign(C,await oe.prompt(S,C)),Object.assign(C,await oe.prompt([b],C)),Object.assign(C,await oe.prompt(L,C));let Ke=String(n.projectRoot??"").trim()||process.cwd(),G=await O(Ke,n.modes,C),K=[];if(Array.isArray(G))K=G.map($=>String($).trim()).filter(Boolean);else if(G!=null){let $=String(G).trim();$&&(K=[$]);}K.length===0&&Array.isArray(n.modes)&&(K=n.modes);let sn={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:$=>$.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([sn],C)),Object.assign(C,await oe.prompt(R,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=Ft(C,["branch","modes"]);let an=await O(Ke,n.job,C),Ye=String(an??"").trim();Ye&&(i=Ye);}catch(p){let g=p,b=g?.message?String(g.message):String(g),S=g?.name?String(g.name):"";Ce(p)&&process.exit(0),q(new d(`Prompt failed: ${b}`,{hint:S?`Error: ${S}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let c=!1,l=()=>{c||(c=!0,console.log());},u=await le();u&&!("triggered_by"in a)&&(a.triggered_by=u);let m=s.modes.map(async p=>{let g=en(`Triggering build for ${y.yellow(p)}...`).start();try{let b=await ge(t,i,{...a,branch:s.branch,mode:p});l(),g.succeed(`${y.green(p)} - Triggered! Queue: ${b}`);}catch(b){throw l(),g.fail(`${y.red(p)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
|
|
38
36
|
\u274C Some builds failed. Check the output above.
|
|
39
|
-
`)),process.exit(1);}}function
|
|
40
|
-
|
|
37
|
+
`)),process.exit(1);}}function Ut(n){let t=n.command("queue").description("Operations for the build queue");t.command("list").alias("ls").description("List items waiting in the build queue").option("-j, --job <job>","Show only queue items for the specified job").action(async e=>{let{config:r,jenkins:o}=await h(),s=e.job?await Bt(r.job,e.job,{projectRoot:r.projectRoot}):void 0,i=await H(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),t.command("cancel").description("Cancel a queued build item").argument("<id...>","Queue id(s)").action(async e=>{let{jenkins:r}=await h(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=E(String(a),"id");try{return await te(r,c),{id:c,ok:!0}}catch(l){return {id:c,ok:!1,message:String(l?.message??l)}}})),i=s.filter(a=>!a.ok);if(i.length===0){console.log(y.green(`Cancelled ${s.length} queue item(s).`));return}console.log(y.yellow(`Cancelled ${s.length-i.length} item(s), failed ${i.length} item(s).`));for(let a of i)console.log(y.red(`- ${a.id}: ${a.message??"Cancel failed"}`));});}function w(n){return {[En.inspect.custom]:()=>n}}function U(n,t){return String(n??"").padEnd(t," ")}function j(n){return Math.max(0,...n.map(t=>String(t??"").length))}function v(n,t){return w(U(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return w(y.gray("-"));let r=s=>w(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",c=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||c?y.cyanBright(t):y.hex("#4EA1FF")(t))}catch{return r(y.hex("#4EA1FF")(t))}return o.startsWith("ws://")||o.startsWith("wss://")?r(y.cyan(t)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(y.magenta(t)):o.startsWith("file://")?r(y.yellow(t)):o.startsWith("mailto:")?r(y.green(t)):o.startsWith("javascript:")||o.startsWith("data:")?r(y.red(t)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(y.cyan(t)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(y.blue(t)):r(t)}function P(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 se(n,t){if(t===!0)return w(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return w(e?e==="SUCCESS"?y.green(e):e==="ABORTED"?y.gray(e):e==="FAILURE"?y.red(e):e==="UNSTABLE"?y.yellow(e):e==="NOT_BUILT"?y.gray(e):e:"-")}function ae(n){let t=String(n??"");if(!t)return w("-");let e=t.endsWith("_anime"),r=e?t.slice(0,-6):t,o=(()=>{switch(r){case"blue":return e?y.blueBright(t):y.blue(t);case"red":return e?y.redBright(t):y.red(t);case"yellow":return e?y.yellowBright(t):y.yellow(t);case"aborted":return y.gray(t);case"disabled":case"notbuilt":case"grey":case"gray":return y.gray(t);default:return t}})();return w(o)}function Lt(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return w("-");if(t<1024)return w(`${t} B`);let e=["KB","MB","GB","TB"],r=t,o=-1;for(;r>=1024&&o<e.length-1;)r/=1024,o+=1;let s=r>=10||o===0?r.toFixed(1):r.toFixed(2);return w(`${s} ${e[o]}`)}var Se=64;function Bn(n){return n!=null&&typeof n=="object"?n.value??n.name??n:n}function Ot(n){let t=String(n?.toString?.()??"").trim();if(!t)return "";let e=t.indexOf("=>");if(e>=0){let s=t.slice(e+2).trim();return s.startsWith("{")&&s.endsWith("}")&&(s=s.slice(1,-1)),s}let r=t.indexOf("{"),o=t.lastIndexOf("}");return r>=0&&o>r?t.slice(r+1,o):""}function Vt(n){let t=new Set,e,r=/\b(\d+)\b/g;for(;e=r.exec(n);){let s=parseInt(e[1],10);Number.isFinite(s)&&t.add(s);}let o=Array.from(t).sort((s,i)=>s-i);return o.length>Se?o.slice(0,Se):o}function Wn(n,t){let e=String(n.type??"").toLowerCase();if(e==="confirm")return [!0,!1];if(e==="number"){let s=n,i=typeof s.min=="number"&&Number.isFinite(s.min)?s.min:null,a=typeof s.max=="number"&&Number.isFinite(s.max)?s.max:null;if(i!=null&&a!=null)return i===a?[i]:i<a?[i,a]:[a,i];if(i!=null)return [i,i+1];if(a!=null)return [a-1,a];if(t){let c=Vt(Ot(t));if(c.length)return c}return [0,1,99,100,101]}let r=Array.isArray(n.choices)?n.choices:null;if(!r?.length)return null;let o=r.map(Bn);if(e==="list"||e==="rawlist")return o;if(e==="checkbox"){let s=[[]];return o.forEach(i=>s.push([i])),o.length<=4&&s.push(o),s}return null}async function Un(n,t){let e=n.when;if(e===void 0||typeof e=="boolean")return e!==!1;if(typeof e!="function")return !0;try{return !!await Promise.resolve(e(t))}catch{return !0}}async function Ln(n,t,e){let r=await xe(n.configs,{projectRoot:t}),o=[{}];for(let s of r){let i=String(s.name??"").trim();if(!i)continue;let a=Wn(s,e);if(!a?.length||o.length*a.length>Se)continue;let c=[];for(let l of o){if(!await Un(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Tn(n){let t=new Map,e=/answers\.(\w+)\s*===\s*(\d+)/g,r;for(;r=e.exec(n);){let s=r[1],i=parseInt(r[2],10);Number.isFinite(i)&&(t.has(s)||t.set(s,new Set),t.get(s).add(i));}let o=new Map;return t.forEach((s,i)=>o.set(i,Array.from(s).sort((a,c)=>a-c))),o}function On(n){return n.split(`
|
|
38
|
+
`).filter(t=>{let e=t.trim();return e&&!e.startsWith("//")}).join(`
|
|
39
|
+
`)}function Vn(n){let t=Ot(n);if(!t)return [];t=On(t);let e=Vt(t),r=Tn(t),o=new Set,s=/return\s+([^;]+?)(?=;|$)/gm,i=/`((?:[^`\\]|\\.|\$\{[^}]*\})*)`/g,a=/\$\{\s*answers\.(\w+)\s*\}/,c;for(;c=s.exec(t);){let l=c[1];for(let f of Tt(l))f&&o.add(f);let u;for(;u=i.exec(l);){let f=u[1];if(!f.includes("${"))continue;let m=f.match(a),p=m&&r.has(m[1])?r.get(m[1]):m?[]:e,g=f.split(/\$\{[^}]*\}/);if(g.length<2)continue;let b=g[0]??"",S=g.slice(1).join("");for(let L of p){let R=b+String(L)+S;R&&o.add(R);}}}if(o.size===0&&!t.includes("{"))for(let l of Tt(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function Tt(n){let t=[],e=/(['"`])((?:\\.|(?!\1).)*)\1/g,r;for(;r=e.exec(n);)if(!(r[1]==="`"&&r[2].includes("${")))try{let o=r[1]==="`"?r[2]:JSON.parse(r[1]+r[2]+r[1]);String(o).trim()&&t.push(String(o).trim());}catch{String(r[2]??"").trim()&&t.push(String(r[2]).trim());}return t}async function In(n,t,e){let r=new Set;for(let i of Vn(n))r.add(i);let o=await Ln(t,e,n),s=async i=>{let a=await Promise.resolve(n(i)),c=typeof a=="string"?String(a).trim():"";return c&&r.add(c),r.size>=Se};if(!o.length)await s({});else for(let i of o)if(await s(i))break;return Array.from(r).sort((i,a)=>i.localeCompare(a,"en",{sensitivity:"base"}))}async function k(n,t){if(t==="")throw new d("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli job info -j my-job"});let e=String(t??"").trim();if(e)return e;let r=String(n.projectRoot??"").trim()||process.cwd(),o=await Et(r,n.job);if(o.isRef&&o.isFunction){let i=await In(o.value,n,r);if(!i.length)throw new d("Job choices not found from config reference.",{hint:"Ensure the job function returns a string (job name).",example:'export function dynamicJob(answers) { return "my-job" }'});if(i.length===1)return i[0];let{job:a}=await oe.prompt([{type:"list",name:"job",message:"\u8BF7\u9009\u62E9\u8981\u64CD\u4F5C\u7684 Job:",choices:i}]);return String(a??"").trim()}let s=await O(r,n.job,{});return String(s??n.job??"").trim()||String(n.job??"")}function $e(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function It(n){let t=Object.entries(n??{}).filter(([r])=>r);return t.length?(t.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"})),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(", ")):"-"}function qt(n){let t=n.command("builds").description("Build operations");t.option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").action(async e=>{let{config:r,jenkins:o}=await h(),s=e.job??$e("builds")??$e(),i=await k(r,s),c=(await dt(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:se(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:w(P(Number(u.timestamp??0))),parameters:It(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:v(u.parameters,l)})));}),t.command("active").description("List active builds").option("-j, --job <job>","Specify job").option("-a, --all","Query all running builds on Jenkins").action(async e=>{let{config:r,jenkins:o}=await h(),s=e.job??$e("active")??$e("builds"),i=await k(r,s),c=((e.all?await de(o):await ee(o,i))??[]).map(u=>({...e.all?{job:w(String(u.job??""))}:{},number:u.number,date:w(u.timestamp?P(Number(u.timestamp)):"-"),parameters:It(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:v(u.parameters,l)})));});}function qn(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function Dt(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:w("-"),s=e?.timestamp?P(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:w(s),author:w("-"),message:w("-")});continue}for(let a of i){let c=qn(a),l=String(a?.msg??"").trim();t.push({build:o,date:w(s),author:w(c||"-"),message:w(l||"-")});}}return t}function Mt(n){n.command("changes").description("List build changes").argument("[id]").option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").option("--raw","Output raw changeSet payload").action(async(t,e)=>{let{config:r,jenkins:o}=await h(),s=await k(r,e.job);if(t){let c=E(t,"id"),l=await he(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(Dt([l]));return}let i=Math.max(1,E(e.limit,"limit")),a=await gt(o,s,{limit:i});if(e.raw){console.log(JSON.stringify(a.map(c=>({build:c.number,changeSet:c.changeSet??{}})),null,2));return}console.table(Dt(a));});}function zt(){let n=process.argv,t=n.findIndex(r=>r==="-j"||r==="--job");if(t<0)return;let e=n[t+1];return !e||e.startsWith("-")?"":e}function _t(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await wt(r,o,s);process.stdout.write(l);return}let a=0,c=!0;for(console.log(y.blue(`--- Following log for ${o} build #${s} ---`));;)try{let{text:l,newOffset:u,hasMoreData:f}=await Jt(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&f)continue;if(!f){let p=(await he(r,o,s)).result||"UNKNOWN",g=p==="SUCCESS"?y.green:y.red;console.log(g(`
|
|
40
|
+
--- Build Finished: ${p} ---`));break}await new Promise(m=>setTimeout(m,1e3));}catch{await new Promise(l=>setTimeout(l,2e3));}};t.argument("<id>","Build number").option("-j, --job <job>","Specify job").option("--no-follow","Do not follow the build log").action(async(r,o)=>{let{config:s,jenkins:i}=await h(),a=o.job??zt();if(a==="")throw new d("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console 123 -j my-job"});let c=await k(s,a),l=E(r,"id");await e(i,c,l,o);}),t.command("last").description("Fetch the latest build log").option("-j, --job <job>","Specify job").option("-s, --status <status>","success | failed | any","any").option("--no-follow","Do not follow the build log").action(async r=>{let{config:o,jenkins:s}=await h(),i=r.job??zt();if(i==="")throw new d("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console last -j my-job -s success"});let a=await k(o,i),c=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(c))throw new d(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await we(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,f=Number(u?.number);if(!f){let m=c==="success"?"successful":c==="failed"?"failed":"last";console.log(y.yellow(`No ${m} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,f,r);});}function Ht(n){n.command("stop").description("Clear queued builds and stop running builds").argument("[id]","Build number").option("-j, --job <job>","Specify Jenkins job").option("-a, --all","Cancel queued items for this job and stop all running builds for it").option("-A, --ALL","Cancel all queued items and stop all running builds on Jenkins").action(async(t,e)=>{let{config:r,jenkins:o}=await h(),s=await k(r,e.job);if(e.ALL){let[i,a]=await Promise.all([H(o),de(o)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>({number:Number(u.number),job:String(u.job??"")})).filter(u=>u.job&&!Number.isNaN(u.number));for(let u of c)await te(o,u);for(let u of l)await ne(o,u.job,u.number);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([H(o,s),ee(o,s)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>Number(u.number)).filter(u=>!Number.isNaN(u));for(let u of c)await te(o,u);for(let u of l)await ne(o,s,u);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!t){console.log(y.red("Missing build id. Provide <id> or use -a/--all."));return}await ne(o,s,E(t,"id")),console.log(y.green("Stop requested"));});}function Qt(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await h(),o=await k(e,t.job),s=await ht(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:v(a?.name??"",i),type:w(String(a?.type??"")),description:w(String(a?.description??"")),default:w(a?.default===void 0?"-":String(a.default)),choices:w(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Gt(n){n.command("me").description("Show Jenkins user info for the current token").option("--raw","Print raw response").action(async t=>{let{jenkins:e}=await h(),r=await ft(e);if(t.raw){console.log(r&&typeof r=="object"?JSON.stringify(r,null,2):String(r));return}let o=r&&typeof r=="object"?r:{value:r},s=(u,f)=>{let m=u.length;if(m>=f)return u;let p=f-m,g=Math.floor(p/2),b=p-g;return `${" ".repeat(g)}${u}${" ".repeat(b)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=w(s(a,c));for(let u of Object.keys(o).sort((f,m)=>f.localeCompare(m,"en"))){if(u==="name")continue;let f=o[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof f=="string"||typeof f=="number")){l[u]=J(String(f));continue}let g=f==null?"":typeof f=="object"?JSON.stringify(f):String(f);l[u]=w(g);}console.table([l]);});}function Kt(n){let t=n.command("config").description("Show Jenkins job configuration");t.command("show").description("Show job configuration").option("-j, --job <job>","Specify job").option("-f, --format <format>","Output format: xml or json","xml").action(async e=>{let{config:r,jenkins:o}=await h(),s=await k(r,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new d(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await Me(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));}),t.command("backup").description("Backup job configuration to files").requiredOption("-d, --destination <path>","Destination folder").option("-a, --all","Backup all jobs").option("-j, --job <job>","Specify job").option("--filter <pattern>","Filter jobs by name (supports glob)").action(async e=>{let{config:r,jenkins:o}=await h(),s=String(e.destination??"").trim();if(!s)throw new d("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli config backup -d ./jenkins-backup"});if(e.all&&e.filter)throw new d("Options --all and --filter are mutually exclusive.",{hint:"Use either -a/--all or --filter, not both.",example:"jenkins-cli config backup -d ./jenkins-backup -a"});let i=String(e.filter??"").trim(),a=e.all?await T(o):i?(await T(o)).filter(l=>je(String(l.name??""),i)):[{name:await k(r,e.job)}];if(a.length===0)throw new d(`No jobs matched filter: ${i}`,{hint:"Check the pattern or remove --filter to use the default job.",example:"jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'"});await promises.mkdir(s,{recursive:!0});let c=[];for(let l of a){let u=String(l?.name??"").trim();if(!u)continue;let f=await Me(o,u),m=z.join(s,u,"config.xml");await promises.mkdir(z.dirname(m),{recursive:!0}),await promises.writeFile(m,f),c.push(m);}console.log(`Backup completed. ${c.length} file(s) saved to ${z.resolve(s)}`);});}function Yt(n){let t=n.command("job").description("Job operations");t.command("list").alias("ls").description("List all jobs").option("--search <keyword>","Filter by name (supports glob)").action(async e=>{let{jenkins:r}=await h(),o=await T(r),s=(e.search??"").trim(),i=s?o.filter(l=>je(String(l.name??""),s)):o;i.sort((l,u)=>String(l?.name??"").localeCompare(String(u?.name??""),"en",{sensitivity:"base"}));let a=j(i.map(l=>l?.name)),c=j(i.map(l=>l?.url));console.table((i??[]).map(l=>({name:v(l.name??"",a),color:ae(l.color),url:J(U(l.url??"",c))})));}),t.command("info").description("Show job info").option("-j, --job <job>","Specify job").option("--json","Output raw JSON").action(async e=>{let{config:r,jenkins:o}=await h(),s=await k(r,e.job),i=await we(o,s);if(e.json){console.log(JSON.stringify(i,null,2));return}let a=i?.lastBuild,c=i?.lastCompletedBuild,l=Array.isArray(i?.healthReport)?i.healthReport:[];console.table([{name:w(String(i?.name??"")),url:J(String(i?.url??"")),color:ae(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:w(a?.number?`#${a.number}`:"-"),lastResult:se(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:w(a?.timestamp?P(Number(a.timestamp)):"-"),lastCompleted:w(c?.number?`#${c.number}`:"-"),lastCompletedResult:se(c?.result,!1),lastCompletedDate:w(c?.timestamp?P(Number(c.timestamp)):"-")}]);});}function zn(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function _n(n){let t=n.split("=");if(t.length<2)throw new d(`Invalid --param: "${n}".`,{hint:"Use the format key=value.",example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});let e=t[0].trim(),r=t.slice(1).join("=").trim();if(!e)throw new d(`Invalid --param: "${n}". Key is empty.`,{hint:'Provide a non-empty key before "=".',example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});return {key:e,value:r}}function Xt(n){n.command("build").description("Trigger Jenkins builds non-interactively").requiredOption("-b, --branch <branch>","Branch (e.g., origin/develop)").option("-m, --mode <mode>","Deployment environment (repeatable or comma-separated: -m dev -m uat / -m dev,uat)",(t,e)=>e.concat(zn(t)),[]).option("--param <key=value>","Extra parameters (repeatable, e.g., --param foo=bar)",(t,e)=>e.concat(t),[]).option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await h(),o=await k(e,t.job),s=String(t.branch??"").trim();if(!s)throw new d("branch is required.",{hint:"Pass a branch with -b or --branch.",example:"jenkins-cli build -b origin/develop -m dev"});let i=(t.mode??[]).map(String).map(p=>p.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new d("mode is required.",{hint:"Pass at least one -m/--mode.",example:"jenkins-cli build -b origin/develop -m dev -m uat"});let c={};for(let p of t.param??[]){let{key:g,value:b}=_n(String(p));c[g]=b;}let l=await le();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,f=()=>{u||(u=!0,console.log());},m=a.map(async p=>{let g=en(`Triggering build for ${y.yellow(p)}...`).start();try{let b=await ge(r,o,{...c,branch:s,mode:p});f(),g.succeed(`${y.green(p)} - Triggered! Queue: ${b}`);}catch(b){throw f(),g.fail(`${y.red(p)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
|
|
41
41
|
\u274C Some builds failed. Check the output above.
|
|
42
|
-
`)),process.exit(1);}});}function
|
|
42
|
+
`)),process.exit(1);}});}function Zt(n){let t=n.command("view").description("View operations");t.command("list").alias("ls").description("List all views").option("--raw","Output raw JSON").action(async e=>{let{jenkins:r}=await h(),o=await re(r,{raw:e.raw});if(e.raw){console.log(JSON.stringify(o,null,2));return}o.sort((c,l)=>String(c?.name??"").localeCompare(String(l?.name??""),"en",{sensitivity:"base"}));let s=(o??[]).map(c=>({name:String(c?.name??""),url:String(c?.url??"")})),i=j(s.map(c=>c.name)),a=j(s.map(c=>c.url));console.table(s.map(c=>({name:v(c.name,i),url:J(U(c.url,a))})));}),t.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await h(),o=await Q(r,e);if(!o.length){console.log("No jobs found in this view.");return}o.sort((a,c)=>String(a?.name??"").localeCompare(String(c?.name??""),"en",{sensitivity:"base"}));let s=j(o.map(a=>a?.name)),i=j(o.map(a=>a?.url));console.table(o.map(a=>({name:v(a.name??"",s),color:ae(a.color),url:J(U(a.url??"",i))})));}),t.command("add").description("Add a job to a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await h();if((await Q(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await Ie(o,e,r),console.log(y.green(`Job "${r}" added to view "${e}".`));}),t.command("remove").alias("rm").description("Remove a job from a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await h();if(!(await Q(o,e)).some(i=>i.name===r))throw new d(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await qe(o,e,r),console.log(y.green(`Job "${r}" removed from view "${e}".`));}),t.command("edit").description("Edit jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await h(),[o,s]=await Promise.all([T(r),Q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((m,p)=>String(m?.name??"").localeCompare(String(p?.name??""),"en",{sensitivity:"base"}));let i=new Set(s.map(m=>String(m?.name??"").trim())),a=o.map(m=>String(m?.name??"").trim()).filter(Boolean).map(m=>({name:m,checked:i.has(m)})),c=await oe.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(m=>String(m).trim())),u=[...l].filter(m=>!i.has(m)),f=[...i].filter(m=>m&&!l.has(m));if(!u.length&&!f.length){console.log(y.yellow("No changes to apply."));return}for(let m of u)await Ie(r,e,m);for(let m of f)await qe(r,e,m);console.log(y.green(`View "${e}" updated.`));}),t.command("create").description("Create a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await h();await ut(r,e),console.log(y.green(`View "${e}" created.`));}),t.command("delete").alias("del").description("Delete a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await h();await mt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function Yn(n){return w(n?P(n):"-")}function Ae(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Qe(n,t,e){let r=t?.parent?.opts()?.job,o=Ae(e)??Ae(t?.name())??Ae("ws")??Ae("workspace");return n??r??o}function tn(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function Xn(n,t){let e=tn(n),r=tn(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function Zn(n){let t=process.platform,e="",r=[];t==="darwin"?(e="open",r=[n]):t==="win32"?(e="cmd",r=["/c","start","",n]):(e="xdg-open",r=[n]);try{spawn(e,r,{stdio:"ignore",detached:!0}).unref();}catch{}}async function A(n){try{return await n()}catch(t){if(t?.response?.status===404)return null;throw t}}async function er(n,t){try{return await pt(n,t)}catch{return}}async function tr(n,t){try{return await Rt(n,t)}catch{return}}async function nr(n){try{return await re(n,{raw:!1})}catch{return []}}async function Ge(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await er(n,t);if(o&&o!==t){let a=await e.direct(o);if(a!==null)return a}if(e.beforeView){let a=await e.beforeView();if(a!==null)return a}let s=await tr(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await nr(n);for(let a of i){let c=String(a?.url??"").trim();if(!c)continue;let l=await e.byViewUrl(c);if(l!==null)return l}return null}async function rr(n,t,e){return await Ge(n,t,{direct:r=>A(()=>De(n,r,{path:e})),byViewName:r=>A(()=>bt(n,r,t,{path:e})),byViewUrl:r=>A(()=>yt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await A(()=>De(n,t,{path:r})):null}})}async function or(n,t,e){return await Ge(n,t,{direct:r=>A(()=>kt(n,r,e)),byViewName:r=>A(()=>jt(n,r,t,e)),byViewUrl:r=>A(()=>St(n,r,t,e))})}async function ir(n,t,e){return await Ge(n,t,{direct:r=>A(()=>xt(n,r,e)),byViewName:r=>A(()=>Ct(n,r,t,e)),byViewUrl:r=>A(()=>$t(n,r,t,e))})}function nn(n){let t=n.command("workspace").alias("ws").description("Workspace operations");t.option("-j, --job <job>","Specify job").option("-p, --path <path>","Workspace sub path").action(async(e,r)=>{let{config:o,jenkins:s}=await h(),i=Qe(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=en(`Loading workspace for job "${a}"...`).start(),u=await rr(s,a,c);if(!u){l.warn(`Workspace not found for job: ${a}. The job may be inside a folder, not run yet, or workspace is disabled.`);return}if(l.succeed(`Workspace loaded for job: ${a}`),!u.length){console.table([{name:"-",type:"-",size:"-",modified:"-",path:"-",url:"-"}]);return}let f=u.map(g=>({name:String(g.name??""),typeRaw:g.type,size:Lt(g.size),modified:Yn(g.modified),path:Xn(c,String(g.path??g.name??""))})),m=j(f.map(g=>g.name)),p=j(f.map(g=>g.path));console.table(f.map(g=>{let b=U(String(g.name??""),m),S=String(g.typeRaw??"");return {name:S==="dir"?w(y.bold.cyan(b)):S==="file"?w(y.green(b)):w(y.gray(b)),size:g.size,modified:g.modified,path:v(String(g.path??""),p)}}));}),t.command("cat").description("Show file content in workspace").argument("<path>","Workspace file path").option("-j, --job <job>","Specify job").option("-o, --out <file>","Save file to path").option("--open","Open file with default application").action(async(e,r,o)=>{let{config:s,jenkins:i}=await h(),a=Qe(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)throw new d("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),f=r.open===!0;if(u||f){let p=await ir(i,c,l);if(p===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let g=u||z.join(Gn.tmpdir(),z.basename(l)||"file");try{u&&await M.pathExists(u)&&(await M.stat(u)).isDirectory()&&(g=z.join(u,z.basename(l)||"file"));}catch{}await M.ensureDir(z.dirname(g)),await M.writeFile(g,p.data),f?(Zn(g),console.log(y.green(`Opened: ${g}`))):console.log(y.green(`Saved to: ${g}`));return}let m=await or(i,c,l);if(m===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(m));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await h(),i=Qe(e.job,r,"wipe"),a=await k(o,i);try{if(!(await oe.prompt([{type:"confirm",name:"confirm",message:`Wipe workspace for job "${a}"? This cannot be undone.`,default:!1}]))?.confirm){console.log(y.yellow("Cancelled."));return}}catch(l){if(Ce(l)){console.log(y.yellow("Cancelled."));return}throw l}let c=en(`Wiping workspace for job "${a}"...`).start();try{await vt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function rn(n){qt(n),Mt(n),Kt(n),Yt(n),_t(n),Qt(n),Ut(n),Ht(n),Xt(n),Gt(n),Zt(n),nn(n);}function on(n,t,e){let r=s=>s.options.some(i=>i.long===t||i.short===t),o=s=>{r(s)||s.option(t,e);for(let i of s.commands)o(i);};for(let s of n.commands)o(s);}program.name("jenkins-cli").description(et).version(Ze,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Wt);rn(program);on(program,"--debug","Print Jenkins request params");async function sr(){try{await program.parseAsync(process.argv);}catch(n){q(n);}}sr();
|