@ts-org/jenkins-cli 3.0.5 → 3.0.6
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 +45 -3
- package/dist/cli.d.ts +0 -0
- package/dist/cli.js +23 -23
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -46,6 +46,8 @@ apiToken: http://user:token@jenkins.example.com
|
|
|
46
46
|
job: your-project-job
|
|
47
47
|
|
|
48
48
|
# 部署环境列表 (将作为参数 `mode` 传入 Jenkins)
|
|
49
|
+
# 选项要与Jenkins配置的对上
|
|
50
|
+
# 这里配置了Jenkins上没有的,比如prod,那么在提交mode = prod时会报500
|
|
49
51
|
modes:
|
|
50
52
|
- dev
|
|
51
53
|
- sit
|
|
@@ -77,6 +79,10 @@ configs:
|
|
|
77
79
|
name: service
|
|
78
80
|
message: '请选择服务:'
|
|
79
81
|
choices: src/config.ts:getServices
|
|
82
|
+
- type: confirm
|
|
83
|
+
name: smokeTest
|
|
84
|
+
message: '是否开启冒烟测试?'
|
|
85
|
+
when: src/config.ts:whenSmokeTest
|
|
80
86
|
- type: input
|
|
81
87
|
name: ticket
|
|
82
88
|
message: '请输入关联的工单号:'
|
|
@@ -90,20 +96,50 @@ export async function getServices() {
|
|
|
90
96
|
return ['user-center', 'order-service']
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
export function filterVersion(input: string) {
|
|
99
|
+
export function filterVersion(input: unknown, answers: Record<string, unknown>) {
|
|
94
100
|
return String(input ?? '').trim()
|
|
95
101
|
}
|
|
96
102
|
|
|
97
|
-
export function transformVersion(
|
|
103
|
+
export function transformVersion(
|
|
104
|
+
input: unknown,
|
|
105
|
+
answers: Record<string, unknown>,
|
|
106
|
+
meta: { isFinal?: boolean },
|
|
107
|
+
) {
|
|
98
108
|
const value = String(input ?? '')
|
|
99
109
|
return value ? `v${value}` : value
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
export function
|
|
112
|
+
export function whenSmokeTest(answers: { mode?: string }) {
|
|
113
|
+
return answers.mode !== 'prod'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function validateTicket(input: unknown, answers: Record<string, unknown>) {
|
|
103
117
|
return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || '工单号格式不正确'
|
|
104
118
|
}
|
|
105
119
|
```
|
|
106
120
|
|
|
121
|
+
**四个函数的完整入参说明**:
|
|
122
|
+
|
|
123
|
+
- `when(answers)`
|
|
124
|
+
- `answers`: 当前已回答的所有答案对象(键为问题的 `name`)。
|
|
125
|
+
- 返回 `boolean | Promise<boolean>`,用于决定是否显示该问题。
|
|
126
|
+
|
|
127
|
+
- `validate(input, answers)`
|
|
128
|
+
- `input`: 用户当前输入的原始值。
|
|
129
|
+
- `answers`: 当前已回答的所有答案对象。
|
|
130
|
+
- 返回 `true` 表示通过;返回 `string` 作为错误提示;也可返回 `Promise<true | string>`。
|
|
131
|
+
|
|
132
|
+
- `filter(input, answers)`
|
|
133
|
+
- `input`: 用户当前输入的原始值。
|
|
134
|
+
- `answers`: 当前已回答的所有答案对象。
|
|
135
|
+
- 返回转换后的值(可同步或返回 Promise)。
|
|
136
|
+
|
|
137
|
+
- `transformer(input, answers, meta)`
|
|
138
|
+
- `input`: 用户当前输入的原始值。
|
|
139
|
+
- `answers`: 当前已回答的所有答案对象。
|
|
140
|
+
- `meta`: 渲染相关的元信息(例如 `isFinal`),不同版本可能略有差异。
|
|
141
|
+
- 返回显示用的字符串,不会修改answers的值(可同步或返回 Promise)。
|
|
142
|
+
|
|
107
143
|
> **提示**: 配置也支持写在 `package.json` 的 `jenkins-cli` 字段里。
|
|
108
144
|
>
|
|
109
145
|
> **查找顺序**: `jenkins-cli.yaml` > `package.json` > 祖先目录的 `jenkins-cli.yaml`。
|
|
@@ -125,6 +161,9 @@ jenkins-cli build -b origin/develop -m dev,sit
|
|
|
125
161
|
jenkins-cli build -b origin/develop -m dev --param version=1.2.3 --param force=true
|
|
126
162
|
```
|
|
127
163
|
|
|
164
|
+
说明:默认会读取 `git config user.name` 并自动追加参数 `triggered_by`。
|
|
165
|
+
如需覆盖,可显式传入 `--param triggered_by=YourName`。
|
|
166
|
+
|
|
128
167
|
---
|
|
129
168
|
|
|
130
169
|
#### `builds` - 构建管理
|
|
@@ -259,6 +298,9 @@ jenkins-cli view add MyView my-job
|
|
|
259
298
|
# 从视图移除 Job
|
|
260
299
|
jenkins-cli view remove MyView my-job
|
|
261
300
|
|
|
301
|
+
# 编辑视图中的 Job(勾选/取消)
|
|
302
|
+
jenkins-cli view edit MyView
|
|
303
|
+
|
|
262
304
|
# 创建视图
|
|
263
305
|
jenkins-cli view create MyView
|
|
264
306
|
|
package/dist/cli.d.ts
CHANGED
|
File without changes
|
package/dist/cli.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program, CommanderError } from 'commander';
|
|
3
|
-
import
|
|
3
|
+
import fn from 'inquirer';
|
|
4
4
|
import w from 'chalk';
|
|
5
|
-
import
|
|
5
|
+
import Vt from 'ora';
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import gn, { promisify } from 'util';
|
|
8
|
+
import T from 'fs-extra';
|
|
9
9
|
import Ht from 'js-yaml';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import V from 'path';
|
|
11
|
+
import Zt from 'axios';
|
|
12
12
|
import { Buffer } from 'buffer';
|
|
13
|
-
import
|
|
13
|
+
import cn 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 Cn from 'os';
|
|
18
18
|
|
|
19
|
-
var
|
|
19
|
+
var Te="3.0.6",Ve="Jenkins deployment CLI";var f=class extends Error{hint;example;details;exitCode;constructor(n,e={}){super(n),this.name="UserError",this.hint=e.hint,this.example=e.example,this.details=e.details,this.exitCode=e.exitCode??1;}};function Mt(t){return t instanceof f?t:t instanceof CommanderError?new f(t.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:t.exitCode??1}):t instanceof Error?new f(t.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new f("Unknown error",{example:"jenkins-cli --help"})}function U(t){let n=Mt(t);if(console.error(w.red(`
|
|
20
20
|
\u274C ${n.message}
|
|
21
|
-
`)),n.details&&console.error(w.gray(n.details)),n.hint&&console.error(w.yellow(`Hint: ${n.hint}`)),n.example!==""){let e=n.example??"jenkins-cli --help";console.error(w.cyan(`Example: ${e}`));}console.error(w.gray('Run "jenkins-cli --help" for usage.')),process.exitCode=n.exitCode;}var
|
|
22
|
-
`).filter(n=>n.includes("/")&&!n.includes("HEAD"))}catch{throw new f("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}var
|
|
21
|
+
`)),n.details&&console.error(w.gray(n.details)),n.hint&&console.error(w.yellow(`Hint: ${n.hint}`)),n.example!==""){let e=n.example??"jenkins-cli --help";console.error(w.cyan(`Example: ${e}`));}console.error(w.gray('Run "jenkins-cli --help" for usage.')),process.exitCode=n.exitCode;}var we=promisify(exec);async function Ie(){try{let{stdout:t}=await we("git branch --show-current"),n=t.trim();if(!n)throw new f("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return n}catch{throw new f("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function Oe(){try{let{stdout:t}=await we('git branch -r --format="%(refname:short)"');return t.split(`
|
|
22
|
+
`).filter(n=>n.includes("/")&&!n.includes("HEAD"))}catch{throw new f("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function te(){try{let{stdout:t}=await we("git config user.name"),n=t.trim();return n||void 0}catch{return}}var L="jenkins-cli.yaml",re="package.json",ne="jenkins-cli",Qt="jenkinsCli";function De(t,n=process.cwd()){let e=n;for(;;){let r=V.join(e,t);if(T.existsSync(r))return r;let o=V.dirname(e);if(o===e)return null;e=o;}}function Gt(t=process.cwd()){return De(L,t)}function Kt(t=process.cwd()){let n=De(re,t);return n?V.dirname(n):null}function Yt(t){return !!(t.apiToken&&t.job&&Array.isArray(t.modes)&&t.modes.length>0)}async function qe(t){let n=await T.readFile(t,"utf-8"),e=Ht.load(n);if(!e||typeof e!="object")throw new f(`Invalid config in ${L}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${L}:
|
|
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 Xt(t){let n=V.join(t,re);if(!await T.pathExists(n))return {};let e=await T.readFile(n,"utf-8"),r=JSON.parse(e),o=r[ne]??r[Qt];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new f(`Invalid ${re} config: "${ne}" must be an object.`,{hint:"Use an object for the config section.",example:`"${ne}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function Me(){let t=process.cwd(),n=Kt(t)??t,e=V.join(n,L),r={},o=V.dirname(n),s=Gt(o);s&&await T.pathExists(s)&&(r=await qe(s));let i=await Xt(n);r={...r,...i};let a=await T.pathExists(e)?await qe(e):{};if(r={...r,...a},!Yt(r))throw new f("Config incomplete or not found.",{hint:`Tried ${L} in project root, "${ne}" in ${re}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${L}:
|
|
26
26
|
apiToken: http://user:token@host
|
|
27
27
|
job: my-job
|
|
28
28
|
modes:
|
|
29
|
-
- dev`});return {...r,projectRoot:n}}var N={maxRedirects:0,validateStatus:t=>t>=200&&t<400};function
|
|
30
|
-
`);return
|
|
31
|
-
${
|
|
32
|
-
${
|
|
33
|
-
${o}`:void 0})}throw e}}async function
|
|
29
|
+
- dev`});return {...r,projectRoot:n}}var N={maxRedirects:0,validateStatus:t=>t>=200&&t<400};function x(t){return `job/${t.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function ie(t){let n=Array.isArray(t)?t.find(o=>o?.parameters):void 0,e=Array.isArray(n?.parameters)?n.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function He(t){try{let e=new URL(t).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 en(t,n){let r=(await t.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",n).toString())).data??{};return {...r,parameters:ie(r.actions)}}function Qe(t,n=400){try{let r=(typeof t=="string"?t:JSON.stringify(t??"",null,2)).trim();return r?r.length>n?`${r.slice(0,n)}...`:r:""}catch{return ""}}function z(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="string"){let n=Number(t);if(Number.isFinite(n))return n}}function _(t){let n=z(t);if(n!==void 0)return n;if(typeof t=="string"){let e=Date.parse(t);if(!Number.isNaN(e))return e}}function v(t){return t.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/")}function ye(t,n){let e=t.replace(/\/+$/,""),r=n.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function I(t){return t.replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function oe(t){return t.replace(/<[^>]*>/g,"")}function tn(t){return /<!doctype\s+html|<html\b|<body\b/i.test(t)}function nn(t){let n=t.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i),e=t.match(/<code[^>]*>([\s\S]*?)<\/code>/i),r=t.match(/<body[^>]*>([\s\S]*?)<\/body>/i),s=(n?.[1]??e?.[1]??r?.[1]??t).replace(/<br\s*\/?>/gi,`
|
|
30
|
+
`);return I(oe(s))}function rn(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)],c="",l="";for(let D of a){let Ue=D[1]??"",ee=I(oe(D[2]??"")).trim();if(!(!ee||ee==="..")&&!/all\s+files\s+in\s+zip/i.test(ee)){c=Ue,l=ee;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",p=I(c.split("?")[0]??"").replace(/\/+$/,"")||l,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),h=m?I(oe(m[1]??"")).trim():"",d=h?_(h):void 0,y=`${l}|${p}`;r.has(y)||(r.add(y),e.push({name:l,path:p,type:u,modified:d}));}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=I(oe(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(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 ke(t,n,e){let r=ye(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:"*/*"}}),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 xe(t,n,e){let r=ye(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,*/*"}}),c=typeof a.data=="string"?a.data:String(a.data??"");if(!c)continue;if(tn(c)){let l=nn(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 je(t,n,e){let r=ye(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 l=(await t.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 t.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let c=rn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function on(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 Ce(t){return on(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,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=z(e?.size)??z(e?.length)??z(e?.fileSize)??z(e?.bytes)??void 0,u=_(e?.lastModified)??_(e?.modified)??_(e?.mtime)??_(e?.timestamp)??void 0,p=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:p,size:l,modified:u,type:c}})}function Ge(t){let n=t.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!n)throw new f("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=Zt.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 Ke(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 Se(t){return t.crumbPromise||(t.crumbPromise=Ke(t,5e3)),t.crumbPromise}async function A(t){await Se(t),t.crumbForm||(t.crumbPromise=Ke(t,15e3),await t.crumbPromise);}async function O(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 H(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:ie(o.actions)}))}async function se(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:[],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 en(t,i.url),c=He(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function Q(t,n){return await A(t),await t.axios.post("queue/cancelItem",new URLSearchParams({id:n.toString()}),N),!0}async function G(t,n,e){try{await A(t),await t.axios.post(`${x(n)}/${e}/stop/`,new URLSearchParams({}),N);}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(w.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(w.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 $e(t,n,e){await A(t);let r=`view/${encodeURIComponent(n)}/addJobToView`;try{await t.axios.post(r,new URLSearchParams({name:e}),N);}catch(o){let s=o?.response?.status;throw s===404?new f(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new f(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function Je(t,n,e){await A(t);let r=`view/${encodeURIComponent(n)}/removeJobFromView`;try{await t.axios.post(r,new URLSearchParams({name:e}),N);}catch(o){let s=o?.response?.status;throw s===404?new f(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new f(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function Ye(t,n){await A(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,N);}catch(r){let o=r?.response?.status;throw o===400?new f(`View already exists: ${n}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new f(`Forbidden to create view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function Xe(t,n){await A(t);let e=`view/${encodeURIComponent(n)}/doDelete`;try{await t.axios.post(e,new URLSearchParams({}),N);}catch(r){let o=r?.response?.status;throw o===404?new f(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new f(`Forbidden to delete view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ae(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([O(t,n),H(t,n)]),c=(p,m)=>{let h=p.actions?.find(y=>y.parameters);return h?h.parameters.find(y=>y.name===m)?.value:void 0},l=e[r];if(s&&l){let p=i.find(m=>c(m,o)===s&&c(m,r)===l);if(p)return new URL(`queue/item/${p.id}/`,t.baseURL).toString()}let u=!1;if(s)for(let p of a)c(p,o)===s&&(console.log(`Stopping running build #${p.number} (mode=${s})...`),await G(t,n,p.number),u=!0);u&&await new Promise(p=>setTimeout(p,1e3));try{return await A(t),(await t.axios.post(`${x(n)}/buildWithParameters/`,new URLSearchParams({...e}),N)).headers.location||""}catch(p){let m=p?.response?.status,h=Qe(p?.response?.data);if(m===400){let d=h?`Jenkins response (snippet):
|
|
31
|
+
${h}`:void 0;throw new f("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:d})}if(m){let d=h?`Jenkins response (snippet):
|
|
32
|
+
${h}`:void 0;throw new f(`Request failed with status code ${m}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:d})}throw p}}async function Ze(t){return (await t.axios.get("whoAmI/api/json")).data}async function W(t){return (await t.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function et(t,n,e){let r=String(n??"").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 t.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 p=String(u.name??"").trim();if(p&&p===r&&u.url){let m=He(String(u.url));m&&c.push(m);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function ce(t,n){try{let r=(await t.axios.get(`${x(n)}/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(n??"").trim();if(!o&&!s)throw new f(`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 f(`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(n??"").trim();throw new f(`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 tt(t,n,e){let r=Math.max(1,e?.limit??20);return ((await t.axios.get(`${x(n)}/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:ie(i.actions)}))}async function nt(t,n,e){let r=Math.max(1,e?.limit??20);return ((await t.axios.get(`${x(n)}/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 le(t,n,e){let o=(await t.axios.get(`${x(n)}/${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:ie(o.actions)}}async function rt(t,n,e){let r=await t.axios.get(`${x(n)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function ot(t,n){return ((await t.axios.get(`${x(n)}/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 ve(t,n,e){let r=String(e?.path??"").trim(),o=r?`${v(r)}/`:"",s=await je(t,x(n),o);return Ce(s??{})}async function it(t,n,e,r){let o=String(r?.path??"").trim(),s=o?`${v(o)}/`:"",i=`view/${encodeURIComponent(n)}/${x(e)}`,a=await je(t,i,s);return Ce(a??{})}async function st(t,n,e,r){let o=String(r?.path??"").trim(),s=o?`${v(o)}/`:"",i=new URL(`${x(e)}/`,n).toString(),a=await je(t,i,s);return Ce(a??{})}async function at(t,n,e){let r=v(String(e??"").trim());return await xe(t,x(n),r)}async function ct(t,n,e){let r=v(String(e??"").trim());return await ke(t,x(n),r)}async function lt(t,n,e,r){let o=v(String(r??"").trim()),s=`view/${encodeURIComponent(n)}/${x(e)}`;return await xe(t,s,o)}async function ut(t,n,e,r){let o=v(String(r??"").trim()),s=`view/${encodeURIComponent(n)}/${x(e)}`;return await ke(t,s,o)}async function mt(t,n,e,r){let o=v(String(r??"").trim()),s=new URL(`${x(e)}/`,n).toString();return await xe(t,s,o)}async function pt(t,n,e,r){let o=v(String(r??"").trim()),s=new URL(`${x(e)}/`,n).toString();return await ke(t,s,o)}async function ft(t,n){try{return await A(t),await t.axios.post(`${x(n)}/doWipeOutWorkspace`,new URLSearchParams({}),N),!0}catch(e){let r=e?.response?.status;if(r){let o=Qe(e?.response?.data);throw new f(`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 Pe(t,n){let e=await t.axios.get(`${x(n)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function dt(t,n,e,r){let o=`${x(n)}/${e}/logText/progressiveText`,s=new URLSearchParams;s.append("start",String(r));let i=await t.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 K(t,n){let e=n?.raw?"views[*]":"views[name,url]";return (await t.axios.get(`api/json?tree=${e}`)).data.views??[]}async function q(t,n){let e=`view/${encodeURIComponent(n)}`;try{return (await t.axios.get(`${e}/api/json?tree=jobs[name,url,color]`)).data.jobs??[]}catch(r){throw r?.response?.status===404?new f(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function gt(t,n){let e=String(n??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await K(t,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await q(t,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function b(){let t=Vt("Loading configuration...").start();try{let n=await Me();t.succeed("Configuration loaded");let e=Ge(n.apiToken);return Se(e),{config:n,jenkins:e}}catch(n){U(n),process.exit(1);}}var un=cn(fileURLToPath(import.meta.url),{interopDefault:!0});async function bt(t){return await Promise.resolve(t)}function mn(t){let n=String(t??"").trim();if(!n)return null;let e=n.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 pn(t,n){return V.isAbsolute(n)?n:V.join(t,n)}function yt(t){return !!t&&typeof t=="object"&&!Array.isArray(t)}async function kt(t,n){let e=mn(n);if(!e)return null;let r=pn(t,e.file);if(!await T.pathExists(r))throw new f(`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=un(r),s=yt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new f(`configs reference export not found: ${n}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function ue(t,n,e){if(typeof n!="string")return n;let r=await kt(t,n);if(!r)return n;if(!e.allow(r.value))throw new f(e.error(n),{hint:e.hint,example:e.example});return r.value}async function wt(t,n){if(typeof n!="string")return n;let e=await kt(t,n);if(!e)return n;if(typeof e.value=="function"){let r=e.value();return await bt(r)}return await bt(e.value)}async function xt(t,n){let e=Array.isArray(t)?t:[];if(e.length===0)return [];let r=String(n.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!yt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await wt(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 f(`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 wt(r,i.default)),i.when!==void 0&&(i.when=await ue(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 ue(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 ue(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 ue(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 jt(t,n){let e=new Set(n),r={};for(let[o,s]of Object.entries(t))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 Ct(){console.log(w.bold.blue(`
|
|
34
34
|
\u{1F680} Jenkins CLI - Jenkins Deployment CLI
|
|
35
|
-
`));let{config:t,jenkins:n}=await
|
|
36
|
-
\u{1F44B} Cancelled by user`)),process.exit(130);},s,i={};try{process.prependOnceListener("SIGINT",o);let
|
|
37
|
-
\u{1F44B} Cancelled by user`)),process.exit(0)),
|
|
35
|
+
`));let{config:t,jenkins:n}=await b(),[e,r]=await Promise.all([Oe(),Ie()]),o=()=>{console.log(w.yellow(`
|
|
36
|
+
\u{1F44B} Cancelled by user`)),process.exit(130);},s,i={};try{process.prependOnceListener("SIGINT",o);let p=await xt(t.configs,{projectRoot:t.projectRoot}),m=new Set(["branch","modes","mode","triggered_by"]);for(let d of p){let y=String(d?.name??"").trim();if(m.has(y))throw new f(`configs name "${y}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let h=await fn.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:t.modes,validate:d=>d.length===0?"You must select at least one environment":!0},...p]);s={branch:String(h.branch??""),modes:h.modes??[]},i=jt(h,["branch","modes"]);}catch(p){let m=p,h=m?.message?String(m.message):String(m),d=m?.name?String(m.name):"";(d==="ExitPromptError"||h.toLowerCase().includes("cancelled")||h.toLowerCase().includes("canceled"))&&(console.log(w.yellow(`
|
|
37
|
+
\u{1F44B} Cancelled by user`)),process.exit(0)),U(new f(`Prompt failed: ${h}`,{hint:d?`Error: ${d}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let a=!1,c=()=>{a||(a=!0,console.log());},l=await te();l&&!("triggered_by"in i)&&(i.triggered_by=l);let u=s.modes.map(async p=>{let m=Vt(`Triggering build for ${w.yellow(p)}...`).start();try{let h=await ae(n,t.job,{...i,branch:s.branch,mode:p});c(),m.succeed(`${w.green(p)} - Triggered! Queue: ${h}`);}catch(h){throw c(),m.fail(`${w.red(p)} - ${h.message}`),h}});try{await Promise.all(u);}catch{console.log(w.bold.red(`
|
|
38
38
|
\u274C Some builds failed. Check the output above.
|
|
39
|
-
`)),process.exit(1);}}function
|
|
40
|
-
--- Build Finished: ${
|
|
39
|
+
`)),process.exit(1);}}function k(t,n){return (n??"").trim()||t}function P(t,n){let e=Number(t);if(!Number.isInteger(e)||e<0)throw new f(`${n} must be a non-negative integer.`,{hint:`Provide a valid ${n}.`,example:`jenkins-cli console ${n==="id"?"123":"last -t 100"}`});return e}function me(t,n){let e=String(n??"").trim();if(!e)return !0;if(!/[*?]/.test(e))return t.includes(e);let s=`^${e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(s).test(t)}catch{return t.includes(e)}}function St(t){let n=t.command("queue").description("Operations for the build queue");n.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 b(),s=e.job?k(r.job,e.job):void 0,i=await O(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),n.command("cancel").description("Cancel a queued build item").argument("<id...>","Queue id(s)").action(async e=>{let{jenkins:r}=await b(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=P(String(a),"id");try{return await Q(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(w.green(`Cancelled ${s.length} queue item(s).`));return}console.log(w.yellow(`Cancelled ${s.length-i.length} item(s), failed ${i.length} item(s).`));for(let a of i)console.log(w.red(`- ${a.id}: ${a.message??"Cancel failed"}`));});}function g(t){return {[gn.inspect.custom]:()=>t}}function B(t,n){return String(t??"").padEnd(n," ")}function j(t){return Math.max(0,...t.map(n=>String(n??"").length))}function C(t,n){return g(B(t,n))}function S(t){let n=String(t??""),e=n.trim();if(!e)return g(w.gray("-"));let r=s=>g(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?w.cyanBright(n):w.hex("#4EA1FF")(n))}catch{return r(w.hex("#4EA1FF")(n))}return o.startsWith("ws://")||o.startsWith("wss://")?r(w.cyan(n)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(w.magenta(n)):o.startsWith("file://")?r(w.yellow(n)):o.startsWith("mailto:")?r(w.green(n)):o.startsWith("javascript:")||o.startsWith("data:")?r(w.red(n)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(w.cyan(n)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(w.blue(n)):r(n)}function $(t){let n=new Date(t),e=r=>String(r).padStart(2,"0");return `${n.getFullYear()}-${e(n.getMonth()+1)}-${e(n.getDate())} ${e(n.getHours())}:${e(n.getMinutes())}:${e(n.getSeconds())}`}function Y(t,n){if(n===!0)return g(w.cyan("BUILDING"));let e=String(t??"").toUpperCase();return g(e?e==="SUCCESS"?w.green(e):e==="ABORTED"?w.gray(e):e==="FAILURE"?w.red(e):e==="UNSTABLE"?w.yellow(e):e==="NOT_BUILT"?w.gray(e):e:"-")}function X(t){let n=String(t??"");if(!n)return g("-");let e=n.endsWith("_anime"),r=e?n.slice(0,-6):n,o=(()=>{switch(r){case"blue":return e?w.blueBright(n):w.blue(n);case"red":return e?w.redBright(n):w.red(n);case"yellow":return e?w.yellowBright(n):w.yellow(n);case"aborted":return w.gray(n);case"disabled":case"notbuilt":case"grey":case"gray":return w.gray(n);default:return n}})();return g(o)}function $t(t){let n=Number(t);if(!Number.isFinite(n)||n<0)return g("-");if(n<1024)return g(`${n} B`);let e=["KB","MB","GB","TB"],r=n,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 g(`${s} ${e[o]}`)}function pe(t){let n=process.argv,e=0;if(t){let r=n.lastIndexOf(t);r>=0&&(e=r+1);}for(let r=e;r<n.length;r+=1){let o=n[r];if(o==="-j"||o==="--job"){let s=n[r+1];return !s||s.startsWith("-")?"":s}}}function Jt(t){let n=Object.entries(t??{}).filter(([r])=>r);return n.length?(n.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"})),n.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 vt(t){let n=t.command("builds").description("Build operations");n.option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").action(async e=>{let{config:r,jenkins:o}=await b(),s=e.job??pe("builds")??pe(),i=k(r.job,s),c=(await tt(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:Y(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:g($(Number(u.timestamp??0))),parameters:Jt(u.parameters),url:S(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:C(u.parameters,l)})));}),n.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 b(),s=e.job??pe("active")??pe("builds"),a=((e.all?await se(o):await H(o,k(r.job,s)))??[]).map(l=>({...e.all?{job:g(String(l.job??""))}:{},number:l.number,date:g(l.timestamp?$(Number(l.timestamp)):"-"),parameters:Jt(l.parameters),url:S(String(l.url??""))})),c=j(a.map(l=>l.parameters));console.table(a.map(l=>({...l,parameters:C(l.parameters,c)})));});}function hn(t){let n=String(t?.authorEmail??"").trim(),e=n?n.split("@")[0]??"":"";return e||n}function Pt(t){let n=[];for(let e of t){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:g("-"),s=e?.timestamp?$(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){n.push({build:o,date:g(s),author:g("-"),message:g("-")});continue}for(let a of i){let c=hn(a),l=String(a?.msg??"").trim();n.push({build:o,date:g(s),author:g(c||"-"),message:g(l||"-")});}}return n}function Rt(t){t.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(n,e)=>{let{config:r,jenkins:o}=await b(),s=k(r.job,e.job);if(n){let c=P(n,"id"),l=await le(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(Pt([l]));return}let i=Math.max(1,P(e.limit,"limit")),a=await nt(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(Pt(a));});}function Nt(){let t=process.argv,n=t.findIndex(r=>r==="-j"||r==="--job");if(n<0)return;let e=t[n+1];return !e||e.startsWith("-")?"":e}function At(t){let n=t.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await rt(r,o,s);process.stdout.write(l);return}let a=0,c=!0;for(console.log(w.blue(`--- Following log for build #${s} ---`));;)try{let{text:l,newOffset:u,hasMoreData:p}=await dt(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&p)continue;if(!p){let h=(await le(r,o,s)).result||"UNKNOWN",d=h==="SUCCESS"?w.green:w.red;console.log(d(`
|
|
40
|
+
--- Build Finished: ${h} ---`));break}await new Promise(m=>setTimeout(m,1e3));}catch{await new Promise(l=>setTimeout(l,2e3));}};n.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 b(),a=o.job??Nt();if(a==="")throw new f("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console 123 -j my-job"});let c=k(s.job,a),l=P(r,"id");await e(i,c,l,o);}),n.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 b(),i=r.job??Nt();if(i==="")throw new f("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console last -j my-job -s success"});let a=k(o.job,i),c=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(c))throw new f(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await ce(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,p=Number(u?.number);if(!p){let m=c==="success"?"successful":c==="failed"?"failed":"last";console.log(w.yellow(`No ${m} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,p,r);});}function Bt(t){t.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(n,e)=>{let{config:r,jenkins:o}=await b(),s=k(r.job,e.job);if(e.ALL){let[i,a]=await Promise.all([O(o),se(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 Q(o,u);for(let u of l)await G(o,u.job,u.number);console.log(w.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([O(o,s),H(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 Q(o,u);for(let u of l)await G(o,s,u);console.log(w.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!n){console.log(w.red("Missing build id. Provide <id> or use -a/--all."));return}await G(o,s,P(n,"id")),console.log(w.green("Stop requested"));});}function Wt(t){t.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async n=>{let{config:e,jenkins:r}=await b(),o=k(e.job,n.job),s=await ot(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:C(a?.name??"",i),type:g(String(a?.type??"")),description:g(String(a?.description??"")),default:g(a?.default===void 0?"-":String(a.default)),choices:g(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Et(t){t.command("me").description("Show Jenkins user info for the current token").option("--raw","Print raw response").action(async n=>{let{jenkins:e}=await b(),r=await Ze(e);if(n.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,p)=>{let m=u.length;if(m>=p)return u;let h=p-m,d=Math.floor(h/2),y=h-d;return `${" ".repeat(d)}${u}${" ".repeat(y)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=g(s(a,c));for(let u of Object.keys(o).sort((p,m)=>p.localeCompare(m,"en"))){if(u==="name")continue;let p=o[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof p=="string"||typeof p=="number")){l[u]=S(String(p));continue}let d=p==null?"":typeof p=="object"?JSON.stringify(p):String(p);l[u]=g(d);}console.table([l]);});}function Ft(t){let n=t.command("config").description("Show Jenkins job configuration");n.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 b(),s=k(r.job,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new f(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await Pe(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));}),n.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 b(),s=String(e.destination??"").trim();if(!s)throw new f("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 f("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 W(o):i?(await W(o)).filter(l=>me(String(l.name??""),i)):[{name:k(r.job,e.job)}];if(a.length===0)throw new f(`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 p=await Pe(o,u),m=V.join(s,u,"config.xml");await promises.mkdir(V.dirname(m),{recursive:!0}),await promises.writeFile(m,p),c.push(m);}console.log(`Backup completed. ${c.length} file(s) saved to ${V.resolve(s)}`);});}function Ut(t){let n=t.command("job").description("Job operations");n.command("list").alias("ls").description("List all jobs").option("--search <keyword>","Filter by name (supports glob)").action(async e=>{let{jenkins:r}=await b(),o=await W(r),s=(e.search??"").trim(),i=s?o.filter(l=>me(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:C(l.name??"",a),color:X(l.color),url:S(B(l.url??"",c))})));}),n.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 b(),s=k(r.job,e.job),i=await ce(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:g(String(i?.name??"")),url:S(String(i?.url??"")),color:X(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:g(a?.number?`#${a.number}`:"-"),lastResult:Y(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:g(a?.timestamp?$(Number(a.timestamp)):"-"),lastCompleted:g(c?.number?`#${c.number}`:"-"),lastCompletedResult:Y(c?.result,!1),lastCompletedDate:g(c?.timestamp?$(Number(c.timestamp)):"-")}]);});}function yn(t){return t.split(",").map(n=>n.trim()).filter(Boolean)}function kn(t){let n=t.split("=");if(n.length<2)throw new f(`Invalid --param: "${t}".`,{hint:"Use the format key=value.",example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});let e=n[0].trim(),r=n.slice(1).join("=").trim();if(!e)throw new f(`Invalid --param: "${t}". 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 Lt(t){t.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)",(n,e)=>e.concat(yn(n)),[]).option("--param <key=value>","Extra parameters (repeatable, e.g., --param foo=bar)",(n,e)=>e.concat(n),[]).option("-j, --job <job>","Specify job").action(async n=>{let{config:e,jenkins:r}=await b(),o=k(e.job,n.job),s=String(n.branch??"").trim();if(!s)throw new f("branch is required.",{hint:"Pass a branch with -b or --branch.",example:"jenkins-cli build -b origin/develop -m dev"});let i=(n.mode??[]).map(String).map(h=>h.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new f("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 h of n.param??[]){let{key:d,value:y}=kn(String(h));c[d]=y;}let l=await te();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,p=()=>{u||(u=!0,console.log());},m=a.map(async h=>{let d=Vt(`Triggering build for ${w.yellow(h)}...`).start();try{let y=await ae(r,o,{...c,branch:s,mode:h});p(),d.succeed(`${w.green(h)} - Triggered! Queue: ${y}`);}catch(y){throw p(),d.fail(`${w.red(h)} - ${y.message}`),y}});try{await Promise.all(m);}catch{console.log(w.bold.red(`
|
|
41
41
|
\u274C Some builds failed. Check the output above.
|
|
42
|
-
`)),process.exit(1);}});}function
|
|
42
|
+
`)),process.exit(1);}});}function Tt(t){let n=t.command("view").description("View operations");n.command("list").alias("ls").description("List all views").option("--raw","Output raw JSON").action(async e=>{let{jenkins:r}=await b(),o=await K(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:C(c.name,i),url:S(B(c.url,a))})));}),n.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b(),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:C(a.name??"",s),color:X(a.color),url:S(B(a.url??"",i))})));}),n.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 b();if((await q(o,e)).some(i=>i.name===r)){console.log(w.yellow(`Job "${r}" is already in view "${e}".`));return}await $e(o,e,r),console.log(w.green(`Job "${r}" added to view "${e}".`));}),n.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 b();if(!(await q(o,e)).some(i=>i.name===r))throw new f(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await Je(o,e,r),console.log(w.green(`Job "${r}" removed from view "${e}".`));}),n.command("edit").description("Edit jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b(),[o,s]=await Promise.all([W(r),q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((m,h)=>String(m?.name??"").localeCompare(String(h?.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 fn.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)),p=[...i].filter(m=>m&&!l.has(m));if(!u.length&&!p.length){console.log(w.yellow("No changes to apply."));return}for(let m of u)await $e(r,e,m);for(let m of p)await Je(r,e,m);console.log(w.green(`View "${e}" updated.`));}),n.command("create").description("Create a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b();await Ye(r,e),console.log(w.green(`View "${e}" created.`));}),n.command("delete").alias("del").description("Delete a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b();await Xe(r,e),console.log(w.green(`View "${e}" deleted.`));});}function $n(t){return g(t?$(t):"-")}function be(t){let n=process.argv,e=0;if(t){let r=n.lastIndexOf(t);r>=0&&(e=r+1);}for(let r=e;r<n.length;r+=1){let o=n[r];if(o==="-j"||o==="--job"){let s=n[r+1];return !s||s.startsWith("-")?"":s}}}function Be(t,n,e){let r=n?.parent?.opts()?.job,o=be(e)??be(n?.name())??be("ws")??be("workspace");return t??r??o}function We(t,n){if(n==="")throw new f("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli ws -j my-job"});return k(t,n)}function It(t){return t.replace(/^\/+/,"").replace(/\/+$/,"")}function Jn(t,n){let e=It(t),r=It(n);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function vn(t){let n=process.platform,e="",r=[];n==="darwin"?(e="open",r=[t]):n==="win32"?(e="cmd",r=["/c","start","",t]):(e="xdg-open",r=[t]);try{spawn(e,r,{stdio:"ignore",detached:!0}).unref();}catch{}}async function J(t){try{return await t()}catch(n){if(n?.response?.status===404)return null;throw n}}async function Pn(t,n){try{return await et(t,n)}catch{return}}async function Rn(t,n){try{return await gt(t,n)}catch{return}}async function Nn(t){try{return await K(t,{raw:!1})}catch{return []}}async function Ee(t,n,e){let r=await e.direct(n);if(r!==null)return r;let o=await Pn(t,n);if(o&&o!==n){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 Rn(t,n);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await Nn(t);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 An(t,n,e){return await Ee(t,n,{direct:r=>J(()=>ve(t,r,{path:e})),byViewName:r=>J(()=>it(t,r,n,{path:e})),byViewUrl:r=>J(()=>st(t,r,n,{path:e})),beforeView:async()=>{if(e)return null;let r=n.split("/").pop();return r?await J(()=>ve(t,n,{path:r})):null}})}async function Bn(t,n,e){return await Ee(t,n,{direct:r=>J(()=>at(t,r,e)),byViewName:r=>J(()=>lt(t,r,n,e)),byViewUrl:r=>J(()=>mt(t,r,n,e))})}async function Wn(t,n,e){return await Ee(t,n,{direct:r=>J(()=>ct(t,r,e)),byViewName:r=>J(()=>ut(t,r,n,e)),byViewUrl:r=>J(()=>pt(t,r,n,e))})}function Ot(t){let n=t.command("workspace").alias("ws").description("Workspace operations");n.option("-j, --job <job>","Specify job").option("-p, --path <path>","Workspace sub path").action(async(e,r)=>{let{config:o,jenkins:s}=await b(),i=Be(e.job,r,"workspace"),a=We(o.job,i),c=String(e.path??"").trim(),l=Vt(`Loading workspace for job "${a}"...`).start(),u=await An(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 p=u.map(d=>({name:String(d.name??""),typeRaw:d.type,size:$t(d.size),modified:$n(d.modified),path:Jn(c,String(d.path??d.name??""))})),m=j(p.map(d=>d.name)),h=j(p.map(d=>d.path));console.table(p.map(d=>{let y=B(String(d.name??""),m),D=String(d.typeRaw??"");return {name:D==="dir"?g(w.bold.cyan(y)):D==="file"?g(w.green(y)):g(w.gray(y)),size:d.size,modified:d.modified,path:C(String(d.path??""),h)}}));}),n.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 b(),a=Be(r.job,o,"cat"),c=We(s.job,a),l=String(e??"").trim();if(!l)throw new f("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),p=r.open===!0;if(u||p){let h=await Wn(i,c,l);if(h===null){console.log(w.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let d=u||V.join(Cn.tmpdir(),V.basename(l)||"file");try{u&&await T.pathExists(u)&&(await T.stat(u)).isDirectory()&&(d=V.join(u,V.basename(l)||"file"));}catch{}await T.ensureDir(V.dirname(d)),await T.writeFile(d,h.data),p?(vn(d),console.log(w.green(`Opened: ${d}`))):console.log(w.green(`Saved to: ${d}`));return}let m=await Bn(i,c,l);if(m===null){console.log(w.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(m));}),n.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await b(),i=Be(e.job,r,"wipe"),a=We(o.job,i);try{if(!(await fn.prompt([{type:"confirm",name:"confirm",message:`Wipe workspace for job "${a}"? This cannot be undone.`,default:!1}]))?.confirm){console.log(w.yellow("Cancelled."));return}}catch(l){let u=l?.message?String(l.message):String(l);if((l?.name?String(l.name):"")==="ExitPromptError"||u.toLowerCase().includes("cancelled")||u.toLowerCase().includes("canceled")){console.log(w.yellow("Cancelled."));return}throw l}let c=Vt(`Wiping workspace for job "${a}"...`).start();try{await ft(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function qt(t){vt(t),Rt(t),Ft(t),Ut(t),At(t),Wt(t),St(t),Bt(t),Lt(t),Et(t),Tt(t),Ot(t);}program.name("jenkins-cli").description(Ve).version(Te,"-v, --version").helpOption("-h, --help").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Ct);qt(program);async function En(){try{await program.parseAsync(process.argv);}catch(t){U(t);}}En();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-org/jenkins-cli",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6",
|
|
4
4
|
"description": "Jenkins deployment CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -8,6 +8,13 @@
|
|
|
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": "eslint . --fix && prettier . --write"
|
|
17
|
+
},
|
|
11
18
|
"keywords": [
|
|
12
19
|
"jenkins",
|
|
13
20
|
"cli",
|
|
@@ -59,11 +66,5 @@
|
|
|
59
66
|
"directories": {
|
|
60
67
|
"doc": "docs"
|
|
61
68
|
},
|
|
62
|
-
"types": "./dist/index.d.ts"
|
|
63
|
-
|
|
64
|
-
"build": "tsup",
|
|
65
|
-
"dev": "NODE_ENV=development tsup --watch",
|
|
66
|
-
"link:test": "npm run build && npm link",
|
|
67
|
-
"format": "eslint . --fix && prettier . --write"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
69
|
+
"types": "./dist/index.d.ts"
|
|
70
|
+
}
|