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/config.py DELETED
@@ -1,268 +0,0 @@
1
- """Configuration management CLI commands for jcli."""
2
-
3
- import click
4
-
5
- from jcli.sdk.config import Config, ProfileNotFoundError
6
- from jcli.sdk.output.formatter import OutputFormatter
7
-
8
-
9
- def _get_config(ctx: click.Context) -> Config:
10
- """Get or create Config from Click context."""
11
- if "config" not in ctx.obj:
12
- ctx.obj["config"] = Config().load()
13
- return ctx.obj["config"]
14
-
15
-
16
- def _get_formatter(ctx: click.Context) -> OutputFormatter:
17
- """Get or create OutputFormatter from Click context."""
18
- if "formatter" not in ctx.obj:
19
- fmt = ctx.obj.get("format", "table")
20
- ctx.obj["formatter"] = OutputFormatter(fmt)
21
- return ctx.obj["formatter"]
22
-
23
-
24
- @click.group()
25
- def config_group():
26
- """Manage jcli configuration."""
27
- pass
28
-
29
-
30
- @config_group.command("init")
31
- @click.option("--force", is_flag=True, help="Overwrite existing config file")
32
- @click.pass_context
33
- def config_init(ctx: click.Context, force: bool):
34
- """Initialize configuration file with default template."""
35
- cfg = _get_config(ctx)
36
- fmt = _get_formatter(ctx)
37
-
38
- if cfg.config_path.exists() and not force:
39
- fmt.print_error(f"Config file already exists: {cfg.config_path}")
40
- fmt.print_info("Use --force to overwrite")
41
- return
42
-
43
- # Create fresh config with template
44
- cfg = Config()
45
- cfg.load()
46
- cfg.save()
47
-
48
- fmt.print_success(f"Config file created: {cfg.config_path}")
49
- fmt.print_info("Edit it with your Jenkins server details:")
50
- fmt.print_info(f" url: https://jenkins.example.com")
51
- fmt.print_info(f" username: admin")
52
- fmt.print_info(f" api_token: your-api-token-here")
53
-
54
-
55
- @config_group.command("show")
56
- @click.option("--profile", "-p", help="Show specific profile (default: active)")
57
- @click.pass_context
58
- def config_show(ctx: click.Context, profile: str | None):
59
- """Show configuration details."""
60
- cfg = _get_config(ctx)
61
- fmt = _get_formatter(ctx)
62
-
63
- if profile:
64
- try:
65
- data = cfg.get_profile(profile)
66
- except ProfileNotFoundError:
67
- fmt.print_error(f"Profile '{profile}' not found")
68
- return
69
- active_name = cfg.get_active_profile_name()
70
- is_active = " (active)" if profile == active_name else ""
71
- fmt.print_info(f"Profile: {profile}{is_active}")
72
- _show_profile(fmt, data)
73
- else:
74
- active_name = cfg.get_active_profile_name()
75
- fmt.print_info(f"Config file: {cfg.config_path}")
76
- fmt.print_info(f"Active profile: {active_name}")
77
- fmt.print_info("")
78
- try:
79
- data = cfg.get_active_profile()
80
- _show_profile(fmt, data)
81
- except ProfileNotFoundError:
82
- fmt.print_error(f"Active profile '{active_name}' not found")
83
-
84
-
85
- def _show_profile(fmt: OutputFormatter, data: dict):
86
- """Display profile fields."""
87
- fields = [
88
- ("url", "URL"),
89
- ("username", "Username"),
90
- ("api_token", "API Token"),
91
- ("description", "Description"),
92
- ]
93
- for key, label in fields:
94
- value = data.get(key, "")
95
- # Mask token for display
96
- if key == "api_token" and value and len(value) > 8:
97
- display = value[:4] + "****" + value[-4:]
98
- else:
99
- display = value
100
- fmt.print_info(f" {label}: {display}")
101
-
102
-
103
- @config_group.command("list")
104
- @click.pass_context
105
- def config_list(ctx: click.Context):
106
- """List all configured profiles."""
107
- cfg = _get_config(ctx)
108
- fmt = _get_formatter(ctx)
109
-
110
- profiles = cfg.list_profiles()
111
- active_name = cfg.get_active_profile_name()
112
-
113
- if not profiles:
114
- fmt.print_info("No profiles configured. Run 'jcli config init' to create one.")
115
- return
116
-
117
- headers = ["Name", "URL", "Username", "Active"]
118
- rows = []
119
- for name, data in profiles.items():
120
- active_mark = "✓" if name == active_name else ""
121
- rows.append([
122
- name,
123
- data.get("url", ""),
124
- data.get("username", ""),
125
- active_mark,
126
- ])
127
-
128
- fmt.print_table(headers, rows, title="Profiles")
129
-
130
-
131
- @config_group.command("set")
132
- @click.argument("profile")
133
- @click.argument("field", type=click.Choice(["url", "username", "api_token", "description"]))
134
- @click.argument("value")
135
- @click.pass_context
136
- def config_set(ctx: click.Context, profile: str, field: str, value: str):
137
- """Set a configuration field for a profile.
138
-
139
- Examples:
140
- jcli config set dev url https://jenkins-dev.example.com
141
- jcli config set dev username admin
142
- jcli config set dev api_token abc123
143
- jcli config set dev description "Development Jenkins"
144
- """
145
- cfg = _get_config(ctx)
146
- fmt = _get_formatter(ctx)
147
-
148
- try:
149
- data = cfg.get_profile(profile)
150
- except ProfileNotFoundError:
151
- fmt.print_error(f"Profile '{profile}' not found")
152
- fmt.print_info(f"Create it first with: jcli config add {profile}")
153
- return
154
-
155
- # Update the field
156
- data[field] = value
157
- cfg.add_profile(
158
- name=profile,
159
- url=data.get("url", ""),
160
- username=data.get("username", ""),
161
- api_token=data.get("api_token", ""),
162
- description=data.get("description", ""),
163
- )
164
-
165
- fmt.print_success(f"Updated profile '{profile}': {field} = {value}")
166
-
167
-
168
- @config_group.command("add")
169
- @click.argument("profile")
170
- @click.option("--url", "-u", default="", help="Jenkins server URL")
171
- @click.option("--username", default="", help="Jenkins username")
172
- @click.option("--api-token", default="", help="Jenkins API token")
173
- @click.option("--description", "-d", default="", help="Profile description")
174
- @click.pass_context
175
- def config_add(
176
- ctx: click.Context,
177
- profile: str,
178
- url: str,
179
- username: str,
180
- api_token: str,
181
- description: str,
182
- ):
183
- """Add a new configuration profile.
184
-
185
- Examples:
186
- jcli config add dev --url https://jenkins-dev.example.com --username admin
187
- jcli config add prod -u https://jenkins-prod.example.com -d "Production"
188
- """
189
- cfg = _get_config(ctx)
190
- fmt = _get_formatter(ctx)
191
-
192
- # Check if profile already exists
193
- profiles = cfg.list_profiles()
194
- if profile in profiles:
195
- fmt.print_error(f"Profile '{profile}' already exists")
196
- fmt.print_info(f"Use 'jcli config set {profile} <field> <value>' to update")
197
- return
198
-
199
- cfg.add_profile(
200
- name=profile,
201
- url=url or "https://jenkins.example.com",
202
- username=username or "admin",
203
- api_token=api_token or "",
204
- description=description or f"{profile} Jenkins instance",
205
- )
206
-
207
- fmt.print_success(f"Profile '{profile}' added")
208
- fmt.print_info("Configure it with:")
209
- fmt.print_info(f" jcli config set {profile} url https://your-jenkins.com")
210
- fmt.print_info(f" jcli config set {profile} api_token your-token")
211
-
212
-
213
- @config_group.command("delete")
214
- @click.argument("profile")
215
- @click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
216
- @click.pass_context
217
- def config_delete(ctx: click.Context, profile: str, yes: bool):
218
- """Delete a configuration profile.
219
-
220
- Examples:
221
- jcli config delete dev
222
- jcli config delete old-server --yes
223
- """
224
- cfg = _get_config(ctx)
225
- fmt = _get_formatter(ctx)
226
-
227
- profiles = cfg.list_profiles()
228
- if profile not in profiles:
229
- fmt.print_error(f"Profile '{profile}' not found")
230
- return
231
-
232
- if not yes:
233
- if not click.confirm(f"Delete profile '{profile}'?"):
234
- fmt.print_info("Cancelled")
235
- return
236
-
237
- cfg.remove_profile(profile)
238
- fmt.print_success(f"Profile '{profile}' deleted")
239
-
240
-
241
- @config_group.command("use")
242
- @click.argument("profile")
243
- @click.pass_context
244
- def config_use(ctx: click.Context, profile: str):
245
- """Switch active profile.
246
-
247
- Examples:
248
- jcli config use prod
249
- jcli config use dev
250
- """
251
- cfg = _get_config(ctx)
252
- fmt = _get_formatter(ctx)
253
-
254
- try:
255
- cfg.set_active_profile(profile)
256
- except ProfileNotFoundError:
257
- fmt.print_error(f"Profile '{profile}' not found")
258
- profiles = cfg.list_profiles()
259
- if profiles:
260
- fmt.print_info(f"Available profiles: {', '.join(profiles.keys())}")
261
- return
262
-
263
- fmt.print_success(f"Active profile set to '{profile}'")
264
-
265
-
266
- def register(group: click.Group) -> None:
267
- """Register config commands with the main CLI group."""
268
- group.add_command(config_group, "config")
@@ -1,210 +0,0 @@
1
- """Jenkins Credential management commands."""
2
-
3
- import sys
4
- from pathlib import Path
5
- from typing import Any
6
-
7
- import click
8
-
9
- from jcli.cli_helpers import get_client, get_formatter
10
- from jcli.sdk.credential import (
11
- create_credential,
12
- delete_credential,
13
- get_credential,
14
- list_credentials,
15
- update_credential,
16
- )
17
-
18
-
19
- def _display_result(ctx: click.Context, result: Any) -> None:
20
- """Display result using the configured formatter.
21
-
22
- If result is a dict with a ``credentials`` key, render as a table.
23
- If result is a dict, pretty-print.
24
- Otherwise, print as JSON.
25
- """
26
- formatter = get_formatter(ctx)
27
-
28
- if isinstance(result, dict) and "credentials" in result:
29
- creds = result["credentials"]
30
- if not creds:
31
- formatter.print_info("No credentials found.")
32
- return
33
- rows = [
34
- [c.get("id", ""), c.get("typeName", ""), c.get("description", "")]
35
- for c in creds
36
- ]
37
- formatter.print_table(
38
- headers=["ID", "Type", "Description"],
39
- rows=rows,
40
- title="Credentials",
41
- )
42
- else:
43
- formatter.print_json(result)
44
-
45
-
46
- @click.group("credential", help="Manage Jenkins credentials.")
47
- def credential_group():
48
- """Credential management commands."""
49
-
50
-
51
- @credential_group.command("list")
52
- @click.option(
53
- "--store", "-s", default="system", help="Credential store ID (default: system)."
54
- )
55
- @click.option(
56
- "--domain", "-d", default="_", help="Credential domain ID (default: _)."
57
- )
58
- @click.pass_context
59
- def cmd_list(ctx: click.Context, store: str, domain: str) -> None:
60
- """List Jenkins credentials."""
61
- client = get_client(ctx)
62
- try:
63
- result = list_credentials(client, store=store, domain=domain, depth=1)
64
- _display_result(ctx, result)
65
- except Exception as exc:
66
- get_formatter(ctx).print_error(str(exc))
67
- sys.exit(1)
68
-
69
-
70
- @credential_group.command("get")
71
- @click.argument("cred_id")
72
- @click.option(
73
- "--store", "-s", default="system", help="Credential store ID (default: system)."
74
- )
75
- @click.pass_context
76
- def cmd_get(ctx: click.Context, cred_id: str, store: str) -> None:
77
- """Get credential details."""
78
- client = get_client(ctx)
79
- try:
80
- result = get_credential(client, cred_id=cred_id, store=store)
81
- get_formatter(ctx).print_json(result)
82
- except Exception as exc:
83
- get_formatter(ctx).print_error(str(exc))
84
- sys.exit(1)
85
-
86
-
87
- @credential_group.command("create")
88
- @click.argument("cred_id")
89
- @click.argument(
90
- "config_xml",
91
- type=click.Path(exists=True, dir_okay=False, path_type=Path),
92
- )
93
- @click.option(
94
- "--store", "-s", default="system", help="Credential store ID (default: system)."
95
- )
96
- @click.option(
97
- "--domain", "-d", default="_", help="Credential domain ID (default: _)."
98
- )
99
- @click.pass_context
100
- def cmd_create(
101
- ctx: click.Context,
102
- cred_id: str,
103
- config_xml: Path,
104
- store: str,
105
- domain: str,
106
- ) -> None:
107
- """Create a credential from an XML config file.
108
-
109
- CRED_ID is the credential ID (ignored by Jenkins API, used for reference).
110
- CONFIG_XML is a path to the credential XML file.
111
-
112
- The XML must follow Jenkins' XStream format for the target credential type.
113
- Example for UsernamePasswordCredentialsImpl:
114
-
115
- \\b
116
- <com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
117
- <scope>GLOBAL</scope>
118
- <id>my-cred-id</id>
119
- <description>My credential</description>
120
- <username>admin</username>
121
- <password>secret</password>
122
- </com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
123
- """
124
- client = get_client(ctx)
125
- formatter = get_formatter(ctx)
126
- try:
127
- xml_data = config_xml.read_text(encoding="utf-8")
128
- create_credential(client, xml_data=xml_data, store=store, domain=domain)
129
- formatter.print_success(f"Credential created in store '{store}' domain '{domain}'.")
130
- except Exception as exc:
131
- formatter.print_error(str(exc))
132
- sys.exit(1)
133
-
134
-
135
- @credential_group.command("update")
136
- @click.argument("cred_id")
137
- @click.argument(
138
- "config_file",
139
- type=click.Path(exists=True, dir_okay=False, path_type=Path),
140
- )
141
- @click.option(
142
- "--store", "-s", default="system", help="Credential store ID (default: system)."
143
- )
144
- @click.pass_context
145
- def cmd_update(
146
- ctx: click.Context,
147
- cred_id: str,
148
- config_file: Path,
149
- store: str,
150
- ) -> None:
151
- """Update a credential from an XML config file.
152
-
153
- CRED_ID is the credential ID to update.
154
- CONFIG_FILE is a path to the credential XML file.
155
-
156
- The XML must follow Jenkins' XStream format for the target credential type.
157
- Example for UsernamePasswordCredentialsImpl:
158
-
159
- \\b
160
- <com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
161
- <scope>GLOBAL</scope>
162
- <id>my-cred-id</id>
163
- <description>My credential</description>
164
- <username>admin</username>
165
- <password>new-secret</password>
166
- </com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
167
- """
168
- client = get_client(ctx)
169
- formatter = get_formatter(ctx)
170
- try:
171
- xml_data = config_file.read_text(encoding="utf-8")
172
- update_credential(client, cred_id=cred_id, xml_data=xml_data, store=store)
173
- formatter.print_success(f"Credential '{cred_id}' updated in store '{store}'.")
174
- except Exception as exc:
175
- formatter.print_error(str(exc))
176
- sys.exit(1)
177
-
178
-
179
- @credential_group.command("delete")
180
- @click.argument("cred_id")
181
- @click.option(
182
- "--store", "-s", default="system", help="Credential store ID (default: system)."
183
- )
184
- @click.option(
185
- "--force", "-f", is_flag=True, default=False, help="Skip confirmation prompt."
186
- )
187
- @click.pass_context
188
- def cmd_delete(
189
- ctx: click.Context, cred_id: str, store: str, force: bool
190
- ) -> None:
191
- """Delete a credential."""
192
- if not force:
193
- click.confirm(
194
- f"Delete credential '{cred_id}' from store '{store}'?",
195
- abort=True,
196
- )
197
-
198
- client = get_client(ctx)
199
- formatter = get_formatter(ctx)
200
- try:
201
- delete_credential(client, cred_id=cred_id, store=store)
202
- formatter.print_success(f"Credential '{cred_id}' deleted.")
203
- except Exception as exc:
204
- formatter.print_error(str(exc))
205
- sys.exit(1)
206
-
207
-
208
- def register(parent_group: click.Group) -> None:
209
- """Register the credential subgroup under the parent Click group."""
210
- parent_group.add_command(credential_group)