ocmo-cli 0.8.19__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.
- LICENSE +201 -0
- NOTICE +16 -0
- ocmo_cli/__init__.py +3 -0
- ocmo_cli/__main__.py +4 -0
- ocmo_cli/_address.py +182 -0
- ocmo_cli/_audit_item.py +33 -0
- ocmo_cli/_auth_resolve.py +66 -0
- ocmo_cli/_body_payload.py +203 -0
- ocmo_cli/_click_groups.py +227 -0
- ocmo_cli/_client.py +351 -0
- ocmo_cli/_client_commands.py +35 -0
- ocmo_cli/_command_registry.py +29 -0
- ocmo_cli/_commands_map.py +65 -0
- ocmo_cli/_config.py +183 -0
- ocmo_cli/_config_content.py +43 -0
- ocmo_cli/_delete_item_output.py +129 -0
- ocmo_cli/_diff_input.py +144 -0
- ocmo_cli/_dry_run.py +292 -0
- ocmo_cli/_errors.py +151 -0
- ocmo_cli/_exit.py +13 -0
- ocmo_cli/_export_metadata.py +47 -0
- ocmo_cli/_generated_registry.py +45 -0
- ocmo_cli/_get_cast_output.py +52 -0
- ocmo_cli/_get_item_list.py +23 -0
- ocmo_cli/_globalpermission_output.py +98 -0
- ocmo_cli/_import_metadata.py +140 -0
- ocmo_cli/_import_plan.py +221 -0
- ocmo_cli/_item_output.py +223 -0
- ocmo_cli/_lazy_mixin.py +29 -0
- ocmo_cli/_ls_wide.py +286 -0
- ocmo_cli/_mutating.py +39 -0
- ocmo_cli/_notice.py +17 -0
- ocmo_cli/_options.py +77 -0
- ocmo_cli/_output.py +424 -0
- ocmo_cli/_output_manifest.py +332 -0
- ocmo_cli/_resolve_draft.py +50 -0
- ocmo_cli/_resolve_options.py +143 -0
- ocmo_cli/_resolve_output.py +228 -0
- ocmo_cli/_resolve_parameters_output.py +72 -0
- ocmo_cli/_resolve_series.py +89 -0
- ocmo_cli/_resolve_series_chart.py +409 -0
- ocmo_cli/_resolve_write.py +185 -0
- ocmo_cli/_resolver_output.py +34 -0
- ocmo_cli/_resource_aliases.py +22 -0
- ocmo_cli/_sdk_dispatch.py +310 -0
- ocmo_cli/_typing.py +27 -0
- ocmo_cli/_version_output.py +39 -0
- ocmo_cli/_whoami_output.py +43 -0
- ocmo_cli/commands/__init__.py +1 -0
- ocmo_cli/commands/api.py +125 -0
- ocmo_cli/commands/api_health.py +12 -0
- ocmo_cli/commands/apply.py +153 -0
- ocmo_cli/commands/auth.py +786 -0
- ocmo_cli/commands/can_i.py +51 -0
- ocmo_cli/commands/completion.py +59 -0
- ocmo_cli/commands/config_cmd.py +235 -0
- ocmo_cli/commands/describe.py +109 -0
- ocmo_cli/commands/diff.py +86 -0
- ocmo_cli/commands/edit.py +200 -0
- ocmo_cli/commands/export.py +242 -0
- ocmo_cli/commands/generated.py +907 -0
- ocmo_cli/commands/gp_move.py +108 -0
- ocmo_cli/commands/import_.py +595 -0
- ocmo_cli/commands/item_relocate.py +187 -0
- ocmo_cli/commands/ls.py +481 -0
- ocmo_cli/commands/resolve.py +658 -0
- ocmo_cli/commands/resolve_draft.py +102 -0
- ocmo_cli/commands/resolve_parameters.py +65 -0
- ocmo_cli/commands/resolve_series.py +109 -0
- ocmo_cli/commands/schema.py +112 -0
- ocmo_cli/commands/timeline.py +135 -0
- ocmo_cli/commands/version_cmd.py +102 -0
- ocmo_cli/commands/whoami.py +12 -0
- ocmo_cli/main.py +241 -0
- ocmo_cli/output_formats.yaml +295 -0
- ocmo_cli-0.8.19.dist-info/METADATA +169 -0
- ocmo_cli-0.8.19.dist-info/RECORD +81 -0
- ocmo_cli-0.8.19.dist-info/WHEEL +4 -0
- ocmo_cli-0.8.19.dist-info/entry_points.txt +2 -0
- ocmo_cli-0.8.19.dist-info/licenses/LICENSE +201 -0
- ocmo_cli-0.8.19.dist-info/licenses/NOTICE +16 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""ocmo apply — upload a local file as a named OCMO item.
|
|
2
|
+
|
|
3
|
+
Replaces the manifest-based approach with a direct file-to-tree-path mapping:
|
|
4
|
+
|
|
5
|
+
ocmo -n prod apply -f nginx.conf app/nginx/site -t template
|
|
6
|
+
ocmo -n prod apply -f config.yaml app/web/main # -t inferred as config
|
|
7
|
+
ocmo -n prod apply -f - app/web/inline -t config # read from stdin
|
|
8
|
+
|
|
9
|
+
The type is inferred from the file extension when -t is omitted:
|
|
10
|
+
*.yaml / *.yml / *.json → config
|
|
11
|
+
everything else → template
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
import click
|
|
21
|
+
|
|
22
|
+
from .._address import parse_address_or_exit
|
|
23
|
+
from .._client import OcmoCtx
|
|
24
|
+
from .._config_content import prepare_config_apply_content
|
|
25
|
+
from .._errors import sdk_command
|
|
26
|
+
from .._exit import USAGE_ERROR
|
|
27
|
+
from .._options import dry_run_option, namespace_option
|
|
28
|
+
from .._output import err, status
|
|
29
|
+
from .._resolver_output import print_resolver_token
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from ocmo import NamespaceView
|
|
33
|
+
|
|
34
|
+
_VALID_TYPES = ("config", "template", "secret", "resolver")
|
|
35
|
+
_VALID_TYPES_DISPLAY = " | ".join(_VALID_TYPES)
|
|
36
|
+
|
|
37
|
+
_HELP = """\
|
|
38
|
+
Create or update a tree item from a local file.
|
|
39
|
+
|
|
40
|
+
When -t is omitted, type is inferred from the file extension:
|
|
41
|
+
*.yaml / *.yml / *.json become config; all other extensions become template.
|
|
42
|
+
|
|
43
|
+
\b
|
|
44
|
+
Examples:
|
|
45
|
+
ocmo -n prod apply -f config.yaml app/web/main
|
|
46
|
+
ocmo -n prod apply -f nginx.conf app/nginx/site -t template
|
|
47
|
+
cat resolver.yaml | ocmo -n prod apply -f - app/resolvers/default -t resolver
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@click.command("apply", help=_HELP)
|
|
52
|
+
@click.argument("tree_path")
|
|
53
|
+
@click.option(
|
|
54
|
+
"-f",
|
|
55
|
+
"--file",
|
|
56
|
+
"file_path",
|
|
57
|
+
required=True,
|
|
58
|
+
metavar="FILE|-",
|
|
59
|
+
help="Source file to upload. Use '-' to read from stdin.",
|
|
60
|
+
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"-t",
|
|
63
|
+
"--type",
|
|
64
|
+
"item_type",
|
|
65
|
+
type=click.Choice(list(_VALID_TYPES)),
|
|
66
|
+
default=None,
|
|
67
|
+
help=f"Item type: {_VALID_TYPES_DISPLAY}. Inferred from extension when omitted.",
|
|
68
|
+
)
|
|
69
|
+
@namespace_option()
|
|
70
|
+
@dry_run_option(help="Print plan without uploading.")
|
|
71
|
+
@click.pass_obj
|
|
72
|
+
@sdk_command
|
|
73
|
+
def apply_cmd(
|
|
74
|
+
ctx: OcmoCtx,
|
|
75
|
+
tree_path: str,
|
|
76
|
+
file_path: str,
|
|
77
|
+
item_type: str | None,
|
|
78
|
+
namespace: str | None,
|
|
79
|
+
dry_run: bool,
|
|
80
|
+
) -> None:
|
|
81
|
+
path, _version = parse_address_or_exit(tree_path)
|
|
82
|
+
|
|
83
|
+
# Read content
|
|
84
|
+
if file_path == "-":
|
|
85
|
+
content = sys.stdin.read()
|
|
86
|
+
source_name = "<stdin>"
|
|
87
|
+
else:
|
|
88
|
+
with open(file_path) as f:
|
|
89
|
+
content = f.read()
|
|
90
|
+
source_name = file_path
|
|
91
|
+
|
|
92
|
+
# Infer type from extension when not given
|
|
93
|
+
kind = item_type or _infer_type(file_path)
|
|
94
|
+
if kind == "config":
|
|
95
|
+
content = prepare_config_apply_content(content, source_name=source_name)
|
|
96
|
+
|
|
97
|
+
if dry_run or ctx.dry_run:
|
|
98
|
+
from .._dry_run import emit_dry_run_plan, format_apply_dry_run # deferred
|
|
99
|
+
|
|
100
|
+
ns = namespace or ctx.namespace
|
|
101
|
+
emit_dry_run_plan(
|
|
102
|
+
format_apply_dry_run(
|
|
103
|
+
kind=kind,
|
|
104
|
+
path=path,
|
|
105
|
+
source_name=source_name,
|
|
106
|
+
namespace=ns if isinstance(ns, str) else None,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
view = ctx.namespace_view(namespace)
|
|
112
|
+
|
|
113
|
+
created, result = _upload(view, path, content, kind)
|
|
114
|
+
verb = "Created" if created else "Updated"
|
|
115
|
+
status(f"{verb} {kind} {path!r}.")
|
|
116
|
+
if kind == "resolver" and created:
|
|
117
|
+
print_resolver_token(result)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _infer_type(file_path: str) -> str:
|
|
121
|
+
if file_path == "-":
|
|
122
|
+
return "config"
|
|
123
|
+
import os
|
|
124
|
+
|
|
125
|
+
ext = os.path.splitext(file_path)[1].lower()
|
|
126
|
+
return "config" if ext in (".yaml", ".yml", ".json") else "template"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _upload(view: NamespaceView, path: str, content: str, kind: str) -> tuple[bool, Any]:
|
|
130
|
+
"""Upload content; returns (created, api_result)."""
|
|
131
|
+
_update, _create = _get_methods(view, kind)
|
|
132
|
+
try:
|
|
133
|
+
result = _update(path=path, content=content)
|
|
134
|
+
return False, result
|
|
135
|
+
except Exception:
|
|
136
|
+
result = _create(path=path, content=content)
|
|
137
|
+
return True, result
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _get_methods(
|
|
141
|
+
view: NamespaceView,
|
|
142
|
+
kind: str,
|
|
143
|
+
) -> tuple[Callable[..., Any], Callable[..., Any]]:
|
|
144
|
+
methods = {
|
|
145
|
+
"config": (view.update_config, view.create_config),
|
|
146
|
+
"template": (view.update_template, view.create_template),
|
|
147
|
+
"secret": (view.update_secret, view.create_secret),
|
|
148
|
+
"resolver": (view.update_resolver, view.create_resolver),
|
|
149
|
+
}
|
|
150
|
+
if kind not in methods:
|
|
151
|
+
err(f"Unknown type {kind!r}. Valid: {_VALID_TYPES_DISPLAY}")
|
|
152
|
+
raise SystemExit(USAGE_ERROR)
|
|
153
|
+
return methods[kind]
|