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
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")
|
jcli/plugins/credential.py
DELETED
|
@@ -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)
|