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.
Files changed (54) hide show
  1. jcli/__init__.py +1 -1
  2. jcli/cli.py +565 -51
  3. jcli/cli_helpers.py +5 -42
  4. jcli/skills/jcli/SKILL.md +15 -2
  5. jcli/skills/jcli/auth/SKILL.md +86 -0
  6. jcli/skills/jcli/build/SKILL.md +123 -0
  7. jcli/skills/jcli/credential/SKILL.md +14 -5
  8. jcli/skills/jcli/job/SKILL.md +20 -4
  9. jcli/skills/jcli/node/SKILL.md +4 -2
  10. jcli/skills/jcli/pipeline/SKILL.md +3 -3
  11. jcli/skills/jcli/plugin/SKILL.md +3 -0
  12. jcli/skills/jcli/system/SKILL.md +15 -2
  13. jcli/skills/jcli/view/SKILL.md +4 -3
  14. jcli/specs/_auth.yaml +15 -0
  15. jcli/{plugins/skills.py → specs/plugins/jcli_commands.py} +124 -63
  16. jcli/specs/plugins/jenkins_auth.py +75 -0
  17. jcli/specs/plugins/jenkins_methods.py +332 -0
  18. jcli/specs/plugins/jenkins_pipeline_validate.py +51 -0
  19. jcli/specs/plugins/jenkins_plugin.py +77 -0
  20. jcli/specs/plugins/jenkins_system.py +137 -0
  21. jcli/specs/resources/build.yaml +160 -0
  22. jcli/specs/resources/credential.yaml +97 -0
  23. jcli/specs/resources/job.yaml +184 -0
  24. jcli/specs/resources/node.yaml +69 -0
  25. jcli/specs/resources/pipeline.yaml +62 -0
  26. jcli/specs/resources/plugin.yaml +65 -0
  27. jcli/specs/resources/system.yaml +72 -0
  28. jcli/specs/resources/view.yaml +75 -0
  29. {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/METADATA +57 -12
  30. python_jcli-1.0.1.dist-info/RECORD +40 -0
  31. {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/WHEEL +1 -1
  32. jcli/plugins/__init__.py +0 -28
  33. jcli/plugins/build.py +0 -197
  34. jcli/plugins/config.py +0 -268
  35. jcli/plugins/credential.py +0 -210
  36. jcli/plugins/job.py +0 -425
  37. jcli/plugins/node.py +0 -134
  38. jcli/plugins/pipeline.py +0 -108
  39. jcli/plugins/plugin.py +0 -173
  40. jcli/plugins/system.py +0 -173
  41. jcli/plugins/view.py +0 -148
  42. jcli/sdk/build.py +0 -182
  43. jcli/sdk/credential.py +0 -143
  44. jcli/sdk/job.py +0 -191
  45. jcli/sdk/job_templates.py +0 -128
  46. jcli/sdk/node.py +0 -139
  47. jcli/sdk/pipeline.py +0 -121
  48. jcli/sdk/plugin.py +0 -155
  49. jcli/sdk/system.py +0 -202
  50. jcli/sdk/view.py +0 -94
  51. jcli/skills/jcli/config/SKILL.md +0 -81
  52. python_jcli-0.1.0.dist-info/RECORD +0 -44
  53. {python_jcli-0.1.0.dist-info → python_jcli-1.0.1.dist-info}/entry_points.txt +0 -0
  54. {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.0
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 with Click and Rich** for a polished terminal experience
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 Main CLI entry point (Click group)
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (83.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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)