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
jcli/plugins/job.py DELETED
@@ -1,425 +0,0 @@
1
- """Jenkins Job 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.exceptions import JenkinsError, JenkinsNotFoundError
9
- from jcli.sdk.job import (
10
- copy_job,
11
- create_folder,
12
- create_job,
13
- delete_folder,
14
- delete_job,
15
- disable_job,
16
- enable_job,
17
- get_job,
18
- get_job_config,
19
- list_jobs,
20
- rename_job,
21
- update_job_config,
22
- )
23
-
24
-
25
- # ------------------------------------------------------------------
26
- # Click group
27
- # ------------------------------------------------------------------
28
-
29
-
30
- @click.group("job", help="Manage Jenkins jobs.")
31
- def job_group() -> None:
32
- """Job management commands."""
33
-
34
-
35
- # ------------------------------------------------------------------
36
- # list
37
- # ------------------------------------------------------------------
38
-
39
-
40
- @job_group.command("list")
41
- @click.pass_context
42
- def list_cmd(ctx: click.Context) -> None:
43
- """List all Jenkins jobs."""
44
- client = get_client(ctx)
45
- fmt = get_formatter(ctx)
46
-
47
- try:
48
- jobs = list_jobs(client)
49
- except JenkinsError as exc:
50
- fmt.print_error(str(exc))
51
- raise SystemExit(1) from exc
52
-
53
- if not jobs:
54
- fmt.print_info("No jobs found.")
55
- return
56
-
57
- headers = ["Name", "URL", "Color"]
58
- rows = [[j.get("name", ""), j.get("url", ""), j.get("color", "")] for j in jobs]
59
- fmt.print_table(headers, rows, title="Jenkins Jobs")
60
-
61
-
62
- # ------------------------------------------------------------------
63
- # get
64
- # ------------------------------------------------------------------
65
-
66
-
67
- @job_group.command("get")
68
- @click.argument("name")
69
- @click.pass_context
70
- def get_cmd(ctx: click.Context, name: str) -> None:
71
- """Show details for a specific job."""
72
- client = get_client(ctx)
73
- fmt = get_formatter(ctx)
74
-
75
- try:
76
- job = get_job(client, name)
77
- except JenkinsNotFoundError:
78
- fmt.print_error(f"Job '{name}' not found.")
79
- raise SystemExit(1)
80
- except JenkinsError as exc:
81
- fmt.print_error(str(exc))
82
- raise SystemExit(1) from exc
83
-
84
- fmt.print_json(job)
85
-
86
-
87
- # ------------------------------------------------------------------
88
- # create
89
- # ------------------------------------------------------------------
90
-
91
-
92
- @job_group.command("create")
93
- @click.argument("name")
94
- @click.argument("config_file", type=click.Path(exists=True, dir_okay=False, readable=True), required=False)
95
- @click.option("--type", "job_type", type=click.Choice(["freestyle", "pipeline"]), help="Job type (skip config_file).")
96
- @click.option("--git-url", help="Git repository URL.")
97
- @click.option("--git-branch", default="main", help="Git branch (default: main).")
98
- @click.option("--description", default="", help="Job description.")
99
- @click.option("--shell", help="Shell script to run (freestyle only).")
100
- @click.option("--cron", help="Cron schedule for periodic build (freestyle only).")
101
- @click.option("--jenkinsfile", default="Jenkinsfile", help="Jenkinsfile path (pipeline only).")
102
- @click.option("--script", help="Inline Pipeline script (pipeline only).")
103
- @click.pass_context
104
- def create_cmd(
105
- ctx: click.Context,
106
- name: str,
107
- config_file: str | None,
108
- job_type: str | None,
109
- git_url: str | None,
110
- git_branch: str,
111
- description: str,
112
- shell: str | None,
113
- cron: str | None,
114
- jenkinsfile: str,
115
- script: str | None,
116
- ) -> None:
117
- """Create a new job from an XML configuration file or parameters.
118
-
119
- NAME is the name for the new job.
120
- CONFIG_FILE is the path to the XML file (optional if --type is specified).
121
-
122
- Examples:
123
- jcli job create myjob config.xml
124
- jcli job create myjob --type freestyle --git-url https://... --shell "make build"
125
- jcli job create mypipe --type pipeline --git-url https://... --jenkinsfile Jenkinsfile
126
- jcli job create mypipe --type pipeline --script "pipeline { agent any; stages { stage('build') { steps { echo 'hi' } } } }"
127
- """
128
- client = get_client(ctx)
129
- fmt = get_formatter(ctx)
130
-
131
- try:
132
- if config_file:
133
- # XML file mode (backward compatible)
134
- try:
135
- with open(config_file, "r", encoding="utf-8") as f:
136
- config_xml = f.read()
137
- except OSError as exc:
138
- fmt.print_error(f"Cannot read config file '{config_file}': {exc}")
139
- raise SystemExit(1) from exc
140
- elif job_type:
141
- # Parameter mode
142
- from jcli.sdk.job_templates import generate_freestyle_xml, generate_pipeline_xml
143
-
144
- if job_type == "freestyle":
145
- config_xml = generate_freestyle_xml(
146
- git_url=git_url or "",
147
- git_branch=git_branch,
148
- shell_script=shell or "",
149
- description=description,
150
- cron_schedule=cron or "",
151
- )
152
- else:
153
- config_xml = generate_pipeline_xml(
154
- git_url=git_url or "",
155
- git_branch=git_branch,
156
- jenkinsfile_path=jenkinsfile,
157
- description=description,
158
- script=script or "",
159
- )
160
- else:
161
- raise click.UsageError("Must specify either CONFIG_FILE or --type")
162
-
163
- create_job(client, name, config_xml)
164
- except JenkinsError as exc:
165
- fmt.print_error(str(exc))
166
- raise SystemExit(1) from exc
167
-
168
- fmt.print_success(f"Job '{name}' created.")
169
-
170
-
171
- # ------------------------------------------------------------------
172
- # update
173
- # ------------------------------------------------------------------
174
-
175
-
176
- @job_group.command("update")
177
- @click.argument("job_name")
178
- @click.argument("config_file", type=click.Path(exists=True, dir_okay=False))
179
- @click.pass_context
180
- def update_cmd(ctx: click.Context, job_name: str, config_file: str) -> None:
181
- """Update a job's configuration from an XML file.
182
-
183
- JOB_NAME is the name of the job to update.
184
- CONFIG_FILE is the path to the new XML configuration file.
185
- """
186
- client = get_client(ctx)
187
- fmt = get_formatter(ctx)
188
-
189
- try:
190
- with open(config_file, "r", encoding="utf-8") as f:
191
- config_xml = f.read()
192
- except OSError as exc:
193
- fmt.print_error(f"Cannot read config file '{config_file}': {exc}")
194
- raise SystemExit(1) from exc
195
-
196
- try:
197
- update_job_config(client, job_name, config_xml)
198
- except JenkinsError as exc:
199
- fmt.print_error(str(exc))
200
- raise SystemExit(1) from exc
201
-
202
- fmt.print_success(f"Job '{job_name}' updated.")
203
-
204
-
205
- # ------------------------------------------------------------------
206
- # delete
207
- # ------------------------------------------------------------------
208
-
209
-
210
- @job_group.command("delete")
211
- @click.argument("name")
212
- @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt.")
213
- @click.pass_context
214
- def delete_cmd(ctx: click.Context, name: str, yes: bool) -> None:
215
- """Delete a job.
216
-
217
- Prompts for confirmation unless --yes/-y is given.
218
- """
219
- client = get_client(ctx)
220
- fmt = get_formatter(ctx)
221
-
222
- if not yes:
223
- confirm = input(f"Are you sure you want to delete job '{name}'? [y/N]: ")
224
- if confirm.lower() not in ("y", "yes"):
225
- fmt.print_info("Delete cancelled.")
226
- return
227
-
228
- try:
229
- delete_job(client, name)
230
- except JenkinsNotFoundError:
231
- fmt.print_error(f"Job '{name}' not found.")
232
- raise SystemExit(1)
233
- except JenkinsError as exc:
234
- fmt.print_error(str(exc))
235
- raise SystemExit(1) from exc
236
-
237
- fmt.print_success(f"Job '{name}' deleted.")
238
-
239
-
240
- # ------------------------------------------------------------------
241
- # copy
242
- # ------------------------------------------------------------------
243
-
244
-
245
- @job_group.command("copy")
246
- @click.argument("from_name")
247
- @click.argument("new_name")
248
- @click.pass_context
249
- def copy_cmd(ctx: click.Context, from_name: str, new_name: str) -> None:
250
- """Copy a job to a new name.
251
-
252
- FROM_NAME is the source job.
253
- NEW_NAME is the name for the copied job.
254
- """
255
- client = get_client(ctx)
256
- fmt = get_formatter(ctx)
257
-
258
- try:
259
- copy_job(client, from_name, new_name)
260
- except JenkinsNotFoundError:
261
- fmt.print_error(f"Source job '{from_name}' not found.")
262
- raise SystemExit(1)
263
- except JenkinsError as exc:
264
- fmt.print_error(str(exc))
265
- raise SystemExit(1) from exc
266
-
267
- fmt.print_success(f"Job '{from_name}' copied to '{new_name}'.")
268
-
269
-
270
- # ------------------------------------------------------------------
271
- # rename
272
- # ------------------------------------------------------------------
273
-
274
-
275
- @job_group.command("rename")
276
- @click.argument("old_name")
277
- @click.argument("new_name")
278
- @click.pass_context
279
- def rename_job_cmd(ctx: click.Context, old_name: str, new_name: str) -> None:
280
- """Rename a job.
281
-
282
- OLD_NAME is the current name of the job.
283
- NEW_NAME is the new name to assign to the job.
284
- """
285
- client = get_client(ctx)
286
- fmt = get_formatter(ctx)
287
-
288
- try:
289
- rename_job(client, old_name, new_name)
290
- except JenkinsNotFoundError:
291
- fmt.print_error(f"Job '{old_name}' not found.")
292
- raise SystemExit(1)
293
- except JenkinsError as exc:
294
- fmt.print_error(str(exc))
295
- raise SystemExit(1) from exc
296
-
297
- fmt.print_success(f"Job renamed from '{old_name}' to '{new_name}'.")
298
-
299
-
300
- # ------------------------------------------------------------------
301
- # enable / disable
302
- # ------------------------------------------------------------------
303
-
304
-
305
- @job_group.command("enable")
306
- @click.argument("name")
307
- @click.pass_context
308
- def enable_cmd(ctx: click.Context, name: str) -> None:
309
- """Enable a disabled job."""
310
- client = get_client(ctx)
311
- fmt = get_formatter(ctx)
312
-
313
- try:
314
- enable_job(client, name)
315
- except JenkinsNotFoundError:
316
- fmt.print_error(f"Job '{name}' not found.")
317
- raise SystemExit(1)
318
- except JenkinsError as exc:
319
- fmt.print_error(str(exc))
320
- raise SystemExit(1) from exc
321
-
322
- fmt.print_success(f"Job '{name}' enabled.")
323
-
324
-
325
- @job_group.command("disable")
326
- @click.argument("name")
327
- @click.pass_context
328
- def disable_cmd(ctx: click.Context, name: str) -> None:
329
- """Disable a job."""
330
- client = get_client(ctx)
331
- fmt = get_formatter(ctx)
332
-
333
- try:
334
- disable_job(client, name)
335
- except JenkinsNotFoundError:
336
- fmt.print_error(f"Job '{name}' not found.")
337
- raise SystemExit(1)
338
- except JenkinsError as exc:
339
- fmt.print_error(str(exc))
340
- raise SystemExit(1) from exc
341
-
342
- fmt.print_success(f"Job '{name}' disabled.")
343
-
344
-
345
- # ------------------------------------------------------------------
346
- # config
347
- # ------------------------------------------------------------------
348
-
349
-
350
- @job_group.command("config")
351
- @click.argument("name")
352
- @click.pass_context
353
- def config_cmd(ctx: click.Context, name: str) -> None:
354
- """Print the XML configuration of a job."""
355
- client = get_client(ctx)
356
- fmt = get_formatter(ctx)
357
-
358
- try:
359
- xml_text = get_job_config(client, name)
360
- except JenkinsNotFoundError:
361
- fmt.print_error(f"Job '{name}' not found.")
362
- raise SystemExit(1)
363
- except JenkinsError as exc:
364
- fmt.print_error(str(exc))
365
- raise SystemExit(1) from exc
366
-
367
- fmt.console.print(xml_text)
368
-
369
-
370
- # ------------------------------------------------------------------
371
- # create-folder
372
- # ------------------------------------------------------------------
373
-
374
-
375
- @job_group.command("create-folder")
376
- @click.argument("name")
377
- @click.pass_context
378
- def create_folder_cmd(ctx: click.Context, name: str) -> None:
379
- """Create a new folder."""
380
- client = get_client(ctx)
381
- fmt = get_formatter(ctx)
382
-
383
- try:
384
- create_folder(client, name)
385
- except JenkinsError as exc:
386
- fmt.print_error(str(exc))
387
- raise SystemExit(1) from exc
388
-
389
- fmt.print_success(f"Folder '{name}' created.")
390
-
391
-
392
- # ------------------------------------------------------------------
393
- # delete-folder
394
- # ------------------------------------------------------------------
395
-
396
-
397
- @job_group.command("delete-folder")
398
- @click.argument("name")
399
- @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt.")
400
- @click.pass_context
401
- def delete_folder_cmd(ctx: click.Context, name: str, yes: bool) -> None:
402
- """Delete a folder and all its contents."""
403
- if not yes:
404
- click.confirm(f"Delete folder '{name}' and ALL its contents?", abort=True)
405
-
406
- client = get_client(ctx)
407
- fmt = get_formatter(ctx)
408
-
409
- try:
410
- delete_folder(client, name)
411
- except JenkinsError as exc:
412
- fmt.print_error(str(exc))
413
- raise SystemExit(1) from exc
414
-
415
- fmt.print_success(f"Folder '{name}' deleted.")
416
-
417
-
418
- # ------------------------------------------------------------------
419
- # Registration
420
- # ------------------------------------------------------------------
421
-
422
-
423
- def register(parent_group: click.Group) -> None:
424
- """Register the job subgroup under the parent Click group."""
425
- parent_group.add_command(job_group)
jcli/plugins/node.py DELETED
@@ -1,134 +0,0 @@
1
- """Jenkins Node 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.node import create_node, delete_node, get_node, list_nodes, toggle_offline
9
-
10
-
11
- @click.group("node", help="Manage Jenkins nodes (agents).")
12
- def node_group():
13
- """Node management commands."""
14
-
15
-
16
- @node_group.command("list")
17
- @click.pass_context
18
- def list_nodes_cmd(ctx: click.Context):
19
- """List all Jenkins nodes."""
20
- fmt = get_formatter(ctx)
21
- client = get_client(ctx)
22
-
23
- try:
24
- nodes = list_nodes(client)
25
- if nodes:
26
- headers = ["Name", "Online", "Temporarily Offline", "Architecture"]
27
- rows = [
28
- [
29
- n.get("displayName", "?"),
30
- "Yes" if not n.get("offline", True) else "No",
31
- "Yes" if n.get("temporarilyOffline", False) else "No",
32
- n.get("monitorData", {}).get("hudson.node_monitors.ArchitectureMonitor", "?"),
33
- ]
34
- for n in nodes
35
- ]
36
- fmt.print_table(headers, rows, title="Jenkins Nodes")
37
- else:
38
- fmt.print_info("No nodes found.")
39
- except Exception as exc:
40
- fmt.print_error(str(exc))
41
-
42
-
43
- @node_group.command("get")
44
- @click.argument("name")
45
- @click.pass_context
46
- def get_node_cmd(ctx: click.Context, name: str):
47
- """Get details of a specific node."""
48
- fmt = get_formatter(ctx)
49
- client = get_client(ctx)
50
-
51
- try:
52
- node = get_node(client, name)
53
- if fmt.format_type == "json":
54
- fmt.print_json(node)
55
- else:
56
- headers = ["Property", "Value"]
57
- rows = [
58
- ["Display Name", node.get("displayName", "?")],
59
- ["Description", node.get("description", "") or "-"],
60
- ["Offline", "Yes" if node.get("offline", True) else "No"],
61
- ["Temporarily Offline", "Yes" if node.get("temporarilyOffline", False) else "No"],
62
- ["Num Executors", str(node.get("numExecutors", "?"))],
63
- ["Idle", "Yes" if node.get("idle", False) else "No"],
64
- ["JNLP Agent", "Yes" if node.get("jnlpAgent", False) else "No"],
65
- ]
66
- # Add monitor data if available
67
- monitor = node.get("monitorData", {})
68
- if monitor:
69
- for key, val in monitor.items():
70
- if key.startswith("hudson.node_monitors."):
71
- label = key.replace("hudson.node_monitors.", "")
72
- rows.append([f"Monitor: {label}", str(val)])
73
- fmt.print_table(headers, rows, title=f"Node: {name}")
74
- except Exception as exc:
75
- fmt.print_error(str(exc))
76
-
77
-
78
- @node_group.command("delete")
79
- @click.argument("name")
80
- @click.option("--force", "-f", is_flag=True, help="Skip confirmation prompt.")
81
- @click.pass_context
82
- def delete_node_cmd(ctx: click.Context, name: str, force: bool):
83
- """Delete a Jenkins node (agent)."""
84
- fmt = get_formatter(ctx)
85
- client = get_client(ctx)
86
-
87
- if not force:
88
- if not click.confirm(f"Are you sure you want to delete node '{name}'?"):
89
- fmt.print_info("Delete cancelled.")
90
- return
91
-
92
- try:
93
- delete_node(client, name)
94
- fmt.print_success(f"Node '{name}' deleted successfully.")
95
- except Exception as exc:
96
- fmt.print_error(str(exc))
97
-
98
-
99
- @node_group.command("toggle")
100
- @click.argument("name")
101
- @click.option("--message", "-m", default="", help="Reason for toggling offline/online.")
102
- @click.pass_context
103
- def toggle_node_cmd(ctx: click.Context, name: str, message: str):
104
- """Toggle a node offline or online."""
105
- fmt = get_formatter(ctx)
106
- client = get_client(ctx)
107
-
108
- try:
109
- toggle_offline(client, name, msg=message)
110
- fmt.print_success(f"Node '{name}' toggled successfully.")
111
- except Exception as exc:
112
- fmt.print_error(str(exc))
113
-
114
-
115
- @node_group.command("create")
116
- @click.argument("name")
117
- @click.option("--executors", type=int, default=1, help="Number of executors (default: 1).")
118
- @click.option("--remote-fs", default="/tmp", help="Remote filesystem root (default: /tmp).")
119
- @click.option("--labels", default="", help="Node labels.")
120
- @click.pass_context
121
- def create_node_cmd(ctx: click.Context, name: str, executors: int, remote_fs: str, labels: str):
122
- """Create a new Jenkins agent node."""
123
- client = get_client(ctx)
124
- fmt = get_formatter(ctx)
125
- try:
126
- create_node(client, name, num_executors=executors, remote_fs=remote_fs, labels=labels)
127
- fmt.print_success(f"Node '{name}' created.")
128
- except Exception as exc:
129
- fmt.print_error(str(exc))
130
-
131
-
132
- def register(parent_group):
133
- """Register the node subgroup under the parent Click group."""
134
- parent_group.add_command(node_group)
jcli/plugins/pipeline.py DELETED
@@ -1,108 +0,0 @@
1
- """Jenkins Pipeline management commands."""
2
-
3
- import click
4
-
5
- from jcli.cli_helpers import get_client, get_formatter
6
- from jcli.sdk.exceptions import (
7
- JenkinsAPIError,
8
- JenkinsAuthError,
9
- JenkinsConnectionError,
10
- JenkinsNotFoundError,
11
- )
12
- from jcli.sdk.pipeline import (
13
- get_pending_input,
14
- get_pipeline_log,
15
- get_pipeline_stages,
16
- validate_jenkinsfile,
17
- )
18
-
19
-
20
- @click.group("pipeline", help="Manage Jenkins pipelines.")
21
- def pipeline_group():
22
- """Pipeline management commands."""
23
-
24
-
25
- @pipeline_group.command("stages")
26
- @click.argument("job_name")
27
- @click.argument("build_number")
28
- @click.pass_context
29
- def stages(ctx, job_name, build_number):
30
- """List stages for a Pipeline build.
31
-
32
- JOB_NAME is the Pipeline job name.
33
- BUILD_NUMBER is the build number to query.
34
- """
35
- client = get_client(ctx)
36
- fmt = get_formatter(ctx)
37
-
38
- try:
39
- result = get_pipeline_stages(client, job_name, build_number)
40
- fmt.print_json(result)
41
- except (JenkinsNotFoundError, JenkinsAuthError, JenkinsConnectionError, JenkinsAPIError) as exc:
42
- fmt.print_error(str(exc))
43
-
44
-
45
- @pipeline_group.command("log")
46
- @click.argument("job_name")
47
- @click.argument("build_number")
48
- @click.argument("node_id")
49
- @click.pass_context
50
- def log(ctx, job_name, build_number, node_id):
51
- """Get log output for a Pipeline step.
52
-
53
- JOB_NAME is the Pipeline job name.
54
- BUILD_NUMBER is the build number to query.
55
- NODE_ID is the flow node ID from stage/step info.
56
- """
57
- client = get_client(ctx)
58
- fmt = get_formatter(ctx)
59
-
60
- try:
61
- result = get_pipeline_log(client, job_name, build_number, node_id)
62
- fmt.print_json(result)
63
- except (JenkinsNotFoundError, JenkinsAuthError, JenkinsConnectionError, JenkinsAPIError) as exc:
64
- fmt.print_error(str(exc))
65
-
66
-
67
- @pipeline_group.command("validate")
68
- @click.argument("file_path", type=click.Path(exists=True, dir_okay=False))
69
- @click.pass_context
70
- def validate(ctx, file_path):
71
- """Validate a Jenkinsfile against the Jenkins server.
72
-
73
- FILE_PATH is the path to the Jenkinsfile to validate.
74
- """
75
- with open(file_path, "r", encoding="utf-8") as f:
76
- content = f.read()
77
-
78
- client = get_client(ctx)
79
- fmt = get_formatter(ctx)
80
-
81
- result = validate_jenkinsfile(client, content)
82
-
83
- fmt.print_json(result)
84
-
85
-
86
- @pipeline_group.command("pending")
87
- @click.argument("job_name")
88
- @click.argument("build_number")
89
- @click.pass_context
90
- def pending(ctx, job_name, build_number):
91
- """Show pending input actions for a Pipeline build.
92
-
93
- JOB_NAME is the Pipeline job name.
94
- BUILD_NUMBER is the build number to query.
95
- """
96
- client = get_client(ctx)
97
- fmt = get_formatter(ctx)
98
-
99
- try:
100
- result = get_pending_input(client, job_name, build_number)
101
- fmt.print_json(result)
102
- except (JenkinsNotFoundError, JenkinsAuthError, JenkinsConnectionError, JenkinsAPIError) as exc:
103
- fmt.print_error(str(exc))
104
-
105
-
106
- def register(parent_group):
107
- """Register the pipeline subgroup under the parent Click group."""
108
- parent_group.add_command(pipeline_group)