@ts-org/jenkins-cli 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ jenkins-cli
30
30
 
31
31
  CLI 会自动加载配置,列出 Git 分支和部署环境供你选择。
32
32
 
33
- ![Demo](docs/images/demo.png)
33
+ ![Demo](https://unpkg.com/@ts-org/jenkins-cli@latest/docs/images/demo.png)
34
34
 
35
35
  ### ⚙️ 配置
36
36
 
@@ -141,8 +141,17 @@ jenkins-cli job info
141
141
  # 查看指定构建号的日志
142
142
  jenkins-cli log 1234
143
143
 
144
- # 实时跟踪日志
145
- jenkins-cli log 1234 -f
144
+ # 查看最后一次构建日志
145
+ jenkins-cli log last
146
+
147
+ # 查看最后一次构建成功日志
148
+ jenkins-cli log last -s success
149
+
150
+ # 查看最后一次构建失败日志
151
+ jenkins-cli log last -s failed
152
+
153
+ # 仅输出最后 N 行
154
+ jenkins-cli log 1234 -t 200
146
155
  ```
147
156
 
148
157
  #### `stop` - 停止构建
@@ -196,4 +205,4 @@ jenkins-cli whoami
196
205
 
197
206
  **Q: `stop` 或 `queue cancel` 命令返回 403 Forbidden?**
198
207
 
199
- A: 通常是 Jenkins 用户权限不足 (需要 `Job > Cancel` 权限)。请确保你使用的是 API Token 而非密码,并检查 Jenkins 的 CSRF 设置。工具会自动处理 CSRF,但权限问题需要手动排查。
208
+ A: 通常是 Jenkins 用户权限不足 (需要 `Job > Cancel` 权限)。请确保你使用的是 API Token 而非密码,并检查 Jenkins 的 CSRF 设置。工具会自动处理 CSRF,但权限问题需要手动排查。
package/dist/cli.d.ts CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -1,36 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
- import He from 'inquirer';
4
- import f from 'chalk';
5
- import Me from 'ora';
3
+ import Gt from 'inquirer';
4
+ import g from 'chalk';
5
+ import Ot from 'ora';
6
6
  import { exec } from 'child_process';
7
- import Ye, { promisify } from 'util';
7
+ import Ht, { promisify } from 'util';
8
8
  import C from 'fs-extra';
9
- import Re from 'js-yaml';
9
+ import Et from 'js-yaml';
10
10
  import S from 'path';
11
- import Te from 'axios';
12
- import We from 'jiti';
11
+ import Lt from 'axios';
12
+ import Wt from 'jiti';
13
13
  import { fileURLToPath } from 'url';
14
14
  import { XMLParser } from 'fast-xml-parser';
15
15
 
16
- var Q="3.0.1",_="Jenkins deployment CLI";var G=promisify(exec);async function H(){try{let{stdout:n}=await G("git branch --show-current"),e=n.trim();if(!e)throw new Error("Not on any branch (detached HEAD state)");return e}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function V(){try{let{stdout:n}=await G('git branch -r --format="%(refname:short)"');return n.split(`
17
- `).filter(e=>e.includes("/")&&!e.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var P="jenkins-cli.yaml",q="package.json",F="jenkins-cli",Ee="jenkinsCli";function z(n,e=process.cwd()){let t=e;for(;;){let r=S.join(t,n);if(C.existsSync(r))return r;let o=S.dirname(t);if(o===t)return null;t=o;}}function Pe(n=process.cwd()){return z(P,n)}function Be(n=process.cwd()){let e=z(q,n);return e?S.dirname(e):null}function Le(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function Y(n){let e=await C.readFile(n,"utf-8"),t=Re.load(e);if(!t||typeof t!="object")throw new Error(`Invalid config in ${P}: expected a YAML object`);return t}async function Ie(n){let e=S.join(n,q);if(!await C.pathExists(e))return {};let t=await C.readFile(e,"utf-8"),r=JSON.parse(t),o=r[F]??r[Ee];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${q} config: "${F}" must be an object`);return o}async function X(){let n=process.cwd(),e=Be(n)??n,t=S.join(e,P),r={},o=S.dirname(e),s=Pe(o);s&&await C.pathExists(s)&&(r=await Y(s));let i=await Ie(e);r={...r,...i};let a=await C.pathExists(t)?await Y(t):{};if(r={...r,...a},!Le(r))throw new Error(`Config incomplete or not found: tried ${P} in project root, "${F}" in $
18
- {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:e}}var U={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function h(n){return `job/${n.split("/").map(t=>t.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function O(n){let e=Array.isArray(n)?n.find(o=>o?.parameters):void 0,t=Array.isArray(e?.parameters)?e.parameters:[],r={};for(let o of t)o?.name&&(r[String(o.name)]=o?.value);return r}function qe(n){try{let t=new URL(n).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<t.length-1;o++)t[o]==="job"&&t[o+1]&&r.push(decodeURIComponent(t[o+1]));return r.join("/")}catch{return ""}}async function Fe(n,e){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",e).toString())).data??{};return {...r,parameters:O(r.actions)}}function Ue(n,e=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>e?`${r.slice(0,e)}...`:r:""}catch{return ""}}function ee(n){let e=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!e)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,t,r,o,s]=e,i=`${t}://${s.replace(/\/$/,"")}/`,a=Te.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function te(n){try{let e=await n.axios.get("crumbIssuer/api/json"),{data:t}=e;t?.crumbRequestField&&t?.crumb&&(n.axios.defaults.headers.common[t.crumbRequestField]=t.crumb,n.crumbForm={field:t.crumbRequestField,value:t.crumb});let r=e.headers["set-cookie"];if(r){let o=Array.isArray(r)?r.map(s=>s.split(";")[0].trim()):[String(r).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=o.join("; ");}}catch{}}async function J(n,e){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(e){let o=e.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function N(n,e){let t=await n.axios.get(`${h(e)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(t.data?.builds)?t.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:O(o.actions)}))}async function B(n){let e=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),t=Array.isArray(e.data?.computer)?e.data.computer:[],r=[];for(let i of t){let a=Array.isArray(i?.executors)?i.executors:[],l=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let u of [...a,...l]){let c=u?.currentExecutable;c?.url&&r.push({number:Number(c.number),url:String(c.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await Fe(n,i.url),l=qe(String(a?.url??i.url));return {...a,job:l}}))).filter(i=>i?.building)}async function A(n,e){return await n.axios.post("queue/cancelItem",new URLSearchParams({id:e.toString()}),U),!0}async function R(n,e,t){try{await n.axios.post(`${h(e)}/${t}/stop/`,new URLSearchParams({}),U);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(f.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(f.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${t} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function L(n,e,t){let r="branch",o="mode",s=t[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([J(n,e),N(n,e)]),l=(m,p)=>{let y=m.actions?.find(v=>v.parameters);return y?y.parameters.find(v=>v.name===p)?.value:void 0},u=t[r];if(s&&u){let m=i.find(p=>l(p,o)===s&&l(p,r)===u);if(m)return console.log(`Build already in queue (ID: ${m.id}). Skipping trigger.`),new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let c=!1;if(s)for(let m of a)l(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await R(n,e,m.number),c=!0);c&&await new Promise(m=>setTimeout(m,1e3)),s&&console.log(`Triggering new build for mode=${s}...`);try{return (await n.axios.post(`${h(e)}/buildWithParameters/`,new URLSearchParams({...t}),U)).headers.location||""}catch(m){let p=m?.response?.status,y=Ue(m?.response?.data);if(p===400){let w=[];w.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),w.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let v=y?`
16
+ var _="3.0.2",G="Jenkins deployment CLI";var V=promisify(exec);async function H(){try{let{stdout:n}=await V("git branch --show-current"),t=n.trim();if(!t)throw new Error("Not on any branch (detached HEAD state)");return t}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function Y(){try{let{stdout:n}=await V('git branch -r --format="%(refname:short)"');return n.split(`
17
+ `).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var P="jenkins-cli.yaml",T="package.json",U="jenkins-cli",vt="jenkinsCli";function X(n,t=process.cwd()){let e=t;for(;;){let r=S.join(e,n);if(C.existsSync(r))return r;let o=S.dirname(e);if(o===e)return null;e=o;}}function Nt(n=process.cwd()){return X(P,n)}function Pt(n=process.cwd()){let t=X(T,n);return t?S.dirname(t):null}function Bt(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function z(n){let t=await C.readFile(n,"utf-8"),e=Et.load(t);if(!e||typeof e!="object")throw new Error(`Invalid config in ${P}: expected a YAML object`);return e}async function It(n){let t=S.join(n,T);if(!await C.pathExists(t))return {};let e=await C.readFile(t,"utf-8"),r=JSON.parse(e),o=r[U]??r[vt];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${T} config: "${U}" must be an object`);return o}async function Z(){let n=process.cwd(),t=Pt(n)??n,e=S.join(t,P),r={},o=S.dirname(t),s=Nt(o);s&&await C.pathExists(s)&&(r=await z(s));let i=await It(t);r={...r,...i};let a=await C.pathExists(e)?await z(e):{};if(r={...r,...a},!Bt(r))throw new Error(`Config incomplete or not found: tried ${P} in project root, "${U}" in $
18
+ {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:t}}var O={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function w(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function D(n){let t=Array.isArray(n)?n.find(o=>o?.parameters):void 0,e=Array.isArray(t?.parameters)?t.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function Ft(n){try{let e=new URL(n).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<e.length-1;o++)e[o]==="job"&&e[o+1]&&r.push(decodeURIComponent(e[o+1]));return r.join("/")}catch{return ""}}async function qt(n,t){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",t).toString())).data??{};return {...r,parameters:D(r.actions)}}function Tt(n,t=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>t?`${r.slice(0,t)}...`:r:""}catch{return ""}}function et(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,e,r,o,s]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=Lt.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function nt(n){try{let t=await n.axios.get("crumbIssuer/api/json"),{data:e}=t;e?.crumbRequestField&&e?.crumb&&(n.axios.defaults.headers.common[e.crumbRequestField]=e.crumb,n.crumbForm={field:e.crumbRequestField,value:e.crumb});let r=t.headers["set-cookie"];if(r){let o=Array.isArray(r)?r.map(s=>s.split(";")[0].trim()):[String(r).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=o.join("; ");}}catch{}}async function J(n,t){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(t){let o=t.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function A(n,t){let e=await n.axios.get(`${w(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:D(o.actions)}))}async function B(n){let t=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(t.data?.computer)?t.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],u=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let l of [...a,...u]){let c=l?.currentExecutable;c?.url&&r.push({number:Number(c.number),url:String(c.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await qt(n,i.url),u=Ft(String(a?.url??i.url));return {...a,job:u}}))).filter(i=>i?.building)}async function E(n,t){return await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),O),!0}async function v(n,t,e){try{await n.axios.post(`${w(t)}/${e}/stop/`,new URLSearchParams({}),O);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(g.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(g.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function I(n,t,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([J(n,t),A(n,t)]),u=(m,d)=>{let b=m.actions?.find(R=>R.parameters);return b?b.parameters.find(R=>R.name===d)?.value:void 0},l=e[r];if(s&&l){let m=i.find(d=>u(d,o)===s&&u(d,r)===l);if(m)return console.log(`Build already in queue (ID: ${m.id}). Skipping trigger.`),new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let c=!1;if(s)for(let m of a)u(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await v(n,t,m.number),c=!0);c&&await new Promise(m=>setTimeout(m,1e3)),s&&console.log(`Triggering new build for mode=${s}...`);try{return (await n.axios.post(`${w(t)}/buildWithParameters/`,new URLSearchParams({...e}),O)).headers.location||""}catch(m){let d=m?.response?.status,b=Tt(m?.response?.data);if(d===400){let y=[];y.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),y.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let R=b?`
19
19
  Jenkins response (snippet):
20
- ${y}`:"";throw new Error(`Request failed with status code 400.${v}
21
- ${w.join(`
22
- `)}`)}if(p){let w=y?`
20
+ ${b}`:"";throw new Error(`Request failed with status code 400.${R}
21
+ ${y.join(`
22
+ `)}`)}if(d){let y=b?`
23
23
  Jenkins response (snippet):
24
- ${y}`:"";throw new Error(`Request failed with status code ${p}.${w}`)}throw m}}async function ne(n){return (await n.axios.get("whoAmI/api/json")).data}async function re(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function oe(n,e){return (await n.axios.get(`${h(e)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration]`)).data}async function ie(n,e,t){let r=Math.max(1,t?.limit??20);return ((await n.axios.get(`${h(e)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:O(i.actions)}))}async function se(n,e,t){let r=await n.axios.get(`${h(e)}/${t}/consoleText`,{responseType:"text"});return String(r.data??"")}async function ae(n,e,t,r){let o=Math.max(0,r?.start??0),s=Math.max(200,r?.intervalMs??1e3);for(;;){let i=await n.axios.get(`${h(e)}/${t}/logText/progressiveText?start=${o}`,{responseType:"text"}),a=String(i.data??"");a&&process.stdout.write(a);let l=i.headers["x-text-size"],u=i.headers["x-more-data"],c=l?Number(l):NaN;if(Number.isNaN(c)||(o=c),!(String(u??"").toLowerCase()==="true"))break;await new Promise(p=>setTimeout(p,s));}}async function ce(n,e){return ((await n.axios.get(`${h(e)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function le(n,e){let t=await n.axios.get(`${h(e)}/config.xml`,{responseType:"text"});return String(t.data??"")}async function g(){let n=Me("Loading configuration...").start();try{let e=await X();n.succeed("Configuration loaded");let t=ee(e.apiToken);return await te(t),{config:e,jenkins:t}}catch(e){n.fail(f.red(e.message)),process.exit(1);}}var Qe=We(fileURLToPath(import.meta.url),{interopDefault:!0});async function M(n){return await Promise.resolve(n)}function _e(n){let e=String(n??"").trim();if(!e)return null;let t=e.split(":");if(t.length<2)return null;if(t.length===2){let[i,a]=t;return !i||!a?null:{file:i,kind:"Var",exportName:a}}let r=t.at(-2),o=t.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let s=t.slice(0,-2).join(":");return s?{file:s,kind:r,exportName:o}:null}function Ge(n,e){return S.isAbsolute(e)?e:S.join(n,e)}function de(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function pe(n,e){let t=_e(e);if(!t)return null;let r=Ge(n,t.file);if(!await C.pathExists(r))throw new Error(`configs reference not found: ${t.file}`);let o=Qe(r),s=de(o)?o:{default:o},i=t.exportName==="default"?s.default:s[t.exportName];if(i===void 0)throw new Error(`configs reference export not found: ${e}`);return {kind:t.kind,value:i}}async function me(n,e){if(typeof e!="string")return e;let t=await pe(n,e);if(!t)return e;if(t.kind==="Var"){if(typeof t.value=="function"){let o=t.value();return await M(o)}return await M(t.value)}if(typeof t.value!="function")throw new Error(`configs reference expected a function: ${e}`);let r=t.value();return await M(r)}async function fe(n,e){let t=Array.isArray(n)?n:[];if(t.length===0)return [];let r=String(e.projectRoot??"").trim()||process.cwd(),o=[];for(let s of t){if(!de(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await me(r,i.choices);let a=String(i.type??"").toLowerCase(),l=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof l!="function"&&!Array.isArray(l))if(l==null)i.choices=[];else if(typeof l=="string"||typeof l=="number"||typeof l=="boolean")i.choices=[String(l)];else if(typeof l[Symbol.iterator]=="function")i.choices=Array.from(l).map(c=>String(c));else throw new Error(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof l})`)}if(i.default!==void 0&&(i.default=await me(r,i.default)),i.validate!==void 0){let a=i.validate;if(typeof a=="string"){let l=await pe(r,a);if(!l)i.validate=a;else if(l.kind==="Var"){if(typeof l.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=l.value;}else {if(typeof l.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=(u,c)=>l.value(u,c);}}else typeof a=="function"&&(i.validate=a);}o.push(i);}return o}function ge(n,e){let t=new Set(e),r={};for(let[o,s]of Object.entries(n))if(!t.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function be(){console.log(f.bold.blue(`
24
+ ${b}`:"";throw new Error(`Request failed with status code ${d}.${y}`)}throw m}}async function rt(n){return (await n.axios.get("whoAmI/api/json")).data}async function ot(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function L(n,t){return (await n.axios.get(`${w(t)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration],lastSuccessfulBuild[number,url,building,result,timestamp,duration],lastFailedBuild[number,url,building,result,timestamp,duration]`)).data}async function it(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${w(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:D(i.actions)}))}async function st(n,t,e){let r=await n.axios.get(`${w(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function at(n,t){return ((await n.axios.get(`${w(t)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function ct(n,t){let e=await n.axios.get(`${w(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function f(){let n=Ot("Loading configuration...").start();try{let t=await Z();n.succeed("Configuration loaded");let e=et(t.apiToken);return await nt(e),{config:t,jenkins:e}}catch(t){n.fail(g.red(t.message)),process.exit(1);}}var Kt=Wt(fileURLToPath(import.meta.url),{interopDefault:!0});async function W(n){return await Promise.resolve(n)}function Qt(n){let t=String(n??"").trim();if(!t)return null;let e=t.split(":");if(e.length<2)return null;if(e.length===2){let[i,a]=e;return !i||!a?null:{file:i,kind:"Var",exportName:a}}let r=e.at(-2),o=e.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let s=e.slice(0,-2).join(":");return s?{file:s,kind:r,exportName:o}:null}function _t(n,t){return S.isAbsolute(t)?t:S.join(n,t)}function mt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function dt(n,t){let e=Qt(t);if(!e)return null;let r=_t(n,e.file);if(!await C.pathExists(r))throw new Error(`configs reference not found: ${e.file}`);let o=Kt(r),s=mt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new Error(`configs reference export not found: ${t}`);return {kind:e.kind,value:i}}async function lt(n,t){if(typeof t!="string")return t;let e=await dt(n,t);if(!e)return t;if(e.kind==="Var"){if(typeof e.value=="function"){let o=e.value();return await W(o)}return await W(e.value)}if(typeof e.value!="function")throw new Error(`configs reference expected a function: ${t}`);let r=e.value();return await W(r)}async function pt(n,t){let e=Array.isArray(n)?n:[];if(e.length===0)return [];let r=String(t.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!mt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await lt(r,i.choices);let a=String(i.type??"").toLowerCase(),u=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof u!="function"&&!Array.isArray(u))if(u==null)i.choices=[];else if(typeof u=="string"||typeof u=="number"||typeof u=="boolean")i.choices=[String(u)];else if(typeof u[Symbol.iterator]=="function")i.choices=Array.from(u).map(c=>String(c));else throw new Error(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof u})`)}if(i.default!==void 0&&(i.default=await lt(r,i.default)),i.validate!==void 0){let a=i.validate;if(typeof a=="string"){let u=await dt(r,a);if(!u)i.validate=a;else if(u.kind==="Var"){if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=u.value;}else {if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=(l,c)=>u.value(l,c);}}else typeof a=="function"&&(i.validate=a);}o.push(i);}return o}function ft(n,t){let e=new Set(t),r={};for(let[o,s]of Object.entries(n))if(!e.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function gt(){console.log(g.bold.blue(`
25
25
  \u{1F680} Jenkins CLI - Jenkins Deployment CLI
26
- `));let{config:n,jenkins:e}=await g(),[t,r]=await Promise.all([V(),H()]),o=()=>{console.log(f.yellow(`
27
- \u{1F44B} Cancelled by user`)),process.exit(130);},s,i={};try{process.prependOnceListener("SIGINT",o);let l=await fe(n.configs,{projectRoot:n.projectRoot}),u=new Set(["branch","modes","mode"]);for(let m of l){let p=String(m?.name??"").trim();if(u.has(p))throw new Error(`configs name "${p}" is reserved. Use another name.`)}let c=await He.prompt([{type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:t,default:t.indexOf(r)},{type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:n.modes,validate:m=>m.length===0?"You must select at least one environment":!0},...l]);s={branch:String(c.branch??""),modes:c.modes??[]},i=ge(c,["branch","modes"]);}catch(l){let u=l,c=u?.message?String(u.message):String(u);((u?.name?String(u.name):"")==="ExitPromptError"||c.toLowerCase().includes("cancelled")||c.toLowerCase().includes("canceled"))&&(console.log(f.yellow(`
28
- \u{1F44B} Cancelled by user`)),process.exit(0)),console.log(f.red(`
29
- \u274C Prompt failed: ${c}
30
- `)),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let a=s.modes.map(async l=>{let u=Me(`Triggering build for ${f.yellow(l)}...`).start();try{let c=await L(e,n.job,{...i,branch:s.branch,mode:l});u.succeed(`${f.green(l)} - Triggered! Queue: ${c}`);}catch(c){throw u.fail(`${f.red(l)} - ${c.message}`),c}});try{await Promise.all(a),console.log();}catch{console.log(f.bold.red(`
26
+ `));let{config:n,jenkins:t}=await f(),[e,r]=await Promise.all([Y(),H()]),o=()=>{console.log(g.yellow(`
27
+ \u{1F44B} Cancelled by user`)),process.exit(130);},s,i={};try{process.prependOnceListener("SIGINT",o);let c=await pt(n.configs,{projectRoot:n.projectRoot}),m=new Set(["branch","modes","mode"]);for(let b of c){let y=String(b?.name??"").trim();if(m.has(y))throw new Error(`configs name "${y}" is reserved. Use another name.`)}let d=await Gt.prompt([{type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},{type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:n.modes,validate:b=>b.length===0?"You must select at least one environment":!0},...c]);s={branch:String(d.branch??""),modes:d.modes??[]},i=ft(d,["branch","modes"]);}catch(c){let m=c,d=m?.message?String(m.message):String(m);((m?.name?String(m.name):"")==="ExitPromptError"||d.toLowerCase().includes("cancelled")||d.toLowerCase().includes("canceled"))&&(console.log(g.yellow(`
28
+ \u{1F44B} Cancelled by user`)),process.exit(0)),console.log(g.red(`
29
+ \u274C Prompt failed: ${d}
30
+ `)),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let a=!1,u=()=>{a||(a=!0,console.log());},l=s.modes.map(async c=>{let m=Ot(`Triggering build for ${g.yellow(c)}...`).start();try{let d=await I(t,n.job,{...i,branch:s.branch,mode:c});u(),m.succeed(`${g.green(c)} - Triggered! Queue: ${d}`);}catch(d){throw u(),m.fail(`${g.red(c)} - ${d.message}`),d}});try{await Promise.all(l);}catch{console.log(g.bold.red(`
31
31
  \u274C Some builds failed. Check the output above.
32
- `)),process.exit(1);}}function b(n,e){return (e??"").trim()||n}function k(n,e){let t=Number(n);if(!Number.isInteger(t)||t<0)throw new Error(`${e} must be a non-negative integer`);return t}function he(n){let e=n.command("queue").description("\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u76F8\u5173\u64CD\u4F5C");e.command("list").description("\u83B7\u53D6\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u5217\u8868").option("-j, --job <job>","\u4EC5\u663E\u793A\u6307\u5B9A Job \u7684\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879").action(async t=>{let{config:r,jenkins:o}=await g(),s=t.job?b(r.job,t.job):void 0,i=await J(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),e.command("cancel").description("\u53D6\u6D88\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u9879").argument("<id>").action(async t=>{let{jenkins:r}=await g(),o=await A(r,k(t,"id"));console.log(o?f.green("Cancelled"):f.red("Cancel failed"));});}function d(n){return {[Ye.inspect.custom]:()=>n}}function j(n){let e=String(n??""),t=e.trim();if(!t)return d(f.gray("-"));let r=s=>d(s),o=t.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(t).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",l=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||l?f.cyanBright(e):f.hex("#4EA1FF")(e))}catch{return r(f.hex("#4EA1FF")(e))}return o.startsWith("ws://")||o.startsWith("wss://")?r(f.cyan(e)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(f.magenta(e)):o.startsWith("file://")?r(f.yellow(e)):o.startsWith("mailto:")?r(f.green(e)):o.startsWith("javascript:")||o.startsWith("data:")?r(f.red(e)):t.startsWith("/")||t.startsWith("./")||t.startsWith("../")?r(f.cyan(e)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(t)?r(f.blue(e)):r(e)}function $(n){let e=new Date(n),t=r=>String(r).padStart(2,"0");return `${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}function E(n,e){if(e===!0)return d(f.cyan("BUILDING"));let t=String(n??"").toUpperCase();return d(t?t==="SUCCESS"?f.green(t):t==="ABORTED"?f.gray(t):t==="FAILURE"?f.red(t):t==="UNSTABLE"?f.yellow(t):t==="NOT_BUILT"?f.gray(t):t:"-")}function D(n){let e=String(n??"");if(!e)return d("-");let t=e.endsWith("_anime"),r=t?e.slice(0,-6):e,o=(()=>{switch(r){case"blue":return t?f.blueBright(e):f.blue(e);case"red":return t?f.redBright(e):f.red(e);case"yellow":return t?f.yellowBright(e):f.yellow(e);case"aborted":return f.gray(e);case"disabled":case"notbuilt":case"grey":case"gray":return f.gray(e);default:return e}})();return d(o)}function ze(n){let e=Object.entries(n??{}).filter(([r])=>r);if(!e.length)return d("-");e.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"}));let t=e.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ");return d(t)}function we(n){let e=n.command("builds").description("\u6784\u5EFA\u76F8\u5173\u64CD\u4F5C");e.command("list").description("\u83B7\u53D6\u6784\u5EFA\u5217\u8868").option("-j, --job <job>","\u6307\u5B9A job").option("-n, --limit <n>","\u6570\u91CF","20").action(async t=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job),i=await ie(o,s,{limit:Number(t.limit)});console.table(i.map(a=>({number:a.number,result:E(a.result,a.building),building:a.building,durationS:Number((Number(a.duration??0)/1e3).toFixed(3)),date:d($(Number(a.timestamp??0)))})));}),e.command("running").description("\u83B7\u53D6\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-j, --job <job>","\u6307\u5B9A job").option("-a, --all","\u67E5\u8BE2 Jenkins \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async t=>{let{config:r,jenkins:o}=await g(),s=t.all?await B(o):await N(o,b(r.job,t.job));console.table((s??[]).map(i=>({...t.all?{job:d(String(i.job??""))}:{},number:i.number,date:d(i.timestamp?$(Number(i.timestamp)):"-"),parameters:ze(i.parameters),url:j(String(i.url??""))})));});}function xe(n){n.command("log").description("\u83B7\u53D6\u6784\u5EFA\u63A7\u5236\u53F0\u65E5\u5FD7").argument("<id>").option("-j, --job <job>","\u6307\u5B9A job").option("--tail <n>","\u4EC5\u8F93\u51FA\u6700\u540E N \u884C").option("-f, --follow","\u6301\u7EED\u8F93\u51FA\uFF08\u76F4\u5230 Jenkins \u8868\u793A\u65E0\u66F4\u591A\u65E5\u5FD7\uFF09").option("--interval <ms>","follow \u62C9\u53D6\u95F4\u9694","1000").action(async(e,t)=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job),i=k(e,"id");if(t.follow){await ae(o,s,i,{intervalMs:Number(t.interval)});return}let a=await se(o,s,i);if(t.tail){let l=k(t.tail,"tail"),u=a.split(`
33
- `);console.log(u.slice(Math.max(0,u.length-l)).join(`
34
- `));return}process.stdout.write(a);});}function ke(n){n.command("stop").description("\u6E05\u7406\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u4E2D\u7684\u4EFB\u52A1\u5E76\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").argument("[id]","\u6784\u5EFA\u7F16\u53F7").option("-j, --job <job>","\u6307\u5B9A Jenkins Job").option("-a, --all","\u53D6\u6D88\u8BE5 Job \u7684\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u8BE5 Jenkins Job \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-A, --ALL","\u53D6\u6D88 Jenkins \u4E0A\u6240\u6709\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async(e,t)=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job);if(t.ALL){let[i,a]=await Promise.all([J(o),B(o)]),l=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),u=(a??[]).map(c=>({number:Number(c.number),job:String(c.job??"")})).filter(c=>c.job&&!Number.isNaN(c.number));for(let c of l)await A(o,c);for(let c of u)await R(o,c.job,c.number);console.log(f.green(`Requested: cancelled ${l.length} queue item(s), stopped ${u.length} running build(s).`));return}if(t.all){let[i,a]=await Promise.all([J(o,s),N(o,s)]),l=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),u=(a??[]).map(c=>Number(c.number)).filter(c=>!Number.isNaN(c));for(let c of l)await A(o,c);for(let c of u)await R(o,s,c);console.log(f.green(`Requested: cancelled ${l.length} queue item(s), stopped ${u.length} running build(s).`));return}if(!e){console.log(f.red("Missing build id. Provide <id> or use -a/--all."));return}await R(o,s,k(e,"id")),console.log(f.green("Stop requested"));});}function je(n){n.command("params").description("\u83B7\u53D6 job \u53C2\u6570\u5B9A\u4E49").option("-j, --job <job>","\u6307\u5B9A job").action(async e=>{let{config:t,jenkins:r}=await g(),o=b(t.job,e.job),s=await ce(r,o),i=Math.max(0,...s.map(a=>String(a?.name??"").length));console.table((s??[]).map(a=>({name:d(String(a?.name??"").padEnd(i," ")),type:d(String(a?.type??"")),description:d(String(a?.description??"")),default:d(a?.default===void 0?"-":String(a.default)),choices:d(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Ce(n){n.command("whoami").description("\u67E5\u770B\u5F53\u524D Token \u5BF9\u5E94\u7684 Jenkins \u7528\u6237\u4FE1\u606F").action(async()=>{let{jenkins:e}=await g(),t=await ne(e),r=t&&typeof t=="object"?t:{value:t},o=(u,c)=>{let m=u.length;if(m>=c)return u;let p=c-m,y=Math.floor(p/2),w=p-y;return `${" ".repeat(y)}${u}${" ".repeat(w)}`},s=r.name??r.fullName??r.id??"",i=s==null?"":String(s),a=Math.max(20,i.length),l={};l.name=d(o(i,a));for(let u of Object.keys(r).sort((c,m)=>c.localeCompare(m,"en"))){if(u==="name")continue;let c=r[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof c=="string"||typeof c=="number")){l[u]=j(String(c));continue}let y=c==null?"":typeof c=="object"?JSON.stringify(c):String(c);l[u]=d(y);}console.table([l]);});}function Se(n){n.command("config").description("\u8BFB\u53D6 Jenkins Job \u914D\u7F6E").command("read").description("\u8BFB\u53D6 job \u914D\u7F6E").option("-j, --job <job>","\u6307\u5B9A job").option("-f, --format <format>","\u8F93\u51FA\u683C\u5F0F\uFF1Axml \u6216 json","xml").action(async t=>{let{config:r,jenkins:o}=await g(),s=b(r.job,t.job),i=String(t.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new Error(`Invalid format: ${t.format}. Expected: xml or json`);let a=await le(o,s);if(i==="xml"){process.stdout.write(a);return}let u=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(u,null,2));});}function Je(n){let e=n.command("job").description("Job \u76F8\u5173\u64CD\u4F5C"),t=(r,o)=>{let s=String(o??"").trim();if(!s)return !0;if(!/[*?]/.test(s))return r.includes(s);let l=`^${s.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(l).test(r)}catch{return r.includes(s)}};e.command("list").description("\u83B7\u53D6\u6240\u6709 job").option("--search <keyword>","\u6309\u540D\u79F0\u8FC7\u6EE4\uFF08\u652F\u6301 glob\uFF09").action(async r=>{let{jenkins:o}=await g(),s=await re(o),i=(r.search??"").trim(),a=i?s.filter(c=>t(String(c.name??""),i)):s;a.sort((c,m)=>String(c?.name??"").localeCompare(String(m?.name??""),"en",{sensitivity:"base"}));let l=Math.max(0,...a.map(c=>String(c?.name??"").length)),u=Math.max(0,...a.map(c=>String(c?.url??"").length));console.table((a??[]).map(c=>({name:d(String(c.name??"").padEnd(l," ")),color:D(c.color),url:j(String(c.url??"").padEnd(u," "))})));}),e.command("info").description("\u83B7\u53D6 job \u4FE1\u606F").option("-j, --job <job>","\u6307\u5B9A job").option("--json","\u8F93\u51FA\u539F\u59CB JSON").action(async r=>{let{config:o,jenkins:s}=await g(),i=b(o.job,r.job),a=await oe(s,i);if(r.json){console.log(JSON.stringify(a,null,2));return}let l=a?.lastBuild,u=a?.lastCompletedBuild,c=Array.isArray(a?.healthReport)?a.healthReport:[];console.table([{name:d(String(a?.name??"")),url:j(String(a?.url??"")),color:D(a?.color),buildable:a?.buildable,inQueue:a?.inQueue,nextBuildNumber:a?.nextBuildNumber,healthScore:c[0]?.score??"-",lastBuild:d(l?.number?`#${l.number}`:"-"),lastResult:E(l?.result,l?.building),lastDurationS:l?.duration===void 0?"-":Number((Number(l.duration)/1e3).toFixed(3)),lastDate:d(l?.timestamp?$(Number(l.timestamp)):"-"),lastCompleted:d(u?.number?`#${u.number}`:"-"),lastCompletedResult:E(u?.result,!1),lastCompletedDate:d(u?.timestamp?$(Number(u.timestamp)):"-")}]);});}function et(n){return n.split(",").map(e=>e.trim()).filter(Boolean)}function tt(n){let e=n.indexOf("=");if(e<=0)throw new Error(`Invalid --param: "${n}". Expected: key=value`);let t=n.slice(0,e).trim(),r=n.slice(e+1).trim();if(!t)throw new Error(`Invalid --param: "${n}". Key is empty`);return {key:t,value:r}}function $e(n){n.command("trigger").description("\u975E\u4EA4\u4E92\u89E6\u53D1 Jenkins \u6784\u5EFA").requiredOption("-b, --branch <branch>","\u5206\u652F\uFF08\u4F8B\u5982 origin/develop\uFF09").option("-m, --mode <mode>","\u90E8\u7F72\u73AF\u5883\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u6216\u7528\u9017\u53F7\u5206\u9694\uFF1A-m dev -m uat / -m dev,uat\uFF09",(e,t)=>t.concat(et(e)),[]).option("--param <key=value>","\u989D\u5916\u53C2\u6570\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4F8B\u5982\uFF1A--param foo=bar\uFF09",(e,t)=>t.concat(e),[]).option("-j, --job <job>","\u6307\u5B9A job").action(async e=>{let{config:t,jenkins:r}=await g(),o=b(t.job,e.job),s=String(e.branch??"").trim();if(!s)throw new Error("branch is required");let i=(e.mode??[]).map(String).map(c=>c.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new Error("mode is required. Example: jenkins-cli trigger -b origin/develop -m dev -m uat");let l={};for(let c of e.param??[]){let{key:m,value:p}=tt(String(c));l[m]=p;}console.log();let u=a.map(async c=>{let m=Me(`Triggering build for ${f.yellow(c)}...`).start();try{let p=await L(r,o,{...l,branch:s,mode:c});m.succeed(`${f.green(c)} - Triggered! Queue: ${p}`);}catch(p){throw m.fail(`${f.red(c)} - ${p.message}`),p}});try{await Promise.all(u),console.log();}catch{console.log(f.bold.red(`
32
+ `)),process.exit(1);}}function h(n,t){return (t??"").trim()||n}function x(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new Error(`${t} must be a non-negative integer`);return e}function yt(n){let t=n.command("queue").description("\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u76F8\u5173\u64CD\u4F5C");t.command("list").alias("ls").description("\u83B7\u53D6\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u5217\u8868").option("-j, --job <job>","\u4EC5\u663E\u793A\u6307\u5B9A Job \u7684\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879").action(async e=>{let{config:r,jenkins:o}=await f(),s=e.job?h(r.job,e.job):void 0,i=await J(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),t.command("cancel").description("\u53D6\u6D88\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u9879").argument("<id>").action(async e=>{let{jenkins:r}=await f(),o=await E(r,x(e,"id"));console.log(o?g.green("Cancelled"):g.red("Cancel failed"));});}function p(n){return {[Ht.inspect.custom]:()=>n}}function k(n){let t=String(n??""),e=t.trim();if(!e)return p(g.gray("-"));let r=s=>p(s),o=e.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(e).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",u=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||u?g.cyanBright(t):g.hex("#4EA1FF")(t))}catch{return r(g.hex("#4EA1FF")(t))}return o.startsWith("ws://")||o.startsWith("wss://")?r(g.cyan(t)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(g.magenta(t)):o.startsWith("file://")?r(g.yellow(t)):o.startsWith("mailto:")?r(g.green(t)):o.startsWith("javascript:")||o.startsWith("data:")?r(g.red(t)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(g.cyan(t)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(g.blue(t)):r(t)}function $(n){let t=new Date(n),e=r=>String(r).padStart(2,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function N(n,t){if(t===!0)return p(g.cyan("BUILDING"));let e=String(n??"").toUpperCase();return p(e?e==="SUCCESS"?g.green(e):e==="ABORTED"?g.gray(e):e==="FAILURE"?g.red(e):e==="UNSTABLE"?g.yellow(e):e==="NOT_BUILT"?g.gray(e):e:"-")}function M(n){let t=String(n??"");if(!t)return p("-");let e=t.endsWith("_anime"),r=e?t.slice(0,-6):t,o=(()=>{switch(r){case"blue":return e?g.blueBright(t):g.blue(t);case"red":return e?g.redBright(t):g.red(t);case"yellow":return e?g.yellowBright(t):g.yellow(t);case"aborted":return g.gray(t);case"disabled":case"notbuilt":case"grey":case"gray":return g.gray(t);default:return t}})();return p(o)}function Yt(n){let t=Object.entries(n??{}).filter(([r])=>r);if(!t.length)return p("-");t.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"}));let e=t.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ");return p(e)}function ht(n){let t=n.command("builds").description("\u6784\u5EFA\u76F8\u5173\u64CD\u4F5C");t.command("list").alias("ls").description("\u83B7\u53D6\u6784\u5EFA\u5217\u8868").option("-j, --job <job>","\u6307\u5B9A job").option("-n, --limit <n>","\u6570\u91CF","20").action(async e=>{let{config:r,jenkins:o}=await f(),s=h(r.job,e.job),i=await it(o,s,{limit:Number(e.limit)});console.table(i.map(a=>({number:a.number,result:N(a.result,a.building),building:a.building,durationS:Number((Number(a.duration??0)/1e3).toFixed(3)),date:p($(Number(a.timestamp??0)))})));}),t.command("running").description("\u83B7\u53D6\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-j, --job <job>","\u6307\u5B9A job").option("-a, --all","\u67E5\u8BE2 Jenkins \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async e=>{let{config:r,jenkins:o}=await f(),s=e.all?await B(o):await A(o,h(r.job,e.job));console.table((s??[]).map(i=>({...e.all?{job:p(String(i.job??""))}:{},number:i.number,date:p(i.timestamp?$(Number(i.timestamp)):"-"),parameters:Yt(i.parameters),url:k(String(i.url??""))})));});}function wt(n){let t=n.command("log").description("\u83B7\u53D6\u6784\u5EFA\u63A7\u5236\u53F0\u65E5\u5FD7"),e=async(r,o,s)=>{let{jenkins:i}=await f(),a=await st(i,r,o);if(s.tail){let u=x(s.tail,"tail"),l=a.split(`
33
+ `);console.log(l.slice(Math.max(0,l.length-u)).join(`
34
+ `));return}process.stdout.write(a);};t.argument("<id>").option("-j, --job <job>","\u6307\u5B9A job").option("-t, --tail <n>","\u4EC5\u8F93\u51FA\u6700\u540E N \u884C").action(async(r,o)=>{let{config:s}=await f(),i=h(s.job,o.job),a=x(r,"id");await e(i,a,o);}),t.command("last").description("\u83B7\u53D6\u6700\u540E\u4E00\u6B21\u6784\u5EFA\u65E5\u5FD7").option("-j, --job <job>","\u6307\u5B9A job").option("-s, --status <status>","success | failed | any","any").option("-t, --tail <n>","\u4EC5\u8F93\u51FA\u6700\u540E N \u884C").action(async r=>{let{config:o,jenkins:s}=await f(),i=h(o.job,r.job),a=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(a))throw new Error(`Invalid status: ${r.status}. Use success | failed | any.`);let u=await L(s,i),l=a==="success"?u?.lastSuccessfulBuild:a==="failed"?u?.lastFailedBuild:u?.lastBuild,c=Number(l?.number);if(!c){let m=a==="success"?"successful":a==="failed"?"failed":"last";throw new Error(`No ${m} build found for job: ${i}`)}await e(i,c,r);});}function jt(n){n.command("stop").description("\u6E05\u7406\u7B49\u5F85\u6784\u5EFA\u7684\u961F\u5217\u4E2D\u7684\u4EFB\u52A1\u5E76\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").argument("[id]","\u6784\u5EFA\u7F16\u53F7").option("-j, --job <job>","\u6307\u5B9A Jenkins Job").option("-a, --all","\u53D6\u6D88\u8BE5 Job \u7684\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u8BE5 Jenkins Job \u4E0B\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").option("-A, --ALL","\u53D6\u6D88 Jenkins \u4E0A\u6240\u6709\u7B49\u5F85\u6784\u5EFA\u961F\u5217\u9879\uFF0C\u5E76\u505C\u6B62\u6240\u6709\u6B63\u5728\u8FD0\u884C\u7684\u6784\u5EFA").action(async(t,e)=>{let{config:r,jenkins:o}=await f(),s=h(r.job,e.job);if(e.ALL){let[i,a]=await Promise.all([J(o),B(o)]),u=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),l=(a??[]).map(c=>({number:Number(c.number),job:String(c.job??"")})).filter(c=>c.job&&!Number.isNaN(c.number));for(let c of u)await E(o,c);for(let c of l)await v(o,c.job,c.number);console.log(g.green(`Requested: cancelled ${u.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([J(o,s),A(o,s)]),u=(i??[]).map(c=>Number(c.id)).filter(c=>!Number.isNaN(c)),l=(a??[]).map(c=>Number(c.number)).filter(c=>!Number.isNaN(c));for(let c of u)await E(o,c);for(let c of l)await v(o,s,c);console.log(g.green(`Requested: cancelled ${u.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!t){console.log(g.red("Missing build id. Provide <id> or use -a/--all."));return}await v(o,s,x(t,"id")),console.log(g.green("Stop requested"));});}function xt(n){n.command("params").description("\u83B7\u53D6 job \u53C2\u6570\u5B9A\u4E49").option("-j, --job <job>","\u6307\u5B9A job").action(async t=>{let{config:e,jenkins:r}=await f(),o=h(e.job,t.job),s=await at(r,o),i=Math.max(0,...s.map(a=>String(a?.name??"").length));console.table((s??[]).map(a=>({name:p(String(a?.name??"").padEnd(i," ")),type:p(String(a?.type??"")),description:p(String(a?.description??"")),default:p(a?.default===void 0?"-":String(a.default)),choices:p(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function kt(n){n.command("whoami").description("\u67E5\u770B\u5F53\u524D Token \u5BF9\u5E94\u7684 Jenkins \u7528\u6237\u4FE1\u606F").action(async()=>{let{jenkins:t}=await f(),e=await rt(t),r=e&&typeof e=="object"?e:{value:e},o=(l,c)=>{let m=l.length;if(m>=c)return l;let d=c-m,b=Math.floor(d/2),y=d-b;return `${" ".repeat(b)}${l}${" ".repeat(y)}`},s=r.name??r.fullName??r.id??"",i=s==null?"":String(s),a=Math.max(20,i.length),u={};u.name=p(o(i,a));for(let l of Object.keys(r).sort((c,m)=>c.localeCompare(m,"en"))){if(l==="name")continue;let c=r[l],m=l.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof c=="string"||typeof c=="number")){u[l]=k(String(c));continue}let b=c==null?"":typeof c=="object"?JSON.stringify(c):String(c);u[l]=p(b);}console.table([u]);});}function Ct(n){n.command("config").description("\u8BFB\u53D6 Jenkins Job \u914D\u7F6E").command("read").description("\u8BFB\u53D6 job \u914D\u7F6E").option("-j, --job <job>","\u6307\u5B9A job").option("-f, --format <format>","\u8F93\u51FA\u683C\u5F0F\uFF1Axml \u6216 json","xml").action(async e=>{let{config:r,jenkins:o}=await f(),s=h(r.job,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new Error(`Invalid format: ${e.format}. Expected: xml or json`);let a=await ct(o,s);if(i==="xml"){process.stdout.write(a);return}let l=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(l,null,2));});}function St(n){let t=n.command("job").description("Job \u76F8\u5173\u64CD\u4F5C"),e=(r,o)=>{let s=String(o??"").trim();if(!s)return !0;if(!/[*?]/.test(s))return r.includes(s);let u=`^${s.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(u).test(r)}catch{return r.includes(s)}};t.command("list").alias("ls").description("\u83B7\u53D6\u6240\u6709 job").option("--search <keyword>","\u6309\u540D\u79F0\u8FC7\u6EE4\uFF08\u652F\u6301 glob\uFF09").action(async r=>{let{jenkins:o}=await f(),s=await ot(o),i=(r.search??"").trim(),a=i?s.filter(c=>e(String(c.name??""),i)):s;a.sort((c,m)=>String(c?.name??"").localeCompare(String(m?.name??""),"en",{sensitivity:"base"}));let u=Math.max(0,...a.map(c=>String(c?.name??"").length)),l=Math.max(0,...a.map(c=>String(c?.url??"").length));console.table((a??[]).map(c=>({name:p(String(c.name??"").padEnd(u," ")),color:M(c.color),url:k(String(c.url??"").padEnd(l," "))})));}),t.command("info").description("\u83B7\u53D6 job \u4FE1\u606F").option("-j, --job <job>","\u6307\u5B9A job").option("--json","\u8F93\u51FA\u539F\u59CB JSON").action(async r=>{let{config:o,jenkins:s}=await f(),i=h(o.job,r.job),a=await L(s,i);if(r.json){console.log(JSON.stringify(a,null,2));return}let u=a?.lastBuild,l=a?.lastCompletedBuild,c=Array.isArray(a?.healthReport)?a.healthReport:[];console.table([{name:p(String(a?.name??"")),url:k(String(a?.url??"")),color:M(a?.color),buildable:a?.buildable,inQueue:a?.inQueue,nextBuildNumber:a?.nextBuildNumber,healthScore:c[0]?.score??"-",lastBuild:p(u?.number?`#${u.number}`:"-"),lastResult:N(u?.result,u?.building),lastDurationS:u?.duration===void 0?"-":Number((Number(u.duration)/1e3).toFixed(3)),lastDate:p(u?.timestamp?$(Number(u.timestamp)):"-"),lastCompleted:p(l?.number?`#${l.number}`:"-"),lastCompletedResult:N(l?.result,!1),lastCompletedDate:p(l?.timestamp?$(Number(l.timestamp)):"-")}]);});}function Zt(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function te(n){let t=n.indexOf("=");if(t<=0)throw new Error(`Invalid --param: "${n}". Expected: key=value`);let e=n.slice(0,t).trim(),r=n.slice(t+1).trim();if(!e)throw new Error(`Invalid --param: "${n}". Key is empty`);return {key:e,value:r}}function Jt(n){n.command("trigger").description("\u975E\u4EA4\u4E92\u89E6\u53D1 Jenkins \u6784\u5EFA").requiredOption("-b, --branch <branch>","\u5206\u652F\uFF08\u4F8B\u5982 origin/develop\uFF09").option("-m, --mode <mode>","\u90E8\u7F72\u73AF\u5883\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u6216\u7528\u9017\u53F7\u5206\u9694\uFF1A-m dev -m uat / -m dev,uat\uFF09",(t,e)=>e.concat(Zt(t)),[]).option("--param <key=value>","\u989D\u5916\u53C2\u6570\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4F8B\u5982\uFF1A--param foo=bar\uFF09",(t,e)=>e.concat(t),[]).option("-j, --job <job>","\u6307\u5B9A job").action(async t=>{let{config:e,jenkins:r}=await f(),o=h(e.job,t.job),s=String(t.branch??"").trim();if(!s)throw new Error("branch is required");let i=(t.mode??[]).map(String).map(d=>d.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new Error("mode is required. Example: jenkins-cli trigger -b origin/develop -m dev -m uat");let u={};for(let d of t.param??[]){let{key:b,value:y}=te(String(d));u[b]=y;}console.log();let l=!1,c=()=>{l||(l=!0,console.log());},m=a.map(async d=>{let b=Ot(`Triggering build for ${g.yellow(d)}...`).start();try{let y=await I(r,o,{...u,branch:s,mode:d});c(),b.succeed(`${g.green(d)} - Triggered! Queue: ${y}`);}catch(y){throw c(),b.fail(`${g.red(d)} - ${y.message}`),y}});try{await Promise.all(m);}catch{console.log(g.bold.red(`
35
35
  \u274C Some builds failed. Check the output above.
36
- `)),process.exit(1);}});}function ve(n){we(n),Se(n),Je(n),xe(n),je(n),he(n),ke(n),$e(n),Ce(n);}program.name("jenkins-cli").description(_).version(Q,"-v, --version").helpOption("-h, --help").action(be);ve(program);program.parse();
36
+ `)),process.exit(1);}});}function $t(n){ht(n),Ct(n),St(n),wt(n),xt(n),yt(n),jt(n),Jt(n),kt(n);}program.name("jenkins-cli").description(G).version(_,"-v, --version").helpOption("-h, --help").action(gt);$t(program);program.parse();
package/dist/index.js CHANGED
@@ -3,19 +3,19 @@ import B from 'js-yaml';
3
3
  import g from 'path';
4
4
  import { exec } from 'child_process';
5
5
  import { promisify } from 'util';
6
- import K from 'axios';
6
+ import _ from 'axios';
7
7
  import R from 'chalk';
8
8
  import Z from 'jiti';
9
9
  import { fileURLToPath } from 'url';
10
10
 
11
- var h="jenkins-cli.yaml",b="package.json",x="jenkins-cli",N="jenkinsCli";function A(e,n=process.cwd()){let t=n;for(;;){let r=g.join(t,e);if(d.existsSync(r))return r;let o=g.dirname(t);if(o===t)return null;t=o;}}function I(e=process.cwd()){return A(h,e)}function F(e=process.cwd()){let n=A(b,e);return n?g.dirname(n):null}function T(e){return !!(e.apiToken&&e.job&&Array.isArray(e.modes)&&e.modes.length>0)}async function P(e){let n=await d.readFile(e,"utf-8"),t=B.load(n);if(!t||typeof t!="object")throw new Error(`Invalid config in ${h}: expected a YAML object`);return t}async function L(e){let n=g.join(e,b);if(!await d.pathExists(n))return {};let t=await d.readFile(n,"utf-8"),r=JSON.parse(t),o=r[x]??r[N];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${b} config: "${x}" must be an object`);return o}async function U(){let e=process.cwd(),n=F(e)??e,t=g.join(n,h),r={},o=g.dirname(n),s=I(o);s&&await d.pathExists(s)&&(r=await P(s));let i=await L(n);r={...r,...i};let a=await d.pathExists(t)?await P(t):{};if(r={...r,...a},!T(r))throw new Error(`Config incomplete or not found: tried ${h} in project root, "${x}" in $
12
- {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:n}}var J=promisify(exec);async function M(){try{let{stdout:e}=await J("git branch --show-current"),n=e.trim();if(!n)throw new Error("Not on any branch (detached HEAD state)");return n}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function D(){try{let{stdout:e}=await J('git branch -r --format="%(refname:short)"');return e.split(`
13
- `).filter(n=>n.includes("/")&&!n.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var $={maxRedirects:0,validateStatus:e=>e>=200&&e<400};function k(e){return `job/${e.split("/").map(t=>t.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function H(e){let n=Array.isArray(e)?e.find(o=>o?.parameters):void 0,t=Array.isArray(n?.parameters)?n.parameters:[],r={};for(let o of t)o?.name&&(r[String(o.name)]=o?.value);return r}function _(e,n=400){try{let r=(typeof e=="string"?e:JSON.stringify(e??"",null,2)).trim();return r?r.length>n?`${r.slice(0,n)}...`:r:""}catch{return ""}}function V(e){let n=e.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!n)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,t,r,o,s]=n,i=`${t}://${s.replace(/\/$/,"")}/`,a=K.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function G(e,n){let r=(await e.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(n){let o=n.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function Y(e,n){let t=await e.axios.get(`${k(n)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(t.data?.builds)?t.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:H(o.actions)}))}async function Q(e,n,t){try{await e.axios.post(`${k(n)}/${t}/stop/`,new URLSearchParams({}),$);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(R.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(R.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${t} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function z(e,n,t){return W(e,n,{branch:t.branch,mode:t.mode})}async function W(e,n,t){let r="branch",o="mode",s=t[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([G(e,n),Y(e,n)]),u=(c,l)=>{let f=c.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===l)?.value:void 0},y=t[r];if(s&&y){let c=i.find(l=>u(l,o)===s&&u(l,r)===y);if(c)return console.log(`Build already in queue (ID: ${c.id}). Skipping trigger.`),new URL(`queue/item/${c.id}/`,e.baseURL).toString()}let p=!1;if(s)for(let c of a)u(c,o)===s&&(console.log(`Stopping running build #${c.number} (mode=${s})...`),await Q(e,n,c.number),p=!0);p&&await new Promise(c=>setTimeout(c,1e3)),s&&console.log(`Triggering new build for mode=${s}...`);try{return (await e.axios.post(`${k(n)}/buildWithParameters/`,new URLSearchParams({...t}),$)).headers.location||""}catch(c){let l=c?.response?.status,f=_(c?.response?.data);if(l===400){let m=[];m.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),m.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let w=f?`
11
+ var h="jenkins-cli.yaml",b="package.json",x="jenkins-cli",N="jenkinsCli";function P(n,e=process.cwd()){let t=e;for(;;){let r=g.join(t,n);if(d.existsSync(r))return r;let o=g.dirname(t);if(o===t)return null;t=o;}}function F(n=process.cwd()){return P(h,n)}function I(n=process.cwd()){let e=P(b,n);return e?g.dirname(e):null}function L(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function A(n){let e=await d.readFile(n,"utf-8"),t=B.load(e);if(!t||typeof t!="object")throw new Error(`Invalid config in ${h}: expected a YAML object`);return t}async function T(n){let e=g.join(n,b);if(!await d.pathExists(e))return {};let t=await d.readFile(e,"utf-8"),r=JSON.parse(t),o=r[x]??r[N];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${b} config: "${x}" must be an object`);return o}async function U(){let n=process.cwd(),e=I(n)??n,t=g.join(e,h),r={},o=g.dirname(e),i=F(o);i&&await d.pathExists(i)&&(r=await A(i));let s=await T(e);r={...r,...s};let a=await d.pathExists(t)?await A(t):{};if(r={...r,...a},!L(r))throw new Error(`Config incomplete or not found: tried ${h} in project root, "${x}" in $
12
+ {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return {...r,projectRoot:e}}var J=promisify(exec);async function D(){try{let{stdout:n}=await J("git branch --show-current"),e=n.trim();if(!e)throw new Error("Not on any branch (detached HEAD state)");return e}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function K(){try{let{stdout:n}=await J('git branch -r --format="%(refname:short)"');return n.split(`
13
+ `).filter(e=>e.includes("/")&&!e.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}var E={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function k(n){return `job/${n.split("/").map(t=>t.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function H(n){let e=Array.isArray(n)?n.find(o=>o?.parameters):void 0,t=Array.isArray(e?.parameters)?e.parameters:[],r={};for(let o of t)o?.name&&(r[String(o.name)]=o?.value);return r}function V(n,e=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>e?`${r.slice(0,e)}...`:r:""}catch{return ""}}function M(n){let e=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!e)throw new Error("Invalid apiToken format. Expected: http(s)://username:token@host:port");let[,t,r,o,i]=e,s=`${t}://${i.replace(/\/$/,"")}/`,a=_.create({baseURL:s,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:s,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function G(n,e){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(e){let o=e.trim(),i=o.split("/").filter(Boolean).pop();return r.filter(s=>{let a=s.task?.name;return a===o||(i?a===i:!1)})}return r}async function Y(n,e){let t=await n.axios.get(`${k(e)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(t.data?.builds)?t.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:H(o.actions)}))}async function Q(n,e,t){try{await n.axios.post(`${k(e)}/${t}/stop/`,new URLSearchParams({}),E);}catch(r){let o=r.response?.status;if(o===403){let i=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),s=/crumb|csrf/i.test(i);console.log(R.red("[Error] 403 Forbidden: Jenkins \u62D2\u7EDD\u505C\u6B62\u6784\u5EFA\u8BF7\u6C42\uFF08\u53EF\u80FD\u662F\u6743\u9650\u6216 CSRF\uFF09\u3002")),console.log(R.yellow('[Hint] \u8BF7\u786E\u8BA4\u5F53\u524D\u7528\u6237\u5BF9\u8BE5 Job \u62E5\u6709 "Job" -> "Cancel" \u6743\u9650\u3002')),console.log(s?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":i?`[Hint] Jenkins response: ${i}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${t} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function z(n,e,t){return W(n,e,{branch:t.branch,mode:t.mode})}async function W(n,e,t){let r="branch",o="mode",i=t[o];i&&console.log(`Checking for conflicting builds for mode: ${i}...`);let[s,a]=await Promise.all([G(n,e),Y(n,e)]),u=(c,l)=>{let f=c.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===l)?.value:void 0},y=t[r];if(i&&y){let c=s.find(l=>u(l,o)===i&&u(l,r)===y);if(c)return console.log(`Build already in queue (ID: ${c.id}). Skipping trigger.`),new URL(`queue/item/${c.id}/`,n.baseURL).toString()}let p=!1;if(i)for(let c of a)u(c,o)===i&&(console.log(`Stopping running build #${c.number} (mode=${i})...`),await Q(n,e,c.number),p=!0);p&&await new Promise(c=>setTimeout(c,1e3)),i&&console.log(`Triggering new build for mode=${i}...`);try{return (await n.axios.post(`${k(e)}/buildWithParameters/`,new URLSearchParams({...t}),E)).headers.location||""}catch(c){let l=c?.response?.status,f=V(c?.response?.data);if(l===400){let m=[];m.push("Hint: Jenkins returned 400. Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs)."),m.push(`Hint: This CLI sends parameters "${r}" and "${o}". Ensure your Jenkins Job has matching parameter names.`);let w=f?`
14
14
  Jenkins response (snippet):
15
15
  ${f}`:"";throw new Error(`Request failed with status code 400.${w}
16
16
  ${m.join(`
17
17
  `)}`)}if(l){let m=f?`
18
18
  Jenkins response (snippet):
19
- ${f}`:"";throw new Error(`Request failed with status code ${l}.${m}`)}throw c}}var ne=Z(fileURLToPath(import.meta.url),{interopDefault:!0});async function C(e){return await Promise.resolve(e)}function te(e){let n=String(e??"").trim();if(!n)return null;let t=n.split(":");if(t.length<2)return null;if(t.length===2){let[i,a]=t;return !i||!a?null:{file:i,kind:"Var",exportName:a}}let r=t.at(-2),o=t.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let s=t.slice(0,-2).join(":");return s?{file:s,kind:r,exportName:o}:null}function re(e,n){return g.isAbsolute(n)?n:g.join(e,n)}function j(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}async function S(e,n){let t=te(n);if(!t)return null;let r=re(e,t.file);if(!await d.pathExists(r))throw new Error(`configs reference not found: ${t.file}`);let o=ne(r),s=j(o)?o:{default:o},i=t.exportName==="default"?s.default:s[t.exportName];if(i===void 0)throw new Error(`configs reference export not found: ${n}`);return {kind:t.kind,value:i}}async function v(e,n){if(typeof n!="string")return n;let t=await S(e,n);if(!t)return n;if(t.kind==="Var"){if(typeof t.value=="function"){let o=t.value();return await C(o)}return await C(t.value)}if(typeof t.value!="function")throw new Error(`configs reference expected a function: ${n}`);let r=t.value();return await C(r)}async function oe(e,n){let t=Array.isArray(e)?e:[];if(t.length===0)return [];let r=String(n.projectRoot??"").trim()||process.cwd(),o=[];for(let s of t){if(!j(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await v(r,i.choices);let a=String(i.type??"").toLowerCase(),u=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof u!="function"&&!Array.isArray(u))if(u==null)i.choices=[];else if(typeof u=="string"||typeof u=="number"||typeof u=="boolean")i.choices=[String(u)];else if(typeof u[Symbol.iterator]=="function")i.choices=Array.from(u).map(p=>String(p));else throw new Error(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof u})`)}if(i.default!==void 0&&(i.default=await v(r,i.default)),i.validate!==void 0){let a=i.validate;if(typeof a=="string"){let u=await S(r,a);if(!u)i.validate=a;else if(u.kind==="Var"){if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=u.value;}else {if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);i.validate=(y,p)=>u.value(y,p);}}else typeof a=="function"&&(i.validate=a);}o.push(i);}return o}function se(e,n){let t=new Set(n),r={};for(let[o,s]of Object.entries(e))if(!t.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}
19
+ ${f}`:"";throw new Error(`Request failed with status code ${l}.${m}`)}throw c}}var en=Z(fileURLToPath(import.meta.url),{interopDefault:!0});async function C(n){return await Promise.resolve(n)}function tn(n){let e=String(n??"").trim();if(!e)return null;let t=e.split(":");if(t.length<2)return null;if(t.length===2){let[s,a]=t;return !s||!a?null:{file:s,kind:"Var",exportName:a}}let r=t.at(-2),o=t.at(-1);if(r!=="Fun"&&r!=="Var"||!o)return null;let i=t.slice(0,-2).join(":");return i?{file:i,kind:r,exportName:o}:null}function rn(n,e){return g.isAbsolute(e)?e:g.join(n,e)}function v(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function S(n,e){let t=tn(e);if(!t)return null;let r=rn(n,t.file);if(!await d.pathExists(r))throw new Error(`configs reference not found: ${t.file}`);let o=en(r),i=v(o)?o:{default:o},s=t.exportName==="default"?i.default:i[t.exportName];if(s===void 0)throw new Error(`configs reference export not found: ${e}`);return {kind:t.kind,value:s}}async function j(n,e){if(typeof e!="string")return e;let t=await S(n,e);if(!t)return e;if(t.kind==="Var"){if(typeof t.value=="function"){let o=t.value();return await C(o)}return await C(t.value)}if(typeof t.value!="function")throw new Error(`configs reference expected a function: ${e}`);let r=t.value();return await C(r)}async function on(n,e){let t=Array.isArray(n)?n:[];if(t.length===0)return [];let r=String(e.projectRoot??"").trim()||process.cwd(),o=[];for(let i of t){if(!v(i))continue;let s={...i};if(s.choices!==void 0){s.choices=await j(r,s.choices);let a=String(s.type??"").toLowerCase(),u=s.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof u!="function"&&!Array.isArray(u))if(u==null)s.choices=[];else if(typeof u=="string"||typeof u=="number"||typeof u=="boolean")s.choices=[String(u)];else if(typeof u[Symbol.iterator]=="function")s.choices=Array.from(u).map(p=>String(p));else throw new Error(`configs "${String(s.name??"")}" choices must resolve to an array (got ${typeof u})`)}if(s.default!==void 0&&(s.default=await j(r,s.default)),s.validate!==void 0){let a=s.validate;if(typeof a=="string"){let u=await S(r,a);if(!u)s.validate=a;else if(u.kind==="Var"){if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);s.validate=u.value;}else {if(typeof u.value!="function")throw new Error(`validate reference must be a function: ${a}`);s.validate=(y,p)=>u.value(y,p);}}else typeof a=="function"&&(s.validate=a);}o.push(s);}return o}function sn(n,e){let t=new Set(e),r={};for(let[o,i]of Object.entries(n))if(!t.has(o)&&i!==void 0){if(i===null){r[o]="";continue}if(Array.isArray(i)){r[o]=i.map(s=>String(s)).join(",");continue}if(typeof i=="object"){try{r[o]=JSON.stringify(i);}catch{r[o]=String(i);}continue}r[o]=String(i);}return r}
20
20
 
21
- export { se as answersToJenkinsParams, V as createJenkinsClient, D as getAllBranches, M as getCurrentBranch, U as loadConfig, oe as resolveExtraQuestions, z as triggerBuild };
21
+ export { sn as answersToJenkinsParams, M as createJenkinsClient, K as getAllBranches, D as getCurrentBranch, U as loadConfig, on as resolveExtraQuestions, z as triggerBuild };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-org/jenkins-cli",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Jenkins deployment CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,16 +8,6 @@
8
8
  "jenkins-cli": "dist/cli.js",
9
9
  "jc": "dist/cli.js"
10
10
  },
11
- "scripts": {
12
- "build": "tsup",
13
- "dev": "NODE_ENV=development tsup --watch",
14
- "prepublishOnly": "npm run build",
15
- "link:test": "npm run build && npm link",
16
- "format": "prettier . --write",
17
- "format:check": "prettier . --check",
18
- "lint": "eslint .",
19
- "lint:fix": "eslint . --fix"
20
- },
21
11
  "keywords": [
22
12
  "jenkins",
23
13
  "cli",
@@ -70,5 +60,14 @@
70
60
  "directories": {
71
61
  "doc": "docs"
72
62
  },
73
- "types": "./dist/index.d.ts"
63
+ "types": "./dist/index.d.ts",
64
+ "scripts": {
65
+ "build": "tsup",
66
+ "dev": "NODE_ENV=development tsup --watch",
67
+ "link:test": "npm run build && npm link",
68
+ "format": "prettier . --write",
69
+ "format:check": "prettier . --check",
70
+ "lint": "eslint .",
71
+ "lint:fix": "eslint . --fix"
72
+ }
74
73
  }