tangle-cli 0.0.1a1__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 (48) hide show
  1. tangle_cli/__init__.py +19 -0
  2. tangle_cli/api_cli.py +787 -0
  3. tangle_cli/api_schema.py +633 -0
  4. tangle_cli/api_transport.py +461 -0
  5. tangle_cli/args_container.py +244 -0
  6. tangle_cli/artifacts.py +293 -0
  7. tangle_cli/artifacts_cli.py +108 -0
  8. tangle_cli/cli.py +57 -0
  9. tangle_cli/cli_helpers.py +116 -0
  10. tangle_cli/cli_options.py +52 -0
  11. tangle_cli/client.py +677 -0
  12. tangle_cli/component_from_func.py +1856 -0
  13. tangle_cli/component_generator.py +298 -0
  14. tangle_cli/component_inspector.py +494 -0
  15. tangle_cli/component_publisher.py +921 -0
  16. tangle_cli/components_cli.py +269 -0
  17. tangle_cli/dynamic_discovery_client.py +296 -0
  18. tangle_cli/generated_model_extensions.py +405 -0
  19. tangle_cli/generated_runtime.py +43 -0
  20. tangle_cli/handler.py +96 -0
  21. tangle_cli/hydration_trust.py +222 -0
  22. tangle_cli/logger.py +166 -0
  23. tangle_cli/models.py +407 -0
  24. tangle_cli/module_bundler.py +662 -0
  25. tangle_cli/openapi/__init__.py +0 -0
  26. tangle_cli/openapi/codegen.py +1090 -0
  27. tangle_cli/openapi/parser.py +77 -0
  28. tangle_cli/pipeline_dehydrator.py +720 -0
  29. tangle_cli/pipeline_hydrator.py +1785 -0
  30. tangle_cli/pipeline_run_annotations.py +41 -0
  31. tangle_cli/pipeline_run_details.py +203 -0
  32. tangle_cli/pipeline_run_manager.py +1994 -0
  33. tangle_cli/pipeline_run_search.py +712 -0
  34. tangle_cli/pipeline_runner.py +620 -0
  35. tangle_cli/pipeline_runs_cli.py +584 -0
  36. tangle_cli/pipelines.py +581 -0
  37. tangle_cli/pipelines_cli.py +271 -0
  38. tangle_cli/published_components_cli.py +373 -0
  39. tangle_cli/py.typed +0 -0
  40. tangle_cli/quickstart.py +110 -0
  41. tangle_cli/secrets.py +156 -0
  42. tangle_cli/secrets_cli.py +269 -0
  43. tangle_cli/utils.py +942 -0
  44. tangle_cli/version_manager.py +470 -0
  45. tangle_cli-0.0.1a1.dist-info/METADATA +561 -0
  46. tangle_cli-0.0.1a1.dist-info/RECORD +48 -0
  47. tangle_cli-0.0.1a1.dist-info/WHEEL +4 -0
  48. tangle_cli-0.0.1a1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,271 @@
1
+ """`tangle sdk pipelines` local pipeline commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pathlib
6
+ from typing import Annotated, Any
7
+
8
+ from cyclopts import App, Parameter
9
+
10
+ from .cli_helpers import LazyTangleApiClient, load_config_or_exit, optional_path
11
+ from .cli_options import (
12
+ AuthHeaderOption,
13
+ BaseUrlOption,
14
+ ConfigOption,
15
+ HeaderOption,
16
+ LogTypeOption,
17
+ TokenOption,
18
+ )
19
+ from .logger import logger_for_log_type
20
+ from .pipelines import (
21
+ PipelineValidationError,
22
+ generate_mermaid,
23
+ hydrate_pipeline_file,
24
+ layout_pipeline_file,
25
+ validate_pipeline_file,
26
+ )
27
+
28
+ app = App(
29
+ name="pipelines",
30
+ help="Validate and visualize local Tangle pipeline specs.",
31
+ )
32
+
33
+
34
+ @app.command(name="validate")
35
+ def pipelines_validate(
36
+ pipeline_path: pathlib.Path,
37
+ *,
38
+ log_type: LogTypeOption = "console",
39
+ ) -> None:
40
+ """Validate a local pipeline YAML file."""
41
+
42
+ logger, finalize_logs = logger_for_log_type(log_type)
43
+ try:
44
+ try:
45
+ validate_pipeline_file(pipeline_path)
46
+ except PipelineValidationError as exc:
47
+ raise SystemExit(str(exc)) from exc
48
+ print(f"Valid pipeline: {pipeline_path}")
49
+ finally:
50
+ finalize_logs()
51
+
52
+
53
+ @app.command(name="diagram")
54
+ def pipelines_diagram(
55
+ pipeline_path: pathlib.Path,
56
+ *,
57
+ log_type: LogTypeOption = "console",
58
+ ) -> None:
59
+ """Print a Mermaid dependency diagram for a local pipeline YAML file."""
60
+
61
+ logger, finalize_logs = logger_for_log_type(log_type)
62
+ try:
63
+ try:
64
+ pipeline = validate_pipeline_file(pipeline_path)
65
+ except PipelineValidationError as exc:
66
+ raise SystemExit(str(exc)) from exc
67
+ print(generate_mermaid(pipeline))
68
+ finally:
69
+ finalize_logs()
70
+
71
+
72
+ def _optional_str(value: object) -> str | None:
73
+ return value if isinstance(value, str) else None
74
+
75
+
76
+ def _header_entries(cli_header: list[str] | None, config: dict[str, object]) -> list[str] | None:
77
+ if cli_header is not None:
78
+ return cli_header
79
+ config_header = config.get("header")
80
+ if isinstance(config_header, list):
81
+ return [str(entry) for entry in config_header]
82
+ if isinstance(config_header, str):
83
+ return [config_header]
84
+ return None
85
+
86
+
87
+ def _trusted_hydration_config(config: dict[str, object]) -> dict[str, Any]:
88
+ trusted = config.get("trusted_hydration", {})
89
+ return trusted if isinstance(trusted, dict) else {}
90
+
91
+
92
+ def _trusted_sources(
93
+ cli_sources: list[str] | None,
94
+ config: dict[str, object],
95
+ ) -> list[str]:
96
+ sources: list[str] = []
97
+ config_sources = _trusted_hydration_config(config).get("trusted_python_sources", [])
98
+ if isinstance(config_sources, str):
99
+ sources.append(config_sources)
100
+ elif isinstance(config_sources, list):
101
+ sources.extend(str(source) for source in config_sources)
102
+ if cli_sources:
103
+ sources.extend(cli_sources)
104
+ return [source for source in sources if source]
105
+
106
+
107
+ def _allow_all_hydration(
108
+ trusted_hydration: bool | None,
109
+ config: dict[str, object],
110
+ ) -> bool:
111
+ return bool(trusted_hydration or _trusted_hydration_config(config).get("allow_all", False))
112
+
113
+
114
+ def _parse_vars(values: list[str] | dict[str, object] | None) -> dict[str, str]:
115
+ parsed: dict[str, str] = {}
116
+ if isinstance(values, dict):
117
+ return {str(key): str(value) for key, value in values.items()}
118
+ for value in values or []:
119
+ if "=" not in value:
120
+ raise SystemExit("--var entries must use KEY=VALUE syntax")
121
+ key, parsed_value = value.split("=", 1)
122
+ if not key:
123
+ raise SystemExit("--var entries must use KEY=VALUE syntax")
124
+ parsed[key] = parsed_value
125
+ return parsed
126
+
127
+
128
+ @app.command(name="hydrate")
129
+ def pipelines_hydrate(
130
+ pipeline_path: pathlib.Path,
131
+ *,
132
+ output: Annotated[
133
+ pathlib.Path | None,
134
+ Parameter(
135
+ name="--output",
136
+ alias="-o",
137
+ help="Output path. Defaults to printing hydrated YAML to stdout.",
138
+ ),
139
+ ] = None,
140
+ var: Annotated[
141
+ list[str] | None,
142
+ Parameter(
143
+ name="--var",
144
+ help="Template override as KEY=VALUE. Repeat for multiple overrides.",
145
+ negative_iterable=(),
146
+ ),
147
+ ] = None,
148
+ base_url: BaseUrlOption = None,
149
+ token: TokenOption = None,
150
+ auth_header: AuthHeaderOption = None,
151
+ header: HeaderOption = None,
152
+ trusted_source: Annotated[
153
+ list[str] | None,
154
+ Parameter(
155
+ name="--trusted-source",
156
+ help="Trusted local_from_python source root or glob. Repeat for multiple.",
157
+ negative_iterable=(),
158
+ ),
159
+ ] = None,
160
+ trusted_hydration: Annotated[
161
+ bool | None,
162
+ Parameter(
163
+ name="--trusted-hydration",
164
+ help="Allow all local_from_python execution during hydration for trusted inputs.",
165
+ ),
166
+ ] = None,
167
+ config: ConfigOption = None,
168
+ log_type: LogTypeOption = "console",
169
+ ) -> None:
170
+ """Hydrate a local pipeline YAML file."""
171
+
172
+ config_values = load_config_or_exit(config)
173
+ config_base_url = _optional_str(config_values.get("base_url"))
174
+ resolved_base_url = base_url if base_url is not None else config_base_url
175
+ include_env_credentials = not (base_url is None and config_base_url is not None)
176
+ resolved_var = var if var is not None else config_values.get("var")
177
+ resolved_token = token
178
+ if resolved_token is None:
179
+ resolved_token = _optional_str(config_values.get("token"))
180
+
181
+ logger, finalize_logs = logger_for_log_type(log_type)
182
+ try:
183
+ result = hydrate_pipeline_file(
184
+ pipeline_path,
185
+ output=output or optional_path(config_values.get("output")),
186
+ overrides=_parse_vars(resolved_var),
187
+ base_url=resolved_base_url,
188
+ token=resolved_token,
189
+ auth_header=(
190
+ auth_header
191
+ if auth_header is not None
192
+ else _optional_str(config_values.get("auth_header"))
193
+ ),
194
+ header=_header_entries(header, config_values),
195
+ include_env_credentials=include_env_credentials,
196
+ logger=logger,
197
+ trusted_python_sources=_trusted_sources(trusted_source, config_values),
198
+ allow_all_hydration=_allow_all_hydration(trusted_hydration, config_values),
199
+ client=LazyTangleApiClient(
200
+ command_name="pipeline hydration with API-backed component references",
201
+ base_url=resolved_base_url,
202
+ token=resolved_token,
203
+ auth_header=(
204
+ auth_header
205
+ if auth_header is not None
206
+ else _optional_str(config_values.get("auth_header"))
207
+ ),
208
+ header=_header_entries(header, config_values),
209
+ include_env_credentials=include_env_credentials,
210
+ ),
211
+ )
212
+ except PipelineValidationError as exc:
213
+ raise SystemExit(str(exc)) from exc
214
+ finally:
215
+ finalize_logs()
216
+
217
+ if result.output_path is None:
218
+ print(result.content, end="" if result.content.endswith("\n") else "\n")
219
+ else:
220
+ print(
221
+ f"Hydrated {pipeline_path} -> {result.output_path} "
222
+ f"({result.resolved_components} component(s) resolved)."
223
+ )
224
+
225
+
226
+ @app.command(name="layout")
227
+ def pipelines_layout(
228
+ pipeline_path: pathlib.Path,
229
+ *,
230
+ output: Annotated[
231
+ pathlib.Path | None,
232
+ Parameter(
233
+ name="--output",
234
+ alias="-o",
235
+ help="Output path. Defaults to overwriting the input file.",
236
+ ),
237
+ ] = None,
238
+ recursive: Annotated[
239
+ bool | None,
240
+ Parameter(help="Also layout nested graph component specs."),
241
+ ] = None,
242
+ x_spacing: Annotated[
243
+ int,
244
+ Parameter(help="Horizontal spacing between dependency layers."),
245
+ ] = 300,
246
+ y_spacing: Annotated[
247
+ int,
248
+ Parameter(help="Vertical spacing between tasks in the same layer."),
249
+ ] = 120,
250
+ log_type: LogTypeOption = "console",
251
+ ) -> None:
252
+ """Add or update editor.position annotations in a local pipeline YAML file."""
253
+
254
+ logger, finalize_logs = logger_for_log_type(log_type)
255
+ try:
256
+ result = layout_pipeline_file(
257
+ pipeline_path,
258
+ output=output,
259
+ recursive=bool(recursive),
260
+ x_spacing=x_spacing,
261
+ y_spacing=y_spacing,
262
+ )
263
+ except PipelineValidationError as exc:
264
+ raise SystemExit(str(exc)) from exc
265
+ finally:
266
+ finalize_logs()
267
+ print(
268
+ f"Positioned {result.tasks_positioned} task(s) across "
269
+ f"{result.graphs_positioned} graph(s)."
270
+ )
271
+ print(f"Wrote layout to: {result.output_path}")
@@ -0,0 +1,373 @@
1
+ """`tangle sdk published-components` command implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pathlib
6
+ from typing import Annotated, Any
7
+
8
+ from cyclopts import App, Parameter
9
+
10
+ from .cli_helpers import (
11
+ LazyTangleApiClient,
12
+ api_arg_specs,
13
+ include_env_credentials_for_args,
14
+ load_args_or_exit,
15
+ optional_path,
16
+ print_json,
17
+ )
18
+ from .cli_options import (
19
+ AuthHeaderOption,
20
+ BaseUrlOption,
21
+ ConfigOption,
22
+ HeaderOption,
23
+ LogTypeOption,
24
+ TokenOption,
25
+ )
26
+ from .component_publisher import ComponentPublisher, deprecate_component
27
+ from .logger import logger_for_log_type
28
+
29
+
30
+ def _client_from_options(
31
+ *,
32
+ base_url: str | None = None,
33
+ token: str | None = None,
34
+ auth_header: str | None = None,
35
+ header: list[str] | str | None = None,
36
+ include_env_credentials: bool = True,
37
+ command_name: str = "published-component commands",
38
+ ) -> LazyTangleApiClient:
39
+ """Create a lazy static client proxy for published-component commands.
40
+
41
+ Kept as a module-level seam so downstream wrappers/tests can monkeypatch
42
+ client construction without replacing the shared LazyTangleApiClient class.
43
+ """
44
+
45
+ return LazyTangleApiClient(
46
+ base_url=base_url,
47
+ token=token,
48
+ auth_header=auth_header,
49
+ header=header,
50
+ include_env_credentials=include_env_credentials,
51
+ command_name=command_name,
52
+ )
53
+
54
+
55
+ app = App(
56
+ name="published-components",
57
+ help="Inspect and search published Tangle components from the registry.",
58
+ )
59
+
60
+
61
+ @app.command(name="search")
62
+ def published_components_search(
63
+ name: str | None = None,
64
+ *,
65
+ include_deprecated: bool | None = None,
66
+ published_by: str | None = None,
67
+ digest: str | None = None,
68
+ base_url: BaseUrlOption = None,
69
+ token: TokenOption = None,
70
+ auth_header: AuthHeaderOption = None,
71
+ header: HeaderOption = None,
72
+ config: ConfigOption = None,
73
+ log_type: LogTypeOption = "console",
74
+ ) -> None:
75
+ """Search published component metadata."""
76
+
77
+ for args in load_args_or_exit(
78
+ config,
79
+ name=(name, None),
80
+ include_deprecated=(include_deprecated, None),
81
+ published_by=(published_by, None),
82
+ digest=(digest, None),
83
+ log_type=(log_type, "console"),
84
+ **api_arg_specs(
85
+ base_url=base_url,
86
+ token=token,
87
+ auth_header=auth_header,
88
+ header=header,
89
+ ),
90
+ ):
91
+ logger, finalize_logs = logger_for_log_type(args.log_type)
92
+ try:
93
+ client = _client_from_options(
94
+ base_url=args.base_url,
95
+ token=args.token,
96
+ auth_header=args.auth_header,
97
+ header=args.header,
98
+ include_env_credentials=include_env_credentials_for_args(args, base_url),
99
+ command_name="published-component commands",
100
+ )
101
+ if require_available := getattr(client, "require_available", None):
102
+ require_available()
103
+
104
+ from .component_inspector import ComponentInspector
105
+
106
+ print_json(
107
+ ComponentInspector(client=client, logger=logger, base_url=args.base_url).search_components(
108
+ name=args.name,
109
+ include_deprecated=bool(args.include_deprecated),
110
+ published_by=args.published_by,
111
+ digest=args.digest,
112
+ )
113
+ )
114
+ finally:
115
+ finalize_logs()
116
+
117
+
118
+ @app.command(name="inspect")
119
+ def published_components_inspect(
120
+ name: str | None = None,
121
+ *,
122
+ digest: str | None = None,
123
+ all_versions: bool | None = None,
124
+ include_deprecated: bool | None = None,
125
+ follow_deprecated: bool | None = None,
126
+ full_spec: bool | None = None,
127
+ published_by: str | None = None,
128
+ base_url: BaseUrlOption = None,
129
+ token: TokenOption = None,
130
+ auth_header: AuthHeaderOption = None,
131
+ header: HeaderOption = None,
132
+ config: ConfigOption = None,
133
+ log_type: LogTypeOption = "console",
134
+ ) -> None:
135
+ """Inspect a published component by exact name or digest."""
136
+
137
+ for args in load_args_or_exit(
138
+ config,
139
+ name=(name, None),
140
+ digest=(digest, None),
141
+ all_versions=(all_versions, None),
142
+ include_deprecated=(include_deprecated, None),
143
+ follow_deprecated=(follow_deprecated, None),
144
+ full_spec=(full_spec, None),
145
+ published_by=(published_by, None),
146
+ log_type=(log_type, "console"),
147
+ **api_arg_specs(
148
+ base_url=base_url,
149
+ token=token,
150
+ auth_header=auth_header,
151
+ header=header,
152
+ ),
153
+ ):
154
+ logger, finalize_logs = logger_for_log_type(args.log_type)
155
+ try:
156
+ if bool(args.name) == bool(args.digest):
157
+ raise SystemExit("Provide exactly one of NAME or --digest DIGEST")
158
+
159
+ client = _client_from_options(
160
+ base_url=args.base_url,
161
+ token=args.token,
162
+ auth_header=args.auth_header,
163
+ header=args.header,
164
+ include_env_credentials=include_env_credentials_for_args(args, base_url),
165
+ command_name="published-component commands",
166
+ )
167
+ if require_available := getattr(client, "require_available", None):
168
+ require_available()
169
+ from .component_inspector import ComponentInspector
170
+
171
+ inspector = ComponentInspector(client=client, logger=logger, base_url=args.base_url)
172
+ if args.digest:
173
+ result = inspector.inspect_by_digest(
174
+ args.digest,
175
+ full_spec=bool(args.full_spec),
176
+ follow_deprecated=bool(args.follow_deprecated),
177
+ )
178
+ else:
179
+ result = inspector.inspect_by_name(
180
+ args.name or "",
181
+ include_all_versions=bool(args.all_versions),
182
+ include_deprecated=bool(args.include_deprecated),
183
+ full_spec=bool(args.full_spec),
184
+ published_by=args.published_by,
185
+ )
186
+ print_json(result)
187
+ finally:
188
+ finalize_logs()
189
+
190
+
191
+ @app.command(name="library")
192
+ def published_components_library(
193
+ *,
194
+ base_url: BaseUrlOption = None,
195
+ token: TokenOption = None,
196
+ auth_header: AuthHeaderOption = None,
197
+ header: HeaderOption = None,
198
+ config: ConfigOption = None,
199
+ log_type: LogTypeOption = "console",
200
+ ) -> None:
201
+ """Print the curated standard component library."""
202
+
203
+ for args in load_args_or_exit(
204
+ config,
205
+ log_type=(log_type, "console"),
206
+ **api_arg_specs(
207
+ base_url=base_url,
208
+ token=token,
209
+ auth_header=auth_header,
210
+ header=header,
211
+ ),
212
+ ):
213
+ logger, finalize_logs = logger_for_log_type(args.log_type)
214
+ try:
215
+ client = _client_from_options(
216
+ base_url=args.base_url,
217
+ token=args.token,
218
+ auth_header=args.auth_header,
219
+ header=args.header,
220
+ include_env_credentials=include_env_credentials_for_args(args, base_url),
221
+ command_name="published-component commands",
222
+ )
223
+ if require_available := getattr(client, "require_available", None):
224
+ require_available()
225
+
226
+ from .component_inspector import ComponentInspector
227
+
228
+ print_json(ComponentInspector(client=client, logger=logger, base_url=args.base_url).get_standard_library())
229
+ finally:
230
+ finalize_logs()
231
+
232
+
233
+ @app.command(name="publish")
234
+ def published_components_publish(
235
+ component_path: pathlib.Path | None = None,
236
+ *,
237
+ image: str | None = None,
238
+ name: str | None = None,
239
+ description: str | None = None,
240
+ annotations: Annotated[
241
+ str | None,
242
+ Parameter(help="Custom annotations as a JSON object."),
243
+ ] = None,
244
+ dry_run: bool | None = None,
245
+ git_remote_sha: str | None = None,
246
+ git_remote_branch: str | None = None,
247
+ git_remote_url: str | None = None,
248
+ git_root: pathlib.Path | None = None,
249
+ published_by: str | None = None,
250
+ base_url: BaseUrlOption = None,
251
+ token: TokenOption = None,
252
+ auth_header: AuthHeaderOption = None,
253
+ header: HeaderOption = None,
254
+ config: ConfigOption = None,
255
+ log_type: LogTypeOption = "console",
256
+ ) -> None:
257
+ """Publish one component YAML file to a Tangle component registry."""
258
+
259
+ all_args = load_args_or_exit(
260
+ config,
261
+ component_path=("component_path", component_path, None, False, True, optional_path),
262
+ image=(image, None),
263
+ name=(name, None),
264
+ description=(description, None),
265
+ annotations=("annotations", annotations, None, True),
266
+ dry_run=(dry_run, None),
267
+ git_remote_sha=(git_remote_sha, None),
268
+ git_remote_branch=(git_remote_branch, None),
269
+ git_remote_url=(git_remote_url, None),
270
+ git_root=(git_root, None, optional_path),
271
+ published_by=(published_by, None),
272
+ log_type=(log_type, "console"),
273
+ **api_arg_specs(
274
+ base_url=base_url,
275
+ token=token,
276
+ auth_header=auth_header,
277
+ header=header,
278
+ ),
279
+ )
280
+ results: list[dict[str, Any]] = []
281
+ for args in all_args:
282
+ logger, finalize_logs = logger_for_log_type(args.log_type)
283
+ try:
284
+ client = None if args.dry_run else _client_from_options(
285
+ base_url=args.base_url,
286
+ token=args.token,
287
+ auth_header=args.auth_header,
288
+ header=args.header,
289
+ include_env_credentials=include_env_credentials_for_args(args, base_url),
290
+ command_name="published-component commands",
291
+ )
292
+ publisher = ComponentPublisher(
293
+ dry_run=bool(args.dry_run),
294
+ git_remote_sha=args.git_remote_sha,
295
+ git_remote_branch=args.git_remote_branch,
296
+ git_remote_url=args.git_remote_url,
297
+ git_root=args.git_root,
298
+ published_by=args.published_by,
299
+ client=client,
300
+ logger=logger,
301
+ )
302
+ result = publisher.publish_component(
303
+ args.component_path,
304
+ image=args.image,
305
+ name=args.name,
306
+ description=args.description,
307
+ annotations=args.annotations,
308
+ )
309
+ result_dict = result.to_dict() if hasattr(result, "to_dict") else dict(result)
310
+ results.append({"component_path": str(args.component_path), **result_dict})
311
+ finally:
312
+ finalize_logs()
313
+
314
+ error_count = sum(1 for result in results if result.get("status") in {"error", "failed"})
315
+ summary = {
316
+ "status": "failed" if error_count else "success",
317
+ "components_count": len(results),
318
+ "error_count": error_count,
319
+ "results": results,
320
+ }
321
+ print_json(summary)
322
+ if error_count:
323
+ raise SystemExit(1)
324
+
325
+
326
+ @app.command(name="deprecate")
327
+ def published_components_deprecate(
328
+ digest: str | None = None,
329
+ *,
330
+ superseded_by: str | None = None,
331
+ base_url: BaseUrlOption = None,
332
+ token: TokenOption = None,
333
+ auth_header: AuthHeaderOption = None,
334
+ header: HeaderOption = None,
335
+ config: ConfigOption = None,
336
+ log_type: LogTypeOption = "console",
337
+ ) -> None:
338
+ """Deprecate a published component by digest."""
339
+
340
+ for args in load_args_or_exit(
341
+ config,
342
+ digest=("digest", digest, None, False, True),
343
+ superseded_by=(superseded_by, None),
344
+ log_type=(log_type, "console"),
345
+ **api_arg_specs(
346
+ base_url=base_url,
347
+ token=token,
348
+ auth_header=auth_header,
349
+ header=header,
350
+ ),
351
+ ):
352
+ logger, finalize_logs = logger_for_log_type(args.log_type)
353
+ try:
354
+ client = _client_from_options(
355
+ base_url=args.base_url,
356
+ token=args.token,
357
+ auth_header=args.auth_header,
358
+ header=args.header,
359
+ include_env_credentials=include_env_credentials_for_args(args, base_url),
360
+ command_name="published-component commands",
361
+ )
362
+ result = deprecate_component(
363
+ client,
364
+ args.digest,
365
+ superseded_by=args.superseded_by,
366
+ logger=logger,
367
+ )
368
+ result_dict = result.to_dict() if hasattr(result, "to_dict") else result
369
+ print_json(result_dict)
370
+ if isinstance(result_dict, dict) and not result_dict.get("success", result_dict.get("status") != "failed"):
371
+ raise SystemExit(1)
372
+ finally:
373
+ finalize_logs()
tangle_cli/py.typed ADDED
File without changes