@ts-org/jenkins-cli 3.1.1 → 3.1.3
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 +137 -119
- package/dist/cli.js +28 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,63 +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
17
|
pnpm install -g @ts-org/jenkins-cli
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
### 🚀
|
|
20
|
+
### 🚀 Usage
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Run from your project root:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
jenkins-cli
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
CLI
|
|
28
|
+
The CLI will auto-load your config, then list Git branches and deployment environments for you to choose from.
|
|
29
29
|
|
|
30
30
|

|
|
31
31
|
|
|
32
|
-
### ⚙️
|
|
32
|
+
### ⚙️ Configuration
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Create `jenkins-cli.yaml` in your project root:
|
|
35
35
|
|
|
36
36
|
```yaml
|
|
37
37
|
# yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json
|
|
38
38
|
|
|
39
|
-
# Jenkins API
|
|
39
|
+
# Jenkins API URL format: http(s)://username:token@host:port
|
|
40
40
|
apiToken: http://user:token@jenkins.example.com
|
|
41
41
|
|
|
42
|
-
# Jenkins
|
|
42
|
+
# Jenkins job name (can be overridden with -j, or provided via function reference)
|
|
43
43
|
job: your-project-job
|
|
44
44
|
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
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.
|
|
48
49
|
modes:
|
|
49
50
|
- dev
|
|
50
51
|
- sit
|
|
51
52
|
- uat
|
|
52
53
|
```
|
|
53
54
|
|
|
54
|
-
|
|
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.
|
|
55
56
|
|
|
56
|
-
####
|
|
57
|
+
#### Dynamic `job` / `modes`
|
|
57
58
|
|
|
58
|
-
`job`
|
|
59
|
+
`job` and `modes` support function references in the form `path:exportName`. The function receives `answers` and returns the final value.
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
Example:
|
|
61
62
|
|
|
62
63
|
```yaml
|
|
63
64
|
job: jenkins-utils.ts:dynamicJob
|
|
@@ -74,47 +75,47 @@ export function dynamicModes(answers: Record<string, unknown>) {
|
|
|
74
75
|
}
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
####
|
|
78
|
+
#### Extended Interactive Parameters
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
With the `configs` field, you can add custom interactive prompts. Their results are passed to Jenkins as build parameters.
|
|
80
81
|
|
|
81
|
-
- `type`:
|
|
82
|
-
- `choices`, `default`, `validate`, `when`, `filter`, `transformer`:
|
|
83
|
-
- `path:exportName
|
|
84
|
-
- `offset`:
|
|
85
|
-
- `-1
|
|
86
|
-
- `<= -2
|
|
87
|
-
- `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`.
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
**Example:**
|
|
90
91
|
|
|
91
92
|
```yaml
|
|
92
93
|
configs:
|
|
93
94
|
- type: number
|
|
94
95
|
name: buildNumber
|
|
95
|
-
message: '
|
|
96
|
+
message: 'Select build number:'
|
|
96
97
|
offset: -2
|
|
97
98
|
- type: input
|
|
98
99
|
name: version
|
|
99
|
-
message: '
|
|
100
|
+
message: 'Enter version:'
|
|
100
101
|
default: '1.0.0'
|
|
101
102
|
filter: src/config.ts:filterVersion
|
|
102
103
|
transformer: src/config.ts:transformVersion
|
|
103
104
|
- type: list
|
|
104
105
|
name: service
|
|
105
|
-
message: '
|
|
106
|
+
message: 'Select service:'
|
|
106
107
|
choices: src/config.ts:getServices
|
|
107
108
|
- type: confirm
|
|
108
109
|
name: smokeTest
|
|
109
|
-
message: '
|
|
110
|
+
message: 'Enable smoke test?'
|
|
110
111
|
when: src/config.ts:whenSmokeTest
|
|
111
112
|
- type: input
|
|
112
113
|
name: ticket
|
|
113
|
-
message: '
|
|
114
|
+
message: 'Enter ticket ID:'
|
|
114
115
|
validate: src/config.ts:validateTicket
|
|
115
116
|
```
|
|
116
117
|
|
|
117
|
-
|
|
118
|
+
Corresponding `src/config.ts`:
|
|
118
119
|
|
|
119
120
|
```ts
|
|
120
121
|
export async function getServices() {
|
|
@@ -139,233 +140,250 @@ export function whenSmokeTest(answers: { mode?: string }) {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
export function validateTicket(input: unknown, answers: Record<string, unknown>) {
|
|
142
|
-
return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || '
|
|
143
|
+
return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || 'Invalid ticket format'
|
|
143
144
|
}
|
|
144
145
|
```
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
**Complete parameter details for the four functions:**
|
|
147
148
|
|
|
148
149
|
- `when(answers)`
|
|
149
|
-
- `answers`:
|
|
150
|
-
-
|
|
150
|
+
- `answers`: all answers collected so far (keys are prompt `name` values).
|
|
151
|
+
- Returns `boolean | Promise<boolean>` to decide whether to show the prompt.
|
|
151
152
|
|
|
152
153
|
- `validate(input, answers)`
|
|
153
|
-
- `input`:
|
|
154
|
-
- `answers`:
|
|
155
|
-
-
|
|
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.
|
|
156
157
|
|
|
157
158
|
- `filter(input, answers)`
|
|
158
|
-
- `input`:
|
|
159
|
-
- `answers`:
|
|
160
|
-
-
|
|
159
|
+
- `input`: raw value currently entered by the user.
|
|
160
|
+
- `answers`: all answers collected so far.
|
|
161
|
+
- Returns the transformed value (sync or async).
|
|
161
162
|
|
|
162
163
|
- `transformer(input, answers, meta)`
|
|
163
|
-
- `input`:
|
|
164
|
-
- `answers`:
|
|
165
|
-
- `meta`:
|
|
166
|
-
-
|
|
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).
|
|
167
168
|
|
|
168
|
-
>
|
|
169
|
+
> **Tip**: You can also put configuration under the `jenkins-cli` field in `package.json`.
|
|
169
170
|
>
|
|
170
|
-
>
|
|
171
|
+
> **Lookup order**: `jenkins-cli.yaml` > `package.json` > ancestor directory `jenkins-cli.yaml`.
|
|
171
172
|
|
|
172
|
-
### 🤖
|
|
173
|
+
### 🤖 Command Reference
|
|
173
174
|
|
|
174
|
-
#### `build` (
|
|
175
|
+
#### `build` (non-interactive trigger)
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
Useful for CI or other scripted scenarios.
|
|
177
178
|
|
|
178
179
|
```bash
|
|
179
|
-
#
|
|
180
|
+
# Trigger a build for the dev environment
|
|
180
181
|
jenkins-cli build -b origin/develop -m dev
|
|
181
182
|
|
|
182
|
-
#
|
|
183
|
+
# Trigger multiple environments at once
|
|
183
184
|
jenkins-cli build -b origin/develop -m dev,sit
|
|
184
185
|
|
|
185
|
-
#
|
|
186
|
+
# Pass extra parameters
|
|
186
187
|
jenkins-cli build -b origin/develop -m dev --param version=1.2.3 --param force=true
|
|
187
188
|
```
|
|
188
189
|
|
|
189
|
-
|
|
190
|
-
|
|
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.
|
|
191
192
|
|
|
192
193
|
---
|
|
193
194
|
|
|
194
|
-
#### `builds` -
|
|
195
|
+
#### `builds` - build management
|
|
195
196
|
|
|
196
197
|
```bash
|
|
197
|
-
#
|
|
198
|
+
# Show the latest 20 builds
|
|
198
199
|
jenkins-cli builds
|
|
199
200
|
|
|
200
|
-
#
|
|
201
|
+
# Show running builds
|
|
201
202
|
jenkins-cli builds active
|
|
202
203
|
|
|
203
|
-
#
|
|
204
|
+
# Show all running builds on Jenkins
|
|
204
205
|
jenkins-cli builds active -a
|
|
205
206
|
|
|
206
207
|
```
|
|
207
208
|
|
|
208
|
-
#### `changes` -
|
|
209
|
+
#### `changes` - change logs
|
|
209
210
|
|
|
210
211
|
```bash
|
|
211
|
-
#
|
|
212
|
+
# Show change logs for the latest 20 builds
|
|
212
213
|
jenkins-cli changes
|
|
213
214
|
|
|
214
|
-
#
|
|
215
|
+
# Show change logs for the latest 50 builds
|
|
215
216
|
jenkins-cli changes -n 50
|
|
216
217
|
|
|
217
|
-
#
|
|
218
|
+
# Show change logs for a specific build
|
|
218
219
|
jenkins-cli changes 1234
|
|
219
220
|
```
|
|
220
221
|
|
|
221
|
-
#### `config` -
|
|
222
|
+
#### `config` - job configuration
|
|
222
223
|
|
|
223
224
|
```bash
|
|
224
|
-
#
|
|
225
|
+
# Read current job XML config
|
|
225
226
|
jenkins-cli config show
|
|
226
227
|
|
|
227
|
-
#
|
|
228
|
+
# Output as JSON
|
|
228
229
|
jenkins-cli config show -f json
|
|
229
230
|
|
|
230
|
-
#
|
|
231
|
+
# Back up current job config
|
|
231
232
|
jenkins-cli config backup -d ./jenkins-backup
|
|
232
233
|
|
|
233
|
-
#
|
|
234
|
+
# Back up a specific job config
|
|
234
235
|
jenkins-cli config backup -d ./jenkins-backup -j remote-factory-admin
|
|
235
236
|
|
|
236
|
-
#
|
|
237
|
+
# Back up multiple job configs filtered by glob
|
|
237
238
|
jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'
|
|
238
239
|
|
|
239
|
-
#
|
|
240
|
+
# Back up all job configs
|
|
240
241
|
jenkins-cli config backup -d ./jenkins-backup -a
|
|
241
242
|
```
|
|
242
243
|
|
|
243
|
-
#### `
|
|
244
|
+
#### `plugin` - plugin management
|
|
244
245
|
|
|
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
|
+
#### `backup` - full Jenkins backup
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Back up Jenkins global config, jobs, plugins, and credentials
|
|
258
|
+
jenkins-cli backup -d ./jenkins-backup
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### `console` - view logs
|
|
262
|
+
|
|
263
|
+
Supports live log following, enabled by default.
|
|
246
264
|
|
|
247
265
|
```bash
|
|
248
|
-
#
|
|
266
|
+
# Show logs for a specific build number; follow if running, otherwise print once
|
|
249
267
|
jenkins-cli console 1234
|
|
250
268
|
|
|
251
|
-
#
|
|
269
|
+
# Show logs for the latest build; follow if running, otherwise print once
|
|
252
270
|
jenkins-cli console last
|
|
253
271
|
|
|
254
|
-
#
|
|
272
|
+
# Print current logs only, without following
|
|
255
273
|
jenkins-cli console last --no-follow
|
|
256
274
|
|
|
257
|
-
#
|
|
275
|
+
# Show logs from the latest successful build
|
|
258
276
|
jenkins-cli console last -s success
|
|
259
277
|
|
|
260
|
-
#
|
|
278
|
+
# Show logs from the latest failed build
|
|
261
279
|
jenkins-cli console last -s failed
|
|
262
280
|
```
|
|
263
281
|
|
|
264
|
-
#### `job` -
|
|
282
|
+
#### `job` - job management
|
|
265
283
|
|
|
266
284
|
```bash
|
|
267
|
-
#
|
|
285
|
+
# List all jobs (supports glob filtering)
|
|
268
286
|
jenkins-cli job list --search 'project-*'
|
|
269
287
|
|
|
270
|
-
#
|
|
288
|
+
# Show current job info
|
|
271
289
|
jenkins-cli job info
|
|
272
290
|
```
|
|
273
291
|
|
|
274
|
-
#### `me` -
|
|
292
|
+
#### `me` - user information
|
|
275
293
|
|
|
276
294
|
```bash
|
|
277
|
-
#
|
|
295
|
+
# Verify API token and show current user
|
|
278
296
|
jenkins-cli me
|
|
279
297
|
```
|
|
280
298
|
|
|
281
|
-
#### `params` -
|
|
299
|
+
#### `params` - job parameters
|
|
282
300
|
|
|
283
301
|
```bash
|
|
284
|
-
#
|
|
302
|
+
# Show parameter definitions for the current job
|
|
285
303
|
jenkins-cli params
|
|
286
304
|
```
|
|
287
305
|
|
|
288
|
-
#### `queue` -
|
|
306
|
+
#### `queue` - pending build queue management
|
|
289
307
|
|
|
290
308
|
```bash
|
|
291
|
-
#
|
|
309
|
+
# Show pending build queue
|
|
292
310
|
jenkins-cli queue list
|
|
293
311
|
|
|
294
|
-
#
|
|
312
|
+
# Cancel one queued item
|
|
295
313
|
jenkins-cli queue cancel 5678
|
|
296
314
|
```
|
|
297
315
|
|
|
298
|
-
#### `stop` -
|
|
316
|
+
#### `stop` - stop builds
|
|
299
317
|
|
|
300
318
|
```bash
|
|
301
|
-
#
|
|
319
|
+
# Stop one build
|
|
302
320
|
jenkins-cli stop 1234
|
|
303
321
|
|
|
304
|
-
#
|
|
322
|
+
# Clear queue and stop all running builds for current job
|
|
305
323
|
jenkins-cli stop -a
|
|
306
324
|
|
|
307
|
-
#
|
|
325
|
+
# Stop all builds and queue items on Jenkins (use with caution)
|
|
308
326
|
jenkins-cli stop -A
|
|
309
327
|
```
|
|
310
328
|
|
|
311
|
-
#### `view` -
|
|
329
|
+
#### `view` - view management
|
|
312
330
|
|
|
313
331
|
```bash
|
|
314
|
-
#
|
|
332
|
+
# Show all views
|
|
315
333
|
jenkins-cli view list
|
|
316
334
|
|
|
317
|
-
#
|
|
335
|
+
# Show jobs in a view
|
|
318
336
|
jenkins-cli view show MyView
|
|
319
337
|
|
|
320
|
-
#
|
|
338
|
+
# Add a job to a view
|
|
321
339
|
jenkins-cli view add MyView my-job
|
|
322
340
|
|
|
323
|
-
#
|
|
341
|
+
# Remove a job from a view
|
|
324
342
|
jenkins-cli view remove MyView my-job
|
|
325
343
|
|
|
326
|
-
#
|
|
344
|
+
# Edit jobs in a view (check/uncheck)
|
|
327
345
|
jenkins-cli view edit MyView
|
|
328
346
|
|
|
329
|
-
#
|
|
347
|
+
# Create a view
|
|
330
348
|
jenkins-cli view create MyView
|
|
331
349
|
|
|
332
|
-
#
|
|
350
|
+
# Delete a view
|
|
333
351
|
jenkins-cli view delete MyView
|
|
334
352
|
```
|
|
335
353
|
|
|
336
|
-
#### `workspace` -
|
|
354
|
+
#### `workspace` - workspace
|
|
337
355
|
|
|
338
356
|
```bash
|
|
339
|
-
#
|
|
357
|
+
# Show workspace for current job (reads `job` from jenkins-cli.yaml by default)
|
|
340
358
|
jenkins-cli ws
|
|
341
359
|
|
|
342
|
-
#
|
|
360
|
+
# Show subdirectory (current job)
|
|
343
361
|
jenkins-cli ws -p dist
|
|
344
362
|
|
|
345
|
-
#
|
|
363
|
+
# Specify job
|
|
346
364
|
jenkins-cli ws -j my-job
|
|
347
365
|
|
|
348
|
-
#
|
|
366
|
+
# Show file contents
|
|
349
367
|
jenkins-cli ws cat dist/index.html
|
|
350
368
|
|
|
351
|
-
#
|
|
369
|
+
# Show file contents for a specific job
|
|
352
370
|
jenkins-cli ws cat dist/index.html -j my-job
|
|
353
371
|
|
|
354
|
-
#
|
|
372
|
+
# Save file locally
|
|
355
373
|
jenkins-cli ws cat dist/index.html -o ./index.html
|
|
356
374
|
|
|
357
|
-
#
|
|
375
|
+
# Download and open with default app (useful for images)
|
|
358
376
|
jenkins-cli ws cat public/favicon.ico --open
|
|
359
377
|
|
|
360
|
-
#
|
|
378
|
+
# Wipe workspace (will prompt for confirmation)
|
|
361
379
|
jenkins-cli ws wipe
|
|
362
380
|
|
|
363
|
-
#
|
|
381
|
+
# Wipe workspace for a specific job
|
|
364
382
|
jenkins-cli ws wipe -j my-job
|
|
365
383
|
```
|
|
366
384
|
|
|
367
385
|
### ❓ FAQ
|
|
368
386
|
|
|
369
|
-
**Q: `stop`
|
|
387
|
+
**Q: Why does `stop` or `queue cancel` return 403 Forbidden?**
|
|
370
388
|
|
|
371
|
-
A:
|
|
389
|
+
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
|
@@ -1,42 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program, CommanderError } from 'commander';
|
|
3
|
-
import
|
|
3
|
+
import ce from 'inquirer';
|
|
4
4
|
import y from 'chalk';
|
|
5
|
-
import
|
|
5
|
+
import un from 'ora';
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
7
|
+
import _n, { promisify } from 'util';
|
|
8
|
+
import _ from 'fs-extra';
|
|
9
|
+
import vn from 'js-yaml';
|
|
10
|
+
import W from 'path';
|
|
11
|
+
import En from 'axios';
|
|
12
12
|
import { Buffer } from 'buffer';
|
|
13
|
-
import
|
|
13
|
+
import Vn 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 lr from 'os';
|
|
18
18
|
|
|
19
|
-
var
|
|
19
|
+
var it="3.1.3",st="Jenkins deployment CLI";var g=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 jn(n){return n instanceof g?n:n instanceof CommanderError?new g(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new g(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new g("Unknown error",{example:"jenkins-cli --help"})}function M(n){let t=jn(n);if(console.error(y.red(`
|
|
20
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
|
|
22
|
-
`).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new
|
|
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 Ue=promisify(exec);async function at(){try{let{stdout:n}=await Ue("git branch --show-current"),t=n.trim();if(!t)throw new g("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}catch{throw new g("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function ct(){try{let{stdout:n}=await Ue('git branch -r --format="%(refname:short)"');return n.split(`
|
|
22
|
+
`).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new g("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function fe(){try{let{stdout:n}=await Ue("git config user.name"),t=n.trim();return t||void 0}catch{return}}var z="jenkins-cli.yaml",ge="package.json",de="jenkins-cli",$n="jenkinsCli";function ut(n,t=process.cwd()){let e=t;for(;;){let r=W.join(e,n);if(_.existsSync(r))return r;let o=W.dirname(e);if(o===e)return null;e=o;}}function Jn(n=process.cwd()){return ut(z,n)}function Pn(n=process.cwd()){let t=ut(ge,n);return t?W.dirname(t):null}function Rn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function lt(n){let t=await _.readFile(n,"utf-8"),e=vn.load(t);if(!e||typeof e!="object")throw new g(`Invalid config in ${z}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${z}:
|
|
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 Nn(n){let t=W.join(n,ge);if(!await _.pathExists(t))return {};let e=await _.readFile(t,"utf-8"),r=JSON.parse(e),o=r[de]??r[$n];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new g(`Invalid ${ge} config: "${de}" must be an object.`,{hint:"Use an object for the config section.",example:`"${de}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function mt(){let n=process.cwd(),t=Pn(n)??n,e=W.join(t,z),r={},o=W.dirname(t),s=Jn(o);s&&await _.pathExists(s)&&(r=await lt(s));let i=await Nn(t);r={...r,...i};let a=await _.pathExists(e)?await lt(e):{};if(r={...r,...a},!Rn(r))throw new g("Config incomplete or not found.",{hint:`Tried ${z} in project root, "${de}" in ${ge}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${z}:
|
|
26
26
|
apiToken: http://user:token@host
|
|
27
27
|
job: my-job
|
|
28
28
|
modes:
|
|
29
|
-
- dev`});return {...r,projectRoot:t}}var
|
|
30
|
-
`);return _(fe(s))}function jn(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 S of a){let L=S[1]??"",R=_(fe(S[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",f=_(c.split("?")[0]??"").replace(/\/+$/,"")||l,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),p=m?_(fe(m[1]??"")).trim():"",g=p?Z(p):void 0,b=`${l}|${f}`;r.has(b)||(r.add(b),e.push({name:l,path:f,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(kn(c)){let l=xn(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=jn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Cn(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 Cn(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,f=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:f,size:l,modified:u,type:c}})}function ct(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)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]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=bn.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(),f=new URL(String(l.url??""),l.baseURL??i).toString(),m=l.params,p=l.data;p instanceof URLSearchParams?p=Object.fromEntries(p):typeof p=="string"?String(l.headers?.["Content-Type"]??l.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?p=Object.fromEntries(new URLSearchParams(p)):p=p.trim()?p:void 0:Buffer.isBuffer(p)&&(p=`[Buffer ${p.length} bytes]`);let g={method:u,url:f};return m&&Object.keys(m).length>0&&(g.params=m),p!=null&&p!==""&&(g.data=p),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 lt(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=lt(n,5e3)),n.crumbPromise}async function W(n){await Ve(n),n.crumbForm||(n.crumbPromise=lt(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 yn(n,i.url),c=st(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 d(`View not found: ${t}`,{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 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 d(`View not found: ${t}`,{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 ut(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 d(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new d(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function mt(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 d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new d(`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=(f,m)=>{let p=f.actions?.find(b=>b.parameters);return p?p.parameters.find(b=>b.name===m)?.value:void 0},l=e[r];if(s&&l){let f=i.find(m=>c(m,o)===s&&c(m,r)===l);if(f)return new URL(`queue/item/${f.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let f of a)c(f,o)===s&&(console.log(`Stopping running build #${f.number} (mode=${s})...`),await ne(n,t,f.number),u=!0);u&&await new Promise(f=>setTimeout(f,1e3));try{return await W(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),B)).headers.location||""}catch(f){let m=f?.response?.status,p=at(f?.response?.data);if(m===400){let g=p?`Jenkins response (snippet):
|
|
31
|
-
${
|
|
32
|
-
${
|
|
33
|
-
${o}`:void 0})}throw e}}async function
|
|
29
|
+
- dev`});return {...r,projectRoot:t}}var U={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 he(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 Oe(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 An(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:he(r.actions)}}function pt(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 ee(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 te(n){let t=ee(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 Ve(n,t){let e=n.replace(/\/+$/,""),r=t.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function Q(n){return n.replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function we(n){return n.replace(/<[^>]*>/g,"")}function Fn(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function Bn(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 Q(we(s))}function Wn(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 O=v[1]??"",P=Q(we(v[2]??"")).trim();if(!(!P||P==="..")&&!/all\s+files\s+in\s+zip/i.test(P)){c=O,l=P;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",p=Q(c.split("?")[0]??"").replace(/\/+$/,"")||l,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),f=m?Q(we(m[1]??"")).trim():"",d=f?te(f):void 0,b=`${l}|${p}`;r.has(b)||(r.add(b),e.push({name:l,path:p,type:u,modified:d}));}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=Q(we(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=Q(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 Ie(n,t,e){let r=Ve(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 qe(n,t,e){let r=Ve(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(Fn(c)){let l=Bn(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 De(n,t,e){let r=Ve(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=Wn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Un(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 Me(n){return Un(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=ee(e?.size)??ee(e?.length)??ee(e?.fileSize)??ee(e?.bytes)??void 0,u=te(e?.lastModified)??te(e?.modified)??te(e?.mtime)??te(e?.timestamp)??void 0,p=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:p,size:l,modified:u,type:c}})}function ft(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new g("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=En.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(),p=new URL(String(l.url??""),l.baseURL??i).toString(),m=l.params,f=l.data;f instanceof URLSearchParams?f=Object.fromEntries(f):typeof f=="string"?String(l.headers?.["Content-Type"]??l.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 d={method:u,url:p};return m&&Object.keys(m).length>0&&(d.params=m),f!=null&&f!==""&&(d.data=f),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(d,null,2)),l}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function dt(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 ze(n){return n.crumbPromise||(n.crumbPromise=dt(n,5e3)),n.crumbPromise}async function L(n){await ze(n),n.crumbForm||(n.crumbPromise=dt(n,15e3),await n.crumbPromise);}async function G(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 ne(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:he(o.actions)}))}async function be(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 An(n,i.url),c=Oe(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function re(n,t){return await L(n),await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),U),!0}async function oe(n,t,e){try{await L(n),await n.axios.post(`${x(t)}/${e}/stop/`,new URLSearchParams({}),U);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(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 _e(n,t,e){await L(n);let r=`view/${encodeURIComponent(t)}/addJobToView`;try{await n.axios.post(r,new URLSearchParams({name:e}),U);}catch(o){let s=o?.response?.status;throw s===404?new g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new g(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function He(n,t,e){await L(n);let r=`view/${encodeURIComponent(t)}/removeJobFromView`;try{await n.axios.post(r,new URLSearchParams({name:e}),U);}catch(o){let s=o?.response?.status;throw s===404?new g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new g(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function gt(n,t){await L(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,U);}catch(r){let o=r?.response?.status;throw o===400?new g(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new g(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function wt(n,t){await L(n);let e=`view/${encodeURIComponent(t)}/doDelete`;try{await n.axios.post(e,new URLSearchParams({}),U);}catch(r){let o=r?.response?.status;throw o===404?new g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new g(`Forbidden to delete view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ye(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([G(n,t),ne(n,t)]),c=(p,m)=>{let f=p.actions?.find(b=>b.parameters);return f?f.parameters.find(b=>b.name===m)?.value:void 0},l=e[r];if(s&&l){let p=i.find(m=>c(m,o)===s&&c(m,r)===l);if(p)return new URL(`queue/item/${p.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let p of a)c(p,o)===s&&(console.log(`Stopping running build #${p.number} (mode=${s})...`),await oe(n,t,p.number),u=!0);u&&await new Promise(p=>setTimeout(p,1e3));try{return await L(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),U)).headers.location||""}catch(p){let m=p?.response?.status,f=pt(p?.response?.data);if(m===400){let d=f?`Jenkins response (snippet):
|
|
31
|
+
${f}`:void 0;throw new g("Jenkins rejected the request (400).",{hint:`Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs). This CLI sends parameters "${r}" and "${o}".`,example:"jenkins-cli build -b origin/main -m dev",details:d})}if(m){let d=f?`Jenkins response (snippet):
|
|
32
|
+
${f}`:void 0;throw new g(`Request failed with status code ${m}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:d})}throw p}}async function ht(n){return (await n.axios.get("whoAmI/api/json")).data}async function V(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function ie(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 bt(n){let t=await n.axios.get("config.xml",{responseType:"text"});return String(t.data??"")}async function yt(n){let t=await n.axios.get("credentials/store/system/domain/_/config.xml",{responseType:"text"});return String(t.data??"")}async function kt(n){return (await n.axios.get("credentials/store/system/domain/_/api/json?tree=credentials[id,displayName,description,typeName]")).data??{}}function Ln(n){let t="jobs[name,url]";for(let e=0;e<n;e+=1)t=`jobs[name,url,${t}]`;return t}async function xt(n,t){let e=Math.max(1,t?.depth??8),r=Ln(e),o=await n.axios.get(`api/json?tree=${encodeURIComponent(r)}`),s=Array.isArray(o.data?.jobs)?o.data.jobs:[],i=new Set,a=[...s];for(;a.length;){let c=a.shift();if(!c)continue;let l=String(c.name??"").trim(),u=String(c.url??"").trim();if(l&&u){let p=Oe(u);p?i.add(p):i.add(l);}Array.isArray(c.jobs)&&a.push(...c.jobs);}return [...i].sort((c,l)=>c.localeCompare(l,"en",{sensitivity:"base"}))}async function jt(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 p=String(u.name??"").trim();if(p&&p===r&&u.url){let m=Oe(String(u.url));m&&c.push(m);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function ke(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 g(`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 g(`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 g(`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 Ct(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:he(i.actions)}))}async function St(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 xe(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:he(o.actions)}}async function vt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function $t(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 Qe(n,t,e){let r=String(e?.path??"").trim(),o=r?`${E(r)}/`:"",s=await De(n,x(t),o);return Me(s??{})}async function Jt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await De(n,i,s);return Me(a??{})}async function Pt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await De(n,i,s);return Me(a??{})}async function Rt(n,t,e){let r=E(String(e??"").trim());return await qe(n,x(t),r)}async function Nt(n,t,e){let r=E(String(e??"").trim());return await Ie(n,x(t),r)}async function Et(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await qe(n,s,o)}async function At(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ie(n,s,o)}async function Ft(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await qe(n,s,o)}async function Bt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ie(n,s,o)}async function Wt(n,t){try{return await L(n),await n.axios.post(`${x(t)}/doWipeOutWorkspace`,new URLSearchParams({}),U),!0}catch(e){let r=e?.response?.status;if(r){let o=pt(e?.response?.data);throw new g(`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 se(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Ut(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 ae(n,t){let e=t?.raw?"views[*]":"views[name,url]";return (await n.axios.get(`api/json?tree=${e}`)).data.views??[]}async function K(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 g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Lt(n,t){let e=String(t??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await ae(n,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await K(n,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function h(){let n=un("Loading configuration...").start();try{let t=await mt();n.succeed("Configuration loaded");let e=ft(t.apiToken);return ze(e),{config:t,jenkins:e}}catch(t){M(t),process.exit(1);}}var qn=Vn(fileURLToPath(import.meta.url),{interopDefault:!0});async function Ce(n){return await Promise.resolve(n)}function Dn(n){let t=String(n??"").trim();if(!t)return null;let e=t.lastIndexOf(":");if(e<=0||e===t.length-1)return null;let r=t.slice(0,e).trim(),o=t.slice(e+1).trim();if(!r||!o)return null;let s=r.lastIndexOf(":");if(s>0){let i=r.slice(s+1).trim(),a=r.slice(0,s).trim();i&&a&&!/[\\/]/.test(i)&&(r=a);}return {file:r,exportName:o}}function Mn(n,t){return W.isAbsolute(t)?t:W.join(n,t)}function Vt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function Se(n,t){let e=Dn(t);if(!e)return null;let r=Mn(n,e.file);if(!await _.pathExists(r))throw new g(`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=qn(r),s=Vt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new g(`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 It(n,t){if(typeof t!="string")return {isRef:!1,isFunction:!1,value:t};let e=await Se(n,t);return e?{isRef:!0,isFunction:typeof e.value=="function",value:e.value}:{isRef:!1,isFunction:!1,value:t}}async function je(n,t,e){if(typeof t!="string")return t;let r=await Se(n,t);if(!r)return t;if(!e.allow(r.value))throw new g(e.error(t),{hint:e.hint,example:e.example});return r.value}async function Ot(n,t){if(typeof t!="string")return t;let e=await Se(n,t);if(!e)return t;if(typeof e.value=="function"){let r=e.value();return await Ce(r)}return await Ce(e.value)}async function I(n,t,e){if(typeof t!="string")return t;let r=await Se(n,t);if(!r)return t;if(typeof r.value=="function"){let o=r.value(e);return await Ce(o)}return await Ce(r.value)}async function ve(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(!Vt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await Ot(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 g(`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 Ot(r,i.default)),i.when!==void 0&&(i.when=await je(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 je(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 je(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 je(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 qt(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 Dt(n,t,e={}){let r=(t??"").trim();if(r)return r;let o=e.projectRoot??process.cwd(),s=await I(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 g(`${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 $e(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 Je(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 Mt(){console.log(y.bold.blue(`
|
|
34
34
|
\u{1F680} Jenkins CLI - Jenkins Deployment CLI
|
|
35
|
-
`));let{config:n,jenkins:t}=await h(),[e,r]=await Promise.all([
|
|
35
|
+
`));let{config:n,jenkins:t}=await h(),[e,r]=await Promise.all([ct(),at()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let f=await ve(n.configs,{projectRoot:n.projectRoot}),d=new Set(["branch","modes","mode","triggered_by"]);for(let $ of f){let D=String($?.name??"").trim();if(d.has(D))throw new g(`configs name "${D}" 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=[],O=[],P=[];for(let $ of f){let D=Number($.offset);if(Number.isFinite(D)&&D<=-2){v.push($);continue}if(D===-1){O.push($);continue}P.push($);}let C={};Object.assign(C,await ce.prompt(v,C)),Object.assign(C,await ce.prompt([b],C)),Object.assign(C,await ce.prompt(O,C));let nt=String(n.projectRoot??"").trim()||process.cwd(),X=await I(nt,n.modes,C),Y=[];if(Array.isArray(X))Y=X.map($=>String($).trim()).filter(Boolean);else if(X!=null){let $=String(X).trim();$&&(Y=[$]);}Y.length===0&&Array.isArray(n.modes)&&(Y=n.modes);let yn={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:Y,validate:$=>$.length===0?"You must select at least one environment":!0};Object.assign(C,await ce.prompt([yn],C)),Object.assign(C,await ce.prompt(P,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=qt(C,["branch","modes"]);let kn=await I(nt,n.job,C),rt=String(kn??"").trim();rt&&(i=rt);}catch(f){let d=f,b=d?.message?String(d.message):String(d),v=d?.name?String(d.name):"";Je(f)&&process.exit(0),M(new g(`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 fe();u&&!("triggered_by"in a)&&(a.triggered_by=u);let m=s.modes.map(async f=>{let d=un(`Triggering build for ${y.yellow(f)}...`).start();try{let b=await ye(t,i,{...a,branch:s.branch,mode:f});l(),d.succeed(`${y.green(f)} - Triggered! Queue: ${b}`);}catch(b){throw l(),d.fail(`${y.red(f)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
|
|
36
36
|
\u274C Some builds failed. Check the output above.
|
|
37
|
-
`)),process.exit(1);}}function
|
|
37
|
+
`)),process.exit(1);}}function zt(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 h(),s=e.job?await Dt(r.job,e.job,{projectRoot:r.projectRoot}):void 0,i=await G(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 h(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=A(String(a),"id");try{return await re(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 w(n){return {[_n.inspect.custom]:()=>n}}function T(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 w(T(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return w(y.gray("-"));let r=s=>w(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 R(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 ue(n,t){if(t===!0)return w(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return w(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 me(n){let t=String(n??"");if(!t)return w("-");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 w(o)}function _t(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return w("-");if(t<1024)return w(`${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 w(`${s} ${e[o]}`)}var Pe=64;function Qn(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 Gt(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>Pe?o.slice(0,Pe):o}function Gn(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=Gt(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(Qn);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 Kn(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 Xn(n,t,e){let r=await ve(n.configs,{projectRoot:t}),o=[{}];for(let s of r){let i=String(s.name??"").trim();if(!i)continue;let a=Gn(s,e);if(!a?.length||o.length*a.length>Pe)continue;let c=[];for(let l of o){if(!await Kn(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Yn(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
38
|
`).filter(t=>{let e=t.trim();return e&&!e.startsWith("//")}).join(`
|
|
39
|
-
`)}function
|
|
40
|
-
--- Build Finished: ${
|
|
39
|
+
`)}function er(n){let t=Qt(n);if(!t)return [];t=Zn(t);let e=Gt(t),r=Yn(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 p of Ht(l))p&&o.add(p);let u;for(;u=i.exec(l);){let p=u[1];if(!p.includes("${"))continue;let m=p.match(a),f=m&&r.has(m[1])?r.get(m[1]):m?[]:e,d=p.split(/\$\{[^}]*\}/);if(d.length<2)continue;let b=d[0]??"",v=d.slice(1).join("");for(let O of f){let P=b+String(O)+v;P&&o.add(P);}}}if(o.size===0&&!t.includes("{"))for(let l of Ht(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function Ht(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 tr(n,t,e){let r=new Set;for(let i of er(n))r.add(i);let o=await Xn(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>=Pe};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 g("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 It(r,n.job);if(o.isRef&&o.isFunction){let i=await tr(o.value,n,r);if(!i.length)throw new g("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 ce.prompt([{type:"list",name:"job",message:"\u8BF7\u9009\u62E9\u8981\u64CD\u4F5C\u7684 Job:",choices:i}]);return String(a??"").trim()}let s=await I(r,n.job,{});return String(s??n.job??"").trim()||String(n.job??"")}function Re(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 Kt(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 Xt(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 h(),s=e.job??Re("builds")??Re(),i=await k(r,s),c=(await Ct(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:ue(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:w(R(Number(u.timestamp??0))),parameters:Kt(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 h(),s=e.job??Re("active")??Re("builds"),i=await k(r,s),c=((e.all?await be(o):await ne(o,i))??[]).map(u=>({...e.all?{job:w(String(u.job??""))}:{},number:u.number,date:w(u.timestamp?R(Number(u.timestamp)):"-"),parameters:Kt(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 nr(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function Yt(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:w("-"),s=e?.timestamp?R(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:w(s),author:w("-"),message:w("-")});continue}for(let a of i){let c=nr(a),l=String(a?.msg??"").trim();t.push({build:o,date:w(s),author:w(c||"-"),message:w(l||"-")});}}return t}function Zt(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 h(),s=await k(r,e.job);if(t){let c=A(t,"id"),l=await xe(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(Yt([l]));return}let i=Math.max(1,A(e.limit,"limit")),a=await St(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(Yt(a));});}function en(){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 tn(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await vt(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:p}=await Ut(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&p)continue;if(!p){let f=(await xe(r,o,s)).result||"UNKNOWN",d=f==="SUCCESS"?y.green:y.red;console.log(d(`
|
|
40
|
+
--- Build Finished: ${f} ---`));break}await new Promise(m=>setTimeout(m,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 h(),a=o.job??en();if(a==="")throw new g("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 h(),i=r.job??en();if(i==="")throw new g("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 g(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await ke(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,p=Number(u?.number);if(!p){let m=c==="success"?"successful":c==="failed"?"failed":"last";console.log(y.yellow(`No ${m} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,p,r);});}function nn(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 h(),s=await k(r,e.job);if(e.ALL){let[i,a]=await Promise.all([G(o),be(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 re(o,u);for(let u of l)await oe(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([G(o,s),ne(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 re(o,u);for(let u of l)await oe(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 oe(o,s,A(t,"id")),console.log(y.green("Stop requested"));});}function rn(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await h(),o=await k(e,t.job),s=await $t(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:S(a?.name??"",i),type:w(String(a?.type??"")),description:w(String(a?.description??"")),default:w(a?.default===void 0?"-":String(a.default)),choices:w(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function on(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 h(),r=await ht(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,p)=>{let m=u.length;if(m>=p)return u;let f=p-m,d=Math.floor(f/2),b=f-d;return `${" ".repeat(d)}${u}${" ".repeat(b)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=w(s(a,c));for(let u of Object.keys(o).sort((p,m)=>p.localeCompare(m,"en"))){if(u==="name")continue;let p=o[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof p=="string"||typeof p=="number")){l[u]=J(String(p));continue}let d=p==null?"":typeof p=="object"?JSON.stringify(p):String(p);l[u]=w(d);}console.table([l]);});}function sn(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 h(),s=await k(r,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new g(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await se(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 h(),s=String(e.destination??"").trim();if(!s)throw new g("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 g("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 V(o):i?(await V(o)).filter(l=>$e(String(l.name??""),i)):[{name:await k(r,e.job)}];if(a.length===0)throw new g(`No jobs matched filter: ${i}`,{hint:"Check the pattern or remove --filter to use the default job.",example:"jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'"});await promises.mkdir(s,{recursive:!0});let c=[];for(let l of a){let u=String(l?.name??"").trim();if(!u)continue;let p=await se(o,u),m=W.join(s,u,"config.xml");await promises.mkdir(W.dirname(m),{recursive:!0}),await promises.writeFile(m,p),c.push(m);}console.log(`Backup completed. ${c.length} file(s) saved to ${W.resolve(s)}`);});}function an(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 h(),o=await V(r),s=(e.search??"").trim(),i=s?o.filter(l=>$e(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:me(l.color),url:J(T(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 h(),s=await k(r,e.job),i=await ke(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:w(String(i?.name??"")),url:J(String(i?.url??"")),color:me(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:w(a?.number?`#${a.number}`:"-"),lastResult:ue(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:w(a?.timestamp?R(Number(a.timestamp)):"-"),lastCompleted:w(c?.number?`#${c.number}`:"-"),lastCompletedResult:ue(c?.result,!1),lastCompletedDate:w(c?.timestamp?R(Number(c.timestamp)):"-")}]);});}function ir(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function sr(n){let t=n.split("=");if(t.length<2)throw new g(`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 g(`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 cn(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(ir(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 h(),o=await k(e,t.job),s=String(t.branch??"").trim();if(!s)throw new g("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(f=>f.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new g("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 f of t.param??[]){let{key:d,value:b}=sr(String(f));c[d]=b;}let l=await fe();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,p=()=>{u||(u=!0,console.log());},m=a.map(async f=>{let d=un(`Triggering build for ${y.yellow(f)}...`).start();try{let b=await ye(r,o,{...c,branch:s,mode:f});p(),d.succeed(`${y.green(f)} - Triggered! Queue: ${b}`);}catch(b){throw p(),d.fail(`${y.red(f)} - ${b.message}`),b}});try{await Promise.all(m);}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 ln(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 h(),o=await ae(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(T(c.url,a))})));}),t.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await h(),o=await K(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:me(a.color),url:J(T(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 h();if((await K(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await _e(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 h();if(!(await K(o,e)).some(i=>i.name===r))throw new g(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await He(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 h(),[o,s]=await Promise.all([V(r),K(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((m,f)=>String(m?.name??"").localeCompare(String(f?.name??""),"en",{sensitivity:"base"}));let i=new Set(s.map(m=>String(m?.name??"").trim())),a=o.map(m=>String(m?.name??"").trim()).filter(Boolean).map(m=>({name:m,checked:i.has(m)})),c=await ce.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(m=>String(m).trim())),u=[...l].filter(m=>!i.has(m)),p=[...i].filter(m=>m&&!l.has(m));if(!u.length&&!p.length){console.log(y.yellow("No changes to apply."));return}for(let m of u)await _e(r,e,m);for(let m of p)await He(r,e,m);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 h();await gt(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 h();await wt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function mr(n){return w(n?R(n):"-")}function Be(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 Ye(n,t,e){let r=t?.parent?.opts()?.job,o=Be(e)??Be(t?.name())??Be("ws")??Be("workspace");return n??r??o}function mn(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function pr(n,t){let e=mn(n),r=mn(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function fr(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 dr(n,t){try{return await jt(n,t)}catch{return}}async function gr(n,t){try{return await Lt(n,t)}catch{return}}async function wr(n){try{return await ae(n,{raw:!1})}catch{return []}}async function Ze(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await dr(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 gr(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await wr(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 hr(n,t,e){return await Ze(n,t,{direct:r=>N(()=>Qe(n,r,{path:e})),byViewName:r=>N(()=>Jt(n,r,t,{path:e})),byViewUrl:r=>N(()=>Pt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await N(()=>Qe(n,t,{path:r})):null}})}async function br(n,t,e){return await Ze(n,t,{direct:r=>N(()=>Rt(n,r,e)),byViewName:r=>N(()=>Et(n,r,t,e)),byViewUrl:r=>N(()=>Ft(n,r,t,e))})}async function yr(n,t,e){return await Ze(n,t,{direct:r=>N(()=>Nt(n,r,e)),byViewName:r=>N(()=>At(n,r,t,e)),byViewUrl:r=>N(()=>Bt(n,r,t,e))})}function pn(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 h(),i=Ye(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=un(`Loading workspace for job "${a}"...`).start(),u=await hr(s,a,c);if(!u){l.warn(`Workspace not found for job: ${a}. The job may be inside a folder, not run yet, or workspace is disabled.`);return}if(l.succeed(`Workspace loaded for job: ${a}`),!u.length){console.table([{name:"-",type:"-",size:"-",modified:"-",path:"-",url:"-"}]);return}let p=u.map(d=>({name:String(d.name??""),typeRaw:d.type,size:_t(d.size),modified:mr(d.modified),path:pr(c,String(d.path??d.name??""))})),m=j(p.map(d=>d.name)),f=j(p.map(d=>d.path));console.table(p.map(d=>{let b=T(String(d.name??""),m),v=String(d.typeRaw??"");return {name:v==="dir"?w(y.bold.cyan(b)):v==="file"?w(y.green(b)):w(y.gray(b)),size:d.size,modified:d.modified,path:S(String(d.path??""),f)}}));}),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 h(),a=Ye(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)throw new g("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),p=r.open===!0;if(u||p){let f=await yr(i,c,l);if(f===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let d=u||W.join(lr.tmpdir(),W.basename(l)||"file");try{u&&await _.pathExists(u)&&(await _.stat(u)).isDirectory()&&(d=W.join(u,W.basename(l)||"file"));}catch{}await _.ensureDir(W.dirname(d)),await _.writeFile(d,f.data),p?(fr(d),console.log(y.green(`Opened: ${d}`))):console.log(y.green(`Saved to: ${d}`));return}let m=await br(i,c,l);if(m===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(m));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await h(),i=Ye(e.job,r,"wipe"),a=await k(o,i);try{if(!(await ce.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(Je(l)){console.log(y.yellow("Cancelled."));return}throw l}let c=un(`Wiping workspace for job "${a}"...`).start();try{await Wt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function fn(n){return n.active===!0||n.enabled===!0}function dn(n,t){return String(n.shortName??"").localeCompare(String(t.shortName??""),"en",{sensitivity:"base"})}function gn(n){let t=n.command("plugin").description("Plugin operations");t.command("show").description("Show used plugins").action(async()=>{let{jenkins:e}=await h(),o=(await ie(e)??[]).filter(a=>fn(a)&&String(a.shortName??"").trim());if(o.sort(dn),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?w("-"):a.enabled,active:a.active===void 0?w("-"):a.active,update:a.hasUpdate===void 0?w("-"):a.hasUpdate})));}),t.command("backup").description("Backup plugins list").requiredOption("-d, --destination <path>","Destination folder").action(async e=>{let{jenkins:r}=await h(),o=String(e.destination??"").trim();if(!o)throw new g("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli plugin backup -d ./jenkins-backup"});let i=(await ie(r)??[]).filter(l=>fn(l)&&String(l.shortName??"").trim());if(i.sort(dn),i.length===0)throw new g("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=W.join(o,"plugins.json"),c=W.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(),p=String(l.version??"").trim();return p?`${u}:${p}`:u}).filter(Boolean).join(`
|
|
43
|
+
`)}
|
|
44
|
+
`),console.log(`Backup completed. ${i.length} plugin(s) saved to ${W.resolve(o)}`);});}function kr(n){return n.active===!0||n.enabled===!0}function xr(n){let t=String(n??"").trim();return t?!(/<credentials>/i.test(t)||/com\.cloudbees\.plugins\.credentials\.impl/i.test(t)||/org\.jenkinsci\.plugins\./i.test(t)):!0}function wn(n){n.command("backup").description("Backup Jenkins global config, jobs, plugins, and credentials").requiredOption("-d, --destination <path>","Destination folder").action(async t=>{let{jenkins:e}=await h(),r=String(t.destination??"").trim();if(!r)throw new g("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli backup -d ./jenkins-backup"});await promises.mkdir(r,{recursive:!0});let o=W.resolve(r),s=[],i=await bt(e);await promises.writeFile(W.join(o,"config.xml"),i);let a=await xt(e,{depth:8}),c=0;for(let m of a){let f=await se(e,m),d=W.join(o,"jobs",...m.split("/"),"config.xml");await promises.mkdir(W.dirname(d),{recursive:!0}),await promises.writeFile(d,f),c+=1;}let u=(await ie(e)??[]).filter(m=>kr(m)&&String(m.shortName??"").trim()).sort((m,f)=>String(m.shortName??"").localeCompare(String(f.shortName??""),"en",{sensitivity:"base"})),p=W.join(o,"plugins");await promises.mkdir(p,{recursive:!0}),await promises.writeFile(W.join(p,"plugins.json"),JSON.stringify(u,null,2)),await promises.writeFile(W.join(p,"plugins.txt"),`${u.map(m=>{let f=String(m.shortName??"").trim(),d=String(m.version??"").trim();return d?`${f}:${d}`:f}).filter(Boolean).join(`
|
|
45
|
+
`)}
|
|
46
|
+
`);try{let m=await yt(e);await promises.writeFile(W.join(o,"credentials.xml"),m);try{let f=await kt(e);await promises.writeFile(W.join(o,"credentials.json"),JSON.stringify(f,null,2));}catch{}xr(m)&&s.push("credentials.xml looks like domain-only data. This often means no readable credentials in system store or missing credentials view permission.");}catch(m){let f=m?.response?.status;s.push(f?`credentials.xml backup skipped (HTTP ${f}, possibly missing permission or plugin).`:"credentials.xml backup skipped due to request failure.");}if(console.log(`Backup completed: ${o}`),console.log("- config.xml: saved"),console.log(`- jobs/: ${c} job config file(s)`),console.log(`- plugins/: ${u.length} plugin(s)`),console.log(`- credentials.xml: ${s.some(m=>m.startsWith("credentials.xml backup skipped"))?"skipped":"saved"}`),s.length){console.log(`
|
|
47
|
+
Warnings:`);for(let m of s)console.log(`- ${m}`);}});}function hn(n){wn(n),Xt(n),Zt(n),sn(n),an(n),tn(n),rn(n),gn(n),zt(n),nn(n),cn(n),on(n),ln(n),pn(n);}function bn(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(st).version(it,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Mt);hn(program);bn(program,"--debug","Print Jenkins request params");async function jr(){try{await program.parseAsync(process.argv);}catch(n){M(n);}}jr();
|