ominfra 0.0.0.dev172__py3-none-any.whl → 0.0.0.dev174__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.
- ominfra/manage/deploy/apps.py +48 -80
- ominfra/manage/deploy/conf.py +55 -50
- ominfra/manage/deploy/deploy.py +45 -1
- ominfra/manage/deploy/paths/paths.py +39 -45
- ominfra/manage/deploy/paths/types.py +8 -0
- ominfra/manage/deploy/specs.py +47 -13
- ominfra/manage/deploy/tags.py +225 -0
- ominfra/manage/deploy/types.py +0 -30
- ominfra/scripts/journald2aws.py +46 -0
- ominfra/scripts/manage.py +529 -191
- ominfra/scripts/supervisor.py +46 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev174.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev174.dist-info}/RECORD +17 -15
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev174.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev174.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev174.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev174.dist-info}/top_level.txt +0 -0
ominfra/scripts/manage.py
CHANGED
@@ -77,6 +77,9 @@ TomlParseFloat = ta.Callable[[str], ta.Any]
|
|
77
77
|
TomlKey = ta.Tuple[str, ...]
|
78
78
|
TomlPos = int # ta.TypeAlias
|
79
79
|
|
80
|
+
# deploy/paths/types.py
|
81
|
+
DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
|
82
|
+
|
80
83
|
# ../../omlish/asyncs/asyncio/timeouts.py
|
81
84
|
AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
|
82
85
|
|
@@ -92,6 +95,11 @@ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
|
92
95
|
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
93
96
|
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
94
97
|
|
98
|
+
# ../../omlish/lite/typing.py
|
99
|
+
A0 = ta.TypeVar('A0')
|
100
|
+
A1 = ta.TypeVar('A1')
|
101
|
+
A2 = ta.TypeVar('A2')
|
102
|
+
|
95
103
|
# ../../omdev/packaging/specifiers.py
|
96
104
|
UnparsedVersion = ta.Union['Version', str]
|
97
105
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
@@ -101,10 +109,6 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
101
109
|
CommandT = ta.TypeVar('CommandT', bound='Command')
|
102
110
|
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
103
111
|
|
104
|
-
# deploy/types.py
|
105
|
-
DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
|
106
|
-
DeployPathPlaceholder = ta.Literal['app', 'tag', 'conf'] # ta.TypeAlias
|
107
|
-
|
108
112
|
# ../../omlish/argparse/cli.py
|
109
113
|
ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
110
114
|
|
@@ -125,6 +129,9 @@ AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
|
125
129
|
# ../configs.py
|
126
130
|
ConfigMapping = ta.Mapping[str, ta.Any]
|
127
131
|
|
132
|
+
# deploy/specs.py
|
133
|
+
KeyDeployTagT = ta.TypeVar('KeyDeployTagT', bound='KeyDeployTag')
|
134
|
+
|
128
135
|
# ../../omlish/subprocesses.py
|
129
136
|
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
130
137
|
|
@@ -1380,6 +1387,25 @@ class DeployConfig:
|
|
1380
1387
|
deploy_home: ta.Optional[str] = None
|
1381
1388
|
|
1382
1389
|
|
1390
|
+
########################################
|
1391
|
+
# ../deploy/paths/types.py
|
1392
|
+
|
1393
|
+
|
1394
|
+
##
|
1395
|
+
|
1396
|
+
|
1397
|
+
########################################
|
1398
|
+
# ../deploy/types.py
|
1399
|
+
|
1400
|
+
|
1401
|
+
##
|
1402
|
+
|
1403
|
+
|
1404
|
+
DeployHome = ta.NewType('DeployHome', str)
|
1405
|
+
|
1406
|
+
DeployRev = ta.NewType('DeployRev', str)
|
1407
|
+
|
1408
|
+
|
1383
1409
|
########################################
|
1384
1410
|
# ../../pyremote.py
|
1385
1411
|
"""
|
@@ -2641,6 +2667,9 @@ def pycharm_debug_preamble(prd: PycharmRemoteDebug) -> str:
|
|
2641
2667
|
# ../../../omlish/lite/reflect.py
|
2642
2668
|
|
2643
2669
|
|
2670
|
+
##
|
2671
|
+
|
2672
|
+
|
2644
2673
|
_GENERIC_ALIAS_TYPES = (
|
2645
2674
|
ta._GenericAlias, # type: ignore # noqa
|
2646
2675
|
*([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
|
@@ -2658,6 +2687,9 @@ is_union_alias = functools.partial(is_generic_alias, origin=ta.Union)
|
|
2658
2687
|
is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
|
2659
2688
|
|
2660
2689
|
|
2690
|
+
##
|
2691
|
+
|
2692
|
+
|
2661
2693
|
def is_optional_alias(spec: ta.Any) -> bool:
|
2662
2694
|
return (
|
2663
2695
|
isinstance(spec, _GENERIC_ALIAS_TYPES) and # noqa
|
@@ -2672,6 +2704,9 @@ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
|
2672
2704
|
return it
|
2673
2705
|
|
2674
2706
|
|
2707
|
+
##
|
2708
|
+
|
2709
|
+
|
2675
2710
|
def is_new_type(spec: ta.Any) -> bool:
|
2676
2711
|
if isinstance(ta.NewType, type):
|
2677
2712
|
return isinstance(spec, ta.NewType)
|
@@ -2684,6 +2719,26 @@ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
|
|
2684
2719
|
return spec.__supertype__
|
2685
2720
|
|
2686
2721
|
|
2722
|
+
##
|
2723
|
+
|
2724
|
+
|
2725
|
+
def is_literal_type(spec: ta.Any) -> bool:
|
2726
|
+
if hasattr(ta, '_LiteralGenericAlias'):
|
2727
|
+
return isinstance(spec, ta._LiteralGenericAlias) # noqa
|
2728
|
+
else:
|
2729
|
+
return (
|
2730
|
+
isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
|
2731
|
+
spec.__origin__ is ta.Literal
|
2732
|
+
)
|
2733
|
+
|
2734
|
+
|
2735
|
+
def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
|
2736
|
+
return spec.__args__
|
2737
|
+
|
2738
|
+
|
2739
|
+
##
|
2740
|
+
|
2741
|
+
|
2687
2742
|
def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
2688
2743
|
seen = set()
|
2689
2744
|
todo = list(reversed(cls.__subclasses__()))
|
@@ -2823,6 +2878,54 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
2823
2878
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
2824
2879
|
|
2825
2880
|
|
2881
|
+
########################################
|
2882
|
+
# ../../../omlish/lite/typing.py
|
2883
|
+
|
2884
|
+
|
2885
|
+
##
|
2886
|
+
# A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
|
2887
|
+
|
2888
|
+
|
2889
|
+
@dc.dataclass(frozen=True)
|
2890
|
+
class AnyFunc(ta.Generic[T]):
|
2891
|
+
fn: ta.Callable[..., T]
|
2892
|
+
|
2893
|
+
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
2894
|
+
return self.fn(*args, **kwargs)
|
2895
|
+
|
2896
|
+
|
2897
|
+
@dc.dataclass(frozen=True)
|
2898
|
+
class Func0(ta.Generic[T]):
|
2899
|
+
fn: ta.Callable[[], T]
|
2900
|
+
|
2901
|
+
def __call__(self) -> T:
|
2902
|
+
return self.fn()
|
2903
|
+
|
2904
|
+
|
2905
|
+
@dc.dataclass(frozen=True)
|
2906
|
+
class Func1(ta.Generic[A0, T]):
|
2907
|
+
fn: ta.Callable[[A0], T]
|
2908
|
+
|
2909
|
+
def __call__(self, a0: A0) -> T:
|
2910
|
+
return self.fn(a0)
|
2911
|
+
|
2912
|
+
|
2913
|
+
@dc.dataclass(frozen=True)
|
2914
|
+
class Func2(ta.Generic[A0, A1, T]):
|
2915
|
+
fn: ta.Callable[[A0, A1], T]
|
2916
|
+
|
2917
|
+
def __call__(self, a0: A0, a1: A1) -> T:
|
2918
|
+
return self.fn(a0, a1)
|
2919
|
+
|
2920
|
+
|
2921
|
+
@dc.dataclass(frozen=True)
|
2922
|
+
class Func3(ta.Generic[A0, A1, A2, T]):
|
2923
|
+
fn: ta.Callable[[A0, A1, A2], T]
|
2924
|
+
|
2925
|
+
def __call__(self, a0: A0, a1: A1, a2: A2) -> T:
|
2926
|
+
return self.fn(a0, a1, a2)
|
2927
|
+
|
2928
|
+
|
2826
2929
|
########################################
|
2827
2930
|
# ../../../omlish/logs/filters.py
|
2828
2931
|
|
@@ -4149,39 +4252,227 @@ def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
|
4149
4252
|
|
4150
4253
|
|
4151
4254
|
########################################
|
4152
|
-
# ../deploy/
|
4255
|
+
# ../deploy/tags.py
|
4153
4256
|
|
4154
4257
|
|
4155
4258
|
##
|
4156
4259
|
|
4157
4260
|
|
4158
|
-
|
4261
|
+
DEPLOY_TAG_SIGIL = '@'
|
4159
4262
|
|
4160
|
-
|
4161
|
-
|
4162
|
-
|
4163
|
-
|
4263
|
+
DEPLOY_TAG_SEPARATOR = '--'
|
4264
|
+
|
4265
|
+
DEPLOY_TAG_DELIMITERS: ta.AbstractSet[str] = frozenset([
|
4266
|
+
DEPLOY_TAG_SEPARATOR,
|
4267
|
+
'.',
|
4268
|
+
])
|
4269
|
+
|
4270
|
+
DEPLOY_TAG_ILLEGAL_STRS: ta.AbstractSet[str] = frozenset([
|
4271
|
+
DEPLOY_TAG_SIGIL,
|
4272
|
+
*DEPLOY_TAG_DELIMITERS,
|
4273
|
+
'/',
|
4274
|
+
])
|
4164
4275
|
|
4165
4276
|
|
4166
4277
|
##
|
4167
4278
|
|
4168
4279
|
|
4169
4280
|
@dc.dataclass(frozen=True)
|
4170
|
-
class
|
4171
|
-
|
4172
|
-
tag: DeployTag
|
4281
|
+
class DeployTag(abc.ABC): # noqa
|
4282
|
+
s: str
|
4173
4283
|
|
4174
4284
|
def __post_init__(self) -> None:
|
4175
|
-
|
4176
|
-
|
4177
|
-
|
4285
|
+
check.not_in(abc.ABC, type(self).__bases__)
|
4286
|
+
check.non_empty_str(self.s)
|
4287
|
+
for ch in DEPLOY_TAG_ILLEGAL_STRS:
|
4288
|
+
check.state(ch not in self.s)
|
4178
4289
|
|
4179
|
-
|
4180
|
-
|
4181
|
-
|
4182
|
-
|
4290
|
+
#
|
4291
|
+
|
4292
|
+
tag_name: ta.ClassVar[str]
|
4293
|
+
tag_kwarg: ta.ClassVar[str]
|
4294
|
+
|
4295
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
4296
|
+
super().__init_subclass__(**kwargs)
|
4297
|
+
|
4298
|
+
if abc.ABC in cls.__bases__:
|
4299
|
+
return
|
4300
|
+
|
4301
|
+
for b in cls.__bases__:
|
4302
|
+
if issubclass(b, DeployTag):
|
4303
|
+
check.in_(abc.ABC, b.__bases__)
|
4304
|
+
|
4305
|
+
check.non_empty_str(tn := cls.tag_name)
|
4306
|
+
check.equal(tn, tn.lower().strip())
|
4307
|
+
check.not_in('_', tn)
|
4308
|
+
|
4309
|
+
check.state(not hasattr(cls, 'tag_kwarg'))
|
4310
|
+
cls.tag_kwarg = tn.replace('-', '_')
|
4311
|
+
|
4312
|
+
|
4313
|
+
##
|
4314
|
+
|
4315
|
+
|
4316
|
+
_DEPLOY_TAGS: ta.Set[ta.Type[DeployTag]] = set()
|
4317
|
+
DEPLOY_TAGS: ta.AbstractSet[ta.Type[DeployTag]] = _DEPLOY_TAGS
|
4318
|
+
|
4319
|
+
_DEPLOY_TAGS_BY_NAME: ta.Dict[str, ta.Type[DeployTag]] = {}
|
4320
|
+
DEPLOY_TAGS_BY_NAME: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_NAME
|
4321
|
+
|
4322
|
+
_DEPLOY_TAGS_BY_KWARG: ta.Dict[str, ta.Type[DeployTag]] = {}
|
4323
|
+
DEPLOY_TAGS_BY_KWARG: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_KWARG
|
4324
|
+
|
4325
|
+
|
4326
|
+
def _register_deploy_tag(cls):
|
4327
|
+
check.not_in(cls.tag_name, _DEPLOY_TAGS_BY_NAME)
|
4328
|
+
check.not_in(cls.tag_kwarg, _DEPLOY_TAGS_BY_KWARG)
|
4329
|
+
|
4330
|
+
_DEPLOY_TAGS.add(cls)
|
4331
|
+
_DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
|
4332
|
+
_DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
|
4333
|
+
|
4334
|
+
return cls
|
4335
|
+
|
4336
|
+
|
4337
|
+
##
|
4338
|
+
|
4339
|
+
|
4340
|
+
@_register_deploy_tag
|
4341
|
+
class DeployTime(DeployTag):
|
4342
|
+
tag_name: ta.ClassVar[str] = 'time'
|
4343
|
+
|
4344
|
+
|
4345
|
+
##
|
4346
|
+
|
4347
|
+
|
4348
|
+
class NameDeployTag(DeployTag, abc.ABC): # noqa
|
4349
|
+
pass
|
4350
|
+
|
4351
|
+
|
4352
|
+
@_register_deploy_tag
|
4353
|
+
class DeployApp(NameDeployTag):
|
4354
|
+
tag_name: ta.ClassVar[str] = 'app'
|
4355
|
+
|
4356
|
+
|
4357
|
+
@_register_deploy_tag
|
4358
|
+
class DeployConf(NameDeployTag):
|
4359
|
+
tag_name: ta.ClassVar[str] = 'conf'
|
4360
|
+
|
4361
|
+
|
4362
|
+
##
|
4363
|
+
|
4364
|
+
|
4365
|
+
class KeyDeployTag(DeployTag, abc.ABC): # noqa
|
4366
|
+
pass
|
4367
|
+
|
4368
|
+
|
4369
|
+
@_register_deploy_tag
|
4370
|
+
class DeployKey(KeyDeployTag):
|
4371
|
+
tag_name: ta.ClassVar[str] = 'deploy-key'
|
4372
|
+
|
4373
|
+
|
4374
|
+
@_register_deploy_tag
|
4375
|
+
class DeployAppKey(KeyDeployTag):
|
4376
|
+
tag_name: ta.ClassVar[str] = 'app-key'
|
4377
|
+
|
4378
|
+
|
4379
|
+
##
|
4380
|
+
|
4381
|
+
|
4382
|
+
class RevDeployTag(DeployTag, abc.ABC): # noqa
|
4383
|
+
pass
|
4384
|
+
|
4385
|
+
|
4386
|
+
@_register_deploy_tag
|
4387
|
+
class DeployAppRev(RevDeployTag):
|
4388
|
+
tag_name: ta.ClassVar[str] = 'app-rev'
|
4389
|
+
|
4390
|
+
|
4391
|
+
##
|
4392
|
+
|
4393
|
+
|
4394
|
+
class DeployTagMap:
|
4395
|
+
def __init__(
|
4396
|
+
self,
|
4397
|
+
*args: DeployTag,
|
4398
|
+
**kwargs: str,
|
4399
|
+
) -> None:
|
4400
|
+
super().__init__()
|
4401
|
+
|
4402
|
+
dct: ta.Dict[ta.Type[DeployTag], DeployTag] = {}
|
4403
|
+
|
4404
|
+
for a in args:
|
4405
|
+
c = type(check.isinstance(a, DeployTag))
|
4406
|
+
check.not_in(c, dct)
|
4407
|
+
dct[c] = a
|
4408
|
+
|
4409
|
+
for k, v in kwargs.items():
|
4410
|
+
c = DEPLOY_TAGS_BY_KWARG[k]
|
4411
|
+
check.not_in(c, dct)
|
4412
|
+
dct[c] = c(v)
|
4413
|
+
|
4414
|
+
self._dct = dct
|
4415
|
+
self._tup = tuple(sorted((type(t).tag_kwarg, t.s) for t in dct.values()))
|
4416
|
+
|
4417
|
+
#
|
4418
|
+
|
4419
|
+
def add(self, *args: ta.Any, **kwargs: ta.Any) -> 'DeployTagMap':
|
4420
|
+
return DeployTagMap(
|
4421
|
+
*self,
|
4422
|
+
*args,
|
4423
|
+
**kwargs,
|
4424
|
+
)
|
4425
|
+
|
4426
|
+
def remove(self, *tags_or_names: ta.Union[ta.Type[DeployTag], str]) -> 'DeployTagMap':
|
4427
|
+
dcs = {
|
4428
|
+
check.issubclass(a, DeployTag) if isinstance(a, type) else DEPLOY_TAGS_BY_NAME[a]
|
4429
|
+
for a in tags_or_names
|
4183
4430
|
}
|
4184
4431
|
|
4432
|
+
return DeployTagMap(*[
|
4433
|
+
t
|
4434
|
+
for t in self._dct.values()
|
4435
|
+
if t not in dcs
|
4436
|
+
])
|
4437
|
+
|
4438
|
+
#
|
4439
|
+
|
4440
|
+
def __repr__(self) -> str:
|
4441
|
+
return f'{self.__class__.__name__}({", ".join(f"{k}={v!r}" for k, v in self._tup)})'
|
4442
|
+
|
4443
|
+
def __hash__(self) -> int:
|
4444
|
+
return hash(self._tup)
|
4445
|
+
|
4446
|
+
def __eq__(self, other: object) -> bool:
|
4447
|
+
if isinstance(other, DeployTagMap):
|
4448
|
+
return self._tup == other._tup
|
4449
|
+
else:
|
4450
|
+
return NotImplemented
|
4451
|
+
|
4452
|
+
#
|
4453
|
+
|
4454
|
+
def __len__(self) -> int:
|
4455
|
+
return len(self._dct)
|
4456
|
+
|
4457
|
+
def __iter__(self) -> ta.Iterator[DeployTag]:
|
4458
|
+
return iter(self._dct.values())
|
4459
|
+
|
4460
|
+
def __getitem__(self, key: ta.Union[ta.Type[DeployTag], str]) -> DeployTag:
|
4461
|
+
if isinstance(key, str):
|
4462
|
+
return self._dct[DEPLOY_TAGS_BY_NAME[key]]
|
4463
|
+
elif isinstance(key, type):
|
4464
|
+
return self._dct[key]
|
4465
|
+
else:
|
4466
|
+
raise TypeError(key)
|
4467
|
+
|
4468
|
+
def __contains__(self, key: ta.Union[ta.Type[DeployTag], str]) -> bool:
|
4469
|
+
if isinstance(key, str):
|
4470
|
+
return DEPLOY_TAGS_BY_NAME[key] in self._dct
|
4471
|
+
elif isinstance(key, type):
|
4472
|
+
return key in self._dct
|
4473
|
+
else:
|
4474
|
+
raise TypeError(key)
|
4475
|
+
|
4185
4476
|
|
4186
4477
|
########################################
|
4187
4478
|
# ../remote/config.py
|
@@ -5787,6 +6078,18 @@ class OptionalObjMarshaler(ObjMarshaler):
|
|
5787
6078
|
return self.item.unmarshal(o, ctx)
|
5788
6079
|
|
5789
6080
|
|
6081
|
+
@dc.dataclass(frozen=True)
|
6082
|
+
class LiteralObjMarshaler(ObjMarshaler):
|
6083
|
+
item: ObjMarshaler
|
6084
|
+
vs: frozenset
|
6085
|
+
|
6086
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
6087
|
+
return self.item.marshal(check.in_(o, self.vs), ctx)
|
6088
|
+
|
6089
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
6090
|
+
return check.in_(self.item.unmarshal(o, ctx), self.vs)
|
6091
|
+
|
6092
|
+
|
5790
6093
|
@dc.dataclass(frozen=True)
|
5791
6094
|
class MappingObjMarshaler(ObjMarshaler):
|
5792
6095
|
ty: type
|
@@ -5998,6 +6301,11 @@ class ObjMarshalerManager:
|
|
5998
6301
|
if is_new_type(ty):
|
5999
6302
|
return rec(get_new_type_supertype(ty))
|
6000
6303
|
|
6304
|
+
if is_literal_type(ty):
|
6305
|
+
lvs = frozenset(get_literal_type_args(ty))
|
6306
|
+
lty = check.single(set(map(type, lvs)))
|
6307
|
+
return LiteralObjMarshaler(rec(lty), lvs)
|
6308
|
+
|
6001
6309
|
if is_generic_alias(ty):
|
6002
6310
|
try:
|
6003
6311
|
mt = self._generic_mapping_types[ta.get_origin(ty)]
|
@@ -6634,28 +6942,13 @@ TODO:
|
|
6634
6942
|
##
|
6635
6943
|
|
6636
6944
|
|
6637
|
-
DEPLOY_PATH_PLACEHOLDER_SIGIL = '@'
|
6638
|
-
DEPLOY_PATH_PLACEHOLDER_SEPARATOR = '--'
|
6639
|
-
|
6640
|
-
DEPLOY_PATH_PLACEHOLDER_DELIMITERS: ta.AbstractSet[str] = frozenset([
|
6641
|
-
DEPLOY_PATH_PLACEHOLDER_SEPARATOR,
|
6642
|
-
'.',
|
6643
|
-
])
|
6644
|
-
|
6645
|
-
DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
|
6646
|
-
'app',
|
6647
|
-
'tag',
|
6648
|
-
'conf',
|
6649
|
-
])
|
6650
|
-
|
6651
|
-
|
6652
6945
|
class DeployPathError(Exception):
|
6653
6946
|
pass
|
6654
6947
|
|
6655
6948
|
|
6656
6949
|
class DeployPathRenderable(abc.ABC):
|
6657
6950
|
@abc.abstractmethod
|
6658
|
-
def render(self,
|
6951
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6659
6952
|
raise NotImplementedError
|
6660
6953
|
|
6661
6954
|
|
@@ -6666,26 +6959,30 @@ class DeployPathNamePart(DeployPathRenderable, abc.ABC):
|
|
6666
6959
|
@classmethod
|
6667
6960
|
def parse(cls, s: str) -> 'DeployPathNamePart':
|
6668
6961
|
check.non_empty_str(s)
|
6669
|
-
if s.startswith(
|
6670
|
-
return
|
6671
|
-
elif s in
|
6962
|
+
if s.startswith(DEPLOY_TAG_SIGIL):
|
6963
|
+
return TagDeployPathNamePart(s[1:])
|
6964
|
+
elif s in DEPLOY_TAG_DELIMITERS:
|
6672
6965
|
return DelimiterDeployPathNamePart(s)
|
6673
6966
|
else:
|
6674
6967
|
return ConstDeployPathNamePart(s)
|
6675
6968
|
|
6676
6969
|
|
6677
6970
|
@dc.dataclass(frozen=True)
|
6678
|
-
class
|
6679
|
-
|
6971
|
+
class TagDeployPathNamePart(DeployPathNamePart):
|
6972
|
+
name: str
|
6680
6973
|
|
6681
6974
|
def __post_init__(self) -> None:
|
6682
|
-
check.in_(self.
|
6975
|
+
check.in_(self.name, DEPLOY_TAGS_BY_NAME)
|
6683
6976
|
|
6684
|
-
|
6685
|
-
|
6686
|
-
|
6977
|
+
@property
|
6978
|
+
def tag(self) -> ta.Type[DeployTag]:
|
6979
|
+
return DEPLOY_TAGS_BY_NAME[self.name]
|
6980
|
+
|
6981
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6982
|
+
if tags is not None:
|
6983
|
+
return tags[self.tag].s
|
6687
6984
|
else:
|
6688
|
-
return
|
6985
|
+
return DEPLOY_TAG_SIGIL + self.name
|
6689
6986
|
|
6690
6987
|
|
6691
6988
|
@dc.dataclass(frozen=True)
|
@@ -6693,9 +6990,9 @@ class DelimiterDeployPathNamePart(DeployPathNamePart):
|
|
6693
6990
|
delimiter: str
|
6694
6991
|
|
6695
6992
|
def __post_init__(self) -> None:
|
6696
|
-
check.in_(self.delimiter,
|
6993
|
+
check.in_(self.delimiter, DEPLOY_TAG_DELIMITERS)
|
6697
6994
|
|
6698
|
-
def render(self,
|
6995
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6699
6996
|
return self.delimiter
|
6700
6997
|
|
6701
6998
|
|
@@ -6705,10 +7002,10 @@ class ConstDeployPathNamePart(DeployPathNamePart):
|
|
6705
7002
|
|
6706
7003
|
def __post_init__(self) -> None:
|
6707
7004
|
check.non_empty_str(self.const)
|
6708
|
-
for c in [*
|
7005
|
+
for c in [*DEPLOY_TAG_DELIMITERS, DEPLOY_TAG_SIGIL, '/']:
|
6709
7006
|
check.not_in(c, self.const)
|
6710
7007
|
|
6711
|
-
def render(self,
|
7008
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6712
7009
|
return self.const
|
6713
7010
|
|
6714
7011
|
|
@@ -6723,8 +7020,8 @@ class DeployPathName(DeployPathRenderable):
|
|
6723
7020
|
if len(gl := list(g)) > 1:
|
6724
7021
|
raise DeployPathError(f'May not have consecutive path name part types: {k} {gl}')
|
6725
7022
|
|
6726
|
-
def render(self,
|
6727
|
-
return ''.join(p.render(
|
7023
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
7024
|
+
return ''.join(p.render(tags) for p in self.parts)
|
6728
7025
|
|
6729
7026
|
@classmethod
|
6730
7027
|
def parse(cls, s: str) -> 'DeployPathName':
|
@@ -6734,7 +7031,7 @@ class DeployPathName(DeployPathRenderable):
|
|
6734
7031
|
i = 0
|
6735
7032
|
ps = []
|
6736
7033
|
while i < len(s):
|
6737
|
-
ns = [(n, d) for d in
|
7034
|
+
ns = [(n, d) for d in DEPLOY_TAG_DELIMITERS if (n := s.find(d, i)) >= 0]
|
6738
7035
|
if not ns:
|
6739
7036
|
ps.append(s[i:])
|
6740
7037
|
break
|
@@ -6758,8 +7055,8 @@ class DeployPathPart(DeployPathRenderable, abc.ABC): # noqa
|
|
6758
7055
|
def kind(self) -> DeployPathKind:
|
6759
7056
|
raise NotImplementedError
|
6760
7057
|
|
6761
|
-
def render(self,
|
6762
|
-
return self.name.render(
|
7058
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
7059
|
+
return self.name.render(tags) + ('/' if self.kind == 'dir' else '')
|
6763
7060
|
|
6764
7061
|
@classmethod
|
6765
7062
|
def parse(cls, s: str) -> 'DeployPathPart':
|
@@ -6805,20 +7102,20 @@ class DeployPath:
|
|
6805
7102
|
for p in self.parts[:-1]:
|
6806
7103
|
check.equal(p.kind, 'dir')
|
6807
7104
|
|
6808
|
-
|
7105
|
+
@cached_nullary
|
7106
|
+
def tag_indices(self) -> ta.Mapping[ta.Type[DeployTag], ta.Sequence[int]]:
|
7107
|
+
pd: ta.Dict[ta.Type[DeployTag], ta.List[int]] = {}
|
6809
7108
|
for i, np in enumerate(self.name_parts):
|
6810
|
-
if isinstance(np,
|
6811
|
-
pd.setdefault(
|
6812
|
-
|
6813
|
-
# if 'tag' in pd and 'app' not in pd:
|
6814
|
-
# raise DeployPathError('Tag placeholder in path without app', self)
|
7109
|
+
if isinstance(np, TagDeployPathNamePart):
|
7110
|
+
pd.setdefault(np.tag, []).append(i)
|
7111
|
+
return pd
|
6815
7112
|
|
6816
7113
|
@property
|
6817
7114
|
def kind(self) -> ta.Literal['file', 'dir']:
|
6818
7115
|
return self.parts[-1].kind
|
6819
7116
|
|
6820
|
-
def render(self,
|
6821
|
-
return ''.join([p.render(
|
7117
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
7118
|
+
return ''.join([p.render(tags) for p in self.parts])
|
6822
7119
|
|
6823
7120
|
@classmethod
|
6824
7121
|
def parse(cls, s: str) -> 'DeployPath':
|
@@ -6842,6 +7139,16 @@ def check_valid_deploy_spec_path(s: str) -> str:
|
|
6842
7139
|
return s
|
6843
7140
|
|
6844
7141
|
|
7142
|
+
class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
|
7143
|
+
@cached_nullary
|
7144
|
+
def _key_str(self) -> str:
|
7145
|
+
return hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8]
|
7146
|
+
|
7147
|
+
@abc.abstractmethod
|
7148
|
+
def key(self) -> KeyDeployTagT:
|
7149
|
+
raise NotImplementedError
|
7150
|
+
|
7151
|
+
|
6845
7152
|
##
|
6846
7153
|
|
6847
7154
|
|
@@ -6887,7 +7194,7 @@ class DeployVenvSpec:
|
|
6887
7194
|
|
6888
7195
|
|
6889
7196
|
@dc.dataclass(frozen=True)
|
6890
|
-
class
|
7197
|
+
class DeployAppConfFile:
|
6891
7198
|
path: str
|
6892
7199
|
body: str
|
6893
7200
|
|
@@ -6899,7 +7206,7 @@ class DeployConfFile:
|
|
6899
7206
|
|
6900
7207
|
|
6901
7208
|
@dc.dataclass(frozen=True)
|
6902
|
-
class
|
7209
|
+
class DeployAppConfLink(abc.ABC): # noqa
|
6903
7210
|
"""
|
6904
7211
|
May be either:
|
6905
7212
|
- @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -6915,11 +7222,11 @@ class DeployConfLink(abc.ABC): # noqa
|
|
6915
7222
|
check.equal(self.src.count('/'), 1)
|
6916
7223
|
|
6917
7224
|
|
6918
|
-
class
|
7225
|
+
class CurrentOnlyDeployAppConfLink(DeployAppConfLink):
|
6919
7226
|
pass
|
6920
7227
|
|
6921
7228
|
|
6922
|
-
class
|
7229
|
+
class AllActiveDeployAppConfLink(DeployAppConfLink):
|
6923
7230
|
pass
|
6924
7231
|
|
6925
7232
|
|
@@ -6927,10 +7234,10 @@ class TagDeployConfLink(DeployConfLink):
|
|
6927
7234
|
|
6928
7235
|
|
6929
7236
|
@dc.dataclass(frozen=True)
|
6930
|
-
class
|
6931
|
-
files: ta.Optional[ta.Sequence[
|
7237
|
+
class DeployAppConfSpec:
|
7238
|
+
files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
|
6932
7239
|
|
6933
|
-
links: ta.Optional[ta.Sequence[
|
7240
|
+
links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
|
6934
7241
|
|
6935
7242
|
def __post_init__(self) -> None:
|
6936
7243
|
if self.files:
|
@@ -6944,18 +7251,37 @@ class DeployConfSpec:
|
|
6944
7251
|
|
6945
7252
|
|
6946
7253
|
@dc.dataclass(frozen=True)
|
6947
|
-
class
|
7254
|
+
class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
|
6948
7255
|
app: DeployApp
|
6949
7256
|
|
6950
7257
|
git: DeployGitSpec
|
6951
7258
|
|
6952
7259
|
venv: ta.Optional[DeployVenvSpec] = None
|
6953
7260
|
|
6954
|
-
conf: ta.Optional[
|
7261
|
+
conf: ta.Optional[DeployAppConfSpec] = None
|
6955
7262
|
|
6956
|
-
@
|
7263
|
+
# @ta.override
|
7264
|
+
def key(self) -> DeployAppKey:
|
7265
|
+
return DeployAppKey(self._key_str())
|
7266
|
+
|
7267
|
+
|
7268
|
+
##
|
7269
|
+
|
7270
|
+
|
7271
|
+
@dc.dataclass(frozen=True)
|
7272
|
+
class DeploySpec(DeploySpecKeyed[DeployKey]):
|
7273
|
+
apps: ta.Sequence[DeployAppSpec]
|
7274
|
+
|
7275
|
+
def __post_init__(self) -> None:
|
7276
|
+
seen: ta.Set[DeployApp] = set()
|
7277
|
+
for a in self.apps:
|
7278
|
+
if a.app in seen:
|
7279
|
+
raise KeyError(a.app)
|
7280
|
+
seen.add(a.app)
|
7281
|
+
|
7282
|
+
# @ta.override
|
6957
7283
|
def key(self) -> DeployKey:
|
6958
|
-
return DeployKey(
|
7284
|
+
return DeployKey(self._key_str())
|
6959
7285
|
|
6960
7286
|
|
6961
7287
|
########################################
|
@@ -7575,18 +7901,18 @@ class DeployConfManager:
|
|
7575
7901
|
|
7576
7902
|
#
|
7577
7903
|
|
7578
|
-
async def
|
7904
|
+
async def _write_app_conf_file(
|
7579
7905
|
self,
|
7580
|
-
|
7581
|
-
|
7906
|
+
acf: DeployAppConfFile,
|
7907
|
+
app_conf_dir: str,
|
7582
7908
|
) -> None:
|
7583
|
-
conf_file = os.path.join(
|
7584
|
-
check.arg(is_path_in_dir(
|
7909
|
+
conf_file = os.path.join(app_conf_dir, acf.path)
|
7910
|
+
check.arg(is_path_in_dir(app_conf_dir, conf_file))
|
7585
7911
|
|
7586
7912
|
os.makedirs(os.path.dirname(conf_file), exist_ok=True)
|
7587
7913
|
|
7588
7914
|
with open(conf_file, 'w') as f: # noqa
|
7589
|
-
f.write(
|
7915
|
+
f.write(acf.body)
|
7590
7916
|
|
7591
7917
|
#
|
7592
7918
|
|
@@ -7595,15 +7921,18 @@ class DeployConfManager:
|
|
7595
7921
|
link_src: str
|
7596
7922
|
link_dst: str
|
7597
7923
|
|
7598
|
-
|
7924
|
+
_UNIQUE_LINK_NAME_STR = '@app--@time--@app-key'
|
7925
|
+
_UNIQUE_LINK_NAME = DeployPath.parse(_UNIQUE_LINK_NAME_STR)
|
7926
|
+
|
7927
|
+
def _compute_app_conf_link_dst(
|
7599
7928
|
self,
|
7600
|
-
link:
|
7601
|
-
|
7602
|
-
|
7603
|
-
|
7929
|
+
link: DeployAppConfLink,
|
7930
|
+
tags: DeployTagMap,
|
7931
|
+
app_conf_dir: str,
|
7932
|
+
conf_link_dir: str,
|
7604
7933
|
) -> _ComputedConfLink:
|
7605
|
-
link_src = os.path.join(
|
7606
|
-
check.arg(is_path_in_dir(
|
7934
|
+
link_src = os.path.join(app_conf_dir, link.src)
|
7935
|
+
check.arg(is_path_in_dir(app_conf_dir, link_src))
|
7607
7936
|
|
7608
7937
|
#
|
7609
7938
|
|
@@ -7618,7 +7947,7 @@ class DeployConfManager:
|
|
7618
7947
|
d, f = os.path.split(link.src)
|
7619
7948
|
# TODO: check filename :|
|
7620
7949
|
link_dst_pfx = d + '/'
|
7621
|
-
link_dst_sfx =
|
7950
|
+
link_dst_sfx = DEPLOY_TAG_SEPARATOR + f
|
7622
7951
|
|
7623
7952
|
else: # noqa
|
7624
7953
|
# @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -7632,10 +7961,10 @@ class DeployConfManager:
|
|
7632
7961
|
|
7633
7962
|
#
|
7634
7963
|
|
7635
|
-
if isinstance(link,
|
7636
|
-
link_dst_mid = str(
|
7637
|
-
elif isinstance(link,
|
7638
|
-
link_dst_mid =
|
7964
|
+
if isinstance(link, CurrentOnlyDeployAppConfLink):
|
7965
|
+
link_dst_mid = str(tags[DeployApp].s)
|
7966
|
+
elif isinstance(link, AllActiveDeployAppConfLink):
|
7967
|
+
link_dst_mid = self._UNIQUE_LINK_NAME.render(tags)
|
7639
7968
|
else:
|
7640
7969
|
raise TypeError(link)
|
7641
7970
|
|
@@ -7646,7 +7975,7 @@ class DeployConfManager:
|
|
7646
7975
|
link_dst_mid,
|
7647
7976
|
link_dst_sfx,
|
7648
7977
|
])
|
7649
|
-
link_dst = os.path.join(
|
7978
|
+
link_dst = os.path.join(conf_link_dir, link_dst_name)
|
7650
7979
|
|
7651
7980
|
return DeployConfManager._ComputedConfLink(
|
7652
7981
|
is_dir=is_dir,
|
@@ -7654,24 +7983,24 @@ class DeployConfManager:
|
|
7654
7983
|
link_dst=link_dst,
|
7655
7984
|
)
|
7656
7985
|
|
7657
|
-
async def
|
7986
|
+
async def _make_app_conf_link(
|
7658
7987
|
self,
|
7659
|
-
link:
|
7660
|
-
|
7661
|
-
|
7662
|
-
|
7988
|
+
link: DeployAppConfLink,
|
7989
|
+
tags: DeployTagMap,
|
7990
|
+
app_conf_dir: str,
|
7991
|
+
conf_link_dir: str,
|
7663
7992
|
) -> None:
|
7664
|
-
comp = self.
|
7993
|
+
comp = self._compute_app_conf_link_dst(
|
7665
7994
|
link,
|
7666
|
-
|
7667
|
-
|
7668
|
-
|
7995
|
+
tags,
|
7996
|
+
app_conf_dir,
|
7997
|
+
conf_link_dir,
|
7669
7998
|
)
|
7670
7999
|
|
7671
8000
|
#
|
7672
8001
|
|
7673
|
-
check.arg(is_path_in_dir(
|
7674
|
-
check.arg(is_path_in_dir(
|
8002
|
+
check.arg(is_path_in_dir(app_conf_dir, comp.link_src))
|
8003
|
+
check.arg(is_path_in_dir(conf_link_dir, comp.link_dst))
|
7675
8004
|
|
7676
8005
|
if comp.is_dir:
|
7677
8006
|
check.arg(os.path.isdir(comp.link_src))
|
@@ -7689,27 +8018,27 @@ class DeployConfManager:
|
|
7689
8018
|
|
7690
8019
|
#
|
7691
8020
|
|
7692
|
-
async def
|
8021
|
+
async def write_app_conf(
|
7693
8022
|
self,
|
7694
|
-
spec:
|
7695
|
-
|
7696
|
-
|
7697
|
-
|
8023
|
+
spec: DeployAppConfSpec,
|
8024
|
+
tags: DeployTagMap,
|
8025
|
+
app_conf_dir: str,
|
8026
|
+
conf_link_dir: str,
|
7698
8027
|
) -> None:
|
7699
|
-
for
|
7700
|
-
await self.
|
7701
|
-
|
7702
|
-
|
8028
|
+
for acf in spec.files or []:
|
8029
|
+
await self._write_app_conf_file(
|
8030
|
+
acf,
|
8031
|
+
app_conf_dir,
|
7703
8032
|
)
|
7704
8033
|
|
7705
8034
|
#
|
7706
8035
|
|
7707
8036
|
for link in spec.links or []:
|
7708
|
-
await self.
|
8037
|
+
await self._make_app_conf_link(
|
7709
8038
|
link,
|
7710
|
-
|
7711
|
-
|
7712
|
-
|
8039
|
+
tags,
|
8040
|
+
app_conf_dir,
|
8041
|
+
conf_link_dir,
|
7713
8042
|
)
|
7714
8043
|
|
7715
8044
|
|
@@ -9312,19 +9641,6 @@ def bind_commands(
|
|
9312
9641
|
# ../deploy/apps.py
|
9313
9642
|
|
9314
9643
|
|
9315
|
-
def make_deploy_tag(
|
9316
|
-
rev: DeployRev,
|
9317
|
-
key: DeployKey,
|
9318
|
-
*,
|
9319
|
-
utcnow: ta.Optional[datetime.datetime] = None,
|
9320
|
-
) -> DeployTag:
|
9321
|
-
if utcnow is None:
|
9322
|
-
utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
9323
|
-
now_fmt = '%Y%m%dT%H%M%SZ'
|
9324
|
-
now_str = utcnow.strftime(now_fmt)
|
9325
|
-
return DeployTag('-'.join([now_str, rev, key]))
|
9326
|
-
|
9327
|
-
|
9328
9644
|
class DeployAppManager(DeployPathOwner):
|
9329
9645
|
def __init__(
|
9330
9646
|
self,
|
@@ -9345,32 +9661,27 @@ class DeployAppManager(DeployPathOwner):
|
|
9345
9661
|
|
9346
9662
|
#
|
9347
9663
|
|
9348
|
-
|
9349
|
-
|
9664
|
+
_APP_DIR_STR = 'apps/@app/@time--@app-rev--@app-key/'
|
9665
|
+
_APP_DIR = DeployPath.parse(_APP_DIR_STR)
|
9350
9666
|
|
9351
|
-
|
9352
|
-
_CONF_TAG_DIR = DeployPath.parse(_CONF_TAG_DIR_STR)
|
9353
|
-
|
9354
|
-
_DEPLOY_DIR_STR = 'deploys/@tag--@app/'
|
9667
|
+
_DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
|
9355
9668
|
_DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
|
9356
9669
|
|
9357
9670
|
_APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
|
9358
|
-
|
9671
|
+
_CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
|
9359
9672
|
|
9360
9673
|
@cached_nullary
|
9361
9674
|
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
9362
9675
|
return {
|
9363
|
-
self.
|
9364
|
-
|
9365
|
-
self._CONF_TAG_DIR,
|
9676
|
+
self._APP_DIR,
|
9366
9677
|
|
9367
9678
|
self._DEPLOY_DIR,
|
9368
9679
|
|
9369
9680
|
self._APP_DEPLOY_LINK,
|
9370
|
-
self.
|
9681
|
+
self._CONF_DEPLOY_DIR,
|
9371
9682
|
|
9372
9683
|
*[
|
9373
|
-
DeployPath.parse(f'{self.
|
9684
|
+
DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
|
9374
9685
|
for sfx in [
|
9375
9686
|
'conf',
|
9376
9687
|
'git',
|
@@ -9383,26 +9694,21 @@ class DeployAppManager(DeployPathOwner):
|
|
9383
9694
|
|
9384
9695
|
async def prepare_app(
|
9385
9696
|
self,
|
9386
|
-
spec:
|
9697
|
+
spec: DeployAppSpec,
|
9698
|
+
tags: DeployTagMap,
|
9387
9699
|
) -> None:
|
9388
|
-
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.git.rev, spec.key()))
|
9389
|
-
|
9390
|
-
#
|
9391
|
-
|
9392
9700
|
deploy_home = check.non_empty_str(self._deploy_home)
|
9393
9701
|
|
9394
9702
|
def build_path(pth: DeployPath) -> str:
|
9395
|
-
return os.path.join(deploy_home, pth.render(
|
9703
|
+
return os.path.join(deploy_home, pth.render(tags))
|
9396
9704
|
|
9397
|
-
|
9398
|
-
conf_tag_dir = build_path(self._CONF_TAG_DIR)
|
9705
|
+
app_dir = build_path(self._APP_DIR)
|
9399
9706
|
deploy_dir = build_path(self._DEPLOY_DIR)
|
9400
9707
|
app_deploy_link = build_path(self._APP_DEPLOY_LINK)
|
9401
|
-
conf_deploy_link_file = build_path(self._CONF_DEPLOY_LINK)
|
9402
9708
|
|
9403
9709
|
#
|
9404
9710
|
|
9405
|
-
os.makedirs(deploy_dir)
|
9711
|
+
os.makedirs(deploy_dir, exist_ok=True)
|
9406
9712
|
|
9407
9713
|
deploying_link = os.path.join(deploy_home, 'deploys/deploying')
|
9408
9714
|
relative_symlink(
|
@@ -9414,9 +9720,9 @@ class DeployAppManager(DeployPathOwner):
|
|
9414
9720
|
|
9415
9721
|
#
|
9416
9722
|
|
9417
|
-
os.makedirs(
|
9723
|
+
os.makedirs(app_dir)
|
9418
9724
|
relative_symlink(
|
9419
|
-
|
9725
|
+
app_dir,
|
9420
9726
|
app_deploy_link,
|
9421
9727
|
target_is_directory=True,
|
9422
9728
|
make_dirs=True,
|
@@ -9424,37 +9730,33 @@ class DeployAppManager(DeployPathOwner):
|
|
9424
9730
|
|
9425
9731
|
#
|
9426
9732
|
|
9427
|
-
os.
|
9428
|
-
|
9429
|
-
conf_tag_dir,
|
9430
|
-
conf_deploy_link_file,
|
9431
|
-
target_is_directory=True,
|
9432
|
-
make_dirs=True,
|
9433
|
-
)
|
9733
|
+
deploy_conf_dir = os.path.join(deploy_dir, 'conf')
|
9734
|
+
os.makedirs(deploy_conf_dir, exist_ok=True)
|
9434
9735
|
|
9435
9736
|
#
|
9436
9737
|
|
9437
|
-
def mirror_symlinks(src: str, dst: str) -> None:
|
9438
|
-
|
9439
|
-
|
9440
|
-
|
9441
|
-
|
9442
|
-
|
9443
|
-
|
9444
|
-
|
9445
|
-
|
9446
|
-
|
9447
|
-
|
9448
|
-
|
9449
|
-
|
9450
|
-
|
9451
|
-
|
9452
|
-
|
9453
|
-
|
9454
|
-
|
9455
|
-
|
9738
|
+
# def mirror_symlinks(src: str, dst: str) -> None:
|
9739
|
+
# def mirror_link(lp: str) -> None:
|
9740
|
+
# check.state(os.path.islink(lp))
|
9741
|
+
# shutil.copy2(
|
9742
|
+
# lp,
|
9743
|
+
# os.path.join(dst, os.path.relpath(lp, src)),
|
9744
|
+
# follow_symlinks=False,
|
9745
|
+
# )
|
9746
|
+
#
|
9747
|
+
# for dp, dns, fns in os.walk(src, followlinks=False):
|
9748
|
+
# for fn in fns:
|
9749
|
+
# mirror_link(os.path.join(dp, fn))
|
9750
|
+
#
|
9751
|
+
# for dn in dns:
|
9752
|
+
# dp2 = os.path.join(dp, dn)
|
9753
|
+
# if os.path.islink(dp2):
|
9754
|
+
# mirror_link(dp2)
|
9755
|
+
# else:
|
9756
|
+
# os.makedirs(os.path.join(dst, os.path.relpath(dp2, src)))
|
9456
9757
|
|
9457
9758
|
current_link = os.path.join(deploy_home, 'deploys/current')
|
9759
|
+
|
9458
9760
|
# if os.path.exists(current_link):
|
9459
9761
|
# mirror_symlinks(
|
9460
9762
|
# os.path.join(current_link, 'conf'),
|
@@ -9467,31 +9769,31 @@ class DeployAppManager(DeployPathOwner):
|
|
9467
9769
|
|
9468
9770
|
#
|
9469
9771
|
|
9470
|
-
|
9772
|
+
app_git_dir = os.path.join(app_dir, 'git')
|
9471
9773
|
await self._git.checkout(
|
9472
9774
|
spec.git,
|
9473
|
-
|
9775
|
+
app_git_dir,
|
9474
9776
|
)
|
9475
9777
|
|
9476
9778
|
#
|
9477
9779
|
|
9478
9780
|
if spec.venv is not None:
|
9479
|
-
|
9781
|
+
app_venv_dir = os.path.join(app_dir, 'venv')
|
9480
9782
|
await self._venvs.setup_venv(
|
9481
9783
|
spec.venv,
|
9482
|
-
|
9483
|
-
|
9784
|
+
app_git_dir,
|
9785
|
+
app_venv_dir,
|
9484
9786
|
)
|
9485
9787
|
|
9486
9788
|
#
|
9487
9789
|
|
9488
9790
|
if spec.conf is not None:
|
9489
|
-
|
9490
|
-
await self._conf.
|
9791
|
+
app_conf_dir = os.path.join(app_dir, 'conf')
|
9792
|
+
await self._conf.write_app_conf(
|
9491
9793
|
spec.conf,
|
9492
|
-
|
9493
|
-
|
9494
|
-
|
9794
|
+
tags,
|
9795
|
+
app_conf_dir,
|
9796
|
+
deploy_conf_dir,
|
9495
9797
|
)
|
9496
9798
|
|
9497
9799
|
#
|
@@ -10232,18 +10534,37 @@ class SystemInterpProvider(InterpProvider):
|
|
10232
10534
|
# ../deploy/deploy.py
|
10233
10535
|
|
10234
10536
|
|
10537
|
+
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
10538
|
+
|
10539
|
+
|
10540
|
+
DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
|
10541
|
+
|
10542
|
+
|
10235
10543
|
class DeployManager:
|
10236
10544
|
def __init__(
|
10237
10545
|
self,
|
10238
10546
|
*,
|
10239
10547
|
apps: DeployAppManager,
|
10240
10548
|
paths: DeployPathsManager,
|
10549
|
+
|
10550
|
+
utc_clock: ta.Optional[DeployManagerUtcClock] = None,
|
10241
10551
|
):
|
10242
10552
|
super().__init__()
|
10243
10553
|
|
10244
10554
|
self._apps = apps
|
10245
10555
|
self._paths = paths
|
10246
10556
|
|
10557
|
+
self._utc_clock = utc_clock
|
10558
|
+
|
10559
|
+
def _utc_now(self) -> datetime.datetime:
|
10560
|
+
if self._utc_clock is not None:
|
10561
|
+
return self._utc_clock() # noqa
|
10562
|
+
else:
|
10563
|
+
return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
10564
|
+
|
10565
|
+
def _make_deploy_time(self) -> DeployTime:
|
10566
|
+
return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
|
10567
|
+
|
10247
10568
|
async def run_deploy(
|
10248
10569
|
self,
|
10249
10570
|
spec: DeploySpec,
|
@@ -10252,7 +10573,24 @@ class DeployManager:
|
|
10252
10573
|
|
10253
10574
|
#
|
10254
10575
|
|
10255
|
-
|
10576
|
+
deploy_tags = DeployTagMap(
|
10577
|
+
self._make_deploy_time(),
|
10578
|
+
spec.key(),
|
10579
|
+
)
|
10580
|
+
|
10581
|
+
#
|
10582
|
+
|
10583
|
+
for app in spec.apps:
|
10584
|
+
app_tags = deploy_tags.add(
|
10585
|
+
app.app,
|
10586
|
+
app.key(),
|
10587
|
+
DeployAppRev(app.git.rev),
|
10588
|
+
)
|
10589
|
+
|
10590
|
+
await self._apps.prepare_app(
|
10591
|
+
app,
|
10592
|
+
app_tags,
|
10593
|
+
)
|
10256
10594
|
|
10257
10595
|
|
10258
10596
|
########################################
|