@ts-org/jenkins-cli 3.1.0 → 3.1.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 +135 -130
- package/dist/cli.js +24 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,67 +1,64 @@
|
|
|
1
1
|
## Jenkins CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A lightweight Jenkins deployment tool that helps you trigger parameterized Jenkins builds from the command line, either interactively or non-interactively.
|
|
4
4
|
|
|
5
|
-
### ✨
|
|
5
|
+
### ✨ Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
7
|
+
- Interactive CLI flow for selecting branches and deployment environments.
|
|
8
|
+
- Smart triggering: automatically stops running builds and reuses identical queued tasks.
|
|
9
|
+
- Flexible parameterized builds with support for extra runtime parameters.
|
|
10
|
+
- Global `--debug` flag to print Jenkins request details.
|
|
11
|
+
- Rich command set covering common operations for Jobs, Builds, Queue, and more.
|
|
12
|
+
- Project-level config via `jenkins-cli.yaml` or `package.json`.
|
|
13
13
|
|
|
14
|
-
### 📦
|
|
14
|
+
### 📦 Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
# Node.js >= 16
|
|
18
|
-
npm install -g @ts-org/jenkins-cli
|
|
19
|
-
|
|
20
|
-
# Node.js >= 18,推荐使用pnpm
|
|
21
17
|
pnpm install -g @ts-org/jenkins-cli
|
|
22
18
|
```
|
|
23
19
|
|
|
24
|
-
### 🚀
|
|
20
|
+
### 🚀 Usage
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
Run from your project root:
|
|
27
23
|
|
|
28
24
|
```bash
|
|
29
25
|
jenkins-cli
|
|
30
26
|
```
|
|
31
27
|
|
|
32
|
-
CLI
|
|
28
|
+
The CLI will auto-load your config, then list Git branches and deployment environments for you to choose from.
|
|
33
29
|
|
|
34
30
|

|
|
35
31
|
|
|
36
|
-
### ⚙️
|
|
32
|
+
### ⚙️ Configuration
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
Create `jenkins-cli.yaml` in your project root:
|
|
39
35
|
|
|
40
36
|
```yaml
|
|
41
37
|
# yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json
|
|
42
38
|
|
|
43
|
-
# Jenkins API
|
|
39
|
+
# Jenkins API URL format: http(s)://username:token@host:port
|
|
44
40
|
apiToken: http://user:token@jenkins.example.com
|
|
45
41
|
|
|
46
|
-
# Jenkins
|
|
42
|
+
# Jenkins job name (can be overridden with -j, or provided via function reference)
|
|
47
43
|
job: your-project-job
|
|
48
44
|
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
45
|
+
# Deployment environments (passed to Jenkins as the `mode` parameter, or provided via function reference)
|
|
46
|
+
# The options must match your Jenkins configuration.
|
|
47
|
+
# If you configure a mode that does not exist in Jenkins (for example `prod`),
|
|
48
|
+
# Jenkins may return HTTP 500 when `mode=prod` is submitted.
|
|
52
49
|
modes:
|
|
53
50
|
- dev
|
|
54
51
|
- sit
|
|
55
52
|
- uat
|
|
56
53
|
```
|
|
57
54
|
|
|
58
|
-
|
|
55
|
+
It is recommended to add `# yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json` at the top of `jenkins-cli.yaml` for a better editing experience, as shown above.
|
|
59
56
|
|
|
60
|
-
####
|
|
57
|
+
#### Dynamic `job` / `modes`
|
|
61
58
|
|
|
62
|
-
`job`
|
|
59
|
+
`job` and `modes` support function references in the form `path:exportName`. The function receives `answers` and returns the final value.
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
Example:
|
|
65
62
|
|
|
66
63
|
```yaml
|
|
67
64
|
job: jenkins-utils.ts:dynamicJob
|
|
@@ -74,53 +71,51 @@ export function dynamicJob(answers: Record<string, unknown>) {
|
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
export function dynamicModes(answers: Record<string, unknown>) {
|
|
77
|
-
return Number(answers.buildNumber) > 100
|
|
78
|
-
? ['dev', 'sit', 'uat', 'prod']
|
|
79
|
-
: ['dev', 'sit', 'uat']
|
|
74
|
+
return Number(answers.buildNumber) > 100 ? ['dev', 'sit', 'uat', 'prod'] : ['dev', 'sit', 'uat']
|
|
80
75
|
}
|
|
81
76
|
```
|
|
82
77
|
|
|
83
|
-
####
|
|
78
|
+
#### Extended Interactive Parameters
|
|
84
79
|
|
|
85
|
-
|
|
80
|
+
With the `configs` field, you can add custom interactive prompts. Their results are passed to Jenkins as build parameters.
|
|
86
81
|
|
|
87
|
-
- `type`:
|
|
88
|
-
- `choices`, `default`, `validate`, `when`, `filter`, `transformer`:
|
|
89
|
-
- `path:exportName
|
|
90
|
-
- `offset`:
|
|
91
|
-
- `-1
|
|
92
|
-
- `<= -2
|
|
93
|
-
- `name`:
|
|
82
|
+
- `type`: supports `input`, `number`, `confirm`, `list`, `rawlist`, `checkbox`, `password`, and more.
|
|
83
|
+
- `choices`, `default`, `validate`, `when`, `filter`, `transformer`: can be dynamically loaded from local TS/JS files.
|
|
84
|
+
- `path:exportName`: auto-resolves exported values; calls them if they are functions (async supported).
|
|
85
|
+
- `offset`: negative values adjust prompt order.
|
|
86
|
+
- `-1`: inserted after `branch`, before `modes`
|
|
87
|
+
- `<= -2`: inserted before `branch`
|
|
88
|
+
- `name`: parameter key. Avoid reserved names such as `branch`, `mode`, `modes`.
|
|
94
89
|
|
|
95
|
-
|
|
90
|
+
**Example:**
|
|
96
91
|
|
|
97
92
|
```yaml
|
|
98
93
|
configs:
|
|
99
94
|
- type: number
|
|
100
95
|
name: buildNumber
|
|
101
|
-
message: '
|
|
96
|
+
message: 'Select build number:'
|
|
102
97
|
offset: -2
|
|
103
98
|
- type: input
|
|
104
99
|
name: version
|
|
105
|
-
message: '
|
|
100
|
+
message: 'Enter version:'
|
|
106
101
|
default: '1.0.0'
|
|
107
102
|
filter: src/config.ts:filterVersion
|
|
108
103
|
transformer: src/config.ts:transformVersion
|
|
109
104
|
- type: list
|
|
110
105
|
name: service
|
|
111
|
-
message: '
|
|
106
|
+
message: 'Select service:'
|
|
112
107
|
choices: src/config.ts:getServices
|
|
113
108
|
- type: confirm
|
|
114
109
|
name: smokeTest
|
|
115
|
-
message: '
|
|
110
|
+
message: 'Enable smoke test?'
|
|
116
111
|
when: src/config.ts:whenSmokeTest
|
|
117
112
|
- type: input
|
|
118
113
|
name: ticket
|
|
119
|
-
message: '
|
|
114
|
+
message: 'Enter ticket ID:'
|
|
120
115
|
validate: src/config.ts:validateTicket
|
|
121
116
|
```
|
|
122
117
|
|
|
123
|
-
|
|
118
|
+
Corresponding `src/config.ts`:
|
|
124
119
|
|
|
125
120
|
```ts
|
|
126
121
|
export async function getServices() {
|
|
@@ -145,233 +140,243 @@ export function whenSmokeTest(answers: { mode?: string }) {
|
|
|
145
140
|
}
|
|
146
141
|
|
|
147
142
|
export function validateTicket(input: unknown, answers: Record<string, unknown>) {
|
|
148
|
-
return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || '
|
|
143
|
+
return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || 'Invalid ticket format'
|
|
149
144
|
}
|
|
150
145
|
```
|
|
151
146
|
|
|
152
|
-
|
|
147
|
+
**Complete parameter details for the four functions:**
|
|
153
148
|
|
|
154
|
-
- `when(answers)`
|
|
155
|
-
- `answers`:
|
|
156
|
-
-
|
|
149
|
+
- `when(answers)`
|
|
150
|
+
- `answers`: all answers collected so far (keys are prompt `name` values).
|
|
151
|
+
- Returns `boolean | Promise<boolean>` to decide whether to show the prompt.
|
|
157
152
|
|
|
158
|
-
- `validate(input, answers)`
|
|
159
|
-
- `input`:
|
|
160
|
-
- `answers`:
|
|
161
|
-
-
|
|
153
|
+
- `validate(input, answers)`
|
|
154
|
+
- `input`: raw value currently entered by the user.
|
|
155
|
+
- `answers`: all answers collected so far.
|
|
156
|
+
- Return `true` to pass; return a `string` as an error message; `Promise<true | string>` is also supported.
|
|
162
157
|
|
|
163
|
-
- `filter(input, answers)`
|
|
164
|
-
- `input`:
|
|
165
|
-
- `answers`:
|
|
166
|
-
-
|
|
158
|
+
- `filter(input, answers)`
|
|
159
|
+
- `input`: raw value currently entered by the user.
|
|
160
|
+
- `answers`: all answers collected so far.
|
|
161
|
+
- Returns the transformed value (sync or async).
|
|
167
162
|
|
|
168
|
-
- `transformer(input, answers, meta)`
|
|
169
|
-
- `input`:
|
|
170
|
-
- `answers`:
|
|
171
|
-
- `meta`:
|
|
172
|
-
-
|
|
163
|
+
- `transformer(input, answers, meta)`
|
|
164
|
+
- `input`: raw value currently entered by the user.
|
|
165
|
+
- `answers`: all answers collected so far.
|
|
166
|
+
- `meta`: rendering metadata (for example `isFinal`), which may vary slightly across versions.
|
|
167
|
+
- Returns display text only and does not modify the value in `answers` (sync or async).
|
|
173
168
|
|
|
174
|
-
>
|
|
169
|
+
> **Tip**: You can also put configuration under the `jenkins-cli` field in `package.json`.
|
|
175
170
|
>
|
|
176
|
-
>
|
|
171
|
+
> **Lookup order**: `jenkins-cli.yaml` > `package.json` > ancestor directory `jenkins-cli.yaml`.
|
|
177
172
|
|
|
178
|
-
### 🤖
|
|
173
|
+
### 🤖 Command Reference
|
|
179
174
|
|
|
180
|
-
#### `build` (
|
|
175
|
+
#### `build` (non-interactive trigger)
|
|
181
176
|
|
|
182
|
-
|
|
177
|
+
Useful for CI or other scripted scenarios.
|
|
183
178
|
|
|
184
179
|
```bash
|
|
185
|
-
#
|
|
180
|
+
# Trigger a build for the dev environment
|
|
186
181
|
jenkins-cli build -b origin/develop -m dev
|
|
187
182
|
|
|
188
|
-
#
|
|
183
|
+
# Trigger multiple environments at once
|
|
189
184
|
jenkins-cli build -b origin/develop -m dev,sit
|
|
190
185
|
|
|
191
|
-
#
|
|
186
|
+
# Pass extra parameters
|
|
192
187
|
jenkins-cli build -b origin/develop -m dev --param version=1.2.3 --param force=true
|
|
193
188
|
```
|
|
194
189
|
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
Note: by default, the CLI reads `git config user.name` and appends it as `triggered_by`.
|
|
191
|
+
To override it, pass `--param triggered_by=YourName` explicitly.
|
|
197
192
|
|
|
198
193
|
---
|
|
199
194
|
|
|
200
|
-
#### `builds` -
|
|
195
|
+
#### `builds` - build management
|
|
201
196
|
|
|
202
197
|
```bash
|
|
203
|
-
#
|
|
198
|
+
# Show the latest 20 builds
|
|
204
199
|
jenkins-cli builds
|
|
205
200
|
|
|
206
|
-
#
|
|
201
|
+
# Show running builds
|
|
207
202
|
jenkins-cli builds active
|
|
208
203
|
|
|
209
|
-
#
|
|
204
|
+
# Show all running builds on Jenkins
|
|
210
205
|
jenkins-cli builds active -a
|
|
211
206
|
|
|
212
207
|
```
|
|
213
208
|
|
|
214
|
-
#### `changes` -
|
|
209
|
+
#### `changes` - change logs
|
|
215
210
|
|
|
216
211
|
```bash
|
|
217
|
-
#
|
|
212
|
+
# Show change logs for the latest 20 builds
|
|
218
213
|
jenkins-cli changes
|
|
219
214
|
|
|
220
|
-
#
|
|
215
|
+
# Show change logs for the latest 50 builds
|
|
221
216
|
jenkins-cli changes -n 50
|
|
222
217
|
|
|
223
|
-
#
|
|
218
|
+
# Show change logs for a specific build
|
|
224
219
|
jenkins-cli changes 1234
|
|
225
220
|
```
|
|
226
221
|
|
|
227
|
-
#### `config` -
|
|
222
|
+
#### `config` - job configuration
|
|
228
223
|
|
|
229
224
|
```bash
|
|
230
|
-
#
|
|
225
|
+
# Read current job XML config
|
|
231
226
|
jenkins-cli config show
|
|
232
227
|
|
|
233
|
-
#
|
|
228
|
+
# Output as JSON
|
|
234
229
|
jenkins-cli config show -f json
|
|
235
230
|
|
|
236
|
-
#
|
|
231
|
+
# Back up current job config
|
|
237
232
|
jenkins-cli config backup -d ./jenkins-backup
|
|
238
233
|
|
|
239
|
-
#
|
|
234
|
+
# Back up a specific job config
|
|
240
235
|
jenkins-cli config backup -d ./jenkins-backup -j remote-factory-admin
|
|
241
236
|
|
|
242
|
-
#
|
|
237
|
+
# Back up multiple job configs filtered by glob
|
|
243
238
|
jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'
|
|
244
239
|
|
|
245
|
-
#
|
|
240
|
+
# Back up all job configs
|
|
246
241
|
jenkins-cli config backup -d ./jenkins-backup -a
|
|
247
242
|
```
|
|
248
243
|
|
|
249
|
-
#### `
|
|
244
|
+
#### `plugin` - plugin management
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Show enabled plugins
|
|
248
|
+
jenkins-cli plugin show
|
|
249
|
+
|
|
250
|
+
# Back up enabled plugin list
|
|
251
|
+
jenkins-cli plugin backup -d ./jenkins-backup
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### `console` - view logs
|
|
250
255
|
|
|
251
|
-
|
|
256
|
+
Supports live log following, enabled by default.
|
|
252
257
|
|
|
253
258
|
```bash
|
|
254
|
-
#
|
|
259
|
+
# Show logs for a specific build number; follow if running, otherwise print once
|
|
255
260
|
jenkins-cli console 1234
|
|
256
261
|
|
|
257
|
-
#
|
|
262
|
+
# Show logs for the latest build; follow if running, otherwise print once
|
|
258
263
|
jenkins-cli console last
|
|
259
264
|
|
|
260
|
-
#
|
|
265
|
+
# Print current logs only, without following
|
|
261
266
|
jenkins-cli console last --no-follow
|
|
262
267
|
|
|
263
|
-
#
|
|
268
|
+
# Show logs from the latest successful build
|
|
264
269
|
jenkins-cli console last -s success
|
|
265
270
|
|
|
266
|
-
#
|
|
271
|
+
# Show logs from the latest failed build
|
|
267
272
|
jenkins-cli console last -s failed
|
|
268
273
|
```
|
|
269
274
|
|
|
270
|
-
#### `job` -
|
|
275
|
+
#### `job` - job management
|
|
271
276
|
|
|
272
277
|
```bash
|
|
273
|
-
#
|
|
278
|
+
# List all jobs (supports glob filtering)
|
|
274
279
|
jenkins-cli job list --search 'project-*'
|
|
275
280
|
|
|
276
|
-
#
|
|
281
|
+
# Show current job info
|
|
277
282
|
jenkins-cli job info
|
|
278
283
|
```
|
|
279
284
|
|
|
280
|
-
#### `me` -
|
|
285
|
+
#### `me` - user information
|
|
281
286
|
|
|
282
287
|
```bash
|
|
283
|
-
#
|
|
288
|
+
# Verify API token and show current user
|
|
284
289
|
jenkins-cli me
|
|
285
290
|
```
|
|
286
291
|
|
|
287
|
-
#### `params` -
|
|
292
|
+
#### `params` - job parameters
|
|
288
293
|
|
|
289
294
|
```bash
|
|
290
|
-
#
|
|
295
|
+
# Show parameter definitions for the current job
|
|
291
296
|
jenkins-cli params
|
|
292
297
|
```
|
|
293
298
|
|
|
294
|
-
#### `queue` -
|
|
299
|
+
#### `queue` - pending build queue management
|
|
295
300
|
|
|
296
301
|
```bash
|
|
297
|
-
#
|
|
302
|
+
# Show pending build queue
|
|
298
303
|
jenkins-cli queue list
|
|
299
304
|
|
|
300
|
-
#
|
|
305
|
+
# Cancel one queued item
|
|
301
306
|
jenkins-cli queue cancel 5678
|
|
302
307
|
```
|
|
303
308
|
|
|
304
|
-
#### `stop` -
|
|
309
|
+
#### `stop` - stop builds
|
|
305
310
|
|
|
306
311
|
```bash
|
|
307
|
-
#
|
|
312
|
+
# Stop one build
|
|
308
313
|
jenkins-cli stop 1234
|
|
309
314
|
|
|
310
|
-
#
|
|
315
|
+
# Clear queue and stop all running builds for current job
|
|
311
316
|
jenkins-cli stop -a
|
|
312
317
|
|
|
313
|
-
#
|
|
318
|
+
# Stop all builds and queue items on Jenkins (use with caution)
|
|
314
319
|
jenkins-cli stop -A
|
|
315
320
|
```
|
|
316
321
|
|
|
317
|
-
#### `view` -
|
|
322
|
+
#### `view` - view management
|
|
318
323
|
|
|
319
324
|
```bash
|
|
320
|
-
#
|
|
325
|
+
# Show all views
|
|
321
326
|
jenkins-cli view list
|
|
322
327
|
|
|
323
|
-
#
|
|
328
|
+
# Show jobs in a view
|
|
324
329
|
jenkins-cli view show MyView
|
|
325
330
|
|
|
326
|
-
#
|
|
331
|
+
# Add a job to a view
|
|
327
332
|
jenkins-cli view add MyView my-job
|
|
328
333
|
|
|
329
|
-
#
|
|
334
|
+
# Remove a job from a view
|
|
330
335
|
jenkins-cli view remove MyView my-job
|
|
331
336
|
|
|
332
|
-
#
|
|
337
|
+
# Edit jobs in a view (check/uncheck)
|
|
333
338
|
jenkins-cli view edit MyView
|
|
334
339
|
|
|
335
|
-
#
|
|
340
|
+
# Create a view
|
|
336
341
|
jenkins-cli view create MyView
|
|
337
342
|
|
|
338
|
-
#
|
|
343
|
+
# Delete a view
|
|
339
344
|
jenkins-cli view delete MyView
|
|
340
345
|
```
|
|
341
346
|
|
|
342
|
-
#### `workspace` -
|
|
347
|
+
#### `workspace` - workspace
|
|
343
348
|
|
|
344
349
|
```bash
|
|
345
|
-
#
|
|
350
|
+
# Show workspace for current job (reads `job` from jenkins-cli.yaml by default)
|
|
346
351
|
jenkins-cli ws
|
|
347
352
|
|
|
348
|
-
#
|
|
353
|
+
# Show subdirectory (current job)
|
|
349
354
|
jenkins-cli ws -p dist
|
|
350
355
|
|
|
351
|
-
#
|
|
356
|
+
# Specify job
|
|
352
357
|
jenkins-cli ws -j my-job
|
|
353
358
|
|
|
354
|
-
#
|
|
359
|
+
# Show file contents
|
|
355
360
|
jenkins-cli ws cat dist/index.html
|
|
356
361
|
|
|
357
|
-
#
|
|
362
|
+
# Show file contents for a specific job
|
|
358
363
|
jenkins-cli ws cat dist/index.html -j my-job
|
|
359
364
|
|
|
360
|
-
#
|
|
365
|
+
# Save file locally
|
|
361
366
|
jenkins-cli ws cat dist/index.html -o ./index.html
|
|
362
367
|
|
|
363
|
-
#
|
|
368
|
+
# Download and open with default app (useful for images)
|
|
364
369
|
jenkins-cli ws cat public/favicon.ico --open
|
|
365
370
|
|
|
366
|
-
#
|
|
371
|
+
# Wipe workspace (will prompt for confirmation)
|
|
367
372
|
jenkins-cli ws wipe
|
|
368
373
|
|
|
369
|
-
#
|
|
374
|
+
# Wipe workspace for a specific job
|
|
370
375
|
jenkins-cli ws wipe -j my-job
|
|
371
376
|
```
|
|
372
377
|
|
|
373
378
|
### ❓ FAQ
|
|
374
379
|
|
|
375
|
-
**Q: `stop`
|
|
380
|
+
**Q: Why does `stop` or `queue cancel` return 403 Forbidden?**
|
|
376
381
|
|
|
377
|
-
A:
|
|
382
|
+
A: Most likely the Jenkins user lacks permission (requires `Job > Cancel`). Make sure you use an API token instead of a password, and verify Jenkins CSRF settings. This tool handles CSRF automatically, but permission issues must be fixed manually.
|
package/dist/cli.js
CHANGED
|
@@ -2,41 +2,43 @@
|
|
|
2
2
|
import { program, CommanderError } from 'commander';
|
|
3
3
|
import oe from 'inquirer';
|
|
4
4
|
import y from 'chalk';
|
|
5
|
-
import
|
|
5
|
+
import rn from 'ora';
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
|
-
import
|
|
7
|
+
import Tn, { promisify } from 'util';
|
|
8
8
|
import M from 'fs-extra';
|
|
9
|
-
import
|
|
9
|
+
import bn from 'js-yaml';
|
|
10
10
|
import z from 'path';
|
|
11
|
-
import
|
|
11
|
+
import Sn from 'axios';
|
|
12
12
|
import { Buffer } from 'buffer';
|
|
13
|
-
import
|
|
13
|
+
import An from 'jiti';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { promises } from 'fs';
|
|
16
16
|
import { XMLParser } from 'fast-xml-parser';
|
|
17
|
-
import
|
|
17
|
+
import tr from 'os';
|
|
18
18
|
|
|
19
|
-
var
|
|
20
|
-
\u274C ${
|
|
21
|
-
`)),
|
|
22
|
-
`).filter(
|
|
19
|
+
var nt="3.1.2",rt="Jenkins deployment CLI";var p=class extends Error{hint;example;details;exitCode;constructor(t,e={}){super(t),this.name="UserError",this.hint=e.hint,this.example=e.example,this.details=e.details,this.exitCode=e.exitCode??1;}};function gn(n){return n instanceof p?n:n instanceof CommanderError?new p(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new p(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new p("Unknown error",{example:"jenkins-cli --help"})}function q(n){let t=gn(n);if(console.error(y.red(`
|
|
20
|
+
\u274C ${t.message}
|
|
21
|
+
`)),t.details&&console.error(y.gray(t.details)),t.hint&&console.error(y.yellow(`Hint: ${t.hint}`)),t.example!==""){let e=t.example??"jenkins-cli --help";console.error(y.cyan(`Example: ${e}`));}console.error(y.gray('Run "jenkins-cli --help" for usage.')),process.exitCode=t.exitCode;}var Ae=promisify(exec);async function ot(){try{let{stdout:n}=await Ae("git branch --show-current"),t=n.trim();if(!t)throw new p("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}catch{throw new p("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function it(){try{let{stdout:n}=await Ae('git branch -r --format="%(refname:short)"');return n.split(`
|
|
22
|
+
`).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new p("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function le(){try{let{stdout:n}=await Ae("git config user.name"),t=n.trim();return t||void 0}catch{return}}var D="jenkins-cli.yaml",me="package.json",ue="jenkins-cli",yn="jenkinsCli";function at(n,t=process.cwd()){let e=t;for(;;){let r=z.join(e,n);if(M.existsSync(r))return r;let o=z.dirname(e);if(o===e)return null;e=o;}}function kn(n=process.cwd()){return at(D,n)}function xn(n=process.cwd()){let t=at(me,n);return t?z.dirname(t):null}function jn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function st(n){let t=await M.readFile(n,"utf-8"),e=bn.load(t);if(!e||typeof e!="object")throw new p(`Invalid config in ${D}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${D}:
|
|
23
23
|
apiToken: http://user:token@host
|
|
24
24
|
job: my-job
|
|
25
|
-
modes: [dev]`});return e}async function
|
|
25
|
+
modes: [dev]`});return e}async function Cn(n){let t=z.join(n,me);if(!await M.pathExists(t))return {};let e=await M.readFile(t,"utf-8"),r=JSON.parse(e),o=r[ue]??r[yn];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new p(`Invalid ${me} config: "${ue}" must be an object.`,{hint:"Use an object for the config section.",example:`"${ue}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function ct(){let n=process.cwd(),t=xn(n)??n,e=z.join(t,D),r={},o=z.dirname(t),s=kn(o);s&&await M.pathExists(s)&&(r=await st(s));let i=await Cn(t);r={...r,...i};let a=await M.pathExists(e)?await st(e):{};if(r={...r,...a},!jn(r))throw new p("Config incomplete or not found.",{hint:`Tried ${D} in project root, "${ue}" in ${me}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${D}:
|
|
26
26
|
apiToken: http://user:token@host
|
|
27
27
|
job: my-job
|
|
28
28
|
modes:
|
|
29
|
-
- dev`});return {...r,projectRoot:
|
|
30
|
-
`);return _(me(s))}function hn(t){let n=[...t.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of n){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],l="",c="";for(let $ of a){let O=$[1]??"",F=_(me($[2]??"")).trim();if(!(!F||F==="..")&&!/all\s+files\s+in\s+zip/i.test(F)){l=O,c=F;break}}if(!l||!c||l==="../"||l==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||l.endsWith("/")?"dir":"file",p=_(l.split("?")[0]??"").replace(/\/+$/,"")||c,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),f=m?_(me(m[1]??"")).trim():"",g=f?Z(f):void 0,w=`${c}|${p}`;r.has(w)||(r.add(w),e.push({name:c,path:p,type:u,modified:g}));}if(e.length)return e;let o=[...t.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(me(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let l=i.endsWith("/")?"dir":"file",c=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${c}`;r.has(u)||(r.add(u),e.push({name:a,path:c,type:l}));}return e}async function Ae(t,n,e){let r=Ne(n,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await t.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),l=a.data??new ArrayBuffer(0),c=Buffer.from(l);if(!c.length)continue;let u=a.headers?.["content-type"];return {data:c,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Be(t,n,e){let r=Ne(n,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await t.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),l=typeof a.data=="string"?a.data:String(a.data??"");if(!l)continue;if(dn(l)){let c=gn(l);if(c.trim())return c;continue}return l}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function We(t,n,e){let r=Ne(n,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let c=(await t.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof c=="string")try{c=JSON.parse(c);}catch{continue}if(c&&typeof c=="object")return c}catch(l){if(i=l,l?.response?.status===404)continue;throw l}try{let a=await t.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let l=hn(a.data);if(l.length)return {children:l}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function bn(t){if(Array.isArray(t))return t;let n=[t?.children,t?.files,t?.childs,t?.items,t?.entries,t?.content,t?.tree];for(let e of n){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return t?.children&&Array.isArray(t.children?.children)?t.children.children:[]}function Ee(t){return bn(t).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,l=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),c=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,p=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:p,size:c,modified:u,type:l}})}function st(t){let n=t.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!n)throw new d("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=n,i=`${e}://${s.replace(/\/$/,"")}/`,a=pn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(c=>{let u=String(c.method??"get").toUpperCase(),p=new URL(String(c.url??""),c.baseURL??i).toString(),m=c.params,f=c.data;f instanceof URLSearchParams?f=Object.fromEntries(f):typeof f=="string"?String(c.headers?.["Content-Type"]??c.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?f=Object.fromEntries(new URLSearchParams(f)):f=f.trim()?f:void 0:Buffer.isBuffer(f)&&(f=`[Buffer ${f.length} bytes]`);let g={method:u,url:p};return m&&Object.keys(m).length>0&&(g.params=m),f!=null&&f!==""&&(g.data=f),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),c}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function at(t,n=5e3){try{let e=await t.axios.get("crumbIssuer/api/json",{timeout:n}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(t.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,t.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];t.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ue(t){return t.crumbPromise||(t.crumbPromise=at(t,5e3)),t.crumbPromise}async function E(t){await Ue(t),t.crumbForm||(t.crumbPromise=at(t,15e3),await t.crumbPromise);}async function H(t,n){let r=(await t.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(n){let o=n.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function ee(t,n){let e=await t.axios.get(`${x(n)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:pe(o.actions)}))}async function fe(t){let n=await t.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(n.data?.computer)?n.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],l=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let c of [...a,...l]){let u=c?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await fn(t,i.url),l=ot(String(a?.url??i.url));return {...a,job:l}}))).filter(i=>i?.building)}async function te(t,n){return await E(t),await t.axios.post("queue/cancelItem",new URLSearchParams({id:n.toString()}),W),!0}async function ne(t,n,e){try{await E(t),await t.axios.post(`${x(n)}/${e}/stop/`,new URLSearchParams({}),W);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function Fe(t,n,e){await E(t);let r=`view/${encodeURIComponent(n)}/addJobToView`;try{await t.axios.post(r,new URLSearchParams({name:e}),W);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function Le(t,n,e){await E(t);let r=`view/${encodeURIComponent(n)}/removeJobFromView`;try{await t.axios.post(r,new URLSearchParams({name:e}),W);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function ct(t,n){await E(t);let e=new URLSearchParams({name:n,mode:"hudson.model.ListView",json:JSON.stringify({name:n,mode:"hudson.model.ListView"})});try{await t.axios.post("createView",e,W);}catch(r){let o=r?.response?.status;throw o===400?new d(`View already exists: ${n}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new d(`Forbidden to create view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function lt(t,n){await E(t);let e=`view/${encodeURIComponent(n)}/doDelete`;try{await t.axios.post(e,new URLSearchParams({}),W);}catch(r){let o=r?.response?.status;throw o===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new d(`Forbidden to delete view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function de(t,n,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([H(t,n),ee(t,n)]),l=(p,m)=>{let f=p.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===m)?.value:void 0},c=e[r];if(s&&c){let p=i.find(m=>l(m,o)===s&&l(m,r)===c);if(p)return new URL(`queue/item/${p.id}/`,t.baseURL).toString()}let u=!1;if(s)for(let p of a)l(p,o)===s&&(console.log(`Stopping running build #${p.number} (mode=${s})...`),await ne(t,n,p.number),u=!0);u&&await new Promise(p=>setTimeout(p,1e3));try{return await E(t),(await t.axios.post(`${x(n)}/buildWithParameters/`,new URLSearchParams({...e}),W)).headers.location||""}catch(p){let m=p?.response?.status,f=it(p?.response?.data);if(m===400){let g=f?`Jenkins response (snippet):
|
|
31
|
-
${
|
|
32
|
-
${
|
|
33
|
-
${o}`:void 0})}throw e}}async function
|
|
29
|
+
- dev`});return {...r,projectRoot:t}}var B={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function x(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function pe(n){let t=Array.isArray(n)?n.find(o=>o?.parameters):void 0,e=Array.isArray(t?.parameters)?t.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function lt(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 vn(n,t){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",t).toString())).data??{};return {...r,parameters:pe(r.actions)}}function ut(n,t=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>t?`${r.slice(0,t)}...`:r:""}catch{return ""}}function X(n){if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){let t=Number(n);if(Number.isFinite(t))return t}}function Z(n){let t=X(n);if(t!==void 0)return t;if(typeof n=="string"){let e=Date.parse(n);if(!Number.isNaN(e))return e}}function E(n){return n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/")}function We(n,t){let e=n.replace(/\/+$/,""),r=t.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function _(n){return n.replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function fe(n){return n.replace(/<[^>]*>/g,"")}function $n(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function Jn(n){let t=n.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i),e=n.match(/<code[^>]*>([\s\S]*?)<\/code>/i),r=n.match(/<body[^>]*>([\s\S]*?)<\/body>/i),s=(t?.[1]??e?.[1]??r?.[1]??n).replace(/<br\s*\/?>/gi,`
|
|
30
|
+
`);return _(fe(s))}function Rn(n){let t=[...n.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of t){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],c="",l="";for(let v of a){let L=v[1]??"",R=_(fe(v[2]??"")).trim();if(!(!R||R==="..")&&!/all\s+files\s+in\s+zip/i.test(R)){c=L,l=R;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",m=_(c.split("?")[0]??"").replace(/\/+$/,"")||l,f=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),d=f?_(fe(f[1]??"")).trim():"",g=d?Z(d):void 0,b=`${l}|${m}`;r.has(b)||(r.add(b),e.push({name:l,path:m,type:u,modified:g}));}if(e.length)return e;let o=[...n.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(fe(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let c=i.endsWith("/")?"dir":"file",l=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${l}`;r.has(u)||(r.add(u),e.push({name:a,path:l,type:c}));}return e}async function Ue(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),c=a.data??new ArrayBuffer(0),l=Buffer.from(c);if(!l.length)continue;let u=a.headers?.["content-type"];return {data:l,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Le(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),c=typeof a.data=="string"?a.data:String(a.data??"");if(!c)continue;if($n(c)){let l=Jn(c);if(l.trim())return l;continue}return c}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Te(n,t,e){let r=We(t,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let l=(await n.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof l=="string")try{l=JSON.parse(l);}catch{continue}if(l&&typeof l=="object")return l}catch(c){if(i=c,c?.response?.status===404)continue;throw c}try{let a=await n.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let c=Rn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Pn(n){if(Array.isArray(n))return n;let t=[n?.children,n?.files,n?.childs,n?.items,n?.entries,n?.content,n?.tree];for(let e of t){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return n?.children&&Array.isArray(n.children?.children)?n.children.children:[]}function Oe(n){return Pn(n).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,c=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),l=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,m=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:m,size:l,modified:u,type:c}})}function mt(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new p("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=Sn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(l=>{let u=String(l.method??"get").toUpperCase(),m=new URL(String(l.url??""),l.baseURL??i).toString(),f=l.params,d=l.data;d instanceof URLSearchParams?d=Object.fromEntries(d):typeof d=="string"?String(l.headers?.["Content-Type"]??l.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?d=Object.fromEntries(new URLSearchParams(d)):d=d.trim()?d:void 0:Buffer.isBuffer(d)&&(d=`[Buffer ${d.length} bytes]`);let g={method:u,url:m};return f&&Object.keys(f).length>0&&(g.params=f),d!=null&&d!==""&&(g.data=d),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),l}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function ft(n,t=5e3){try{let e=await n.axios.get("crumbIssuer/api/json",{timeout:t}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(n.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,n.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ve(n){return n.crumbPromise||(n.crumbPromise=ft(n,5e3)),n.crumbPromise}async function W(n){await Ve(n),n.crumbForm||(n.crumbPromise=ft(n,15e3),await n.crumbPromise);}async function H(n,t){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(t){let o=t.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function ee(n,t){let e=await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:pe(o.actions)}))}async function de(n){let t=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(t.data?.computer)?t.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],c=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let l of [...a,...c]){let u=l?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await vn(n,i.url),c=lt(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function te(n,t){return await W(n),await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),B),!0}async function ne(n,t,e){try{await W(n),await n.axios.post(`${x(t)}/${e}/stop/`,new URLSearchParams({}),B);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function Ie(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/addJobToView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new p(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function qe(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/removeJobFromView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new p(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function pt(n,t){await W(n);let e=new URLSearchParams({name:t,mode:"hudson.model.ListView",json:JSON.stringify({name:t,mode:"hudson.model.ListView"})});try{await n.axios.post("createView",e,B);}catch(r){let o=r?.response?.status;throw o===400?new p(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new p(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function dt(n,t){await W(n);let e=`view/${encodeURIComponent(t)}/doDelete`;try{await n.axios.post(e,new URLSearchParams({}),B);}catch(r){let o=r?.response?.status;throw o===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new p(`Forbidden to delete view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ge(n,t,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([H(n,t),ee(n,t)]),c=(m,f)=>{let d=m.actions?.find(b=>b.parameters);return d?d.parameters.find(b=>b.name===f)?.value:void 0},l=e[r];if(s&&l){let m=i.find(f=>c(f,o)===s&&c(f,r)===l);if(m)return new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let m of a)c(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await ne(n,t,m.number),u=!0);u&&await new Promise(m=>setTimeout(m,1e3));try{return await W(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),B)).headers.location||""}catch(m){let f=m?.response?.status,d=ut(m?.response?.data);if(f===400){let g=d?`Jenkins response (snippet):
|
|
31
|
+
${d}`:void 0;throw new p("Jenkins rejected the request (400).",{hint:`Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs). This CLI sends parameters "${r}" and "${o}".`,example:"jenkins-cli build -b origin/main -m dev",details:g})}if(f){let g=d?`Jenkins response (snippet):
|
|
32
|
+
${d}`:void 0;throw new p(`Request failed with status code ${f}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:g})}throw m}}async function gt(n){return (await n.axios.get("whoAmI/api/json")).data}async function T(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function De(n){let t="plugins[shortName,longName,version,enabled,active,hasUpdate,pinned,bundled,deleted]";return (await n.axios.get(`pluginManager/api/json?tree=${t}`)).data.plugins??[]}async function ht(n,t,e){let r=String(t??"").trim();if(!r)return;let o=Math.max(1,e?.depth??5),s="jobs[name,url]";for(let u=0;u<o;u+=1)s=`jobs[name,url,${s}]`;let i=await n.axios.get(`api/json?tree=${encodeURIComponent(s)}`),a=Array.isArray(i.data?.jobs)?i.data.jobs:[],c=[],l=[...a];for(;l.length;){let u=l.shift();if(!u)continue;let m=String(u.name??"").trim();if(m&&m===r&&u.url){let f=lt(String(u.url));f&&c.push(f);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function he(n,t){try{let r=(await n.axios.get(`${x(t)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration],lastSuccessfulBuild[number,url,building,result,timestamp,duration],lastFailedBuild[number,url,building,result,timestamp,duration]`)).data??{},o=typeof r?.fullName=="string"?r.fullName:"",s=typeof r?.name=="string"?r.name:"",i=String(t??"").trim();if(!o&&!s)throw new p(`Job not found: ${i}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli job show -j my-job"});if(i&&i!==o&&i!==s)throw new p(`Job not found: ${i}`,{hint:`Jenkins returned job "${o||s}".`,example:"jenkins-cli console last -j my-job -s success"});return r}catch(e){if(e?.response?.status===404){let o=String(t??"").trim();throw new p(`Job not found: ${o}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli console last -j my-job -s success"})}throw e}}async function wt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:pe(i.actions)}))}async function bt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,timestamp,changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,timestamp:i.timestamp,url:i.url,changeSet:i.changeSet??{}}))}async function we(n,t,e){let o=(await n.axios.get(`${x(t)}/${e}/api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,description,actions[parameters[name,value]],changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]],culprits[fullName],executor[number,progress]`)).data??{};return {...o,parameters:pe(o.actions)}}async function yt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function kt(n,t){return ((await n.axios.get(`${x(t)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function Me(n,t,e){let r=String(e?.path??"").trim(),o=r?`${E(r)}/`:"",s=await Te(n,x(t),o);return Oe(s??{})}async function xt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await Te(n,i,s);return Oe(a??{})}async function jt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await Te(n,i,s);return Oe(a??{})}async function Ct(n,t,e){let r=E(String(e??"").trim());return await Le(n,x(t),r)}async function St(n,t,e){let r=E(String(e??"").trim());return await Ue(n,x(t),r)}async function vt(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Le(n,s,o)}async function $t(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ue(n,s,o)}async function Jt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Le(n,s,o)}async function Rt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ue(n,s,o)}async function Pt(n,t){try{return await W(n),await n.axios.post(`${x(t)}/doWipeOutWorkspace`,new URLSearchParams({}),B),!0}catch(e){let r=e?.response?.status;if(r){let o=ut(e?.response?.data);throw new p(`Workspace wipe failed (HTTP ${r}).`,{hint:"Check Job/Workspace permission or CSRF settings.",example:"jenkins-cli workspace wipe -j my-job",details:o?`Jenkins response (snippet):
|
|
33
|
+
${o}`:void 0})}throw e}}async function ze(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Nt(n,t,e,r){let o=`${x(t)}/${e}/logText/progressiveText`,s=new URLSearchParams;s.append("start",String(r));let i=await n.axios.post(o,s,{responseType:"text"}),a=String(i.data??""),c=i.headers["x-text-size"],l=i.headers["x-more-data"],u=c?Number(c):r+Buffer.byteLength(a);return {text:a,newOffset:u,hasMoreData:l==="true"}}async function re(n,t){let e=t?.raw?"views[*]":"views[name,url]";return (await n.axios.get(`api/json?tree=${e}`)).data.views??[]}async function Q(n,t){let e=`view/${encodeURIComponent(t)}`;try{return (await n.axios.get(`${e}/api/json?tree=jobs[name,url,color]`)).data.jobs??[]}catch(r){throw r?.response?.status===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Et(n,t){let e=String(t??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await re(n,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await Q(n,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function w(){let n=rn("Loading configuration...").start();try{let t=await ct();n.succeed("Configuration loaded");let e=mt(t.apiToken);return Ve(e),{config:t,jenkins:e}}catch(t){q(t),process.exit(1);}}var Bn=An(fileURLToPath(import.meta.url),{interopDefault:!0});async function ye(n){return await Promise.resolve(n)}function Wn(n){let t=String(n??"").trim();if(!t)return null;let e=t.split(":");if(e.length<2)return null;if(e.length===2){let[s,i]=e;return !s||!i?null:{file:s,exportName:i}}let r=e.at(-1);if(!r)return null;let o=e.slice(0,-2).join(":");return o?{file:o,exportName:r}:null}function Un(n,t){return z.isAbsolute(t)?t:z.join(n,t)}function Bt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function ke(n,t){let e=Wn(t);if(!e)return null;let r=Un(n,e.file);if(!await M.pathExists(r))throw new p(`configs reference not found: ${e.file}`,{hint:"Check the file path (relative to project root) and file name.",example:'configs: [{ choices: "./scripts/prompts.ts:choices" }]'});let o=Bn(r),s=Bt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new p(`configs reference export not found: ${t}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function Wt(n,t){if(typeof t!="string")return {isRef:!1,isFunction:!1,value:t};let e=await ke(n,t);return e?{isRef:!0,isFunction:typeof e.value=="function",value:e.value}:{isRef:!1,isFunction:!1,value:t}}async function be(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(!e.allow(r.value))throw new p(e.error(t),{hint:e.hint,example:e.example});return r.value}async function Ft(n,t){if(typeof t!="string")return t;let e=await ke(n,t);if(!e)return t;if(typeof e.value=="function"){let r=e.value();return await ye(r)}return await ye(e.value)}async function O(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(typeof r.value=="function"){let o=r.value(e);return await ye(o)}return await ye(r.value)}async function xe(n,t){let e=Array.isArray(n)?n:[];if(e.length===0)return [];let r=String(t.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!Bt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await Ft(r,i.choices);let a=String(i.type??"").toLowerCase(),c=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof c!="function"&&!Array.isArray(c))if(c==null)i.choices=[];else if(typeof c=="string"||typeof c=="number"||typeof c=="boolean")i.choices=[String(c)];else if(typeof c[Symbol.iterator]=="function")i.choices=Array.from(c).map(u=>String(u));else throw new p(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof c}).`,{hint:"Return an array for list/rawlist/checkbox choices.",example:'export const choices = ["dev", "uat"]'})}i.default!==void 0&&(i.default=await Ft(r,i.default)),i.when!==void 0&&(i.when=await be(r,i.when,{allow:a=>typeof a=="function"||typeof a=="boolean",error:a=>`when reference must be a function or boolean: ${a}`,hint:"Export a function or boolean from the referenced file.",example:'export const when = (answers) => answers.env === "dev"'})),i.validate!==void 0&&(i.validate=await be(r,i.validate,{allow:a=>typeof a=="function",error:a=>`validate reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:'export const validate = (input) => !!input || "required"'})),i.filter!==void 0&&(i.filter=await be(r,i.filter,{allow:a=>typeof a=="function",error:a=>`filter reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const filter = (input) => input.trim()"})),i.transformer!==void 0&&(i.transformer=await be(r,i.transformer,{allow:a=>typeof a=="function",error:a=>`transformer reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const transformer = (input) => input.toUpperCase()"})),o.push(i);}return o}function Ut(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 Lt(n,t,e={}){let r=(t??"").trim();if(r)return r;let o=e.projectRoot??process.cwd(),s=await O(o,n,e.answers??{});return String(s??"").trim()||n}function A(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new p(`${t} must be a non-negative integer.`,{hint:`Provide a valid ${t}.`,example:`jenkins-cli console ${t==="id"?"123":"last -t 100"}`});return e}function je(n,t){let e=String(t??"").trim();if(!e)return !0;if(!/[*?]/.test(e))return n.includes(e);let s=`^${e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(s).test(n)}catch{return n.includes(e)}}function Ce(n){let t=n,e=t?.name?String(t.name):"",r=t?.code?String(t.code):"",o=t?.message?String(t.message):String(t??"");if(e==="ExitPromptError"||e==="AbortError"||e==="PromptAbortError"||r==="ABORT_ERR")return !0;let s=o.toLowerCase();return s.includes("cancelled")||s.includes("canceled")}async function Tt(){console.log(y.bold.blue(`
|
|
34
34
|
\u{1F680} Jenkins CLI - Jenkins Deployment CLI
|
|
35
|
-
`));let{config:
|
|
36
|
-
\u{1F44B} Cancelled by user`)),process.exit(130);},s,i=t.job,a={};try{process.prependOnceListener("SIGINT",o);let f=await Nt(t.configs,{projectRoot:t.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let S of f){let I=String(S?.name??"").trim();if(g.has(I))throw new d(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let w={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},$=[],O=[],F=[];for(let S of f){let I=Number(S.offset);if(Number.isFinite(I)&&I<=-2){$.push(S);continue}if(I===-1){O.push(S);continue}F.push(S);}let C={};Object.assign(C,await oe.prompt($,C)),Object.assign(C,await oe.prompt([w],C)),Object.assign(C,await oe.prompt(O,C));let Qe=String(t.projectRoot??"").trim()||process.cwd(),G=await Ie(Qe,t.modes,C),K=[];if(Array.isArray(G))K=G.map(S=>String(S).trim()).filter(Boolean);else if(G!=null){let S=String(G).trim();S&&(K=[S]);}K.length===0&&Array.isArray(t.modes)&&(K=t.modes);let Zt={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:S=>S.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([Zt],C)),Object.assign(C,await oe.prompt(F,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=At(C,["branch","modes"]);let en=await Ie(Qe,t.job,C),Ge=String(en??"").trim();Ge&&(i=Ge);}catch(f){let g=f,w=g?.message?String(g.message):String(g),$=g?.name?String(g.name):"";($==="ExitPromptError"||w.toLowerCase().includes("cancelled")||w.toLowerCase().includes("canceled"))&&(console.log(y.yellow(`
|
|
37
|
-
\u{1F44B} Cancelled by user`)),process.exit(0)),q(new d(`Prompt failed: ${w}`,{hint:$?`Error: ${$}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let l=!1,c=()=>{l||(l=!0,console.log());},u=await ce();u&&!("triggered_by"in a)&&(a.triggered_by=u);let m=s.modes.map(async f=>{let g=Qt(`Triggering build for ${y.yellow(f)}...`).start();try{let w=await de(n,i,{...a,branch:s.branch,mode:f});c(),g.succeed(`${y.green(f)} - Triggered! Queue: ${w}`);}catch(w){throw c(),g.fail(`${y.red(f)} - ${w.message}`),w}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
|
|
35
|
+
`));let{config:n,jenkins:t}=await w(),[e,r]=await Promise.all([it(),ot()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let d=await xe(n.configs,{projectRoot:n.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let $ of d){let I=String($?.name??"").trim();if(g.has(I))throw new p(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let b={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},v=[],L=[],R=[];for(let $ of d){let I=Number($.offset);if(Number.isFinite(I)&&I<=-2){v.push($);continue}if(I===-1){L.push($);continue}R.push($);}let C={};Object.assign(C,await oe.prompt(v,C)),Object.assign(C,await oe.prompt([b],C)),Object.assign(C,await oe.prompt(L,C));let Ze=String(n.projectRoot??"").trim()||process.cwd(),G=await O(Ze,n.modes,C),K=[];if(Array.isArray(G))K=G.map($=>String($).trim()).filter(Boolean);else if(G!=null){let $=String(G).trim();$&&(K=[$]);}K.length===0&&Array.isArray(n.modes)&&(K=n.modes);let fn={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:$=>$.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([fn],C)),Object.assign(C,await oe.prompt(R,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=Ut(C,["branch","modes"]);let pn=await O(Ze,n.job,C),et=String(pn??"").trim();et&&(i=et);}catch(d){let g=d,b=g?.message?String(g.message):String(g),v=g?.name?String(g.name):"";Ce(d)&&process.exit(0),q(new p(`Prompt failed: ${b}`,{hint:v?`Error: ${v}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let c=!1,l=()=>{c||(c=!0,console.log());},u=await le();u&&!("triggered_by"in a)&&(a.triggered_by=u);let f=s.modes.map(async d=>{let g=rn(`Triggering build for ${y.yellow(d)}...`).start();try{let b=await ge(t,i,{...a,branch:s.branch,mode:d});l(),g.succeed(`${y.green(d)} - Triggered! Queue: ${b}`);}catch(b){throw l(),g.fail(`${y.red(d)} - ${b.message}`),b}});try{await Promise.all(f);}catch{console.log(y.bold.red(`
|
|
38
36
|
\u274C Some builds failed. Check the output above.
|
|
39
|
-
`)),process.exit(1);}}function
|
|
40
|
-
|
|
37
|
+
`)),process.exit(1);}}function Ot(n){let t=n.command("queue").description("Operations for the build queue");t.command("list").alias("ls").description("List items waiting in the build queue").option("-j, --job <job>","Show only queue items for the specified job").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job?await Lt(r.job,e.job,{projectRoot:r.projectRoot}):void 0,i=await H(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),t.command("cancel").description("Cancel a queued build item").argument("<id...>","Queue id(s)").action(async e=>{let{jenkins:r}=await w(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=A(String(a),"id");try{return await te(r,c),{id:c,ok:!0}}catch(l){return {id:c,ok:!1,message:String(l?.message??l)}}})),i=s.filter(a=>!a.ok);if(i.length===0){console.log(y.green(`Cancelled ${s.length} queue item(s).`));return}console.log(y.yellow(`Cancelled ${s.length-i.length} item(s), failed ${i.length} item(s).`));for(let a of i)console.log(y.red(`- ${a.id}: ${a.message??"Cancel failed"}`));});}function h(n){return {[Tn.inspect.custom]:()=>n}}function U(n,t){return String(n??"").padEnd(t," ")}function j(n){return Math.max(0,...n.map(t=>String(t??"").length))}function S(n,t){return h(U(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return h(y.gray("-"));let r=s=>h(s),o=e.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(e).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",c=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||c?y.cyanBright(t):y.hex("#4EA1FF")(t))}catch{return r(y.hex("#4EA1FF")(t))}return o.startsWith("ws://")||o.startsWith("wss://")?r(y.cyan(t)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(y.magenta(t)):o.startsWith("file://")?r(y.yellow(t)):o.startsWith("mailto:")?r(y.green(t)):o.startsWith("javascript:")||o.startsWith("data:")?r(y.red(t)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(y.cyan(t)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(y.blue(t)):r(t)}function P(n){let t=new Date(n),e=r=>String(r).padStart(2,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function se(n,t){if(t===!0)return h(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return h(e?e==="SUCCESS"?y.green(e):e==="ABORTED"?y.gray(e):e==="FAILURE"?y.red(e):e==="UNSTABLE"?y.yellow(e):e==="NOT_BUILT"?y.gray(e):e:"-")}function ae(n){let t=String(n??"");if(!t)return h("-");let e=t.endsWith("_anime"),r=e?t.slice(0,-6):t,o=(()=>{switch(r){case"blue":return e?y.blueBright(t):y.blue(t);case"red":return e?y.redBright(t):y.red(t);case"yellow":return e?y.yellowBright(t):y.yellow(t);case"aborted":return y.gray(t);case"disabled":case"notbuilt":case"grey":case"gray":return y.gray(t);default:return t}})();return h(o)}function Vt(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return h("-");if(t<1024)return h(`${t} B`);let e=["KB","MB","GB","TB"],r=t,o=-1;for(;r>=1024&&o<e.length-1;)r/=1024,o+=1;let s=r>=10||o===0?r.toFixed(1):r.toFixed(2);return h(`${s} ${e[o]}`)}var Se=64;function Vn(n){return n!=null&&typeof n=="object"?n.value??n.name??n:n}function qt(n){let t=String(n?.toString?.()??"").trim();if(!t)return "";let e=t.indexOf("=>");if(e>=0){let s=t.slice(e+2).trim();return s.startsWith("{")&&s.endsWith("}")&&(s=s.slice(1,-1)),s}let r=t.indexOf("{"),o=t.lastIndexOf("}");return r>=0&&o>r?t.slice(r+1,o):""}function Dt(n){let t=new Set,e,r=/\b(\d+)\b/g;for(;e=r.exec(n);){let s=parseInt(e[1],10);Number.isFinite(s)&&t.add(s);}let o=Array.from(t).sort((s,i)=>s-i);return o.length>Se?o.slice(0,Se):o}function In(n,t){let e=String(n.type??"").toLowerCase();if(e==="confirm")return [!0,!1];if(e==="number"){let s=n,i=typeof s.min=="number"&&Number.isFinite(s.min)?s.min:null,a=typeof s.max=="number"&&Number.isFinite(s.max)?s.max:null;if(i!=null&&a!=null)return i===a?[i]:i<a?[i,a]:[a,i];if(i!=null)return [i,i+1];if(a!=null)return [a-1,a];if(t){let c=Dt(qt(t));if(c.length)return c}return [0,1,99,100,101]}let r=Array.isArray(n.choices)?n.choices:null;if(!r?.length)return null;let o=r.map(Vn);if(e==="list"||e==="rawlist")return o;if(e==="checkbox"){let s=[[]];return o.forEach(i=>s.push([i])),o.length<=4&&s.push(o),s}return null}async function qn(n,t){let e=n.when;if(e===void 0||typeof e=="boolean")return e!==!1;if(typeof e!="function")return !0;try{return !!await Promise.resolve(e(t))}catch{return !0}}async function Dn(n,t,e){let r=await xe(n.configs,{projectRoot:t}),o=[{}];for(let s of r){let i=String(s.name??"").trim();if(!i)continue;let a=In(s,e);if(!a?.length||o.length*a.length>Se)continue;let c=[];for(let l of o){if(!await qn(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Mn(n){let t=new Map,e=/answers\.(\w+)\s*===\s*(\d+)/g,r;for(;r=e.exec(n);){let s=r[1],i=parseInt(r[2],10);Number.isFinite(i)&&(t.has(s)||t.set(s,new Set),t.get(s).add(i));}let o=new Map;return t.forEach((s,i)=>o.set(i,Array.from(s).sort((a,c)=>a-c))),o}function zn(n){return n.split(`
|
|
38
|
+
`).filter(t=>{let e=t.trim();return e&&!e.startsWith("//")}).join(`
|
|
39
|
+
`)}function _n(n){let t=qt(n);if(!t)return [];t=zn(t);let e=Dt(t),r=Mn(t),o=new Set,s=/return\s+([^;]+?)(?=;|$)/gm,i=/`((?:[^`\\]|\\.|\$\{[^}]*\})*)`/g,a=/\$\{\s*answers\.(\w+)\s*\}/,c;for(;c=s.exec(t);){let l=c[1];for(let m of It(l))m&&o.add(m);let u;for(;u=i.exec(l);){let m=u[1];if(!m.includes("${"))continue;let f=m.match(a),d=f&&r.has(f[1])?r.get(f[1]):f?[]:e,g=m.split(/\$\{[^}]*\}/);if(g.length<2)continue;let b=g[0]??"",v=g.slice(1).join("");for(let L of d){let R=b+String(L)+v;R&&o.add(R);}}}if(o.size===0&&!t.includes("{"))for(let l of It(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function It(n){let t=[],e=/(['"`])((?:\\.|(?!\1).)*)\1/g,r;for(;r=e.exec(n);)if(!(r[1]==="`"&&r[2].includes("${")))try{let o=r[1]==="`"?r[2]:JSON.parse(r[1]+r[2]+r[1]);String(o).trim()&&t.push(String(o).trim());}catch{String(r[2]??"").trim()&&t.push(String(r[2]).trim());}return t}async function Hn(n,t,e){let r=new Set;for(let i of _n(n))r.add(i);let o=await Dn(t,e,n),s=async i=>{let a=await Promise.resolve(n(i)),c=typeof a=="string"?String(a).trim():"";return c&&r.add(c),r.size>=Se};if(!o.length)await s({});else for(let i of o)if(await s(i))break;return Array.from(r).sort((i,a)=>i.localeCompare(a,"en",{sensitivity:"base"}))}async function k(n,t){if(t==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli job info -j my-job"});let e=String(t??"").trim();if(e)return e;let r=String(n.projectRoot??"").trim()||process.cwd(),o=await Wt(r,n.job);if(o.isRef&&o.isFunction){let i=await Hn(o.value,n,r);if(!i.length)throw new p("Job choices not found from config reference.",{hint:"Ensure the job function returns a string (job name).",example:'export function dynamicJob(answers) { return "my-job" }'});if(i.length===1)return i[0];let{job:a}=await oe.prompt([{type:"list",name:"job",message:"\u8BF7\u9009\u62E9\u8981\u64CD\u4F5C\u7684 Job:",choices:i}]);return String(a??"").trim()}let s=await O(r,n.job,{});return String(s??n.job??"").trim()||String(n.job??"")}function ve(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Mt(n){let t=Object.entries(n??{}).filter(([r])=>r);return t.length?(t.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"})),t.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ")):"-"}function zt(n){let t=n.command("builds").description("Build operations");t.option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job??ve("builds")??ve(),i=await k(r,s),c=(await wt(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:se(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:h(P(Number(u.timestamp??0))),parameters:Mt(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:S(u.parameters,l)})));}),t.command("active").description("List active builds").option("-j, --job <job>","Specify job").option("-a, --all","Query all running builds on Jenkins").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job??ve("active")??ve("builds"),i=await k(r,s),c=((e.all?await de(o):await ee(o,i))??[]).map(u=>({...e.all?{job:h(String(u.job??""))}:{},number:u.number,date:h(u.timestamp?P(Number(u.timestamp)):"-"),parameters:Mt(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:S(u.parameters,l)})));});}function Qn(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function _t(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:h("-"),s=e?.timestamp?P(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:h(s),author:h("-"),message:h("-")});continue}for(let a of i){let c=Qn(a),l=String(a?.msg??"").trim();t.push({build:o,date:h(s),author:h(c||"-"),message:h(l||"-")});}}return t}function Ht(n){n.command("changes").description("List build changes").argument("[id]").option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").option("--raw","Output raw changeSet payload").action(async(t,e)=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job);if(t){let c=A(t,"id"),l=await we(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(_t([l]));return}let i=Math.max(1,A(e.limit,"limit")),a=await bt(o,s,{limit:i});if(e.raw){console.log(JSON.stringify(a.map(c=>({build:c.number,changeSet:c.changeSet??{}})),null,2));return}console.table(_t(a));});}function Qt(){let n=process.argv,t=n.findIndex(r=>r==="-j"||r==="--job");if(t<0)return;let e=n[t+1];return !e||e.startsWith("-")?"":e}function Gt(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await yt(r,o,s);process.stdout.write(l);return}let a=0,c=!0;for(console.log(y.blue(`--- Following log for ${o} build #${s} ---`));;)try{let{text:l,newOffset:u,hasMoreData:m}=await Nt(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&m)continue;if(!m){let d=(await we(r,o,s)).result||"UNKNOWN",g=d==="SUCCESS"?y.green:y.red;console.log(g(`
|
|
40
|
+
--- Build Finished: ${d} ---`));break}await new Promise(f=>setTimeout(f,1e3));}catch{await new Promise(l=>setTimeout(l,2e3));}};t.argument("<id>","Build number").option("-j, --job <job>","Specify job").option("--no-follow","Do not follow the build log").action(async(r,o)=>{let{config:s,jenkins:i}=await w(),a=o.job??Qt();if(a==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console 123 -j my-job"});let c=await k(s,a),l=A(r,"id");await e(i,c,l,o);}),t.command("last").description("Fetch the latest build log").option("-j, --job <job>","Specify job").option("-s, --status <status>","success | failed | any","any").option("--no-follow","Do not follow the build log").action(async r=>{let{config:o,jenkins:s}=await w(),i=r.job??Qt();if(i==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console last -j my-job -s success"});let a=await k(o,i),c=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(c))throw new p(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await he(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,m=Number(u?.number);if(!m){let f=c==="success"?"successful":c==="failed"?"failed":"last";console.log(y.yellow(`No ${f} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,m,r);});}function Kt(n){n.command("stop").description("Clear queued builds and stop running builds").argument("[id]","Build number").option("-j, --job <job>","Specify Jenkins job").option("-a, --all","Cancel queued items for this job and stop all running builds for it").option("-A, --ALL","Cancel all queued items and stop all running builds on Jenkins").action(async(t,e)=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job);if(e.ALL){let[i,a]=await Promise.all([H(o),de(o)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>({number:Number(u.number),job:String(u.job??"")})).filter(u=>u.job&&!Number.isNaN(u.number));for(let u of c)await te(o,u);for(let u of l)await ne(o,u.job,u.number);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([H(o,s),ee(o,s)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>Number(u.number)).filter(u=>!Number.isNaN(u));for(let u of c)await te(o,u);for(let u of l)await ne(o,s,u);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!t){console.log(y.red("Missing build id. Provide <id> or use -a/--all."));return}await ne(o,s,A(t,"id")),console.log(y.green("Stop requested"));});}function Yt(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await w(),o=await k(e,t.job),s=await kt(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:S(a?.name??"",i),type:h(String(a?.type??"")),description:h(String(a?.description??"")),default:h(a?.default===void 0?"-":String(a.default)),choices:h(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Xt(n){n.command("me").description("Show Jenkins user info for the current token").option("--raw","Print raw response").action(async t=>{let{jenkins:e}=await w(),r=await gt(e);if(t.raw){console.log(r&&typeof r=="object"?JSON.stringify(r,null,2):String(r));return}let o=r&&typeof r=="object"?r:{value:r},s=(u,m)=>{let f=u.length;if(f>=m)return u;let d=m-f,g=Math.floor(d/2),b=d-g;return `${" ".repeat(g)}${u}${" ".repeat(b)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=h(s(a,c));for(let u of Object.keys(o).sort((m,f)=>m.localeCompare(f,"en"))){if(u==="name")continue;let m=o[u],f=u.toLowerCase();if((f==="url"||f.endsWith("url")||f.includes("url"))&&(typeof m=="string"||typeof m=="number")){l[u]=J(String(m));continue}let g=m==null?"":typeof m=="object"?JSON.stringify(m):String(m);l[u]=h(g);}console.table([l]);});}function Zt(n){let t=n.command("config").description("Show Jenkins job configuration");t.command("show").description("Show job configuration").option("-j, --job <job>","Specify job").option("-f, --format <format>","Output format: xml or json","xml").action(async e=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new p(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await ze(o,s);if(i==="xml"){process.stdout.write(a);return}let l=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(l,null,2));}),t.command("backup").description("Backup job configuration to files").requiredOption("-d, --destination <path>","Destination folder").option("-a, --all","Backup all jobs").option("-j, --job <job>","Specify job").option("--filter <pattern>","Filter jobs by name (supports glob)").action(async e=>{let{config:r,jenkins:o}=await w(),s=String(e.destination??"").trim();if(!s)throw new p("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli config backup -d ./jenkins-backup"});if(e.all&&e.filter)throw new p("Options --all and --filter are mutually exclusive.",{hint:"Use either -a/--all or --filter, not both.",example:"jenkins-cli config backup -d ./jenkins-backup -a"});let i=String(e.filter??"").trim(),a=e.all?await T(o):i?(await T(o)).filter(l=>je(String(l.name??""),i)):[{name:await k(r,e.job)}];if(a.length===0)throw new p(`No jobs matched filter: ${i}`,{hint:"Check the pattern or remove --filter to use the default job.",example:"jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'"});await promises.mkdir(s,{recursive:!0});let c=[];for(let l of a){let u=String(l?.name??"").trim();if(!u)continue;let m=await ze(o,u),f=z.join(s,u,"config.xml");await promises.mkdir(z.dirname(f),{recursive:!0}),await promises.writeFile(f,m),c.push(f);}console.log(`Backup completed. ${c.length} file(s) saved to ${z.resolve(s)}`);});}function en(n){let t=n.command("job").description("Job operations");t.command("list").alias("ls").description("List all jobs").option("--search <keyword>","Filter by name (supports glob)").action(async e=>{let{jenkins:r}=await w(),o=await T(r),s=(e.search??"").trim(),i=s?o.filter(l=>je(String(l.name??""),s)):o;i.sort((l,u)=>String(l?.name??"").localeCompare(String(u?.name??""),"en",{sensitivity:"base"}));let a=j(i.map(l=>l?.name)),c=j(i.map(l=>l?.url));console.table((i??[]).map(l=>({name:S(l.name??"",a),color:ae(l.color),url:J(U(l.url??"",c))})));}),t.command("info").description("Show job info").option("-j, --job <job>","Specify job").option("--json","Output raw JSON").action(async e=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job),i=await he(o,s);if(e.json){console.log(JSON.stringify(i,null,2));return}let a=i?.lastBuild,c=i?.lastCompletedBuild,l=Array.isArray(i?.healthReport)?i.healthReport:[];console.table([{name:h(String(i?.name??"")),url:J(String(i?.url??"")),color:ae(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:h(a?.number?`#${a.number}`:"-"),lastResult:se(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:h(a?.timestamp?P(Number(a.timestamp)):"-"),lastCompleted:h(c?.number?`#${c.number}`:"-"),lastCompletedResult:se(c?.result,!1),lastCompletedDate:h(c?.timestamp?P(Number(c.timestamp)):"-")}]);});}function Yn(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function Xn(n){let t=n.split("=");if(t.length<2)throw new p(`Invalid --param: "${n}".`,{hint:"Use the format key=value.",example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});let e=t[0].trim(),r=t.slice(1).join("=").trim();if(!e)throw new p(`Invalid --param: "${n}". Key is empty.`,{hint:'Provide a non-empty key before "=".',example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});return {key:e,value:r}}function tn(n){n.command("build").description("Trigger Jenkins builds non-interactively").requiredOption("-b, --branch <branch>","Branch (e.g., origin/develop)").option("-m, --mode <mode>","Deployment environment (repeatable or comma-separated: -m dev -m uat / -m dev,uat)",(t,e)=>e.concat(Yn(t)),[]).option("--param <key=value>","Extra parameters (repeatable, e.g., --param foo=bar)",(t,e)=>e.concat(t),[]).option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await w(),o=await k(e,t.job),s=String(t.branch??"").trim();if(!s)throw new p("branch is required.",{hint:"Pass a branch with -b or --branch.",example:"jenkins-cli build -b origin/develop -m dev"});let i=(t.mode??[]).map(String).map(d=>d.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new p("mode is required.",{hint:"Pass at least one -m/--mode.",example:"jenkins-cli build -b origin/develop -m dev -m uat"});let c={};for(let d of t.param??[]){let{key:g,value:b}=Xn(String(d));c[g]=b;}let l=await le();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,m=()=>{u||(u=!0,console.log());},f=a.map(async d=>{let g=rn(`Triggering build for ${y.yellow(d)}...`).start();try{let b=await ge(r,o,{...c,branch:s,mode:d});m(),g.succeed(`${y.green(d)} - Triggered! Queue: ${b}`);}catch(b){throw m(),g.fail(`${y.red(d)} - ${b.message}`),b}});try{await Promise.all(f);}catch{console.log(y.bold.red(`
|
|
41
41
|
\u274C Some builds failed. Check the output above.
|
|
42
|
-
`)),process.exit(1);}});}function
|
|
42
|
+
`)),process.exit(1);}});}function nn(n){let t=n.command("view").description("View operations");t.command("list").alias("ls").description("List all views").option("--raw","Output raw JSON").action(async e=>{let{jenkins:r}=await w(),o=await re(r,{raw:e.raw});if(e.raw){console.log(JSON.stringify(o,null,2));return}o.sort((c,l)=>String(c?.name??"").localeCompare(String(l?.name??""),"en",{sensitivity:"base"}));let s=(o??[]).map(c=>({name:String(c?.name??""),url:String(c?.url??"")})),i=j(s.map(c=>c.name)),a=j(s.map(c=>c.url));console.table(s.map(c=>({name:S(c.name,i),url:J(U(c.url,a))})));}),t.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w(),o=await Q(r,e);if(!o.length){console.log("No jobs found in this view.");return}o.sort((a,c)=>String(a?.name??"").localeCompare(String(c?.name??""),"en",{sensitivity:"base"}));let s=j(o.map(a=>a?.name)),i=j(o.map(a=>a?.url));console.table(o.map(a=>({name:S(a.name??"",s),color:ae(a.color),url:J(U(a.url??"",i))})));}),t.command("add").description("Add a job to a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await w();if((await Q(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await Ie(o,e,r),console.log(y.green(`Job "${r}" added to view "${e}".`));}),t.command("remove").alias("rm").description("Remove a job from a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await w();if(!(await Q(o,e)).some(i=>i.name===r))throw new p(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await qe(o,e,r),console.log(y.green(`Job "${r}" removed from view "${e}".`));}),t.command("edit").description("Edit jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w(),[o,s]=await Promise.all([T(r),Q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((f,d)=>String(f?.name??"").localeCompare(String(d?.name??""),"en",{sensitivity:"base"}));let i=new Set(s.map(f=>String(f?.name??"").trim())),a=o.map(f=>String(f?.name??"").trim()).filter(Boolean).map(f=>({name:f,checked:i.has(f)})),c=await oe.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(f=>String(f).trim())),u=[...l].filter(f=>!i.has(f)),m=[...i].filter(f=>f&&!l.has(f));if(!u.length&&!m.length){console.log(y.yellow("No changes to apply."));return}for(let f of u)await Ie(r,e,f);for(let f of m)await qe(r,e,f);console.log(y.green(`View "${e}" updated.`));}),t.command("create").description("Create a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w();await pt(r,e),console.log(y.green(`View "${e}" created.`));}),t.command("delete").alias("del").description("Delete a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w();await dt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function rr(n){return h(n?P(n):"-")}function Ne(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Ge(n,t,e){let r=t?.parent?.opts()?.job,o=Ne(e)??Ne(t?.name())??Ne("ws")??Ne("workspace");return n??r??o}function on(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function or(n,t){let e=on(n),r=on(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function ir(n){let t=process.platform,e="",r=[];t==="darwin"?(e="open",r=[n]):t==="win32"?(e="cmd",r=["/c","start","",n]):(e="xdg-open",r=[n]);try{spawn(e,r,{stdio:"ignore",detached:!0}).unref();}catch{}}async function N(n){try{return await n()}catch(t){if(t?.response?.status===404)return null;throw t}}async function sr(n,t){try{return await ht(n,t)}catch{return}}async function ar(n,t){try{return await Et(n,t)}catch{return}}async function cr(n){try{return await re(n,{raw:!1})}catch{return []}}async function Ke(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await sr(n,t);if(o&&o!==t){let a=await e.direct(o);if(a!==null)return a}if(e.beforeView){let a=await e.beforeView();if(a!==null)return a}let s=await ar(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await cr(n);for(let a of i){let c=String(a?.url??"").trim();if(!c)continue;let l=await e.byViewUrl(c);if(l!==null)return l}return null}async function lr(n,t,e){return await Ke(n,t,{direct:r=>N(()=>Me(n,r,{path:e})),byViewName:r=>N(()=>xt(n,r,t,{path:e})),byViewUrl:r=>N(()=>jt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await N(()=>Me(n,t,{path:r})):null}})}async function ur(n,t,e){return await Ke(n,t,{direct:r=>N(()=>Ct(n,r,e)),byViewName:r=>N(()=>vt(n,r,t,e)),byViewUrl:r=>N(()=>Jt(n,r,t,e))})}async function mr(n,t,e){return await Ke(n,t,{direct:r=>N(()=>St(n,r,e)),byViewName:r=>N(()=>$t(n,r,t,e)),byViewUrl:r=>N(()=>Rt(n,r,t,e))})}function sn(n){let t=n.command("workspace").alias("ws").description("Workspace operations");t.option("-j, --job <job>","Specify job").option("-p, --path <path>","Workspace sub path").action(async(e,r)=>{let{config:o,jenkins:s}=await w(),i=Ge(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=rn(`Loading workspace for job "${a}"...`).start(),u=await lr(s,a,c);if(!u){l.warn(`Workspace not found for job: ${a}. The job may be inside a folder, not run yet, or workspace is disabled.`);return}if(l.succeed(`Workspace loaded for job: ${a}`),!u.length){console.table([{name:"-",type:"-",size:"-",modified:"-",path:"-",url:"-"}]);return}let m=u.map(g=>({name:String(g.name??""),typeRaw:g.type,size:Vt(g.size),modified:rr(g.modified),path:or(c,String(g.path??g.name??""))})),f=j(m.map(g=>g.name)),d=j(m.map(g=>g.path));console.table(m.map(g=>{let b=U(String(g.name??""),f),v=String(g.typeRaw??"");return {name:v==="dir"?h(y.bold.cyan(b)):v==="file"?h(y.green(b)):h(y.gray(b)),size:g.size,modified:g.modified,path:S(String(g.path??""),d)}}));}),t.command("cat").description("Show file content in workspace").argument("<path>","Workspace file path").option("-j, --job <job>","Specify job").option("-o, --out <file>","Save file to path").option("--open","Open file with default application").action(async(e,r,o)=>{let{config:s,jenkins:i}=await w(),a=Ge(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)throw new p("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),m=r.open===!0;if(u||m){let d=await mr(i,c,l);if(d===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let g=u||z.join(tr.tmpdir(),z.basename(l)||"file");try{u&&await M.pathExists(u)&&(await M.stat(u)).isDirectory()&&(g=z.join(u,z.basename(l)||"file"));}catch{}await M.ensureDir(z.dirname(g)),await M.writeFile(g,d.data),m?(ir(g),console.log(y.green(`Opened: ${g}`))):console.log(y.green(`Saved to: ${g}`));return}let f=await ur(i,c,l);if(f===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(f));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await w(),i=Ge(e.job,r,"wipe"),a=await k(o,i);try{if(!(await oe.prompt([{type:"confirm",name:"confirm",message:`Wipe workspace for job "${a}"? This cannot be undone.`,default:!1}]))?.confirm){console.log(y.yellow("Cancelled."));return}}catch(l){if(Ce(l)){console.log(y.yellow("Cancelled."));return}throw l}let c=rn(`Wiping workspace for job "${a}"...`).start();try{await Pt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function an(n){return n.active===!0||n.enabled===!0}function cn(n,t){return String(n.shortName??"").localeCompare(String(t.shortName??""),"en",{sensitivity:"base"})}function ln(n){let t=n.command("plugin").description("Plugin operations");t.command("show").description("Show used plugins").action(async()=>{let{jenkins:e}=await w(),o=(await De(e)??[]).filter(a=>an(a)&&String(a.shortName??"").trim());if(o.sort(cn),o.length===0){console.log("No plugins found.");return}let s=j(o.map(a=>a.shortName)),i=j(o.map(a=>a.version));console.table(o.map(a=>({name:S(a.shortName??"",s),version:S(a.version??"",i),enabled:a.enabled===void 0?h("-"):a.enabled,active:a.active===void 0?h("-"):a.active,update:a.hasUpdate===void 0?h("-"):a.hasUpdate})));}),t.command("backup").description("Backup plugins list").requiredOption("-d, --destination <path>","Destination folder").action(async e=>{let{jenkins:r}=await w(),o=String(e.destination??"").trim();if(!o)throw new p("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli plugin backup -d ./jenkins-backup"});let i=(await De(r)??[]).filter(l=>an(l)&&String(l.shortName??"").trim());if(i.sort(cn),i.length===0)throw new p("No plugins found to back up.",{hint:"Check Jenkins permissions or ensure plugins are installed.",example:"jenkins-cli plugin show"});await promises.mkdir(o,{recursive:!0});let a=z.join(o,"plugins.json"),c=z.join(o,"plugins.txt");await promises.writeFile(a,JSON.stringify(i,null,2)),await promises.writeFile(c,`${i.map(l=>{let u=String(l.shortName??"").trim(),m=String(l.version??"").trim();return m?`${u}:${m}`:u}).filter(Boolean).join(`
|
|
43
|
+
`)}
|
|
44
|
+
`),console.log(`Backup completed. ${i.length} plugin(s) saved to ${z.resolve(o)}`);});}function un(n){zt(n),Ht(n),Zt(n),en(n),Gt(n),Yt(n),ln(n),Ot(n),Kt(n),tn(n),Xt(n),nn(n),sn(n);}function mn(n,t,e){let r=s=>s.options.some(i=>i.long===t||i.short===t),o=s=>{r(s)||s.option(t,e);for(let i of s.commands)o(i);};for(let s of n.commands)o(s);}program.name("jenkins-cli").description(rt).version(nt,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Tt);un(program);mn(program,"--debug","Print Jenkins request params");async function fr(){try{await program.parseAsync(process.argv);}catch(n){q(n);}}fr();
|