@ts-org/jenkins-cli 3.1.1 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -119
- package/dist/cli.js +22 -20
- 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,243 @@ 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
|
+
#### `console` - view logs
|
|
255
|
+
|
|
256
|
+
Supports live log following, enabled by default.
|
|
246
257
|
|
|
247
258
|
```bash
|
|
248
|
-
#
|
|
259
|
+
# Show logs for a specific build number; follow if running, otherwise print once
|
|
249
260
|
jenkins-cli console 1234
|
|
250
261
|
|
|
251
|
-
#
|
|
262
|
+
# Show logs for the latest build; follow if running, otherwise print once
|
|
252
263
|
jenkins-cli console last
|
|
253
264
|
|
|
254
|
-
#
|
|
265
|
+
# Print current logs only, without following
|
|
255
266
|
jenkins-cli console last --no-follow
|
|
256
267
|
|
|
257
|
-
#
|
|
268
|
+
# Show logs from the latest successful build
|
|
258
269
|
jenkins-cli console last -s success
|
|
259
270
|
|
|
260
|
-
#
|
|
271
|
+
# Show logs from the latest failed build
|
|
261
272
|
jenkins-cli console last -s failed
|
|
262
273
|
```
|
|
263
274
|
|
|
264
|
-
#### `job` -
|
|
275
|
+
#### `job` - job management
|
|
265
276
|
|
|
266
277
|
```bash
|
|
267
|
-
#
|
|
278
|
+
# List all jobs (supports glob filtering)
|
|
268
279
|
jenkins-cli job list --search 'project-*'
|
|
269
280
|
|
|
270
|
-
#
|
|
281
|
+
# Show current job info
|
|
271
282
|
jenkins-cli job info
|
|
272
283
|
```
|
|
273
284
|
|
|
274
|
-
#### `me` -
|
|
285
|
+
#### `me` - user information
|
|
275
286
|
|
|
276
287
|
```bash
|
|
277
|
-
#
|
|
288
|
+
# Verify API token and show current user
|
|
278
289
|
jenkins-cli me
|
|
279
290
|
```
|
|
280
291
|
|
|
281
|
-
#### `params` -
|
|
292
|
+
#### `params` - job parameters
|
|
282
293
|
|
|
283
294
|
```bash
|
|
284
|
-
#
|
|
295
|
+
# Show parameter definitions for the current job
|
|
285
296
|
jenkins-cli params
|
|
286
297
|
```
|
|
287
298
|
|
|
288
|
-
#### `queue` -
|
|
299
|
+
#### `queue` - pending build queue management
|
|
289
300
|
|
|
290
301
|
```bash
|
|
291
|
-
#
|
|
302
|
+
# Show pending build queue
|
|
292
303
|
jenkins-cli queue list
|
|
293
304
|
|
|
294
|
-
#
|
|
305
|
+
# Cancel one queued item
|
|
295
306
|
jenkins-cli queue cancel 5678
|
|
296
307
|
```
|
|
297
308
|
|
|
298
|
-
#### `stop` -
|
|
309
|
+
#### `stop` - stop builds
|
|
299
310
|
|
|
300
311
|
```bash
|
|
301
|
-
#
|
|
312
|
+
# Stop one build
|
|
302
313
|
jenkins-cli stop 1234
|
|
303
314
|
|
|
304
|
-
#
|
|
315
|
+
# Clear queue and stop all running builds for current job
|
|
305
316
|
jenkins-cli stop -a
|
|
306
317
|
|
|
307
|
-
#
|
|
318
|
+
# Stop all builds and queue items on Jenkins (use with caution)
|
|
308
319
|
jenkins-cli stop -A
|
|
309
320
|
```
|
|
310
321
|
|
|
311
|
-
#### `view` -
|
|
322
|
+
#### `view` - view management
|
|
312
323
|
|
|
313
324
|
```bash
|
|
314
|
-
#
|
|
325
|
+
# Show all views
|
|
315
326
|
jenkins-cli view list
|
|
316
327
|
|
|
317
|
-
#
|
|
328
|
+
# Show jobs in a view
|
|
318
329
|
jenkins-cli view show MyView
|
|
319
330
|
|
|
320
|
-
#
|
|
331
|
+
# Add a job to a view
|
|
321
332
|
jenkins-cli view add MyView my-job
|
|
322
333
|
|
|
323
|
-
#
|
|
334
|
+
# Remove a job from a view
|
|
324
335
|
jenkins-cli view remove MyView my-job
|
|
325
336
|
|
|
326
|
-
#
|
|
337
|
+
# Edit jobs in a view (check/uncheck)
|
|
327
338
|
jenkins-cli view edit MyView
|
|
328
339
|
|
|
329
|
-
#
|
|
340
|
+
# Create a view
|
|
330
341
|
jenkins-cli view create MyView
|
|
331
342
|
|
|
332
|
-
#
|
|
343
|
+
# Delete a view
|
|
333
344
|
jenkins-cli view delete MyView
|
|
334
345
|
```
|
|
335
346
|
|
|
336
|
-
#### `workspace` -
|
|
347
|
+
#### `workspace` - workspace
|
|
337
348
|
|
|
338
349
|
```bash
|
|
339
|
-
#
|
|
350
|
+
# Show workspace for current job (reads `job` from jenkins-cli.yaml by default)
|
|
340
351
|
jenkins-cli ws
|
|
341
352
|
|
|
342
|
-
#
|
|
353
|
+
# Show subdirectory (current job)
|
|
343
354
|
jenkins-cli ws -p dist
|
|
344
355
|
|
|
345
|
-
#
|
|
356
|
+
# Specify job
|
|
346
357
|
jenkins-cli ws -j my-job
|
|
347
358
|
|
|
348
|
-
#
|
|
359
|
+
# Show file contents
|
|
349
360
|
jenkins-cli ws cat dist/index.html
|
|
350
361
|
|
|
351
|
-
#
|
|
362
|
+
# Show file contents for a specific job
|
|
352
363
|
jenkins-cli ws cat dist/index.html -j my-job
|
|
353
364
|
|
|
354
|
-
#
|
|
365
|
+
# Save file locally
|
|
355
366
|
jenkins-cli ws cat dist/index.html -o ./index.html
|
|
356
367
|
|
|
357
|
-
#
|
|
368
|
+
# Download and open with default app (useful for images)
|
|
358
369
|
jenkins-cli ws cat public/favicon.ico --open
|
|
359
370
|
|
|
360
|
-
#
|
|
371
|
+
# Wipe workspace (will prompt for confirmation)
|
|
361
372
|
jenkins-cli ws wipe
|
|
362
373
|
|
|
363
|
-
#
|
|
374
|
+
# Wipe workspace for a specific job
|
|
364
375
|
jenkins-cli ws wipe -j my-job
|
|
365
376
|
```
|
|
366
377
|
|
|
367
378
|
### ❓ FAQ
|
|
368
379
|
|
|
369
|
-
**Q: `stop`
|
|
380
|
+
**Q: Why does `stop` or `queue cancel` return 403 Forbidden?**
|
|
370
381
|
|
|
371
|
-
A:
|
|
382
|
+
A: Most likely the Jenkins user lacks permission (requires `Job > Cancel`). Make sure you use an API token instead of a password, and verify Jenkins CSRF settings. This tool handles CSRF automatically, but permission issues must be fixed manually.
|
package/dist/cli.js
CHANGED
|
@@ -2,41 +2,43 @@
|
|
|
2
2
|
import { program, CommanderError } from 'commander';
|
|
3
3
|
import oe from 'inquirer';
|
|
4
4
|
import y from 'chalk';
|
|
5
|
-
import
|
|
5
|
+
import rn from 'ora';
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
|
-
import
|
|
7
|
+
import Tn, { promisify } from 'util';
|
|
8
8
|
import M from 'fs-extra';
|
|
9
|
-
import
|
|
9
|
+
import bn from 'js-yaml';
|
|
10
10
|
import z from 'path';
|
|
11
|
-
import
|
|
11
|
+
import Sn from 'axios';
|
|
12
12
|
import { Buffer } from 'buffer';
|
|
13
|
-
import
|
|
13
|
+
import An from 'jiti';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { promises } from 'fs';
|
|
16
16
|
import { XMLParser } from 'fast-xml-parser';
|
|
17
|
-
import
|
|
17
|
+
import tr from 'os';
|
|
18
18
|
|
|
19
|
-
var
|
|
19
|
+
var nt="3.1.2",rt="Jenkins deployment CLI";var p=class extends Error{hint;example;details;exitCode;constructor(t,e={}){super(t),this.name="UserError",this.hint=e.hint,this.example=e.example,this.details=e.details,this.exitCode=e.exitCode??1;}};function gn(n){return n instanceof p?n:n instanceof CommanderError?new p(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new p(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new p("Unknown error",{example:"jenkins-cli --help"})}function q(n){let t=gn(n);if(console.error(y.red(`
|
|
20
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 Ae=promisify(exec);async function ot(){try{let{stdout:n}=await Ae("git branch --show-current"),t=n.trim();if(!t)throw new p("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}catch{throw new p("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function it(){try{let{stdout:n}=await Ae('git branch -r --format="%(refname:short)"');return n.split(`
|
|
22
|
+
`).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new p("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function le(){try{let{stdout:n}=await Ae("git config user.name"),t=n.trim();return t||void 0}catch{return}}var D="jenkins-cli.yaml",me="package.json",ue="jenkins-cli",yn="jenkinsCli";function at(n,t=process.cwd()){let e=t;for(;;){let r=z.join(e,n);if(M.existsSync(r))return r;let o=z.dirname(e);if(o===e)return null;e=o;}}function kn(n=process.cwd()){return at(D,n)}function xn(n=process.cwd()){let t=at(me,n);return t?z.dirname(t):null}function jn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function st(n){let t=await M.readFile(n,"utf-8"),e=bn.load(t);if(!e||typeof e!="object")throw new p(`Invalid config in ${D}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${D}:
|
|
23
23
|
apiToken: http://user:token@host
|
|
24
24
|
job: my-job
|
|
25
|
-
modes: [dev]`});return e}async function
|
|
25
|
+
modes: [dev]`});return e}async function Cn(n){let t=z.join(n,me);if(!await M.pathExists(t))return {};let e=await M.readFile(t,"utf-8"),r=JSON.parse(e),o=r[ue]??r[yn];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new p(`Invalid ${me} config: "${ue}" must be an object.`,{hint:"Use an object for the config section.",example:`"${ue}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function ct(){let n=process.cwd(),t=xn(n)??n,e=z.join(t,D),r={},o=z.dirname(t),s=kn(o);s&&await M.pathExists(s)&&(r=await st(s));let i=await Cn(t);r={...r,...i};let a=await M.pathExists(e)?await st(e):{};if(r={...r,...a},!jn(r))throw new p("Config incomplete or not found.",{hint:`Tried ${D} in project root, "${ue}" in ${me}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${D}:
|
|
26
26
|
apiToken: http://user:token@host
|
|
27
27
|
job: my-job
|
|
28
28
|
modes:
|
|
29
|
-
- dev`});return {...r,projectRoot:t}}var B={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function x(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function pe(n){let t=Array.isArray(n)?n.find(o=>o?.parameters):void 0,e=Array.isArray(t?.parameters)?t.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function
|
|
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 B={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function x(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function pe(n){let t=Array.isArray(n)?n.find(o=>o?.parameters):void 0,e=Array.isArray(t?.parameters)?t.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function lt(n){try{let e=new URL(n).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<e.length-1;o++)e[o]==="job"&&e[o+1]&&r.push(decodeURIComponent(e[o+1]));return r.join("/")}catch{return ""}}async function vn(n,t){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",t).toString())).data??{};return {...r,parameters:pe(r.actions)}}function ut(n,t=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>t?`${r.slice(0,t)}...`:r:""}catch{return ""}}function X(n){if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){let t=Number(n);if(Number.isFinite(t))return t}}function Z(n){let t=X(n);if(t!==void 0)return t;if(typeof n=="string"){let e=Date.parse(n);if(!Number.isNaN(e))return e}}function E(n){return n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/")}function We(n,t){let e=n.replace(/\/+$/,""),r=t.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function _(n){return n.replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function fe(n){return n.replace(/<[^>]*>/g,"")}function $n(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function Jn(n){let t=n.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i),e=n.match(/<code[^>]*>([\s\S]*?)<\/code>/i),r=n.match(/<body[^>]*>([\s\S]*?)<\/body>/i),s=(t?.[1]??e?.[1]??r?.[1]??n).replace(/<br\s*\/?>/gi,`
|
|
30
|
+
`);return _(fe(s))}function Rn(n){let t=[...n.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of t){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],c="",l="";for(let v of a){let L=v[1]??"",R=_(fe(v[2]??"")).trim();if(!(!R||R==="..")&&!/all\s+files\s+in\s+zip/i.test(R)){c=L,l=R;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",m=_(c.split("?")[0]??"").replace(/\/+$/,"")||l,f=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),d=f?_(fe(f[1]??"")).trim():"",g=d?Z(d):void 0,b=`${l}|${m}`;r.has(b)||(r.add(b),e.push({name:l,path:m,type:u,modified:g}));}if(e.length)return e;let o=[...n.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(fe(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let c=i.endsWith("/")?"dir":"file",l=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${l}`;r.has(u)||(r.add(u),e.push({name:a,path:l,type:c}));}return e}async function Ue(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),c=a.data??new ArrayBuffer(0),l=Buffer.from(c);if(!l.length)continue;let u=a.headers?.["content-type"];return {data:l,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Le(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),c=typeof a.data=="string"?a.data:String(a.data??"");if(!c)continue;if($n(c)){let l=Jn(c);if(l.trim())return l;continue}return c}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Te(n,t,e){let r=We(t,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let l=(await n.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof l=="string")try{l=JSON.parse(l);}catch{continue}if(l&&typeof l=="object")return l}catch(c){if(i=c,c?.response?.status===404)continue;throw c}try{let a=await n.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let c=Rn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Pn(n){if(Array.isArray(n))return n;let t=[n?.children,n?.files,n?.childs,n?.items,n?.entries,n?.content,n?.tree];for(let e of t){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return n?.children&&Array.isArray(n.children?.children)?n.children.children:[]}function Oe(n){return Pn(n).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,c=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),l=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,m=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:m,size:l,modified:u,type:c}})}function mt(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new p("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=Sn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(l=>{let u=String(l.method??"get").toUpperCase(),m=new URL(String(l.url??""),l.baseURL??i).toString(),f=l.params,d=l.data;d instanceof URLSearchParams?d=Object.fromEntries(d):typeof d=="string"?String(l.headers?.["Content-Type"]??l.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?d=Object.fromEntries(new URLSearchParams(d)):d=d.trim()?d:void 0:Buffer.isBuffer(d)&&(d=`[Buffer ${d.length} bytes]`);let g={method:u,url:m};return f&&Object.keys(f).length>0&&(g.params=f),d!=null&&d!==""&&(g.data=d),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),l}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function ft(n,t=5e3){try{let e=await n.axios.get("crumbIssuer/api/json",{timeout:t}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(n.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,n.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ve(n){return n.crumbPromise||(n.crumbPromise=ft(n,5e3)),n.crumbPromise}async function W(n){await Ve(n),n.crumbForm||(n.crumbPromise=ft(n,15e3),await n.crumbPromise);}async function H(n,t){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(t){let o=t.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function ee(n,t){let e=await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:pe(o.actions)}))}async function de(n){let t=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(t.data?.computer)?t.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],c=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let l of [...a,...c]){let u=l?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await vn(n,i.url),c=lt(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function te(n,t){return await W(n),await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),B),!0}async function ne(n,t,e){try{await W(n),await n.axios.post(`${x(t)}/${e}/stop/`,new URLSearchParams({}),B);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function Ie(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/addJobToView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new p(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function qe(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/removeJobFromView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new p(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function pt(n,t){await W(n);let e=new URLSearchParams({name:t,mode:"hudson.model.ListView",json:JSON.stringify({name:t,mode:"hudson.model.ListView"})});try{await n.axios.post("createView",e,B);}catch(r){let o=r?.response?.status;throw o===400?new p(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new p(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function dt(n,t){await W(n);let e=`view/${encodeURIComponent(t)}/doDelete`;try{await n.axios.post(e,new URLSearchParams({}),B);}catch(r){let o=r?.response?.status;throw o===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new p(`Forbidden to delete view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ge(n,t,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([H(n,t),ee(n,t)]),c=(m,f)=>{let d=m.actions?.find(b=>b.parameters);return d?d.parameters.find(b=>b.name===f)?.value:void 0},l=e[r];if(s&&l){let m=i.find(f=>c(f,o)===s&&c(f,r)===l);if(m)return new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let m of a)c(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await ne(n,t,m.number),u=!0);u&&await new Promise(m=>setTimeout(m,1e3));try{return await W(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),B)).headers.location||""}catch(m){let f=m?.response?.status,d=ut(m?.response?.data);if(f===400){let g=d?`Jenkins response (snippet):
|
|
31
|
+
${d}`:void 0;throw new p("Jenkins rejected the request (400).",{hint:`Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs). This CLI sends parameters "${r}" and "${o}".`,example:"jenkins-cli build -b origin/main -m dev",details:g})}if(f){let g=d?`Jenkins response (snippet):
|
|
32
|
+
${d}`:void 0;throw new p(`Request failed with status code ${f}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:g})}throw m}}async function gt(n){return (await n.axios.get("whoAmI/api/json")).data}async function T(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function De(n){let t="plugins[shortName,longName,version,enabled,active,hasUpdate,pinned,bundled,deleted]";return (await n.axios.get(`pluginManager/api/json?tree=${t}`)).data.plugins??[]}async function ht(n,t,e){let r=String(t??"").trim();if(!r)return;let o=Math.max(1,e?.depth??5),s="jobs[name,url]";for(let u=0;u<o;u+=1)s=`jobs[name,url,${s}]`;let i=await n.axios.get(`api/json?tree=${encodeURIComponent(s)}`),a=Array.isArray(i.data?.jobs)?i.data.jobs:[],c=[],l=[...a];for(;l.length;){let u=l.shift();if(!u)continue;let m=String(u.name??"").trim();if(m&&m===r&&u.url){let f=lt(String(u.url));f&&c.push(f);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function he(n,t){try{let r=(await n.axios.get(`${x(t)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration],lastSuccessfulBuild[number,url,building,result,timestamp,duration],lastFailedBuild[number,url,building,result,timestamp,duration]`)).data??{},o=typeof r?.fullName=="string"?r.fullName:"",s=typeof r?.name=="string"?r.name:"",i=String(t??"").trim();if(!o&&!s)throw new p(`Job not found: ${i}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli job show -j my-job"});if(i&&i!==o&&i!==s)throw new p(`Job not found: ${i}`,{hint:`Jenkins returned job "${o||s}".`,example:"jenkins-cli console last -j my-job -s success"});return r}catch(e){if(e?.response?.status===404){let o=String(t??"").trim();throw new p(`Job not found: ${o}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli console last -j my-job -s success"})}throw e}}async function wt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:pe(i.actions)}))}async function bt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,timestamp,changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,timestamp:i.timestamp,url:i.url,changeSet:i.changeSet??{}}))}async function we(n,t,e){let o=(await n.axios.get(`${x(t)}/${e}/api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,description,actions[parameters[name,value]],changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]],culprits[fullName],executor[number,progress]`)).data??{};return {...o,parameters:pe(o.actions)}}async function yt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function kt(n,t){return ((await n.axios.get(`${x(t)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function Me(n,t,e){let r=String(e?.path??"").trim(),o=r?`${E(r)}/`:"",s=await Te(n,x(t),o);return Oe(s??{})}async function xt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await Te(n,i,s);return Oe(a??{})}async function jt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await Te(n,i,s);return Oe(a??{})}async function Ct(n,t,e){let r=E(String(e??"").trim());return await Le(n,x(t),r)}async function St(n,t,e){let r=E(String(e??"").trim());return await Ue(n,x(t),r)}async function vt(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Le(n,s,o)}async function $t(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ue(n,s,o)}async function Jt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Le(n,s,o)}async function Rt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ue(n,s,o)}async function Pt(n,t){try{return await W(n),await n.axios.post(`${x(t)}/doWipeOutWorkspace`,new URLSearchParams({}),B),!0}catch(e){let r=e?.response?.status;if(r){let o=ut(e?.response?.data);throw new p(`Workspace wipe failed (HTTP ${r}).`,{hint:"Check Job/Workspace permission or CSRF settings.",example:"jenkins-cli workspace wipe -j my-job",details:o?`Jenkins response (snippet):
|
|
33
|
+
${o}`:void 0})}throw e}}async function ze(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Nt(n,t,e,r){let o=`${x(t)}/${e}/logText/progressiveText`,s=new URLSearchParams;s.append("start",String(r));let i=await n.axios.post(o,s,{responseType:"text"}),a=String(i.data??""),c=i.headers["x-text-size"],l=i.headers["x-more-data"],u=c?Number(c):r+Buffer.byteLength(a);return {text:a,newOffset:u,hasMoreData:l==="true"}}async function re(n,t){let e=t?.raw?"views[*]":"views[name,url]";return (await n.axios.get(`api/json?tree=${e}`)).data.views??[]}async function Q(n,t){let e=`view/${encodeURIComponent(t)}`;try{return (await n.axios.get(`${e}/api/json?tree=jobs[name,url,color]`)).data.jobs??[]}catch(r){throw r?.response?.status===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Et(n,t){let e=String(t??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await re(n,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await Q(n,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function w(){let n=rn("Loading configuration...").start();try{let t=await ct();n.succeed("Configuration loaded");let e=mt(t.apiToken);return Ve(e),{config:t,jenkins:e}}catch(t){q(t),process.exit(1);}}var Bn=An(fileURLToPath(import.meta.url),{interopDefault:!0});async function ye(n){return await Promise.resolve(n)}function Wn(n){let t=String(n??"").trim();if(!t)return null;let e=t.split(":");if(e.length<2)return null;if(e.length===2){let[s,i]=e;return !s||!i?null:{file:s,exportName:i}}let r=e.at(-1);if(!r)return null;let o=e.slice(0,-2).join(":");return o?{file:o,exportName:r}:null}function Un(n,t){return z.isAbsolute(t)?t:z.join(n,t)}function Bt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function ke(n,t){let e=Wn(t);if(!e)return null;let r=Un(n,e.file);if(!await M.pathExists(r))throw new p(`configs reference not found: ${e.file}`,{hint:"Check the file path (relative to project root) and file name.",example:'configs: [{ choices: "./scripts/prompts.ts:choices" }]'});let o=Bn(r),s=Bt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new p(`configs reference export not found: ${t}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function Wt(n,t){if(typeof t!="string")return {isRef:!1,isFunction:!1,value:t};let e=await ke(n,t);return e?{isRef:!0,isFunction:typeof e.value=="function",value:e.value}:{isRef:!1,isFunction:!1,value:t}}async function be(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(!e.allow(r.value))throw new p(e.error(t),{hint:e.hint,example:e.example});return r.value}async function Ft(n,t){if(typeof t!="string")return t;let e=await ke(n,t);if(!e)return t;if(typeof e.value=="function"){let r=e.value();return await ye(r)}return await ye(e.value)}async function O(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(typeof r.value=="function"){let o=r.value(e);return await ye(o)}return await ye(r.value)}async function xe(n,t){let e=Array.isArray(n)?n:[];if(e.length===0)return [];let r=String(t.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!Bt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await Ft(r,i.choices);let a=String(i.type??"").toLowerCase(),c=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof c!="function"&&!Array.isArray(c))if(c==null)i.choices=[];else if(typeof c=="string"||typeof c=="number"||typeof c=="boolean")i.choices=[String(c)];else if(typeof c[Symbol.iterator]=="function")i.choices=Array.from(c).map(u=>String(u));else throw new p(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof c}).`,{hint:"Return an array for list/rawlist/checkbox choices.",example:'export const choices = ["dev", "uat"]'})}i.default!==void 0&&(i.default=await Ft(r,i.default)),i.when!==void 0&&(i.when=await be(r,i.when,{allow:a=>typeof a=="function"||typeof a=="boolean",error:a=>`when reference must be a function or boolean: ${a}`,hint:"Export a function or boolean from the referenced file.",example:'export const when = (answers) => answers.env === "dev"'})),i.validate!==void 0&&(i.validate=await be(r,i.validate,{allow:a=>typeof a=="function",error:a=>`validate reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:'export const validate = (input) => !!input || "required"'})),i.filter!==void 0&&(i.filter=await be(r,i.filter,{allow:a=>typeof a=="function",error:a=>`filter reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const filter = (input) => input.trim()"})),i.transformer!==void 0&&(i.transformer=await be(r,i.transformer,{allow:a=>typeof a=="function",error:a=>`transformer reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const transformer = (input) => input.toUpperCase()"})),o.push(i);}return o}function Ut(n,t){let e=new Set(t),r={};for(let[o,s]of Object.entries(n))if(!e.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function Lt(n,t,e={}){let r=(t??"").trim();if(r)return r;let o=e.projectRoot??process.cwd(),s=await O(o,n,e.answers??{});return String(s??"").trim()||n}function A(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new p(`${t} must be a non-negative integer.`,{hint:`Provide a valid ${t}.`,example:`jenkins-cli console ${t==="id"?"123":"last -t 100"}`});return e}function je(n,t){let e=String(t??"").trim();if(!e)return !0;if(!/[*?]/.test(e))return n.includes(e);let s=`^${e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(s).test(n)}catch{return n.includes(e)}}function Ce(n){let t=n,e=t?.name?String(t.name):"",r=t?.code?String(t.code):"",o=t?.message?String(t.message):String(t??"");if(e==="ExitPromptError"||e==="AbortError"||e==="PromptAbortError"||r==="ABORT_ERR")return !0;let s=o.toLowerCase();return s.includes("cancelled")||s.includes("canceled")}async function Tt(){console.log(y.bold.blue(`
|
|
34
34
|
\u{1F680} Jenkins CLI - Jenkins Deployment CLI
|
|
35
|
-
`));let{config:n,jenkins:t}=await
|
|
35
|
+
`));let{config:n,jenkins:t}=await w(),[e,r]=await Promise.all([it(),ot()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let d=await xe(n.configs,{projectRoot:n.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let $ of d){let I=String($?.name??"").trim();if(g.has(I))throw new p(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let b={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},v=[],L=[],R=[];for(let $ of d){let I=Number($.offset);if(Number.isFinite(I)&&I<=-2){v.push($);continue}if(I===-1){L.push($);continue}R.push($);}let C={};Object.assign(C,await oe.prompt(v,C)),Object.assign(C,await oe.prompt([b],C)),Object.assign(C,await oe.prompt(L,C));let Ze=String(n.projectRoot??"").trim()||process.cwd(),G=await O(Ze,n.modes,C),K=[];if(Array.isArray(G))K=G.map($=>String($).trim()).filter(Boolean);else if(G!=null){let $=String(G).trim();$&&(K=[$]);}K.length===0&&Array.isArray(n.modes)&&(K=n.modes);let fn={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:$=>$.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([fn],C)),Object.assign(C,await oe.prompt(R,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=Ut(C,["branch","modes"]);let pn=await O(Ze,n.job,C),et=String(pn??"").trim();et&&(i=et);}catch(d){let g=d,b=g?.message?String(g.message):String(g),v=g?.name?String(g.name):"";Ce(d)&&process.exit(0),q(new p(`Prompt failed: ${b}`,{hint:v?`Error: ${v}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let c=!1,l=()=>{c||(c=!0,console.log());},u=await le();u&&!("triggered_by"in a)&&(a.triggered_by=u);let f=s.modes.map(async d=>{let g=rn(`Triggering build for ${y.yellow(d)}...`).start();try{let b=await ge(t,i,{...a,branch:s.branch,mode:d});l(),g.succeed(`${y.green(d)} - Triggered! Queue: ${b}`);}catch(b){throw l(),g.fail(`${y.red(d)} - ${b.message}`),b}});try{await Promise.all(f);}catch{console.log(y.bold.red(`
|
|
36
36
|
\u274C Some builds failed. Check the output above.
|
|
37
|
-
`)),process.exit(1);}}function
|
|
37
|
+
`)),process.exit(1);}}function Ot(n){let t=n.command("queue").description("Operations for the build queue");t.command("list").alias("ls").description("List items waiting in the build queue").option("-j, --job <job>","Show only queue items for the specified job").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job?await Lt(r.job,e.job,{projectRoot:r.projectRoot}):void 0,i=await H(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),t.command("cancel").description("Cancel a queued build item").argument("<id...>","Queue id(s)").action(async e=>{let{jenkins:r}=await w(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=A(String(a),"id");try{return await te(r,c),{id:c,ok:!0}}catch(l){return {id:c,ok:!1,message:String(l?.message??l)}}})),i=s.filter(a=>!a.ok);if(i.length===0){console.log(y.green(`Cancelled ${s.length} queue item(s).`));return}console.log(y.yellow(`Cancelled ${s.length-i.length} item(s), failed ${i.length} item(s).`));for(let a of i)console.log(y.red(`- ${a.id}: ${a.message??"Cancel failed"}`));});}function h(n){return {[Tn.inspect.custom]:()=>n}}function U(n,t){return String(n??"").padEnd(t," ")}function j(n){return Math.max(0,...n.map(t=>String(t??"").length))}function S(n,t){return h(U(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return h(y.gray("-"));let r=s=>h(s),o=e.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(e).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",c=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||c?y.cyanBright(t):y.hex("#4EA1FF")(t))}catch{return r(y.hex("#4EA1FF")(t))}return o.startsWith("ws://")||o.startsWith("wss://")?r(y.cyan(t)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(y.magenta(t)):o.startsWith("file://")?r(y.yellow(t)):o.startsWith("mailto:")?r(y.green(t)):o.startsWith("javascript:")||o.startsWith("data:")?r(y.red(t)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(y.cyan(t)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(y.blue(t)):r(t)}function P(n){let t=new Date(n),e=r=>String(r).padStart(2,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function se(n,t){if(t===!0)return h(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return h(e?e==="SUCCESS"?y.green(e):e==="ABORTED"?y.gray(e):e==="FAILURE"?y.red(e):e==="UNSTABLE"?y.yellow(e):e==="NOT_BUILT"?y.gray(e):e:"-")}function ae(n){let t=String(n??"");if(!t)return h("-");let e=t.endsWith("_anime"),r=e?t.slice(0,-6):t,o=(()=>{switch(r){case"blue":return e?y.blueBright(t):y.blue(t);case"red":return e?y.redBright(t):y.red(t);case"yellow":return e?y.yellowBright(t):y.yellow(t);case"aborted":return y.gray(t);case"disabled":case"notbuilt":case"grey":case"gray":return y.gray(t);default:return t}})();return h(o)}function Vt(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return h("-");if(t<1024)return h(`${t} B`);let e=["KB","MB","GB","TB"],r=t,o=-1;for(;r>=1024&&o<e.length-1;)r/=1024,o+=1;let s=r>=10||o===0?r.toFixed(1):r.toFixed(2);return h(`${s} ${e[o]}`)}var Se=64;function Vn(n){return n!=null&&typeof n=="object"?n.value??n.name??n:n}function qt(n){let t=String(n?.toString?.()??"").trim();if(!t)return "";let e=t.indexOf("=>");if(e>=0){let s=t.slice(e+2).trim();return s.startsWith("{")&&s.endsWith("}")&&(s=s.slice(1,-1)),s}let r=t.indexOf("{"),o=t.lastIndexOf("}");return r>=0&&o>r?t.slice(r+1,o):""}function Dt(n){let t=new Set,e,r=/\b(\d+)\b/g;for(;e=r.exec(n);){let s=parseInt(e[1],10);Number.isFinite(s)&&t.add(s);}let o=Array.from(t).sort((s,i)=>s-i);return o.length>Se?o.slice(0,Se):o}function In(n,t){let e=String(n.type??"").toLowerCase();if(e==="confirm")return [!0,!1];if(e==="number"){let s=n,i=typeof s.min=="number"&&Number.isFinite(s.min)?s.min:null,a=typeof s.max=="number"&&Number.isFinite(s.max)?s.max:null;if(i!=null&&a!=null)return i===a?[i]:i<a?[i,a]:[a,i];if(i!=null)return [i,i+1];if(a!=null)return [a-1,a];if(t){let c=Dt(qt(t));if(c.length)return c}return [0,1,99,100,101]}let r=Array.isArray(n.choices)?n.choices:null;if(!r?.length)return null;let o=r.map(Vn);if(e==="list"||e==="rawlist")return o;if(e==="checkbox"){let s=[[]];return o.forEach(i=>s.push([i])),o.length<=4&&s.push(o),s}return null}async function qn(n,t){let e=n.when;if(e===void 0||typeof e=="boolean")return e!==!1;if(typeof e!="function")return !0;try{return !!await Promise.resolve(e(t))}catch{return !0}}async function Dn(n,t,e){let r=await xe(n.configs,{projectRoot:t}),o=[{}];for(let s of r){let i=String(s.name??"").trim();if(!i)continue;let a=In(s,e);if(!a?.length||o.length*a.length>Se)continue;let c=[];for(let l of o){if(!await qn(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Mn(n){let t=new Map,e=/answers\.(\w+)\s*===\s*(\d+)/g,r;for(;r=e.exec(n);){let s=r[1],i=parseInt(r[2],10);Number.isFinite(i)&&(t.has(s)||t.set(s,new Set),t.get(s).add(i));}let o=new Map;return t.forEach((s,i)=>o.set(i,Array.from(s).sort((a,c)=>a-c))),o}function zn(n){return n.split(`
|
|
38
38
|
`).filter(t=>{let e=t.trim();return e&&!e.startsWith("//")}).join(`
|
|
39
|
-
`)}function
|
|
40
|
-
--- Build Finished: ${
|
|
39
|
+
`)}function _n(n){let t=qt(n);if(!t)return [];t=zn(t);let e=Dt(t),r=Mn(t),o=new Set,s=/return\s+([^;]+?)(?=;|$)/gm,i=/`((?:[^`\\]|\\.|\$\{[^}]*\})*)`/g,a=/\$\{\s*answers\.(\w+)\s*\}/,c;for(;c=s.exec(t);){let l=c[1];for(let m of It(l))m&&o.add(m);let u;for(;u=i.exec(l);){let m=u[1];if(!m.includes("${"))continue;let f=m.match(a),d=f&&r.has(f[1])?r.get(f[1]):f?[]:e,g=m.split(/\$\{[^}]*\}/);if(g.length<2)continue;let b=g[0]??"",v=g.slice(1).join("");for(let L of d){let R=b+String(L)+v;R&&o.add(R);}}}if(o.size===0&&!t.includes("{"))for(let l of It(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function It(n){let t=[],e=/(['"`])((?:\\.|(?!\1).)*)\1/g,r;for(;r=e.exec(n);)if(!(r[1]==="`"&&r[2].includes("${")))try{let o=r[1]==="`"?r[2]:JSON.parse(r[1]+r[2]+r[1]);String(o).trim()&&t.push(String(o).trim());}catch{String(r[2]??"").trim()&&t.push(String(r[2]).trim());}return t}async function Hn(n,t,e){let r=new Set;for(let i of _n(n))r.add(i);let o=await Dn(t,e,n),s=async i=>{let a=await Promise.resolve(n(i)),c=typeof a=="string"?String(a).trim():"";return c&&r.add(c),r.size>=Se};if(!o.length)await s({});else for(let i of o)if(await s(i))break;return Array.from(r).sort((i,a)=>i.localeCompare(a,"en",{sensitivity:"base"}))}async function k(n,t){if(t==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli job info -j my-job"});let e=String(t??"").trim();if(e)return e;let r=String(n.projectRoot??"").trim()||process.cwd(),o=await Wt(r,n.job);if(o.isRef&&o.isFunction){let i=await Hn(o.value,n,r);if(!i.length)throw new p("Job choices not found from config reference.",{hint:"Ensure the job function returns a string (job name).",example:'export function dynamicJob(answers) { return "my-job" }'});if(i.length===1)return i[0];let{job:a}=await oe.prompt([{type:"list",name:"job",message:"\u8BF7\u9009\u62E9\u8981\u64CD\u4F5C\u7684 Job:",choices:i}]);return String(a??"").trim()}let s=await O(r,n.job,{});return String(s??n.job??"").trim()||String(n.job??"")}function ve(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Mt(n){let t=Object.entries(n??{}).filter(([r])=>r);return t.length?(t.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"})),t.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ")):"-"}function zt(n){let t=n.command("builds").description("Build operations");t.option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job??ve("builds")??ve(),i=await k(r,s),c=(await wt(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:se(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:h(P(Number(u.timestamp??0))),parameters:Mt(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:S(u.parameters,l)})));}),t.command("active").description("List active builds").option("-j, --job <job>","Specify job").option("-a, --all","Query all running builds on Jenkins").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job??ve("active")??ve("builds"),i=await k(r,s),c=((e.all?await de(o):await ee(o,i))??[]).map(u=>({...e.all?{job:h(String(u.job??""))}:{},number:u.number,date:h(u.timestamp?P(Number(u.timestamp)):"-"),parameters:Mt(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:S(u.parameters,l)})));});}function Qn(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function _t(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:h("-"),s=e?.timestamp?P(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:h(s),author:h("-"),message:h("-")});continue}for(let a of i){let c=Qn(a),l=String(a?.msg??"").trim();t.push({build:o,date:h(s),author:h(c||"-"),message:h(l||"-")});}}return t}function Ht(n){n.command("changes").description("List build changes").argument("[id]").option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").option("--raw","Output raw changeSet payload").action(async(t,e)=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job);if(t){let c=A(t,"id"),l=await we(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(_t([l]));return}let i=Math.max(1,A(e.limit,"limit")),a=await bt(o,s,{limit:i});if(e.raw){console.log(JSON.stringify(a.map(c=>({build:c.number,changeSet:c.changeSet??{}})),null,2));return}console.table(_t(a));});}function Qt(){let n=process.argv,t=n.findIndex(r=>r==="-j"||r==="--job");if(t<0)return;let e=n[t+1];return !e||e.startsWith("-")?"":e}function Gt(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await yt(r,o,s);process.stdout.write(l);return}let a=0,c=!0;for(console.log(y.blue(`--- Following log for ${o} build #${s} ---`));;)try{let{text:l,newOffset:u,hasMoreData:m}=await Nt(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&m)continue;if(!m){let d=(await we(r,o,s)).result||"UNKNOWN",g=d==="SUCCESS"?y.green:y.red;console.log(g(`
|
|
40
|
+
--- Build Finished: ${d} ---`));break}await new Promise(f=>setTimeout(f,1e3));}catch{await new Promise(l=>setTimeout(l,2e3));}};t.argument("<id>","Build number").option("-j, --job <job>","Specify job").option("--no-follow","Do not follow the build log").action(async(r,o)=>{let{config:s,jenkins:i}=await w(),a=o.job??Qt();if(a==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console 123 -j my-job"});let c=await k(s,a),l=A(r,"id");await e(i,c,l,o);}),t.command("last").description("Fetch the latest build log").option("-j, --job <job>","Specify job").option("-s, --status <status>","success | failed | any","any").option("--no-follow","Do not follow the build log").action(async r=>{let{config:o,jenkins:s}=await w(),i=r.job??Qt();if(i==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console last -j my-job -s success"});let a=await k(o,i),c=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(c))throw new p(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await he(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,m=Number(u?.number);if(!m){let f=c==="success"?"successful":c==="failed"?"failed":"last";console.log(y.yellow(`No ${f} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,m,r);});}function Kt(n){n.command("stop").description("Clear queued builds and stop running builds").argument("[id]","Build number").option("-j, --job <job>","Specify Jenkins job").option("-a, --all","Cancel queued items for this job and stop all running builds for it").option("-A, --ALL","Cancel all queued items and stop all running builds on Jenkins").action(async(t,e)=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job);if(e.ALL){let[i,a]=await Promise.all([H(o),de(o)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>({number:Number(u.number),job:String(u.job??"")})).filter(u=>u.job&&!Number.isNaN(u.number));for(let u of c)await te(o,u);for(let u of l)await ne(o,u.job,u.number);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([H(o,s),ee(o,s)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>Number(u.number)).filter(u=>!Number.isNaN(u));for(let u of c)await te(o,u);for(let u of l)await ne(o,s,u);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!t){console.log(y.red("Missing build id. Provide <id> or use -a/--all."));return}await ne(o,s,A(t,"id")),console.log(y.green("Stop requested"));});}function Yt(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await w(),o=await k(e,t.job),s=await kt(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:S(a?.name??"",i),type:h(String(a?.type??"")),description:h(String(a?.description??"")),default:h(a?.default===void 0?"-":String(a.default)),choices:h(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Xt(n){n.command("me").description("Show Jenkins user info for the current token").option("--raw","Print raw response").action(async t=>{let{jenkins:e}=await w(),r=await gt(e);if(t.raw){console.log(r&&typeof r=="object"?JSON.stringify(r,null,2):String(r));return}let o=r&&typeof r=="object"?r:{value:r},s=(u,m)=>{let f=u.length;if(f>=m)return u;let d=m-f,g=Math.floor(d/2),b=d-g;return `${" ".repeat(g)}${u}${" ".repeat(b)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=h(s(a,c));for(let u of Object.keys(o).sort((m,f)=>m.localeCompare(f,"en"))){if(u==="name")continue;let m=o[u],f=u.toLowerCase();if((f==="url"||f.endsWith("url")||f.includes("url"))&&(typeof m=="string"||typeof m=="number")){l[u]=J(String(m));continue}let g=m==null?"":typeof m=="object"?JSON.stringify(m):String(m);l[u]=h(g);}console.table([l]);});}function Zt(n){let t=n.command("config").description("Show Jenkins job configuration");t.command("show").description("Show job configuration").option("-j, --job <job>","Specify job").option("-f, --format <format>","Output format: xml or json","xml").action(async e=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new p(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await ze(o,s);if(i==="xml"){process.stdout.write(a);return}let l=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(l,null,2));}),t.command("backup").description("Backup job configuration to files").requiredOption("-d, --destination <path>","Destination folder").option("-a, --all","Backup all jobs").option("-j, --job <job>","Specify job").option("--filter <pattern>","Filter jobs by name (supports glob)").action(async e=>{let{config:r,jenkins:o}=await w(),s=String(e.destination??"").trim();if(!s)throw new p("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli config backup -d ./jenkins-backup"});if(e.all&&e.filter)throw new p("Options --all and --filter are mutually exclusive.",{hint:"Use either -a/--all or --filter, not both.",example:"jenkins-cli config backup -d ./jenkins-backup -a"});let i=String(e.filter??"").trim(),a=e.all?await T(o):i?(await T(o)).filter(l=>je(String(l.name??""),i)):[{name:await k(r,e.job)}];if(a.length===0)throw new p(`No jobs matched filter: ${i}`,{hint:"Check the pattern or remove --filter to use the default job.",example:"jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'"});await promises.mkdir(s,{recursive:!0});let c=[];for(let l of a){let u=String(l?.name??"").trim();if(!u)continue;let m=await ze(o,u),f=z.join(s,u,"config.xml");await promises.mkdir(z.dirname(f),{recursive:!0}),await promises.writeFile(f,m),c.push(f);}console.log(`Backup completed. ${c.length} file(s) saved to ${z.resolve(s)}`);});}function en(n){let t=n.command("job").description("Job operations");t.command("list").alias("ls").description("List all jobs").option("--search <keyword>","Filter by name (supports glob)").action(async e=>{let{jenkins:r}=await w(),o=await T(r),s=(e.search??"").trim(),i=s?o.filter(l=>je(String(l.name??""),s)):o;i.sort((l,u)=>String(l?.name??"").localeCompare(String(u?.name??""),"en",{sensitivity:"base"}));let a=j(i.map(l=>l?.name)),c=j(i.map(l=>l?.url));console.table((i??[]).map(l=>({name:S(l.name??"",a),color:ae(l.color),url:J(U(l.url??"",c))})));}),t.command("info").description("Show job info").option("-j, --job <job>","Specify job").option("--json","Output raw JSON").action(async e=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job),i=await he(o,s);if(e.json){console.log(JSON.stringify(i,null,2));return}let a=i?.lastBuild,c=i?.lastCompletedBuild,l=Array.isArray(i?.healthReport)?i.healthReport:[];console.table([{name:h(String(i?.name??"")),url:J(String(i?.url??"")),color:ae(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:h(a?.number?`#${a.number}`:"-"),lastResult:se(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:h(a?.timestamp?P(Number(a.timestamp)):"-"),lastCompleted:h(c?.number?`#${c.number}`:"-"),lastCompletedResult:se(c?.result,!1),lastCompletedDate:h(c?.timestamp?P(Number(c.timestamp)):"-")}]);});}function Yn(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function Xn(n){let t=n.split("=");if(t.length<2)throw new p(`Invalid --param: "${n}".`,{hint:"Use the format key=value.",example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});let e=t[0].trim(),r=t.slice(1).join("=").trim();if(!e)throw new p(`Invalid --param: "${n}". Key is empty.`,{hint:'Provide a non-empty key before "=".',example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});return {key:e,value:r}}function tn(n){n.command("build").description("Trigger Jenkins builds non-interactively").requiredOption("-b, --branch <branch>","Branch (e.g., origin/develop)").option("-m, --mode <mode>","Deployment environment (repeatable or comma-separated: -m dev -m uat / -m dev,uat)",(t,e)=>e.concat(Yn(t)),[]).option("--param <key=value>","Extra parameters (repeatable, e.g., --param foo=bar)",(t,e)=>e.concat(t),[]).option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await w(),o=await k(e,t.job),s=String(t.branch??"").trim();if(!s)throw new p("branch is required.",{hint:"Pass a branch with -b or --branch.",example:"jenkins-cli build -b origin/develop -m dev"});let i=(t.mode??[]).map(String).map(d=>d.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new p("mode is required.",{hint:"Pass at least one -m/--mode.",example:"jenkins-cli build -b origin/develop -m dev -m uat"});let c={};for(let d of t.param??[]){let{key:g,value:b}=Xn(String(d));c[g]=b;}let l=await le();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,m=()=>{u||(u=!0,console.log());},f=a.map(async d=>{let g=rn(`Triggering build for ${y.yellow(d)}...`).start();try{let b=await ge(r,o,{...c,branch:s,mode:d});m(),g.succeed(`${y.green(d)} - Triggered! Queue: ${b}`);}catch(b){throw m(),g.fail(`${y.red(d)} - ${b.message}`),b}});try{await Promise.all(f);}catch{console.log(y.bold.red(`
|
|
41
41
|
\u274C Some builds failed. Check the output above.
|
|
42
|
-
`)),process.exit(1);}});}function
|
|
42
|
+
`)),process.exit(1);}});}function nn(n){let t=n.command("view").description("View operations");t.command("list").alias("ls").description("List all views").option("--raw","Output raw JSON").action(async e=>{let{jenkins:r}=await w(),o=await re(r,{raw:e.raw});if(e.raw){console.log(JSON.stringify(o,null,2));return}o.sort((c,l)=>String(c?.name??"").localeCompare(String(l?.name??""),"en",{sensitivity:"base"}));let s=(o??[]).map(c=>({name:String(c?.name??""),url:String(c?.url??"")})),i=j(s.map(c=>c.name)),a=j(s.map(c=>c.url));console.table(s.map(c=>({name:S(c.name,i),url:J(U(c.url,a))})));}),t.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w(),o=await Q(r,e);if(!o.length){console.log("No jobs found in this view.");return}o.sort((a,c)=>String(a?.name??"").localeCompare(String(c?.name??""),"en",{sensitivity:"base"}));let s=j(o.map(a=>a?.name)),i=j(o.map(a=>a?.url));console.table(o.map(a=>({name:S(a.name??"",s),color:ae(a.color),url:J(U(a.url??"",i))})));}),t.command("add").description("Add a job to a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await w();if((await Q(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await Ie(o,e,r),console.log(y.green(`Job "${r}" added to view "${e}".`));}),t.command("remove").alias("rm").description("Remove a job from a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await w();if(!(await Q(o,e)).some(i=>i.name===r))throw new p(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await qe(o,e,r),console.log(y.green(`Job "${r}" removed from view "${e}".`));}),t.command("edit").description("Edit jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w(),[o,s]=await Promise.all([T(r),Q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((f,d)=>String(f?.name??"").localeCompare(String(d?.name??""),"en",{sensitivity:"base"}));let i=new Set(s.map(f=>String(f?.name??"").trim())),a=o.map(f=>String(f?.name??"").trim()).filter(Boolean).map(f=>({name:f,checked:i.has(f)})),c=await oe.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(f=>String(f).trim())),u=[...l].filter(f=>!i.has(f)),m=[...i].filter(f=>f&&!l.has(f));if(!u.length&&!m.length){console.log(y.yellow("No changes to apply."));return}for(let f of u)await Ie(r,e,f);for(let f of m)await qe(r,e,f);console.log(y.green(`View "${e}" updated.`));}),t.command("create").description("Create a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w();await pt(r,e),console.log(y.green(`View "${e}" created.`));}),t.command("delete").alias("del").description("Delete a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w();await dt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function rr(n){return h(n?P(n):"-")}function Ne(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Ge(n,t,e){let r=t?.parent?.opts()?.job,o=Ne(e)??Ne(t?.name())??Ne("ws")??Ne("workspace");return n??r??o}function on(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function or(n,t){let e=on(n),r=on(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function ir(n){let t=process.platform,e="",r=[];t==="darwin"?(e="open",r=[n]):t==="win32"?(e="cmd",r=["/c","start","",n]):(e="xdg-open",r=[n]);try{spawn(e,r,{stdio:"ignore",detached:!0}).unref();}catch{}}async function N(n){try{return await n()}catch(t){if(t?.response?.status===404)return null;throw t}}async function sr(n,t){try{return await ht(n,t)}catch{return}}async function ar(n,t){try{return await Et(n,t)}catch{return}}async function cr(n){try{return await re(n,{raw:!1})}catch{return []}}async function Ke(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await sr(n,t);if(o&&o!==t){let a=await e.direct(o);if(a!==null)return a}if(e.beforeView){let a=await e.beforeView();if(a!==null)return a}let s=await ar(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await cr(n);for(let a of i){let c=String(a?.url??"").trim();if(!c)continue;let l=await e.byViewUrl(c);if(l!==null)return l}return null}async function lr(n,t,e){return await Ke(n,t,{direct:r=>N(()=>Me(n,r,{path:e})),byViewName:r=>N(()=>xt(n,r,t,{path:e})),byViewUrl:r=>N(()=>jt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await N(()=>Me(n,t,{path:r})):null}})}async function ur(n,t,e){return await Ke(n,t,{direct:r=>N(()=>Ct(n,r,e)),byViewName:r=>N(()=>vt(n,r,t,e)),byViewUrl:r=>N(()=>Jt(n,r,t,e))})}async function mr(n,t,e){return await Ke(n,t,{direct:r=>N(()=>St(n,r,e)),byViewName:r=>N(()=>$t(n,r,t,e)),byViewUrl:r=>N(()=>Rt(n,r,t,e))})}function sn(n){let t=n.command("workspace").alias("ws").description("Workspace operations");t.option("-j, --job <job>","Specify job").option("-p, --path <path>","Workspace sub path").action(async(e,r)=>{let{config:o,jenkins:s}=await w(),i=Ge(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=rn(`Loading workspace for job "${a}"...`).start(),u=await lr(s,a,c);if(!u){l.warn(`Workspace not found for job: ${a}. The job may be inside a folder, not run yet, or workspace is disabled.`);return}if(l.succeed(`Workspace loaded for job: ${a}`),!u.length){console.table([{name:"-",type:"-",size:"-",modified:"-",path:"-",url:"-"}]);return}let m=u.map(g=>({name:String(g.name??""),typeRaw:g.type,size:Vt(g.size),modified:rr(g.modified),path:or(c,String(g.path??g.name??""))})),f=j(m.map(g=>g.name)),d=j(m.map(g=>g.path));console.table(m.map(g=>{let b=U(String(g.name??""),f),v=String(g.typeRaw??"");return {name:v==="dir"?h(y.bold.cyan(b)):v==="file"?h(y.green(b)):h(y.gray(b)),size:g.size,modified:g.modified,path:S(String(g.path??""),d)}}));}),t.command("cat").description("Show file content in workspace").argument("<path>","Workspace file path").option("-j, --job <job>","Specify job").option("-o, --out <file>","Save file to path").option("--open","Open file with default application").action(async(e,r,o)=>{let{config:s,jenkins:i}=await w(),a=Ge(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)throw new p("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),m=r.open===!0;if(u||m){let d=await mr(i,c,l);if(d===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let g=u||z.join(tr.tmpdir(),z.basename(l)||"file");try{u&&await M.pathExists(u)&&(await M.stat(u)).isDirectory()&&(g=z.join(u,z.basename(l)||"file"));}catch{}await M.ensureDir(z.dirname(g)),await M.writeFile(g,d.data),m?(ir(g),console.log(y.green(`Opened: ${g}`))):console.log(y.green(`Saved to: ${g}`));return}let f=await ur(i,c,l);if(f===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(f));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await w(),i=Ge(e.job,r,"wipe"),a=await k(o,i);try{if(!(await oe.prompt([{type:"confirm",name:"confirm",message:`Wipe workspace for job "${a}"? This cannot be undone.`,default:!1}]))?.confirm){console.log(y.yellow("Cancelled."));return}}catch(l){if(Ce(l)){console.log(y.yellow("Cancelled."));return}throw l}let c=rn(`Wiping workspace for job "${a}"...`).start();try{await Pt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function an(n){return n.active===!0||n.enabled===!0}function cn(n,t){return String(n.shortName??"").localeCompare(String(t.shortName??""),"en",{sensitivity:"base"})}function ln(n){let t=n.command("plugin").description("Plugin operations");t.command("show").description("Show used plugins").action(async()=>{let{jenkins:e}=await w(),o=(await De(e)??[]).filter(a=>an(a)&&String(a.shortName??"").trim());if(o.sort(cn),o.length===0){console.log("No plugins found.");return}let s=j(o.map(a=>a.shortName)),i=j(o.map(a=>a.version));console.table(o.map(a=>({name:S(a.shortName??"",s),version:S(a.version??"",i),enabled:a.enabled===void 0?h("-"):a.enabled,active:a.active===void 0?h("-"):a.active,update:a.hasUpdate===void 0?h("-"):a.hasUpdate})));}),t.command("backup").description("Backup plugins list").requiredOption("-d, --destination <path>","Destination folder").action(async e=>{let{jenkins:r}=await w(),o=String(e.destination??"").trim();if(!o)throw new p("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli plugin backup -d ./jenkins-backup"});let i=(await De(r)??[]).filter(l=>an(l)&&String(l.shortName??"").trim());if(i.sort(cn),i.length===0)throw new p("No plugins found to back up.",{hint:"Check Jenkins permissions or ensure plugins are installed.",example:"jenkins-cli plugin show"});await promises.mkdir(o,{recursive:!0});let a=z.join(o,"plugins.json"),c=z.join(o,"plugins.txt");await promises.writeFile(a,JSON.stringify(i,null,2)),await promises.writeFile(c,`${i.map(l=>{let u=String(l.shortName??"").trim(),m=String(l.version??"").trim();return m?`${u}:${m}`:u}).filter(Boolean).join(`
|
|
43
|
+
`)}
|
|
44
|
+
`),console.log(`Backup completed. ${i.length} plugin(s) saved to ${z.resolve(o)}`);});}function un(n){zt(n),Ht(n),Zt(n),en(n),Gt(n),Yt(n),ln(n),Ot(n),Kt(n),tn(n),Xt(n),nn(n),sn(n);}function mn(n,t,e){let r=s=>s.options.some(i=>i.long===t||i.short===t),o=s=>{r(s)||s.option(t,e);for(let i of s.commands)o(i);};for(let s of n.commands)o(s);}program.name("jenkins-cli").description(rt).version(nt,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Tt);un(program);mn(program,"--debug","Print Jenkins request params");async function fr(){try{await program.parseAsync(process.argv);}catch(n){q(n);}}fr();
|