@ts-org/jenkins-cli 1.0.0-prior → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,40 +1,82 @@
1
1
  ## @ts-org/jenkins-cli
2
2
 
3
+ Jenkins 部署命令行工具,支持交互式选择分支和环境。
4
+
3
5
  ### 安装
4
6
 
5
7
  ```bash
6
8
  npm add @ts-org/jenkins-cli
7
9
  ```
8
10
 
9
- ### 使用
11
+ ### 配置
10
12
 
11
- 1. 在项目的根目录创建 `jenkins-cli.yaml`:
13
+ 在项目根目录创建 `jenkins-cli.yaml`:
12
14
 
13
15
  ```yaml
14
- apiToken: https://username:token@jenkins_url
15
-
16
- job: 'your-job-name'
16
+ # Jenkins API Token 格式:http://username:token@host:port
17
+ apiToken: http://username:token@jenkins.example.com:8080
17
18
 
18
- # 分支会通过git命令读取
19
+ # Jenkins Job 名称
20
+ job: your-job-name
19
21
 
20
- # 部署的环境
22
+ # 部署环境列表
21
23
  modes:
22
24
  - dev
23
25
  - sit
24
26
  - uat
25
27
  ```
26
28
 
29
+ 或者在 `package.json` 里配置:
30
+
31
+ ```json
32
+ {
33
+ "jenkins-cli": {
34
+ "apiToken": "http://username:token@jenkins.example.com:8080",
35
+ "job": "your-job-name",
36
+ "modes": ["dev", "sit", "uat"]
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 配置优先级
42
+
43
+ 从高到低:
44
+
45
+ 1. 项目根目录的 `jenkins-cli.yaml`
46
+ 2. 项目根目录的 `package.json` 里的 `jenkins-cli` 字段
47
+ 3. 向上查找到的 `jenkins-cli.yaml`
48
+
49
+ 高优先级配置会覆盖低优先级的同名字段。比如项目根的 YAML 里有 `job`,就不会用 `package.json` 或 上层 YAML 里的 `job`。
50
+
51
+ ### 使用
52
+
53
+ 在项目目录执行:
54
+
55
+ ```bash
56
+ jenkins-cli
57
+ ```
58
+
59
+ 或在 `package.json` 添加 script:
27
60
 
28
- 2. 项目的终端执行:`jenkins-cli`
61
+ ```json
62
+ {
63
+ "scripts": {
64
+ "ship": "jenkins-cli"
65
+ }
66
+ }
67
+ ```
29
68
 
30
- ```
31
- 🚀 Jenkins CLI - Jenkins Deployment CLI
32
-
33
- Configuration loaded
34
- ✔ Found 5 branches
35
- 请选择要打包的分支: origin/develop
36
- 请选择要打包的环境: dev, sit, uat
37
- dev - Build triggered successfully
38
- sit - Build triggered successfully
39
- ✔ uat - Build triggered successfully
40
- ```
69
+ 交互示例:
70
+
71
+ ```
72
+ 🚀 Jenkins CLI - Jenkins Deployment CLI
73
+
74
+ Configuration loaded
75
+ Found 3 branches
76
+ ? 请选择要打包的分支: origin/develop
77
+ ? 请选择要打包的环境: dev, sit, uat
78
+
79
+ ✔ dev - Build triggered successfully
80
+ ✔ sit - Build triggered successfully
81
+ ✔ uat - Build triggered successfully
82
+ ```
package/dist/cli.js CHANGED
@@ -1,23 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
- import J from 'inquirer';
4
- import i from 'chalk';
5
- import x from 'ora';
6
- import f from 'fs-extra';
7
- import k from 'js-yaml';
8
- import m from 'path';
3
+ import O from 'inquirer';
4
+ import a from 'chalk';
5
+ import J from 'ora';
6
+ import c from 'fs-extra';
7
+ import F from 'js-yaml';
8
+ import l from 'path';
9
9
  import { exec } from 'child_process';
10
10
  import { promisify } from 'util';
11
- import T from 'axios';
11
+ import D from 'axios';
12
12
 
13
- var l="jenkins-cli.yaml";function E(r=process.cwd()){let e=r;for(;;){let t=m.join(e,l);if(f.existsSync(t))return t;let o=m.dirname(e);if(o===e)return null;e=o;}}async function h(){let r=E();if(!r)throw new Error(`Config file not found: ${l}`);try{let e=await f.readFile(r,"utf-8"),t=k.load(e);if(!t.apiToken||!t.job||!Array.isArray(t.modes))throw new Error("Invalid config: apiToken, job, and modes are required");if(t.modes.length===0)throw new Error("Invalid config: modes cannot be empty");return t}catch(e){throw e.code==="ENOENT"?new Error(`Config file not found: ${l}`):e}}var p=promisify(exec);async function g(){try{let{stdout:r}=await p("git branch --show-current"),e=r.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 w(){try{let{stdout:r}=await p('git branch -r --format="%(refname:short)"');return r.split(`
14
- `).filter(e=>e.includes("/"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}function y(r){let e=r.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!e)throw new Error("Invalid apiToken format. Expected: http://username:token@host:port");let[,t,o,s,c]=e,a=`${t}://${c}`,n=T.create({baseURL:a,auth:{username:o,password:s},proxy:!1,timeout:3e4});return {baseURL:a,auth:{username:o,password:s},axios:n}}async function b(r,e,t){try{let o=await r.axios.post(`/job/${e}/buildWithParameters`,new URLSearchParams({branch:t.branch,mode:t.mode}),{headers:{"Content-Type":"application/x-www-form-urlencoded"}});if(o.status!==201)throw new Error(`Unexpected status: ${o.status}`)}catch(o){throw o.response?o.response.status===403?new Error(`Failed to trigger build: Jenkins rejected the request (HTTP 403).
13
+ var u="jenkins-cli.yaml",d="package.json",m="jenkins-cli",v="jenkinsCli";function I(n=process.cwd()){let e=n;for(;;){let r=l.join(e,u);if(c.existsSync(r))return r;let t=l.dirname(e);if(t===e)return null;e=t;}}function T(n=process.cwd()){let e=n;for(;;){let r=l.join(e,d);if(c.existsSync(r))return e;let t=l.dirname(e);if(t===e)return null;e=t;}}function h(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function w(n){let e=await c.readFile(n,"utf-8"),r=F.load(e);if(!r||typeof r!="object")throw new Error(`Invalid config in ${u}: expected a YAML object`);return r}async function L(n){let e=l.join(n,d);if(!await c.pathExists(e))return {};let r=await c.readFile(e,"utf-8"),t=JSON.parse(r),o=t[m]??t[v];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${d} config: "${m}" must be an object`);return o}async function y(){let n=process.cwd(),e=T(n)??n,r=l.join(e,u),t=await c.pathExists(r)?await w(r):{};if(h(t))return t;let o={},p=l.dirname(e),s=I(p);s&&await c.pathExists(s)&&(o=await w(s));let i=await L(e);if(o={...o,...i},o={...o,...t},!h(o))throw new Error(`Config incomplete or not found: tried ${u} in project root, "${m}" in $
14
+ {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return o}var k=promisify(exec);async function b(){try{let{stdout:n}=await k("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 C(){try{let{stdout:n}=await k('git branch -r --format="%(refname:short)"');return n.split(`
15
+ `).filter(e=>e.includes("/")&&!e.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}function E(n){let e=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!e)throw new Error("Invalid apiToken format. Expected: http://username:token@host:port");let[,r,t,o,p]=e,s=`${r}://${p}`,i=D.create({baseURL:s,auth:{username:t,password:o},proxy:!1,timeout:3e4});return {baseURL:s,auth:{username:t,password:o},axios:i}}async function x(n,e,r){try{let t=await n.axios.post(`/job/${e}/buildWithParameters`,new URLSearchParams({branch:r.branch,mode:r.mode}),{headers:{"Content-Type":"application/x-www-form-urlencoded"}});if(t.status!==201)throw new Error(`Unexpected status: ${t.status}`)}catch(t){throw t.response?t.response.status===403?new Error(`Failed to trigger build: Jenkins rejected the request (HTTP 403).
15
16
  This might be due to:
16
17
  - Insufficient permissions
17
18
  - Jenkins Quiet Period (try again after a few seconds)
18
- - The job is already queued`):new Error(`Failed to trigger build: HTTP ${o.response.status} - ${o.response.statusText}`):o.code==="ECONNREFUSED"?new Error(`Failed to trigger build: Connection refused to ${r.baseURL}`):new Error(`Failed to trigger build: ${o.message}`)}}async function F(){console.log(i.bold.blue(`
19
+ - The job is already queued`):new Error(`Failed to trigger build: HTTP ${t.response.status} - ${t.response.statusText}`):t.code==="ECONNREFUSED"?new Error(`Failed to trigger build: Connection refused to ${n.baseURL}`):new Error(`Failed to trigger build: ${t.message}`)}}var j="2.0.0",A="Jenkins deployment CLI";async function R(){console.log(a.bold.blue(`
19
20
  \u{1F680} Jenkins CLI - Jenkins Deployment CLI
20
- `));let r=x("Loading configuration...").start(),e=await h().catch(n=>{r.fail(i.red(n.message)),process.exit(1);});r.succeed("Configuration loaded"),r.start("Fetching git branches...");let[t,o]=await Promise.all([w(),g()]).catch(n=>{r.fail(i.red(n.message)),process.exit(1);});r.succeed(`Found ${t.length} branches`);let s;try{s=await J.prompt([{type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:t,default:t.indexOf(o)},{type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:e.modes,validate:n=>n.length===0?"You must select at least one environment":!0}]);}catch{console.log(i.yellow(`
21
- \u{1F44B} Cancelled by user`)),process.exit(0);}console.log();let c=y(e.apiToken),a=s.modes.map(async n=>{let u=x(`Triggering build for ${i.yellow(n)}...`).start();try{await b(c,e.job,{branch:s.branch,mode:n}),u.succeed(`${i.green(n)} - Build triggered successfully`);}catch(d){throw u.fail(`${i.red(n)} - ${d.message}`),d}});try{await Promise.all(a),console.log();}catch{console.log(i.bold.red(`
21
+ `));let n=J("Loading configuration...").start(),e=await y().catch(i=>{n.fail(a.red(i.message)),process.exit(1);});n.succeed("Configuration loaded"),n.start("Fetching git branches...");let[r,t]=await Promise.all([C(),b()]).catch(i=>{n.fail(a.red(i.message)),process.exit(1);});n.succeed(`Found ${r.length} branches`);let o;try{o=await O.prompt([{type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:r,default:r.indexOf(t)},{type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:e.modes,validate:i=>i.length===0?"You must select at least one environment":!0}]);}catch{console.log(a.yellow(`
22
+ \u{1F44B} Cancelled by user`)),process.exit(0);}console.log();let p=E(e.apiToken),s=o.modes.map(async i=>{let f=J(`Triggering build for ${a.yellow(i)}...`).start();try{await x(p,e.job,{branch:o.branch,mode:i}),f.succeed(`${a.green(i)} - Build triggered successfully`);}catch(g){throw f.fail(`${a.red(i)} - ${g.message}`),g}});try{await Promise.all(s),console.log();}catch{console.log(a.bold.red(`
22
23
  \u274C Some builds failed. Check the output above.
23
- `)),process.exit(1);}}program.name("jenkins-cli").description("Jenkins Deployment CLI").version("1.0.0").action(F);program.parse();
24
+ `)),process.exit(1);}}program.name("jenkins-cli").description(A).version(j,"-v, --version").helpOption("-h, --help").action(R);program.parse();
package/dist/index.js CHANGED
@@ -1,15 +1,16 @@
1
- import c from 'fs-extra';
2
- import l from 'js-yaml';
3
- import a from 'path';
1
+ import i from 'fs-extra';
2
+ import h from 'js-yaml';
3
+ import s from 'path';
4
4
  import { exec } from 'child_process';
5
5
  import { promisify } from 'util';
6
- import x from 'axios';
6
+ import J from 'axios';
7
7
 
8
- var n="jenkins-cli.yaml";function h(e=process.cwd()){let r=e;for(;;){let o=a.join(r,n);if(c.existsSync(o))return o;let t=a.dirname(r);if(t===r)return null;r=t;}}async function p(){let e=h();if(!e)throw new Error(`Config file not found: ${n}`);try{let r=await c.readFile(e,"utf-8"),o=l.load(r);if(!o.apiToken||!o.job||!Array.isArray(o.modes))throw new Error("Invalid config: apiToken, job, and modes are required");if(o.modes.length===0)throw new Error("Invalid config: modes cannot be empty");return o}catch(r){throw r.code==="ENOENT"?new Error(`Config file not found: ${n}`):r}}var f=promisify(exec);async function w(){try{let{stdout:e}=await f("git branch --show-current"),r=e.trim();if(!r)throw new Error("Not on any branch (detached HEAD state)");return r}catch{throw new Error("Failed to get current branch. Are you in a git repository?")}}async function y(){try{let{stdout:e}=await f('git branch -r --format="%(refname:short)"');return e.split(`
9
- `).filter(r=>r.includes("/"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}function E(e){let r=e.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!r)throw new Error("Invalid apiToken format. Expected: http://username:token@host:port");let[,o,t,i,d]=r,s=`${o}://${d}`,u=x.create({baseURL:s,auth:{username:t,password:i},proxy:!1,timeout:3e4});return {baseURL:s,auth:{username:t,password:i},axios:u}}async function b(e,r,o){try{let t=await e.axios.post(`/job/${r}/buildWithParameters`,new URLSearchParams({branch:o.branch,mode:o.mode}),{headers:{"Content-Type":"application/x-www-form-urlencoded"}});if(t.status!==201)throw new Error(`Unexpected status: ${t.status}`)}catch(t){throw t.response?t.response.status===403?new Error(`Failed to trigger build: Jenkins rejected the request (HTTP 403).
8
+ var c="jenkins-cli.yaml",f="package.json",p="jenkins-cli",w="jenkinsCli";function y(n=process.cwd()){let t=n;for(;;){let e=s.join(t,c);if(i.existsSync(e))return e;let r=s.dirname(t);if(r===t)return null;t=r;}}function C(n=process.cwd()){let t=n;for(;;){let e=s.join(t,f);if(i.existsSync(e))return t;let r=s.dirname(t);if(r===t)return null;t=r;}}function d(n){return !!(n.apiToken&&n.job&&Array.isArray(n.modes)&&n.modes.length>0)}async function g(n){let t=await i.readFile(n,"utf-8"),e=h.load(t);if(!e||typeof e!="object")throw new Error(`Invalid config in ${c}: expected a YAML object`);return e}async function k(n){let t=s.join(n,f);if(!await i.pathExists(t))return {};let e=await i.readFile(t,"utf-8"),r=JSON.parse(e),o=r[p]??r[w];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new Error(`Invalid ${f} config: "${p}" must be an object`);return o}async function P(){let n=process.cwd(),t=C(n)??n,e=s.join(t,c),r=await i.pathExists(e)?await g(e):{};if(d(r))return r;let o={},u=s.dirname(t),a=y(u);a&&await i.pathExists(a)&&(o=await g(a));let l=await k(t);if(o={...o,...l},o={...o,...r},!d(o))throw new Error(`Config incomplete or not found: tried ${c} in project root, "${p}" in $
9
+ {PACKAGE_JSON}, and walking up from cwd. Required: apiToken, job, modes (non-empty array)`);return o}var m=promisify(exec);async function b(){try{let{stdout:n}=await m("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 A(){try{let{stdout:n}=await m('git branch -r --format="%(refname:short)"');return n.split(`
10
+ `).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new Error("Failed to list branches. Are you in a git repository?")}}function j(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new Error("Invalid apiToken format. Expected: http://username:token@host:port");let[,e,r,o,u]=t,a=`${e}://${u}`,l=J.create({baseURL:a,auth:{username:r,password:o},proxy:!1,timeout:3e4});return {baseURL:a,auth:{username:r,password:o},axios:l}}async function F(n,t,e){try{let r=await n.axios.post(`/job/${t}/buildWithParameters`,new URLSearchParams({branch:e.branch,mode:e.mode}),{headers:{"Content-Type":"application/x-www-form-urlencoded"}});if(r.status!==201)throw new Error(`Unexpected status: ${r.status}`)}catch(r){throw r.response?r.response.status===403?new Error(`Failed to trigger build: Jenkins rejected the request (HTTP 403).
10
11
  This might be due to:
11
12
  - Insufficient permissions
12
13
  - Jenkins Quiet Period (try again after a few seconds)
13
- - The job is already queued`):new Error(`Failed to trigger build: HTTP ${t.response.status} - ${t.response.statusText}`):t.code==="ECONNREFUSED"?new Error(`Failed to trigger build: Connection refused to ${e.baseURL}`):new Error(`Failed to trigger build: ${t.message}`)}}
14
+ - The job is already queued`):new Error(`Failed to trigger build: HTTP ${r.response.status} - ${r.response.statusText}`):r.code==="ECONNREFUSED"?new Error(`Failed to trigger build: Connection refused to ${n.baseURL}`):new Error(`Failed to trigger build: ${r.message}`)}}
14
15
 
15
- export { E as createJenkinsClient, y as getAllBranches, w as getCurrentBranch, p as loadConfig, b as triggerBuild };
16
+ export { j as createJenkinsClient, A as getAllBranches, b as getCurrentBranch, P as loadConfig, F as triggerBuild };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-org/jenkins-cli",
3
- "version": "1.0.0-prior",
3
+ "version": "2.0.0",
4
4
  "description": "Jenkins deployment CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",