python-jcli 0.1.0__py3-none-any.whl → 1.0.1__py3-none-any.whl
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.
- jcli/__init__.py +1 -1
- jcli/cli.py +565 -51
- jcli/cli_helpers.py +5 -42
- jcli/skills/jcli/SKILL.md +15 -2
- jcli/skills/jcli/auth/SKILL.md +86 -0
- jcli/skills/jcli/build/SKILL.md +123 -0
- jcli/skills/jcli/credential/SKILL.md +14 -5
- jcli/skills/jcli/job/SKILL.md +20 -4
- jcli/skills/jcli/node/SKILL.md +4 -2
- jcli/skills/jcli/pipeline/SKILL.md +3 -3
- jcli/skills/jcli/plugin/SKILL.md +3 -0
- jcli/skills/jcli/system/SKILL.md +15 -2
- jcli/skills/jcli/view/SKILL.md +4 -3
- jcli/specs/_auth.yaml +15 -0
- jcli/{plugins/skills.py → specs/plugins/jcli_commands.py} +124 -63
- jcli/specs/plugins/jenkins_auth.py +75 -0
- jcli/specs/plugins/jenkins_methods.py +332 -0
- jcli/specs/plugins/jenkins_pipeline_validate.py +51 -0
- jcli/specs/plugins/jenkins_plugin.py +77 -0
- jcli/specs/plugins/jenkins_system.py +137 -0
- jcli/specs/resources/build.yaml +160 -0
- jcli/specs/resources/credential.yaml +97 -0
- jcli/specs/resources/job.yaml +184 -0
- jcli/specs/resources/node.yaml +69 -0
- jcli/specs/resources/pipeline.yaml +62 -0
- jcli/specs/resources/plugin.yaml +65 -0
- jcli/specs/resources/system.yaml +72 -0
- jcli/specs/resources/view.yaml +75 -0
- {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/METADATA +57 -12
- python_jcli-1.0.1.dist-info/RECORD +40 -0
- {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/WHEEL +1 -1
- jcli/plugins/__init__.py +0 -28
- jcli/plugins/build.py +0 -197
- jcli/plugins/config.py +0 -268
- jcli/plugins/credential.py +0 -210
- jcli/plugins/job.py +0 -425
- jcli/plugins/node.py +0 -134
- jcli/plugins/pipeline.py +0 -108
- jcli/plugins/plugin.py +0 -173
- jcli/plugins/system.py +0 -173
- jcli/plugins/view.py +0 -148
- jcli/sdk/build.py +0 -182
- jcli/sdk/credential.py +0 -143
- jcli/sdk/job.py +0 -191
- jcli/sdk/job_templates.py +0 -128
- jcli/sdk/node.py +0 -139
- jcli/sdk/pipeline.py +0 -121
- jcli/sdk/plugin.py +0 -155
- jcli/sdk/system.py +0 -202
- jcli/sdk/view.py +0 -94
- jcli/skills/jcli/config/SKILL.md +0 -81
- python_jcli-0.1.0.dist-info/RECORD +0 -44
- {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/entry_points.txt +0 -0
- {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
name: view
|
|
2
|
+
description: Manage Jenkins views
|
|
3
|
+
methods:
|
|
4
|
+
list:
|
|
5
|
+
description: List all Jenkins views
|
|
6
|
+
http:
|
|
7
|
+
method: GET
|
|
8
|
+
path: api/json
|
|
9
|
+
query:
|
|
10
|
+
tree: "views[name,url,jobs[name]]"
|
|
11
|
+
output:
|
|
12
|
+
items_path: $.views
|
|
13
|
+
fields:
|
|
14
|
+
- name: name
|
|
15
|
+
alias: Name
|
|
16
|
+
- name: url
|
|
17
|
+
alias: URL
|
|
18
|
+
- name: jobs
|
|
19
|
+
alias: Jobs
|
|
20
|
+
get:
|
|
21
|
+
description: Get details for a view
|
|
22
|
+
http:
|
|
23
|
+
method: GET
|
|
24
|
+
path: "view/{{ name }}/api/json"
|
|
25
|
+
params:
|
|
26
|
+
path:
|
|
27
|
+
- name: name
|
|
28
|
+
type: string
|
|
29
|
+
argument: true
|
|
30
|
+
create:
|
|
31
|
+
description: Create a view from XML config
|
|
32
|
+
http:
|
|
33
|
+
method: POST
|
|
34
|
+
path: createView
|
|
35
|
+
query:
|
|
36
|
+
name: '{{ name }}'
|
|
37
|
+
headers:
|
|
38
|
+
Content-Type: application/xml
|
|
39
|
+
body: '{{ config_xml }}'
|
|
40
|
+
params:
|
|
41
|
+
query:
|
|
42
|
+
- name: name
|
|
43
|
+
type: string
|
|
44
|
+
argument: true
|
|
45
|
+
body:
|
|
46
|
+
- name: config_xml
|
|
47
|
+
type: file
|
|
48
|
+
argument: true
|
|
49
|
+
update:
|
|
50
|
+
description: Update a view's configuration
|
|
51
|
+
http:
|
|
52
|
+
method: POST
|
|
53
|
+
path: "view/{{ name }}/config.xml"
|
|
54
|
+
headers:
|
|
55
|
+
Content-Type: application/xml
|
|
56
|
+
body: '{{ config_xml }}'
|
|
57
|
+
params:
|
|
58
|
+
path:
|
|
59
|
+
- name: name
|
|
60
|
+
type: string
|
|
61
|
+
argument: true
|
|
62
|
+
body:
|
|
63
|
+
- name: config_xml
|
|
64
|
+
type: file
|
|
65
|
+
argument: true
|
|
66
|
+
delete:
|
|
67
|
+
description: Delete a view
|
|
68
|
+
http:
|
|
69
|
+
method: POST
|
|
70
|
+
path: "view/{{ name }}/doDelete"
|
|
71
|
+
params:
|
|
72
|
+
path:
|
|
73
|
+
- name: name
|
|
74
|
+
type: string
|
|
75
|
+
argument: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-jcli
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Python Jenkins CLI tool
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -9,6 +9,7 @@ Requires-Dist: click>=8.1.0
|
|
|
9
9
|
Requires-Dist: requests~=2.31.0
|
|
10
10
|
Requires-Dist: pyyaml~=6.0.1
|
|
11
11
|
Requires-Dist: rich>=13.7.1
|
|
12
|
+
Requires-Dist: cliyard>=0.6.0
|
|
12
13
|
Provides-Extra: dev
|
|
13
14
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
14
15
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
@@ -23,10 +24,38 @@ A command-line tool for managing Jenkins servers. jcli wraps the Jenkins REST AP
|
|
|
23
24
|
## Features
|
|
24
25
|
|
|
25
26
|
- **8 command modules** covering jobs, builds, nodes, plugins, credentials, pipelines, views, and system management
|
|
27
|
+
- **Spec-driven CLI**: commands are declared as cliyard YAML specs in `specs/` and generated at runtime, no hand-written Click groups
|
|
26
28
|
- **Multiple output formats**: human-readable tables (default, via Rich), JSON, and YAML
|
|
27
29
|
- **Multi-profile support** via `~/.jcli/config.yaml` with environment variable overrides
|
|
28
30
|
- **Automatic CSRF (Crumb) handling** for Jenkins servers with CSRF protection enabled
|
|
29
|
-
- **Built
|
|
31
|
+
- **Built on cliyard and Rich** for a polished, maintainable terminal experience
|
|
32
|
+
|
|
33
|
+
## Architecture
|
|
34
|
+
|
|
35
|
+
jcli 2.0 is driven by [cliyard](https://pypi.org/project/cliyard/) YAML specs instead of hand-written Click command groups. At startup, `jcli/cli.py` locates the spec directory (`JCLI_SPEC_DIR` env var > package-local `jcli/specs/`) and calls `cliyard.runtime.create_cli()` to build the whole command tree. Commands stay declarative: adding or changing a command is an edit to a YAML file, not Python plumbing.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
jcli/specs/
|
|
39
|
+
_auth.yaml Server auth chain (basic auth + crumb, as cliyard plugins)
|
|
40
|
+
resources/ One YAML spec per Jenkins domain
|
|
41
|
+
job.yaml job list/get/create/config/copy/enable/disable/delete
|
|
42
|
+
build.yaml build list/get/trigger/replay/log/stop/queue
|
|
43
|
+
node.yaml node list/get/delete/toggle
|
|
44
|
+
plugin.yaml plugin list/get/install/uninstall
|
|
45
|
+
credential.yaml credential list/get/create/delete
|
|
46
|
+
pipeline.yaml pipeline stages/log/pending/validate
|
|
47
|
+
view.yaml view list/get/create/delete
|
|
48
|
+
system.yaml system info/load/quiet-down/restart/script
|
|
49
|
+
plugins/ Python method plugins for non-trivial logic
|
|
50
|
+
jcli_commands.py Skills + completion commands
|
|
51
|
+
jenkins_auth.py Basic + crumb authentication
|
|
52
|
+
jenkins_methods.py Build trigger/replay, node create
|
|
53
|
+
jenkins_pipeline_validate.py Jenkinsfile validation
|
|
54
|
+
jenkins_plugin.py Plugin install/uninstall/update-center
|
|
55
|
+
jenkins_system.py Groovy script, quiet-down, restart
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A resource spec maps a command name to either a plain HTTP call (method, path, params, output mapping) or a `plugin:` reference. The auth chain in `_auth.yaml` injects `Authorization` and crumb headers into every request.
|
|
30
59
|
|
|
31
60
|
## Requirements
|
|
32
61
|
|
|
@@ -147,6 +176,21 @@ Every command accepts these global flags before the subcommand:
|
|
|
147
176
|
-s, --server TEXT Jenkins server URL
|
|
148
177
|
```
|
|
149
178
|
|
|
179
|
+
### auth — Manage authentication profiles
|
|
180
|
+
|
|
181
|
+
Profiles are stored in `~/.jcli/config.yaml` (token shown masked).
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
jcli auth add -n NAME -u USER -p TOKEN -e URL Add (or update) a profile
|
|
185
|
+
jcli auth add -n NAME ... --default ... and make it active
|
|
186
|
+
jcli auth status List profiles (tokens masked)
|
|
187
|
+
jcli auth use NAME Switch the active profile
|
|
188
|
+
jcli auth rm NAME Remove a profile
|
|
189
|
+
jcli auth rm --all Remove all profiles (reset template)
|
|
190
|
+
jcli auth set NAME FIELD VALUE Set url/username/api_token/description
|
|
191
|
+
jcli auth show [NAME] Show a profile's details (default: active)
|
|
192
|
+
```
|
|
193
|
+
|
|
150
194
|
### job — Manage Jenkins jobs
|
|
151
195
|
|
|
152
196
|
```
|
|
@@ -166,6 +210,9 @@ jcli job delete JOB_NAME Delete a job
|
|
|
166
210
|
jcli build list JOB_NAME List recent builds for a job
|
|
167
211
|
jcli build get JOB_NAME BUILD_NUMBER Show details for a specific build
|
|
168
212
|
jcli build trigger JOB_NAME Trigger a new build
|
|
213
|
+
jcli build rebuild JOB_NAME BUILD_NUMBER Rebuild reusing previous parameters
|
|
214
|
+
jcli build rebuild JOB_NAME BUILD_NUMBER --set KEY=VAL ... and override values (repeatable)
|
|
215
|
+
jcli build replay JOB_NAME BUILD_NUMBER JENKINSFILE Replay a build with a custom Jenkinsfile
|
|
169
216
|
jcli build log JOB_NAME BUILD_NUMBER Show console log for a build
|
|
170
217
|
jcli build stop JOB_NAME BUILD_NUMBER Stop a running build
|
|
171
218
|
jcli build queue Show the current build queue
|
|
@@ -268,23 +315,20 @@ pytest --cov=jcli --cov-report=term-missing
|
|
|
268
315
|
|
|
269
316
|
```
|
|
270
317
|
jcli/
|
|
271
|
-
cli.py
|
|
318
|
+
cli.py Entry point: builds the Click CLI from cliyard YAML specs
|
|
319
|
+
cli_helpers.py Output-formatter helper used by the cliyard command plugins
|
|
272
320
|
__init__.py Version
|
|
273
|
-
plugins/ Command modules (one per Jenkins domain)
|
|
274
|
-
job.py
|
|
275
|
-
build.py
|
|
276
|
-
node.py
|
|
277
|
-
plugin.py
|
|
278
|
-
credential.py
|
|
279
|
-
pipeline.py
|
|
280
|
-
view.py
|
|
281
|
-
system.py
|
|
282
321
|
sdk/ Shared libraries
|
|
283
322
|
client.py Jenkins REST API client (HTTP, auth, crumb)
|
|
284
323
|
config.py Configuration management (YAML, env vars)
|
|
285
324
|
output/
|
|
286
325
|
formatter.py Table/JSON/YAML output formatting
|
|
287
326
|
exceptions.py Typed exceptions
|
|
327
|
+
skills/ Bundled skill documentation
|
|
328
|
+
specs/ cliyard YAML specs (command definitions)
|
|
329
|
+
_auth.yaml Auth chain: basic + crumb plugins
|
|
330
|
+
resources/ One YAML per Jenkins domain (job, build, node, ...)
|
|
331
|
+
plugins/ Python method plugins for non-trivial logic
|
|
288
332
|
tests/ pytest test suite
|
|
289
333
|
```
|
|
290
334
|
|
|
@@ -293,6 +337,7 @@ tests/ pytest test suite
|
|
|
293
337
|
| Package | Purpose |
|
|
294
338
|
|-----------------|-----------------------------|
|
|
295
339
|
| click >= 8.1.0 | CLI framework |
|
|
340
|
+
| cliyard >= 0.6.0 | Spec-driven CLI engine (YAML command generation) |
|
|
296
341
|
| requests ~= 2.31 | HTTP client |
|
|
297
342
|
| pyyaml ~= 6.0.1 | YAML config parsing |
|
|
298
343
|
| rich >= 13.7.1 | Terminal formatting (tables) |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
jcli/__init__.py,sha256=d4QHYmS_30j0hPN8NmNPnQ_Z0TphDRbu4MtQj9cT9e8,22
|
|
2
|
+
jcli/__main__.py,sha256=ARL8XYr45T4pIf0gnyCCbK8uljcn1LF7TKcFapxHt_k,34
|
|
3
|
+
jcli/cli.py,sha256=krV91DUIFEQnO_OzS08JahY5GFIQB5Omwvj2YcQVX_Y,21921
|
|
4
|
+
jcli/cli_helpers.py,sha256=xtMiNfoicBnc4g4qi56PJT3S8KwS56up5tOANNE-SmY,610
|
|
5
|
+
jcli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
jcli/sdk/client.py,sha256=hnBZcS82ziPyHepPWq7femUqQppw8QCtwbdMrWYVrxc,8723
|
|
7
|
+
jcli/sdk/config.py,sha256=fSBTWqE0i8jpzmtGfo2M-Ln2Y5eQRQD0StS76glyCsI,6992
|
|
8
|
+
jcli/sdk/exceptions.py,sha256=zv5oYOr_t0rma4lVudLp3gLdL5idSzlKGc7g2MowcL0,1737
|
|
9
|
+
jcli/sdk/output/__init__.py,sha256=Rt0F77oq24tqHvTDV4HyWKAjDtOyPOh9m7ucFy7B77s,70
|
|
10
|
+
jcli/sdk/output/formatter.py,sha256=OUjQxeD-2iSNncuuRaLa01qLi899ux3F39KNkc8ik2Q,3876
|
|
11
|
+
jcli/skills/jcli/SKILL.md,sha256=X9xHxRvNa4F3hUN8bhckBXAJZx2MWY58hGqqd0y158M,3040
|
|
12
|
+
jcli/skills/jcli/auth/SKILL.md,sha256=4_DLD51SwKTXh2Xi0UiQbqG4uXw7qIKv2SOOopS2Qn0,1996
|
|
13
|
+
jcli/skills/jcli/build/SKILL.md,sha256=J2UbuAAuAHiIrMbnzLH2Q8mQcRvy3JkuTS9GyhMfR64,3557
|
|
14
|
+
jcli/skills/jcli/credential/SKILL.md,sha256=3VsVdGalsIySvGfFO79IdL0b5OjF7kJBS9_4P56Wt2A,1244
|
|
15
|
+
jcli/skills/jcli/job/SKILL.md,sha256=bmGyiyLXS2n395bA31gehcMRe094SUBq-FzUlUDvpQM,2066
|
|
16
|
+
jcli/skills/jcli/node/SKILL.md,sha256=k4qi6ApspdIlBcxb0v2ItPrPwT7d9AnFq4N-vGhGcqY,1247
|
|
17
|
+
jcli/skills/jcli/pipeline/SKILL.md,sha256=kUwnxBCUx53zi7efVgtJg9ainO-YuX3griaCqoUyzxU,1231
|
|
18
|
+
jcli/skills/jcli/plugin/SKILL.md,sha256=--1t5LekIHAtnCWUnsQ9BKkPIgAQ7n-p7_QGd8H2jbI,1178
|
|
19
|
+
jcli/skills/jcli/system/SKILL.md,sha256=SHdcLbaJyifjt4KJlmdcHdYLsFGwpF-6lqiWdYpw3co,1932
|
|
20
|
+
jcli/skills/jcli/view/SKILL.md,sha256=f2QZEzvFocIlR7Yh0RKnp5y4sijwUJU_1fxiW7gKJcA,1047
|
|
21
|
+
jcli/specs/_auth.yaml,sha256=h4wfmdjIetxCAQGpPHmp89FIRPXHP9N5vwDkw6PIPcc,276
|
|
22
|
+
jcli/specs/plugins/jcli_commands.py,sha256=6d9HEmPeyjwTaY6z9dFUbD7TYjkOn2C4WkUb_J2U1Wc,15878
|
|
23
|
+
jcli/specs/plugins/jenkins_auth.py,sha256=CbjWOHCEUoKpbV4WkRFTUCoPCLF5srHnWIkcIG1TUAA,2714
|
|
24
|
+
jcli/specs/plugins/jenkins_methods.py,sha256=9Rd8xxqXQl8ikKSlB15kgp61Y0UQDgSL7XsY5u5L2-Q,11793
|
|
25
|
+
jcli/specs/plugins/jenkins_pipeline_validate.py,sha256=N7DuQgtam_idKQaPWSsmxa4G4ohf-c8YUn6hLfF-znQ,1868
|
|
26
|
+
jcli/specs/plugins/jenkins_plugin.py,sha256=Bdg1CZI6kHsa1Po3KaR6057ON2t1SmuRt17MvRLc63U,2533
|
|
27
|
+
jcli/specs/plugins/jenkins_system.py,sha256=n_m_c8gHfIXmqUyDSiGZytblDM7H5QQCoDUEBhI30NQ,4289
|
|
28
|
+
jcli/specs/resources/build.yaml,sha256=niDH8L8HSHrIa_u5BMP9FJXgd3i2UWiTXUXOjKzSsM0,3994
|
|
29
|
+
jcli/specs/resources/credential.yaml,sha256=SUTOmYN_lafMAzuhH8noZj3HJgUvwG4FkXytoTcxA-M,2402
|
|
30
|
+
jcli/specs/resources/job.yaml,sha256=jvy-ARhPKCTaEpSyU1bJ7C1_HHo7l_Kb0b8yOku3FHs,4075
|
|
31
|
+
jcli/specs/resources/node.yaml,sha256=FhMEQ1kxucP6XyoTiVys7yoy8y3o-stbBdKv_SgIckA,1594
|
|
32
|
+
jcli/specs/resources/pipeline.yaml,sha256=kk-0B3adC8gA6YaFOOfQ5uAZQ11X3wCfvO-nLTx8hoE,1646
|
|
33
|
+
jcli/specs/resources/plugin.yaml,sha256=pqR5GUi1FHAL8iFsPbGCEr3sKSmR1UwK9CcZR5dLvJI,1561
|
|
34
|
+
jcli/specs/resources/system.yaml,sha256=ZhRcG5FTrKqIz-QNTxhPdO-y0WE29QVo3-U8oiMXYcc,1529
|
|
35
|
+
jcli/specs/resources/view.yaml,sha256=ELWZFZy667gh7SzwXCpxTacaduiqH7n2PAKdqzgnDLw,1596
|
|
36
|
+
python_jcli-1.0.1.dist-info/METADATA,sha256=4sTjfkW2xI6-6Ghz6RacmyZEvO41jBxAULceIVA_xZc,12519
|
|
37
|
+
python_jcli-1.0.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
38
|
+
python_jcli-1.0.1.dist-info/entry_points.txt,sha256=g-Gb8voGH3Ru4NEoCUMJXJiMZMapdAfYi6Xt1phyFi4,39
|
|
39
|
+
python_jcli-1.0.1.dist-info/top_level.txt,sha256=WFqrbWSEfhvQfbN2m-_Wi5KKCwv1_23LhzPUqo7AdQc,5
|
|
40
|
+
python_jcli-1.0.1.dist-info/RECORD,,
|
jcli/plugins/__init__.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""Jenkins CLI plugins - register all subcommands."""
|
|
2
|
-
|
|
3
|
-
import click
|
|
4
|
-
|
|
5
|
-
from jcli.plugins.build import register as register_build
|
|
6
|
-
from jcli.plugins.config import register as register_config
|
|
7
|
-
from jcli.plugins.credential import register as register_credential
|
|
8
|
-
from jcli.plugins.job import register as register_job
|
|
9
|
-
from jcli.plugins.node import register as register_node
|
|
10
|
-
from jcli.plugins.pipeline import register as register_pipeline
|
|
11
|
-
from jcli.plugins.plugin import register as register_plugin
|
|
12
|
-
from jcli.plugins.skills import register as register_skills
|
|
13
|
-
from jcli.plugins.system import register as register_system
|
|
14
|
-
from jcli.plugins.view import register as register_view
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def register_commands(group: click.Group) -> None:
|
|
18
|
-
"""Register all plugin subcommands under the main CLI group."""
|
|
19
|
-
register_build(group)
|
|
20
|
-
register_config(group)
|
|
21
|
-
register_credential(group)
|
|
22
|
-
register_job(group)
|
|
23
|
-
register_node(group)
|
|
24
|
-
register_pipeline(group)
|
|
25
|
-
register_plugin(group)
|
|
26
|
-
register_skills(group)
|
|
27
|
-
register_system(group)
|
|
28
|
-
register_view(group)
|
jcli/plugins/build.py
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
"""Jenkins Build management commands."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import click
|
|
6
|
-
|
|
7
|
-
from jcli.cli_helpers import get_client, get_formatter
|
|
8
|
-
from jcli.sdk.build import (
|
|
9
|
-
cancel_queue_item,
|
|
10
|
-
get_build,
|
|
11
|
-
get_build_artifacts,
|
|
12
|
-
get_build_log,
|
|
13
|
-
get_builds,
|
|
14
|
-
get_queue,
|
|
15
|
-
stop_build,
|
|
16
|
-
trigger_build,
|
|
17
|
-
)
|
|
18
|
-
from jcli.sdk.exceptions import JenkinsError
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _parse_params(raw_params: tuple[str, ...]) -> dict[str, str]:
|
|
22
|
-
"""Parse ``KEY=VAL`` strings into a dict."""
|
|
23
|
-
params: dict[str, str] = {}
|
|
24
|
-
for item in raw_params:
|
|
25
|
-
if "=" not in item:
|
|
26
|
-
raise click.BadParameter(f"Invalid parameter format: {item} (expected KEY=VAL)")
|
|
27
|
-
key, _, value = item.partition("=")
|
|
28
|
-
params[key.strip()] = value.strip()
|
|
29
|
-
return params
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# ------------------------------------------------------------------
|
|
33
|
-
# Build group
|
|
34
|
-
# ------------------------------------------------------------------
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@click.group("build", help="Manage Jenkins builds.")
|
|
38
|
-
def build_group():
|
|
39
|
-
"""Build management commands."""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@build_group.command("trigger")
|
|
43
|
-
@click.argument("job_name")
|
|
44
|
-
@click.option(
|
|
45
|
-
"--params", "-p",
|
|
46
|
-
multiple=True,
|
|
47
|
-
help="Build parameters as KEY=VAL pairs. Repeatable.",
|
|
48
|
-
)
|
|
49
|
-
@click.pass_context
|
|
50
|
-
def trigger(ctx: click.Context, job_name: str, params: tuple[str, ...]):
|
|
51
|
-
"""Trigger a new build for JOB_NAME.
|
|
52
|
-
|
|
53
|
-
Use --params/-p KEY=VAL to pass build parameters (repeat for multiple).
|
|
54
|
-
"""
|
|
55
|
-
client = get_client(ctx)
|
|
56
|
-
fmt = get_formatter(ctx)
|
|
57
|
-
try:
|
|
58
|
-
param_dict = _parse_params(params) if params else None
|
|
59
|
-
result = trigger_build(client, job_name, param_dict)
|
|
60
|
-
fmt.print_success(f"Build triggered for {job_name}")
|
|
61
|
-
fmt.print_json(result)
|
|
62
|
-
except JenkinsError as exc:
|
|
63
|
-
fmt.print_error(str(exc))
|
|
64
|
-
raise SystemExit(1)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@build_group.command("list")
|
|
68
|
-
@click.argument("job_name")
|
|
69
|
-
@click.option("--limit", "-n", default=10, type=int, help="Maximum builds to show.")
|
|
70
|
-
@click.pass_context
|
|
71
|
-
def list_builds(ctx: click.Context, job_name: str, limit: int):
|
|
72
|
-
"""List recent builds for JOB_NAME."""
|
|
73
|
-
client = get_client(ctx)
|
|
74
|
-
fmt = get_formatter(ctx)
|
|
75
|
-
try:
|
|
76
|
-
builds = get_builds(client, job_name, limit=limit)
|
|
77
|
-
if not builds:
|
|
78
|
-
fmt.print_info(f"No builds found for {job_name}")
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
headers = ["Number", "Result", "Timestamp", "Duration", "Building"]
|
|
82
|
-
rows = [
|
|
83
|
-
[
|
|
84
|
-
b.get("number", ""),
|
|
85
|
-
str(b.get("result") or "IN PROGRESS"),
|
|
86
|
-
str(b.get("timestamp", "")),
|
|
87
|
-
str(b.get("duration", "")),
|
|
88
|
-
str(b.get("building", False)),
|
|
89
|
-
]
|
|
90
|
-
for b in builds
|
|
91
|
-
]
|
|
92
|
-
fmt.print_table(headers, rows, title=f"Builds for {job_name}")
|
|
93
|
-
except JenkinsError as exc:
|
|
94
|
-
fmt.print_error(str(exc))
|
|
95
|
-
raise SystemExit(1)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@build_group.command("get")
|
|
99
|
-
@click.argument("job_name")
|
|
100
|
-
@click.argument("number", type=int)
|
|
101
|
-
@click.pass_context
|
|
102
|
-
def get_build_cmd(ctx: click.Context, job_name: str, number: int):
|
|
103
|
-
"""Show details for a specific build."""
|
|
104
|
-
client = get_client(ctx)
|
|
105
|
-
fmt = get_formatter(ctx)
|
|
106
|
-
try:
|
|
107
|
-
build = get_build(client, job_name, number)
|
|
108
|
-
fmt.print_json(build)
|
|
109
|
-
except JenkinsError as exc:
|
|
110
|
-
fmt.print_error(str(exc))
|
|
111
|
-
raise SystemExit(1)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@build_group.command("log")
|
|
115
|
-
@click.argument("job_name")
|
|
116
|
-
@click.argument("number", type=int)
|
|
117
|
-
@click.pass_context
|
|
118
|
-
def log_cmd(ctx: click.Context, job_name: str, number: int):
|
|
119
|
-
"""Show console log for a build."""
|
|
120
|
-
client = get_client(ctx)
|
|
121
|
-
fmt = get_formatter(ctx)
|
|
122
|
-
try:
|
|
123
|
-
log_text = get_build_log(client, job_name, number)
|
|
124
|
-
fmt.console.print(log_text)
|
|
125
|
-
except JenkinsError as exc:
|
|
126
|
-
fmt.print_error(str(exc))
|
|
127
|
-
raise SystemExit(1)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@build_group.command("stop")
|
|
131
|
-
@click.argument("job_name")
|
|
132
|
-
@click.argument("number", type=int)
|
|
133
|
-
@click.pass_context
|
|
134
|
-
def stop_cmd(ctx: click.Context, job_name: str, number: int):
|
|
135
|
-
"""Stop a running build."""
|
|
136
|
-
client = get_client(ctx)
|
|
137
|
-
fmt = get_formatter(ctx)
|
|
138
|
-
try:
|
|
139
|
-
stop_build(client, job_name, number)
|
|
140
|
-
fmt.print_success(f"Build {job_name}#{number} stopped")
|
|
141
|
-
except JenkinsError as exc:
|
|
142
|
-
fmt.print_error(str(exc))
|
|
143
|
-
raise SystemExit(1)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
@build_group.command("queue")
|
|
147
|
-
@click.pass_context
|
|
148
|
-
def queue_cmd(ctx: click.Context):
|
|
149
|
-
"""Show the current Jenkins build queue."""
|
|
150
|
-
client = get_client(ctx)
|
|
151
|
-
fmt = get_formatter(ctx)
|
|
152
|
-
try:
|
|
153
|
-
items = get_queue(client)
|
|
154
|
-
if not items:
|
|
155
|
-
fmt.print_info("Build queue is empty")
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
headers = ["ID", "Task", "Why", "In Queue Since"]
|
|
159
|
-
rows = [
|
|
160
|
-
[
|
|
161
|
-
str(item.get("id", "")),
|
|
162
|
-
str(item.get("task", {}).get("name", "")),
|
|
163
|
-
str(item.get("why", "")),
|
|
164
|
-
str(item.get("inQueueSince", "")),
|
|
165
|
-
]
|
|
166
|
-
for item in items
|
|
167
|
-
]
|
|
168
|
-
fmt.print_table(headers, rows, title="Build Queue")
|
|
169
|
-
except JenkinsError as exc:
|
|
170
|
-
fmt.print_error(str(exc))
|
|
171
|
-
raise SystemExit(1)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
@build_group.command("artifacts")
|
|
175
|
-
@click.argument("job_name")
|
|
176
|
-
@click.argument("build_number")
|
|
177
|
-
@click.pass_context
|
|
178
|
-
def artifacts_cmd(ctx: click.Context, job_name: str, build_number: str):
|
|
179
|
-
"""List artifacts for a build."""
|
|
180
|
-
client = get_client(ctx)
|
|
181
|
-
fmt = get_formatter(ctx)
|
|
182
|
-
try:
|
|
183
|
-
data = get_build_artifacts(client, job_name, build_number)
|
|
184
|
-
artifacts = data.get("artifacts", [])
|
|
185
|
-
if not artifacts:
|
|
186
|
-
fmt.print_info("No artifacts found.")
|
|
187
|
-
return
|
|
188
|
-
headers = ["File Name", "Relative Path"]
|
|
189
|
-
rows = [[a["fileName"], a["relativePath"]] for a in artifacts]
|
|
190
|
-
fmt.print_table(headers, rows, title=f"Artifacts: {job_name} #{build_number}")
|
|
191
|
-
except Exception as exc:
|
|
192
|
-
fmt.print_error(str(exc))
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def register(parent_group: click.Group):
|
|
196
|
-
"""Register the build subgroup under the parent Click group."""
|
|
197
|
-
parent_group.add_command(build_group)
|