ominfra 0.0.0.dev182__py3-none-any.whl → 0.0.0.dev184__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
ominfra/configs.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  # @omlish-lite
3
+ import io
3
4
  import json
4
5
  import os.path
5
6
  import typing as ta
@@ -14,6 +15,11 @@ T = ta.TypeVar('T')
14
15
 
15
16
  ConfigMapping = ta.Mapping[str, ta.Any]
16
17
 
18
+ IniConfigSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
19
+
20
+
21
+ ##
22
+
17
23
 
18
24
  def parse_config_file(
19
25
  name: str,
@@ -58,6 +64,9 @@ def read_config_file(
58
64
  return msh.unmarshal_obj(config_dct, cls)
59
65
 
60
66
 
67
+ ##
68
+
69
+
61
70
  def build_config_named_children(
62
71
  o: ta.Union[
63
72
  ta.Sequence[ConfigMapping],
@@ -94,3 +103,27 @@ def build_config_named_children(
94
103
  seen.add(n)
95
104
 
96
105
  return lst
106
+
107
+
108
+ ##
109
+
110
+
111
+ def render_ini_config(
112
+ settings_by_section: IniConfigSectionSettingsMap,
113
+ ) -> str:
114
+ out = io.StringIO()
115
+
116
+ for i, (section, settings) in enumerate(settings_by_section.items()):
117
+ if i:
118
+ out.write('\n')
119
+
120
+ out.write(f'[{section}]\n')
121
+
122
+ for k, v in settings.items():
123
+ if isinstance(v, str):
124
+ out.write(f'{k}={v}\n')
125
+ else:
126
+ for vv in v:
127
+ out.write(f'{k}={vv}\n')
128
+
129
+ return out.getvalue()
@@ -6,7 +6,7 @@ from omlish.lite.cached import cached_nullary
6
6
  from omlish.lite.check import check
7
7
  from omlish.os.paths import relative_symlink
8
8
 
9
- from .conf import DeployConfManager
9
+ from .conf.manager import DeployConfManager
10
10
  from .git import DeployGitManager
11
11
  from .paths.owners import DeployPathOwner
12
12
  from .paths.paths import DeployPath
File without changes
@@ -0,0 +1,17 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+ from omlish.lite.inject import InjectorBindingOrBindings
5
+ from omlish.lite.inject import InjectorBindings
6
+ from omlish.lite.inject import inj
7
+
8
+ from ..inject_ import bind_deploy_manager
9
+ from .manager import DeployConfManager
10
+
11
+
12
+ def bind_deploy_conf() -> InjectorBindings:
13
+ lst: ta.List[InjectorBindingOrBindings] = [
14
+ bind_deploy_manager(DeployConfManager),
15
+ ]
16
+
17
+ return inj.as_bindings(*lst)
@@ -20,20 +20,40 @@ import os.path
20
20
  import typing as ta
21
21
 
22
22
  from omlish.lite.check import check
23
+ from omlish.lite.json import json_dumps_pretty
24
+ from omlish.lite.strings import strip_with_newline
23
25
  from omlish.os.paths import is_path_in_dir
24
26
  from omlish.os.paths import relative_symlink
25
27
 
26
- from .paths.paths import DeployPath
28
+ from ....configs import render_ini_config
29
+ from ..paths.paths import DeployPath
30
+ from ..tags import DEPLOY_TAG_SEPARATOR
31
+ from ..tags import DeployApp
32
+ from ..tags import DeployConf
33
+ from ..tags import DeployTagMap
34
+ from .specs import DeployAppConfContent
27
35
  from .specs import DeployAppConfFile
28
36
  from .specs import DeployAppConfLink
29
37
  from .specs import DeployAppConfSpec
30
- from .tags import DEPLOY_TAG_SEPARATOR
31
- from .tags import DeployApp
32
- from .tags import DeployConf
33
- from .tags import DeployTagMap
38
+ from .specs import IniDeployAppConfContent
39
+ from .specs import JsonDeployAppConfContent
40
+ from .specs import RawDeployAppConfContent
34
41
 
35
42
 
36
43
  class DeployConfManager:
44
+ def _render_app_conf_content(self, ac: DeployAppConfContent) -> str:
45
+ if isinstance(ac, RawDeployAppConfContent):
46
+ return ac.body
47
+
48
+ elif isinstance(ac, JsonDeployAppConfContent):
49
+ return strip_with_newline(json_dumps_pretty(ac.obj))
50
+
51
+ elif isinstance(ac, IniDeployAppConfContent):
52
+ return strip_with_newline(render_ini_config(ac.sections))
53
+
54
+ else:
55
+ raise TypeError(ac)
56
+
37
57
  async def _write_app_conf_file(
38
58
  self,
39
59
  acf: DeployAppConfFile,
@@ -42,10 +62,12 @@ class DeployConfManager:
42
62
  conf_file = os.path.join(app_conf_dir, acf.path)
43
63
  check.arg(is_path_in_dir(app_conf_dir, conf_file))
44
64
 
65
+ body = self._render_app_conf_content(acf.content)
66
+
45
67
  os.makedirs(os.path.dirname(conf_file), exist_ok=True)
46
68
 
47
69
  with open(conf_file, 'w') as f: # noqa
48
- f.write(acf.body)
70
+ f.write(body)
49
71
 
50
72
  #
51
73
 
@@ -0,0 +1,95 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import dataclasses as dc
4
+ import typing as ta
5
+
6
+ from omlish.lite.check import check
7
+ from omlish.lite.marshal import register_single_field_type_obj_marshaler
8
+
9
+ from ....configs import IniConfigSectionSettingsMap
10
+ from ..paths.specs import check_valid_deploy_spec_path
11
+
12
+
13
+ ##
14
+
15
+
16
+ class DeployAppConfContent(abc.ABC): # noqa
17
+ pass
18
+
19
+
20
+ #
21
+
22
+
23
+ @register_single_field_type_obj_marshaler('body')
24
+ @dc.dataclass(frozen=True)
25
+ class RawDeployAppConfContent(DeployAppConfContent):
26
+ body: str
27
+
28
+
29
+ #
30
+
31
+
32
+ @register_single_field_type_obj_marshaler('obj')
33
+ @dc.dataclass(frozen=True)
34
+ class JsonDeployAppConfContent(DeployAppConfContent):
35
+ obj: ta.Any
36
+
37
+
38
+ #
39
+
40
+
41
+ @register_single_field_type_obj_marshaler('sections')
42
+ @dc.dataclass(frozen=True)
43
+ class IniDeployAppConfContent(DeployAppConfContent):
44
+ sections: IniConfigSectionSettingsMap
45
+
46
+
47
+ ##
48
+
49
+
50
+ @dc.dataclass(frozen=True)
51
+ class DeployAppConfFile:
52
+ path: str
53
+ content: DeployAppConfContent
54
+
55
+ def __post_init__(self) -> None:
56
+ check_valid_deploy_spec_path(self.path)
57
+
58
+
59
+ ##
60
+
61
+
62
+ @dc.dataclass(frozen=True)
63
+ class DeployAppConfLink: # noqa
64
+ """
65
+ May be either:
66
+ - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
67
+ - @conf/file - links a single file in a single subdir to conf/@conf/@dst--file
68
+ - @conf/ - links a directory in root of app conf dir to conf/@conf/@dst/
69
+ """
70
+
71
+ src: str
72
+
73
+ kind: ta.Literal['current_only', 'all_active'] = 'current_only'
74
+
75
+ def __post_init__(self) -> None:
76
+ check_valid_deploy_spec_path(self.src)
77
+ if '/' in self.src:
78
+ check.equal(self.src.count('/'), 1)
79
+
80
+
81
+ ##
82
+
83
+
84
+ @dc.dataclass(frozen=True)
85
+ class DeployAppConfSpec:
86
+ files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
87
+
88
+ links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
89
+
90
+ def __post_init__(self) -> None:
91
+ if self.files:
92
+ seen: ta.Set[str] = set()
93
+ for f in self.files:
94
+ check.not_in(f.path, seen)
95
+ seen.add(f.path)
@@ -14,16 +14,16 @@ from ..commands.inject import bind_command
14
14
  from .apps import DeployAppManager
15
15
  from .commands import DeployCommand
16
16
  from .commands import DeployCommandExecutor
17
- from .conf import DeployConfManager
17
+ from .conf.inject import bind_deploy_conf
18
18
  from .config import DeployConfig
19
19
  from .deploy import DeployManager
20
20
  from .driver import DeployDriver
21
21
  from .driver import DeployDriverFactory
22
22
  from .git import DeployGitManager
23
+ from .inject_ import bind_deploy_manager
23
24
  from .interp import InterpCommand
24
25
  from .interp import InterpCommandExecutor
25
26
  from .paths.inject import bind_deploy_paths
26
- from .paths.owners import DeployPathOwner
27
27
  from .specs import DeploySpec
28
28
  from .tags import DeployTime
29
29
  from .tmp import DeployHomeAtomics
@@ -90,6 +90,8 @@ def bind_deploy(
90
90
  lst: ta.List[InjectorBindingOrBindings] = [
91
91
  inj.bind(deploy_config),
92
92
 
93
+ bind_deploy_conf(),
94
+
93
95
  bind_deploy_paths(),
94
96
 
95
97
  bind_deploy_scope(),
@@ -97,27 +99,16 @@ def bind_deploy(
97
99
 
98
100
  #
99
101
 
100
- def bind_manager(cls: type) -> InjectorBindings:
101
- return inj.as_bindings(
102
- inj.bind(cls, singleton=True),
103
-
104
- *([inj.bind(DeployPathOwner, to_key=cls, array=True)] if issubclass(cls, DeployPathOwner) else []),
105
- )
106
-
107
- #
108
-
109
102
  lst.extend([
110
- bind_manager(DeployAppManager),
111
-
112
- bind_manager(DeployConfManager),
103
+ bind_deploy_manager(DeployAppManager),
113
104
 
114
- bind_manager(DeployGitManager),
105
+ bind_deploy_manager(DeployGitManager),
115
106
 
116
- bind_manager(DeployManager),
107
+ bind_deploy_manager(DeployManager),
117
108
 
118
- bind_manager(DeployTmpManager),
109
+ bind_deploy_manager(DeployTmpManager),
119
110
 
120
- bind_manager(DeployVenvManager),
111
+ bind_deploy_manager(DeployVenvManager),
121
112
  ])
122
113
 
123
114
  #
@@ -0,0 +1,13 @@
1
+ # ruff: noqa: UP006 UP007
2
+ from omlish.lite.inject import InjectorBindings
3
+ from omlish.lite.inject import inj
4
+
5
+ from .paths.owners import DeployPathOwner
6
+
7
+
8
+ def bind_deploy_manager(cls: type) -> InjectorBindings:
9
+ return inj.as_bindings(
10
+ inj.bind(cls, singleton=True),
11
+
12
+ *([inj.bind(DeployPathOwner, to_key=cls, array=True)] if issubclass(cls, DeployPathOwner) else []),
13
+ )
@@ -0,0 +1,10 @@
1
+ # ruff: noqa: UP006 UP007
2
+ from omlish.lite.check import check
3
+
4
+
5
+ def check_valid_deploy_spec_path(s: str) -> str:
6
+ check.non_empty_str(s)
7
+ for c in ['..', '//']:
8
+ check.not_in(c, s)
9
+ check.arg(not s.startswith('/'))
10
+ return s
@@ -7,6 +7,7 @@ import typing as ta
7
7
  from omlish.lite.cached import cached_nullary
8
8
  from omlish.lite.check import check
9
9
 
10
+ from .conf.specs import DeployAppConfSpec
10
11
  from .tags import DeployApp
11
12
  from .tags import DeployAppKey
12
13
  from .tags import DeployKey
@@ -21,14 +22,6 @@ KeyDeployTagT = ta.TypeVar('KeyDeployTagT', bound='KeyDeployTag')
21
22
  ##
22
23
 
23
24
 
24
- def check_valid_deploy_spec_path(s: str) -> str:
25
- check.non_empty_str(s)
26
- for c in ['..', '//']:
27
- check.not_in(c, s)
28
- check.arg(not s.startswith('/'))
29
- return s
30
-
31
-
32
25
  class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
33
26
  @cached_nullary
34
27
  def _key_str(self) -> str:
@@ -83,57 +76,6 @@ class DeployVenvSpec:
83
76
  ##
84
77
 
85
78
 
86
- @dc.dataclass(frozen=True)
87
- class DeployAppConfFile:
88
- path: str
89
- body: str
90
-
91
- def __post_init__(self) -> None:
92
- check_valid_deploy_spec_path(self.path)
93
-
94
-
95
- #
96
-
97
-
98
- @dc.dataclass(frozen=True)
99
- class DeployAppConfLink: # noqa
100
- """
101
- May be either:
102
- - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
103
- - @conf/file - links a single file in a single subdir to conf/@conf/@dst--file
104
- - @conf/ - links a directory in root of app conf dir to conf/@conf/@dst/
105
- """
106
-
107
- src: str
108
-
109
- kind: ta.Literal['current_only', 'all_active'] = 'current_only'
110
-
111
- def __post_init__(self) -> None:
112
- check_valid_deploy_spec_path(self.src)
113
- if '/' in self.src:
114
- check.equal(self.src.count('/'), 1)
115
-
116
-
117
- #
118
-
119
-
120
- @dc.dataclass(frozen=True)
121
- class DeployAppConfSpec:
122
- files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
123
-
124
- links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
125
-
126
- def __post_init__(self) -> None:
127
- if self.files:
128
- seen: ta.Set[str] = set()
129
- for f in self.files:
130
- check.not_in(f.path, seen)
131
- seen.add(f.path)
132
-
133
-
134
- ##
135
-
136
-
137
79
  @dc.dataclass(frozen=True)
138
80
  class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
139
81
  app: DeployApp
@@ -4,8 +4,7 @@ import dataclasses as dc
4
4
  import typing as ta
5
5
 
6
6
  from omlish.lite.check import check
7
- from omlish.lite.marshal import SingleFieldObjMarshaler
8
- from omlish.lite.marshal import register_type_obj_marshaler
7
+ from omlish.lite.marshal import register_single_field_type_obj_marshaler
9
8
 
10
9
 
11
10
  ##
@@ -84,7 +83,7 @@ def _register_deploy_tag(cls):
84
83
  _DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
85
84
  _DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
86
85
 
87
- register_type_obj_marshaler(cls, SingleFieldObjMarshaler(cls, 's'))
86
+ register_single_field_type_obj_marshaler('s', cls)
88
87
 
89
88
  return cls
90
89
 
@@ -74,6 +74,7 @@ ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
74
74
 
75
75
  # ../../../configs.py
76
76
  ConfigMapping = ta.Mapping[str, ta.Any]
77
+ IniConfigSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
77
78
 
78
79
  # ../../../threadworkers.py
79
80
  ThreadWorkerT = ta.TypeVar('ThreadWorkerT', bound='ThreadWorker')
@@ -2804,6 +2805,17 @@ def register_type_obj_marshaler(ty: type, om: ObjMarshaler) -> None:
2804
2805
  _REGISTERED_OBJ_MARSHALERS_BY_TYPE[ty] = om
2805
2806
 
2806
2807
 
2808
+ def register_single_field_type_obj_marshaler(fld, ty=None):
2809
+ def inner(ty): # noqa
2810
+ register_type_obj_marshaler(ty, SingleFieldObjMarshaler(ty, fld))
2811
+ return ty
2812
+
2813
+ if ty is not None:
2814
+ return inner(ty)
2815
+ else:
2816
+ return inner
2817
+
2818
+
2807
2819
  ##
2808
2820
 
2809
2821
 
@@ -3261,6 +3273,9 @@ class AwsLogMessageBuilder:
3261
3273
  # ../../../../configs.py
3262
3274
 
3263
3275
 
3276
+ ##
3277
+
3278
+
3264
3279
  def parse_config_file(
3265
3280
  name: str,
3266
3281
  f: ta.TextIO,
@@ -3304,6 +3319,9 @@ def read_config_file(
3304
3319
  return msh.unmarshal_obj(config_dct, cls)
3305
3320
 
3306
3321
 
3322
+ ##
3323
+
3324
+
3307
3325
  def build_config_named_children(
3308
3326
  o: ta.Union[
3309
3327
  ta.Sequence[ConfigMapping],
@@ -3342,6 +3360,30 @@ def build_config_named_children(
3342
3360
  return lst
3343
3361
 
3344
3362
 
3363
+ ##
3364
+
3365
+
3366
+ def render_ini_config(
3367
+ settings_by_section: IniConfigSectionSettingsMap,
3368
+ ) -> str:
3369
+ out = io.StringIO()
3370
+
3371
+ for i, (section, settings) in enumerate(settings_by_section.items()):
3372
+ if i:
3373
+ out.write('\n')
3374
+
3375
+ out.write(f'[{section}]\n')
3376
+
3377
+ for k, v in settings.items():
3378
+ if isinstance(v, str):
3379
+ out.write(f'{k}={v}\n')
3380
+ else:
3381
+ for vv in v:
3382
+ out.write(f'{k}={vv}\n')
3383
+
3384
+ return out.getvalue()
3385
+
3386
+
3345
3387
  ########################################
3346
3388
  # ../../../../journald/messages.py
3347
3389
 
ominfra/scripts/manage.py CHANGED
@@ -129,6 +129,7 @@ AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
129
129
 
130
130
  # ../configs.py
131
131
  ConfigMapping = ta.Mapping[str, ta.Any]
132
+ IniConfigSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
132
133
 
133
134
  # ../../omlish/subprocesses.py
134
135
  SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
@@ -4252,6 +4253,18 @@ def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
4252
4253
  return CommandNameMap(dct)
4253
4254
 
4254
4255
 
4256
+ ########################################
4257
+ # ../deploy/paths/specs.py
4258
+
4259
+
4260
+ def check_valid_deploy_spec_path(s: str) -> str:
4261
+ check.non_empty_str(s)
4262
+ for c in ['..', '//']:
4263
+ check.not_in(c, s)
4264
+ check.arg(not s.startswith('/'))
4265
+ return s
4266
+
4267
+
4255
4268
  ########################################
4256
4269
  # ../remote/config.py
4257
4270
 
@@ -6225,6 +6238,17 @@ def register_type_obj_marshaler(ty: type, om: ObjMarshaler) -> None:
6225
6238
  _REGISTERED_OBJ_MARSHALERS_BY_TYPE[ty] = om
6226
6239
 
6227
6240
 
6241
+ def register_single_field_type_obj_marshaler(fld, ty=None):
6242
+ def inner(ty): # noqa
6243
+ register_type_obj_marshaler(ty, SingleFieldObjMarshaler(ty, fld))
6244
+ return ty
6245
+
6246
+ if ty is not None:
6247
+ return inner(ty)
6248
+ else:
6249
+ return inner
6250
+
6251
+
6228
6252
  ##
6229
6253
 
6230
6254
 
@@ -6821,6 +6845,9 @@ def bind_interp_uv() -> InjectorBindings:
6821
6845
  # ../../configs.py
6822
6846
 
6823
6847
 
6848
+ ##
6849
+
6850
+
6824
6851
  def parse_config_file(
6825
6852
  name: str,
6826
6853
  f: ta.TextIO,
@@ -6864,6 +6891,9 @@ def read_config_file(
6864
6891
  return msh.unmarshal_obj(config_dct, cls)
6865
6892
 
6866
6893
 
6894
+ ##
6895
+
6896
+
6867
6897
  def build_config_named_children(
6868
6898
  o: ta.Union[
6869
6899
  ta.Sequence[ConfigMapping],
@@ -6902,6 +6932,30 @@ def build_config_named_children(
6902
6932
  return lst
6903
6933
 
6904
6934
 
6935
+ ##
6936
+
6937
+
6938
+ def render_ini_config(
6939
+ settings_by_section: IniConfigSectionSettingsMap,
6940
+ ) -> str:
6941
+ out = io.StringIO()
6942
+
6943
+ for i, (section, settings) in enumerate(settings_by_section.items()):
6944
+ if i:
6945
+ out.write('\n')
6946
+
6947
+ out.write(f'[{section}]\n')
6948
+
6949
+ for k, v in settings.items():
6950
+ if isinstance(v, str):
6951
+ out.write(f'{k}={v}\n')
6952
+ else:
6953
+ for vv in v:
6954
+ out.write(f'{k}={vv}\n')
6955
+
6956
+ return out.getvalue()
6957
+
6958
+
6905
6959
  ########################################
6906
6960
  # ../commands/marshal.py
6907
6961
 
@@ -7035,7 +7089,7 @@ def _register_deploy_tag(cls):
7035
7089
  _DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
7036
7090
  _DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
7037
7091
 
7038
- register_type_obj_marshaler(cls, SingleFieldObjMarshaler(cls, 's'))
7092
+ register_single_field_type_obj_marshaler('s', cls)
7039
7093
 
7040
7094
  return cls
7041
7095
 
@@ -7808,6 +7862,95 @@ class LocalCommandExecutor(CommandExecutor):
7808
7862
  return await ce.execute(cmd)
7809
7863
 
7810
7864
 
7865
+ ########################################
7866
+ # ../deploy/conf/specs.py
7867
+
7868
+
7869
+ ##
7870
+
7871
+
7872
+ class DeployAppConfContent(abc.ABC): # noqa
7873
+ pass
7874
+
7875
+
7876
+ #
7877
+
7878
+
7879
+ @register_single_field_type_obj_marshaler('body')
7880
+ @dc.dataclass(frozen=True)
7881
+ class RawDeployAppConfContent(DeployAppConfContent):
7882
+ body: str
7883
+
7884
+
7885
+ #
7886
+
7887
+
7888
+ @register_single_field_type_obj_marshaler('obj')
7889
+ @dc.dataclass(frozen=True)
7890
+ class JsonDeployAppConfContent(DeployAppConfContent):
7891
+ obj: ta.Any
7892
+
7893
+
7894
+ #
7895
+
7896
+
7897
+ @register_single_field_type_obj_marshaler('sections')
7898
+ @dc.dataclass(frozen=True)
7899
+ class IniDeployAppConfContent(DeployAppConfContent):
7900
+ sections: IniConfigSectionSettingsMap
7901
+
7902
+
7903
+ ##
7904
+
7905
+
7906
+ @dc.dataclass(frozen=True)
7907
+ class DeployAppConfFile:
7908
+ path: str
7909
+ content: DeployAppConfContent
7910
+
7911
+ def __post_init__(self) -> None:
7912
+ check_valid_deploy_spec_path(self.path)
7913
+
7914
+
7915
+ ##
7916
+
7917
+
7918
+ @dc.dataclass(frozen=True)
7919
+ class DeployAppConfLink: # noqa
7920
+ """
7921
+ May be either:
7922
+ - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
7923
+ - @conf/file - links a single file in a single subdir to conf/@conf/@dst--file
7924
+ - @conf/ - links a directory in root of app conf dir to conf/@conf/@dst/
7925
+ """
7926
+
7927
+ src: str
7928
+
7929
+ kind: ta.Literal['current_only', 'all_active'] = 'current_only'
7930
+
7931
+ def __post_init__(self) -> None:
7932
+ check_valid_deploy_spec_path(self.src)
7933
+ if '/' in self.src:
7934
+ check.equal(self.src.count('/'), 1)
7935
+
7936
+
7937
+ ##
7938
+
7939
+
7940
+ @dc.dataclass(frozen=True)
7941
+ class DeployAppConfSpec:
7942
+ files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
7943
+
7944
+ links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
7945
+
7946
+ def __post_init__(self) -> None:
7947
+ if self.files:
7948
+ seen: ta.Set[str] = set()
7949
+ for f in self.files:
7950
+ check.not_in(f.path, seen)
7951
+ seen.add(f.path)
7952
+
7953
+
7811
7954
  ########################################
7812
7955
  # ../deploy/deploy.py
7813
7956
 
@@ -8037,164 +8180,6 @@ class DeployPath:
8037
8180
  return cls(tuple(DeployPathPart.parse(p) for p in ps))
8038
8181
 
8039
8182
 
8040
- ########################################
8041
- # ../deploy/specs.py
8042
-
8043
-
8044
- ##
8045
-
8046
-
8047
- def check_valid_deploy_spec_path(s: str) -> str:
8048
- check.non_empty_str(s)
8049
- for c in ['..', '//']:
8050
- check.not_in(c, s)
8051
- check.arg(not s.startswith('/'))
8052
- return s
8053
-
8054
-
8055
- class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
8056
- @cached_nullary
8057
- def _key_str(self) -> str:
8058
- return hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8]
8059
-
8060
- @abc.abstractmethod
8061
- def key(self) -> KeyDeployTagT:
8062
- raise NotImplementedError
8063
-
8064
-
8065
- ##
8066
-
8067
-
8068
- @dc.dataclass(frozen=True)
8069
- class DeployGitRepo:
8070
- host: ta.Optional[str] = None
8071
- username: ta.Optional[str] = None
8072
- path: ta.Optional[str] = None
8073
-
8074
- def __post_init__(self) -> None:
8075
- check.not_in('..', check.non_empty_str(self.host))
8076
- check.not_in('.', check.non_empty_str(self.path))
8077
-
8078
-
8079
- @dc.dataclass(frozen=True)
8080
- class DeployGitSpec:
8081
- repo: DeployGitRepo
8082
- rev: DeployRev
8083
-
8084
- subtrees: ta.Optional[ta.Sequence[str]] = None
8085
-
8086
- def __post_init__(self) -> None:
8087
- check.non_empty_str(self.rev)
8088
- if self.subtrees is not None:
8089
- for st in self.subtrees:
8090
- check.non_empty_str(st)
8091
-
8092
-
8093
- ##
8094
-
8095
-
8096
- @dc.dataclass(frozen=True)
8097
- class DeployVenvSpec:
8098
- interp: ta.Optional[str] = None
8099
-
8100
- requirements_files: ta.Optional[ta.Sequence[str]] = None
8101
- extra_dependencies: ta.Optional[ta.Sequence[str]] = None
8102
-
8103
- use_uv: bool = False
8104
-
8105
-
8106
- ##
8107
-
8108
-
8109
- @dc.dataclass(frozen=True)
8110
- class DeployAppConfFile:
8111
- path: str
8112
- body: str
8113
-
8114
- def __post_init__(self) -> None:
8115
- check_valid_deploy_spec_path(self.path)
8116
-
8117
-
8118
- #
8119
-
8120
-
8121
- @dc.dataclass(frozen=True)
8122
- class DeployAppConfLink: # noqa
8123
- """
8124
- May be either:
8125
- - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
8126
- - @conf/file - links a single file in a single subdir to conf/@conf/@dst--file
8127
- - @conf/ - links a directory in root of app conf dir to conf/@conf/@dst/
8128
- """
8129
-
8130
- src: str
8131
-
8132
- kind: ta.Literal['current_only', 'all_active'] = 'current_only'
8133
-
8134
- def __post_init__(self) -> None:
8135
- check_valid_deploy_spec_path(self.src)
8136
- if '/' in self.src:
8137
- check.equal(self.src.count('/'), 1)
8138
-
8139
-
8140
- #
8141
-
8142
-
8143
- @dc.dataclass(frozen=True)
8144
- class DeployAppConfSpec:
8145
- files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
8146
-
8147
- links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
8148
-
8149
- def __post_init__(self) -> None:
8150
- if self.files:
8151
- seen: ta.Set[str] = set()
8152
- for f in self.files:
8153
- check.not_in(f.path, seen)
8154
- seen.add(f.path)
8155
-
8156
-
8157
- ##
8158
-
8159
-
8160
- @dc.dataclass(frozen=True)
8161
- class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
8162
- app: DeployApp
8163
-
8164
- git: DeployGitSpec
8165
-
8166
- venv: ta.Optional[DeployVenvSpec] = None
8167
-
8168
- conf: ta.Optional[DeployAppConfSpec] = None
8169
-
8170
- # @ta.override
8171
- def key(self) -> DeployAppKey:
8172
- return DeployAppKey(self._key_str())
8173
-
8174
-
8175
- ##
8176
-
8177
-
8178
- @dc.dataclass(frozen=True)
8179
- class DeploySpec(DeploySpecKeyed[DeployKey]):
8180
- home: DeployHome
8181
-
8182
- apps: ta.Sequence[DeployAppSpec]
8183
-
8184
- def __post_init__(self) -> None:
8185
- check.non_empty_str(self.home)
8186
-
8187
- seen: ta.Set[DeployApp] = set()
8188
- for a in self.apps:
8189
- if a.app in seen:
8190
- raise KeyError(a.app)
8191
- seen.add(a.app)
8192
-
8193
- # @ta.override
8194
- def key(self) -> DeployKey:
8195
- return DeployKey(self._key_str())
8196
-
8197
-
8198
8183
  ########################################
8199
8184
  # ../remote/execution.py
8200
8185
  """
@@ -9155,7 +9140,7 @@ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCom
9155
9140
 
9156
9141
 
9157
9142
  ########################################
9158
- # ../deploy/conf.py
9143
+ # ../deploy/conf/manager.py
9159
9144
  """
9160
9145
  TODO:
9161
9146
  - @conf DeployPathPlaceholder? :|
@@ -9176,6 +9161,19 @@ TODO:
9176
9161
 
9177
9162
 
9178
9163
  class DeployConfManager:
9164
+ def _render_app_conf_content(self, ac: DeployAppConfContent) -> str:
9165
+ if isinstance(ac, RawDeployAppConfContent):
9166
+ return ac.body
9167
+
9168
+ elif isinstance(ac, JsonDeployAppConfContent):
9169
+ return strip_with_newline(json_dumps_pretty(ac.obj))
9170
+
9171
+ elif isinstance(ac, IniDeployAppConfContent):
9172
+ return strip_with_newline(render_ini_config(ac.sections))
9173
+
9174
+ else:
9175
+ raise TypeError(ac)
9176
+
9179
9177
  async def _write_app_conf_file(
9180
9178
  self,
9181
9179
  acf: DeployAppConfFile,
@@ -9184,10 +9182,12 @@ class DeployConfManager:
9184
9182
  conf_file = os.path.join(app_conf_dir, acf.path)
9185
9183
  check.arg(is_path_in_dir(app_conf_dir, conf_file))
9186
9184
 
9185
+ body = self._render_app_conf_content(acf.content)
9186
+
9187
9187
  os.makedirs(os.path.dirname(conf_file), exist_ok=True)
9188
9188
 
9189
9189
  with open(conf_file, 'w') as f: # noqa
9190
- f.write(acf.body)
9190
+ f.write(body)
9191
9191
 
9192
9192
  #
9193
9193
 
@@ -9363,6 +9363,105 @@ class SingleDirDeployPathOwner(DeployPathOwner, abc.ABC):
9363
9363
  return self._owned_deploy_paths
9364
9364
 
9365
9365
 
9366
+ ########################################
9367
+ # ../deploy/specs.py
9368
+
9369
+
9370
+ ##
9371
+
9372
+
9373
+ class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
9374
+ @cached_nullary
9375
+ def _key_str(self) -> str:
9376
+ return hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8]
9377
+
9378
+ @abc.abstractmethod
9379
+ def key(self) -> KeyDeployTagT:
9380
+ raise NotImplementedError
9381
+
9382
+
9383
+ ##
9384
+
9385
+
9386
+ @dc.dataclass(frozen=True)
9387
+ class DeployGitRepo:
9388
+ host: ta.Optional[str] = None
9389
+ username: ta.Optional[str] = None
9390
+ path: ta.Optional[str] = None
9391
+
9392
+ def __post_init__(self) -> None:
9393
+ check.not_in('..', check.non_empty_str(self.host))
9394
+ check.not_in('.', check.non_empty_str(self.path))
9395
+
9396
+
9397
+ @dc.dataclass(frozen=True)
9398
+ class DeployGitSpec:
9399
+ repo: DeployGitRepo
9400
+ rev: DeployRev
9401
+
9402
+ subtrees: ta.Optional[ta.Sequence[str]] = None
9403
+
9404
+ def __post_init__(self) -> None:
9405
+ check.non_empty_str(self.rev)
9406
+ if self.subtrees is not None:
9407
+ for st in self.subtrees:
9408
+ check.non_empty_str(st)
9409
+
9410
+
9411
+ ##
9412
+
9413
+
9414
+ @dc.dataclass(frozen=True)
9415
+ class DeployVenvSpec:
9416
+ interp: ta.Optional[str] = None
9417
+
9418
+ requirements_files: ta.Optional[ta.Sequence[str]] = None
9419
+ extra_dependencies: ta.Optional[ta.Sequence[str]] = None
9420
+
9421
+ use_uv: bool = False
9422
+
9423
+
9424
+ ##
9425
+
9426
+
9427
+ @dc.dataclass(frozen=True)
9428
+ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
9429
+ app: DeployApp
9430
+
9431
+ git: DeployGitSpec
9432
+
9433
+ venv: ta.Optional[DeployVenvSpec] = None
9434
+
9435
+ conf: ta.Optional[DeployAppConfSpec] = None
9436
+
9437
+ # @ta.override
9438
+ def key(self) -> DeployAppKey:
9439
+ return DeployAppKey(self._key_str())
9440
+
9441
+
9442
+ ##
9443
+
9444
+
9445
+ @dc.dataclass(frozen=True)
9446
+ class DeploySpec(DeploySpecKeyed[DeployKey]):
9447
+ home: DeployHome
9448
+
9449
+ apps: ta.Sequence[DeployAppSpec]
9450
+
9451
+ def __post_init__(self) -> None:
9452
+ check.non_empty_str(self.home)
9453
+
9454
+ seen: ta.Set[DeployApp] = set()
9455
+ for a in self.apps:
9456
+ if a.app in seen:
9457
+ raise KeyError(a.app)
9458
+ seen.add(a.app)
9459
+
9460
+ # @ta.override
9461
+ def key(self) -> DeployKey:
9462
+ return DeployKey(self._key_str())
9463
+
9464
+
9366
9465
  ########################################
9367
9466
  # ../remote/_main.py
9368
9467
 
@@ -10222,6 +10321,18 @@ def bind_commands(
10222
10321
  return inj.as_bindings(*lst)
10223
10322
 
10224
10323
 
10324
+ ########################################
10325
+ # ../deploy/inject_.py
10326
+
10327
+
10328
+ def bind_deploy_manager(cls: type) -> InjectorBindings:
10329
+ return inj.as_bindings(
10330
+ inj.bind(cls, singleton=True),
10331
+
10332
+ *([inj.bind(DeployPathOwner, to_key=cls, array=True)] if issubclass(cls, DeployPathOwner) else []),
10333
+ )
10334
+
10335
+
10225
10336
  ########################################
10226
10337
  # ../deploy/paths/manager.py
10227
10338
 
@@ -10579,6 +10690,18 @@ class PyenvInterpProvider(InterpProvider):
10579
10690
  return Interp(exe, version)
10580
10691
 
10581
10692
 
10693
+ ########################################
10694
+ # ../deploy/conf/inject.py
10695
+
10696
+
10697
+ def bind_deploy_conf() -> InjectorBindings:
10698
+ lst: ta.List[InjectorBindingOrBindings] = [
10699
+ bind_deploy_manager(DeployConfManager),
10700
+ ]
10701
+
10702
+ return inj.as_bindings(*lst)
10703
+
10704
+
10582
10705
  ########################################
10583
10706
  # ../deploy/git.py
10584
10707
  """
@@ -11424,6 +11547,8 @@ def bind_deploy(
11424
11547
  lst: ta.List[InjectorBindingOrBindings] = [
11425
11548
  inj.bind(deploy_config),
11426
11549
 
11550
+ bind_deploy_conf(),
11551
+
11427
11552
  bind_deploy_paths(),
11428
11553
 
11429
11554
  bind_deploy_scope(),
@@ -11431,27 +11556,16 @@ def bind_deploy(
11431
11556
 
11432
11557
  #
11433
11558
 
11434
- def bind_manager(cls: type) -> InjectorBindings:
11435
- return inj.as_bindings(
11436
- inj.bind(cls, singleton=True),
11437
-
11438
- *([inj.bind(DeployPathOwner, to_key=cls, array=True)] if issubclass(cls, DeployPathOwner) else []),
11439
- )
11440
-
11441
- #
11442
-
11443
11559
  lst.extend([
11444
- bind_manager(DeployAppManager),
11445
-
11446
- bind_manager(DeployConfManager),
11560
+ bind_deploy_manager(DeployAppManager),
11447
11561
 
11448
- bind_manager(DeployGitManager),
11562
+ bind_deploy_manager(DeployGitManager),
11449
11563
 
11450
- bind_manager(DeployManager),
11564
+ bind_deploy_manager(DeployManager),
11451
11565
 
11452
- bind_manager(DeployTmpManager),
11566
+ bind_deploy_manager(DeployTmpManager),
11453
11567
 
11454
- bind_manager(DeployVenvManager),
11568
+ bind_deploy_manager(DeployVenvManager),
11455
11569
  ])
11456
11570
 
11457
11571
  #
@@ -143,6 +143,7 @@ SocketHandlerFactory = ta.Callable[[SocketAddress, ta.BinaryIO, ta.BinaryIO], 'S
143
143
 
144
144
  # ../configs.py
145
145
  ConfigMapping = ta.Mapping[str, ta.Any]
146
+ IniConfigSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
146
147
 
147
148
  # ../../omlish/http/handlers.py
148
149
  HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.TypeAlias
@@ -5371,6 +5372,17 @@ def register_type_obj_marshaler(ty: type, om: ObjMarshaler) -> None:
5371
5372
  _REGISTERED_OBJ_MARSHALERS_BY_TYPE[ty] = om
5372
5373
 
5373
5374
 
5375
+ def register_single_field_type_obj_marshaler(fld, ty=None):
5376
+ def inner(ty): # noqa
5377
+ register_type_obj_marshaler(ty, SingleFieldObjMarshaler(ty, fld))
5378
+ return ty
5379
+
5380
+ if ty is not None:
5381
+ return inner(ty)
5382
+ else:
5383
+ return inner
5384
+
5385
+
5374
5386
  ##
5375
5387
 
5376
5388
 
@@ -5839,6 +5851,9 @@ class SocketHandler(abc.ABC):
5839
5851
  # ../../configs.py
5840
5852
 
5841
5853
 
5854
+ ##
5855
+
5856
+
5842
5857
  def parse_config_file(
5843
5858
  name: str,
5844
5859
  f: ta.TextIO,
@@ -5882,6 +5897,9 @@ def read_config_file(
5882
5897
  return msh.unmarshal_obj(config_dct, cls)
5883
5898
 
5884
5899
 
5900
+ ##
5901
+
5902
+
5885
5903
  def build_config_named_children(
5886
5904
  o: ta.Union[
5887
5905
  ta.Sequence[ConfigMapping],
@@ -5920,6 +5938,30 @@ def build_config_named_children(
5920
5938
  return lst
5921
5939
 
5922
5940
 
5941
+ ##
5942
+
5943
+
5944
+ def render_ini_config(
5945
+ settings_by_section: IniConfigSectionSettingsMap,
5946
+ ) -> str:
5947
+ out = io.StringIO()
5948
+
5949
+ for i, (section, settings) in enumerate(settings_by_section.items()):
5950
+ if i:
5951
+ out.write('\n')
5952
+
5953
+ out.write(f'[{section}]\n')
5954
+
5955
+ for k, v in settings.items():
5956
+ if isinstance(v, str):
5957
+ out.write(f'{k}={v}\n')
5958
+ else:
5959
+ for vv in v:
5960
+ out.write(f'{k}={vv}\n')
5961
+
5962
+ return out.getvalue()
5963
+
5964
+
5923
5965
  ########################################
5924
5966
  # ../pipes.py
5925
5967
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev182
3
+ Version: 0.0.0.dev184
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev==0.0.0.dev182
16
- Requires-Dist: omlish==0.0.0.dev182
15
+ Requires-Dist: omdev==0.0.0.dev184
16
+ Requires-Dist: omlish==0.0.0.dev184
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -2,10 +2,9 @@ ominfra/.manifests.json,sha256=8KREXxMAlsilZOktXPYru1ND3V5hFI22vnrp6hT3bio,589
2
2
  ominfra/__about__.py,sha256=6i1AoruFYQCd-PyhhbDQDWY2d1tiQu9nkwWr-fXAqfY,705
3
3
  ominfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ominfra/cmds.py,sha256=E0AfnvEmnKntXWvmLW5L05_NeDpBET1VBXn7vV6EwBQ,2083
5
- ominfra/configs.py,sha256=gY49bZgAQU1IP3lfLbJxOlEuQcL1tlX3TjyJd-XYHt4,2413
5
+ ominfra/configs.py,sha256=UrwZrzsnU4E305OoFH_eAcyI8e82LQZ2yhy8WnuAUGM,3055
6
6
  ominfra/pyremote.py,sha256=HLfAZl3Xw5CpxLS5PS380zqCyE7n3vKVksIYT2Fbdc8,15197
7
7
  ominfra/ssh.py,sha256=jQpc4WvkMckIfk4vILda8zFaeharRqc_6wxW50b0OjQ,5431
8
- ominfra/systemd.py,sha256=sepFytuiRjoWHJbiFH8Ti9zm1gvWBaUXNyvSRiVLi-k,419
9
8
  ominfra/threadworkers.py,sha256=oX4ubZn7h932saXpRIJu2MNhBExgGGMuGhdXarZxLJw,4948
10
9
  ominfra/clouds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
10
  ominfra/clouds/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -45,25 +44,30 @@ ominfra/manage/commands/ping.py,sha256=DVZFzL1Z_f-Bq53vxMrL3xOi0iK_nMonJE4KvQf9w
45
44
  ominfra/manage/commands/subprocess.py,sha256=yHGMbAI-xKe_9BUs5IZ3Yav8qRE-I9aGnBtTwW15Pnw,2440
46
45
  ominfra/manage/commands/types.py,sha256=XFZPeqeIBAaIIQF3pdPbGxLlb-LCrz6WtlDWO2q_vz0,210
47
46
  ominfra/manage/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- ominfra/manage/deploy/apps.py,sha256=LeZ2iX2YHviOAnvgut7Oz15301eJoYYCpmD3ffoIagA,4742
47
+ ominfra/manage/deploy/apps.py,sha256=d0OwAVqLyr-_lrsmaeTH69G4b6vIm6ESvmWqKjA-Xn0,4750
49
48
  ominfra/manage/deploy/commands.py,sha256=U74HYQ4nhvH7znAKyXGOmZfdnll2gMWxKWVW4GzxG-0,830
50
- ominfra/manage/deploy/conf.py,sha256=fNfFlIb-bB3KAzaYZcjrbqaqKSiSq0Lpk0mIF6WgXiw,5410
51
49
  ominfra/manage/deploy/config.py,sha256=kPpl8TRisz295cM4oj-RHA6oh5jdcJ_N9pVpkl_doO8,114
52
50
  ominfra/manage/deploy/deploy.py,sha256=vyBTbBm51HhRE-MQNvxEt39F8uOYsB4ToqZ3zmVkpqU,819
53
51
  ominfra/manage/deploy/driver.py,sha256=kSriXFC8L_EpOcHFGxNZDBGyxV_fZAAF95y-czisnwE,1352
54
52
  ominfra/manage/deploy/git.py,sha256=g4wzUuSu9HwWSDhdVX-7BvA2htMwtWbRcHaoDy-xOJ4,3960
55
- ominfra/manage/deploy/inject.py,sha256=eTQC8Kyk8S8ogTJqti2lTwD2j7ha5qQG6KxqeIpLoxU,3610
53
+ ominfra/manage/deploy/inject.py,sha256=8Y1wUEnWCFh9a7YEzHi1GaPfK18c6wP6eVVkKKheScw,3378
54
+ ominfra/manage/deploy/inject_.py,sha256=LR7gEkVx2ldUoQZsGvIcm1Y4QXCVDnLIAdc3LHhQiD4,392
56
55
  ominfra/manage/deploy/interp.py,sha256=_5fuHrY5ZA0eGbnNcOqAhAMSWI1UNvbm0l29eDjE5fI,1037
57
- ominfra/manage/deploy/specs.py,sha256=usi5AmTlv8OGFcwhVHnu8Qrz1Criu5QP6_ScNMi9ehM,3748
58
- ominfra/manage/deploy/tags.py,sha256=NVEJhHKMwoDuCFd8lInH_jIe99FQILBX3wrulh3GiDg,5166
56
+ ominfra/manage/deploy/specs.py,sha256=-WSVpRwiwgXVUh4HVdFUYxadxbLz1WX1ARmQXvmbLlw,2445
57
+ ominfra/manage/deploy/tags.py,sha256=3BU0OtHQJ1Vk9B_U7Q3Cq1rbVgxcXrFki24WU0nPX1M,5106
59
58
  ominfra/manage/deploy/tmp.py,sha256=FqXoVpIpVe8-KWNu7kXt37A4jKdK_y7h_YFvtrUoOG8,729
60
59
  ominfra/manage/deploy/types.py,sha256=ZcIoheZ3zW7n0IZiqTRW_Uo3JyWWeWg5nyKGryvGc2I,112
61
60
  ominfra/manage/deploy/venvs.py,sha256=1Ic3yKap9bTduqHorz1MpmSiQPh9x-vxhLLxgpVpRdc,1592
61
+ ominfra/manage/deploy/conf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ ominfra/manage/deploy/conf/inject.py,sha256=xHZQr7oaJCxaKgpJ0H-hgiQNGt4QsskhT3f5v7xrFwE,451
63
+ ominfra/manage/deploy/conf/manager.py,sha256=29txtcAeRNUqK2MD1V5Q-82EoeExdkkV4fRvPnc8GTs,6226
64
+ ominfra/manage/deploy/conf/specs.py,sha256=r9Tf6i0TPVOTuoS__F5Yz1v14Btfqtb1nvUdgEaNeBU,2030
62
65
  ominfra/manage/deploy/paths/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
66
  ominfra/manage/deploy/paths/inject.py,sha256=X81C-Qhef1LQ7tILWvkomBwFTvgooLVmWRnKL7TeVoI,596
64
67
  ominfra/manage/deploy/paths/manager.py,sha256=Dnl8euyZQYDGwDzkMvgPAwOssseducr5kP6T0qzVXQk,929
65
68
  ominfra/manage/deploy/paths/owners.py,sha256=sgCdKOFve8XZOtoTjrFrOrJd_MhZOGXo4yFJAFGRQ_s,1229
66
69
  ominfra/manage/deploy/paths/paths.py,sha256=i7g8YdYOh4M_jgJXtafTbFkRhlu469cfGxAJAuB3fVY,5531
70
+ ominfra/manage/deploy/paths/specs.py,sha256=0cXJI7xjevv4qco3qn4jAs9i5pGyupJ3CFAyJh8y1z0,244
67
71
  ominfra/manage/deploy/paths/types.py,sha256=TGgtSASmdyuZ2maZnvahfA0QxPLWlHBtpDeIEoEDGxk,112
68
72
  ominfra/manage/remote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
73
  ominfra/manage/remote/_main.py,sha256=p5KoiS2WMw6QAqlDl_Zun-JybmCsy8awIfpBMLBjGMY,4356
@@ -87,9 +91,9 @@ ominfra/manage/targets/connection.py,sha256=rVI1YJxFClcF-sdttqWyIz9_XjPI01GUdwxY
87
91
  ominfra/manage/targets/inject.py,sha256=P4597xWM-V3I_gCt2O71OLhYQkkXtuJvkYRsIbhhMcE,1561
88
92
  ominfra/manage/targets/targets.py,sha256=7GP6UAZyJFEhpkJN6UQdpr_WN3p7C76v-s445y-WB6U,1885
89
93
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- ominfra/scripts/journald2aws.py,sha256=yEnBAd0Q_3lBkVoTvlJ_uCcUxz7Ckn2qoSWZVhMihvQ,157696
91
- ominfra/scripts/manage.py,sha256=TT4-RKT2fpIAmTmfGU9ANDbrtKwx63BOSkUPK7myjh0,327805
92
- ominfra/scripts/supervisor.py,sha256=aEngsVXbK7DNEtILJ_eMMlUu113kfT2uFJ-bdbcPTro,281984
94
+ ominfra/scripts/journald2aws.py,sha256=-XlQ9s2yKZpOQqTQhvEb44q-cYX317mobkjlyCyfUro,158588
95
+ ominfra/scripts/manage.py,sha256=ZH8YdqXxnFdcGb6uCcXotg2wbke0MnniRquPwy3aAPg,330216
96
+ ominfra/scripts/supervisor.py,sha256=WpK_AR5LwmvxLfCyjylMV4_Ud2iNZ6k9QBUxiseSmqY,282876
93
97
  ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
94
98
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
95
99
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -131,9 +135,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
131
135
  ominfra/tailscale/cli.py,sha256=3FnJbgpLw6gInTfhERd1mDy9ijjMUGxkdYVo43Tnxx4,3555
132
136
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
137
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
134
- ominfra-0.0.0.dev182.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
135
- ominfra-0.0.0.dev182.dist-info/METADATA,sha256=7uiF4hH6SWBDS3BfzJL-Yia-Z2v4jchDSMpBZs1Ex6Q,731
136
- ominfra-0.0.0.dev182.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
137
- ominfra-0.0.0.dev182.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
138
- ominfra-0.0.0.dev182.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
139
- ominfra-0.0.0.dev182.dist-info/RECORD,,
138
+ ominfra-0.0.0.dev184.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
139
+ ominfra-0.0.0.dev184.dist-info/METADATA,sha256=_2qh8ot4_hmhmocdB0x3-yeQWQ8jzM5fpedBUm9rFrs,731
140
+ ominfra-0.0.0.dev184.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
141
+ ominfra-0.0.0.dev184.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
142
+ ominfra-0.0.0.dev184.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
143
+ ominfra-0.0.0.dev184.dist-info/RECORD,,
ominfra/systemd.py DELETED
@@ -1,18 +0,0 @@
1
- # @omlish-lite
2
- import io
3
- import typing as ta
4
-
5
-
6
- def render_systemd_unit(settings_by_section: ta.Mapping[str, ta.Mapping[str, str]]) -> str:
7
- out = io.StringIO()
8
-
9
- for i, (section, settings) in enumerate(settings_by_section.items()):
10
- if i:
11
- out.write('\n')
12
-
13
- out.write(f'[{section}]\n')
14
-
15
- for k, v in settings.items():
16
- out.write(f'{k}={v}\n')
17
-
18
- return out.getvalue()