@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 +61 -19
- package/dist/cli.js +14 -13
- package/dist/index.js +9 -8
- package/package.json +1 -1
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
|
-
|
|
13
|
+
在项目根目录创建 `jenkins-cli.yaml`:
|
|
12
14
|
|
|
13
15
|
```yaml
|
|
14
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"scripts": {
|
|
64
|
+
"ship": "jenkins-cli"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
29
68
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
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
|
|
11
|
+
import D from 'axios';
|
|
12
12
|
|
|
13
|
-
var
|
|
14
|
-
|
|
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 ${
|
|
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
|
|
21
|
-
\u{1F44B} Cancelled by user`)),process.exit(0);}console.log();let
|
|
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("
|
|
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
|
|
2
|
-
import
|
|
3
|
-
import
|
|
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
|
|
6
|
+
import J from 'axios';
|
|
7
7
|
|
|
8
|
-
var
|
|
9
|
-
|
|
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 ${
|
|
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 {
|
|
16
|
+
export { j as createJenkinsClient, A as getAllBranches, b as getCurrentBranch, P as loadConfig, F as triggerBuild };
|