ominfra 0.0.0.dev172__py3-none-any.whl → 0.0.0.dev173__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/manage.py +483 -191
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev173.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev173.dist-info}/RECORD +15 -13
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev173.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev173.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev173.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev172.dist-info → ominfra-0.0.0.dev173.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
|
"""
|
@@ -2823,6 +2849,54 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
2823
2849
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
2824
2850
|
|
2825
2851
|
|
2852
|
+
########################################
|
2853
|
+
# ../../../omlish/lite/typing.py
|
2854
|
+
|
2855
|
+
|
2856
|
+
##
|
2857
|
+
# A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
|
2858
|
+
|
2859
|
+
|
2860
|
+
@dc.dataclass(frozen=True)
|
2861
|
+
class AnyFunc(ta.Generic[T]):
|
2862
|
+
fn: ta.Callable[..., T]
|
2863
|
+
|
2864
|
+
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
2865
|
+
return self.fn(*args, **kwargs)
|
2866
|
+
|
2867
|
+
|
2868
|
+
@dc.dataclass(frozen=True)
|
2869
|
+
class Func0(ta.Generic[T]):
|
2870
|
+
fn: ta.Callable[[], T]
|
2871
|
+
|
2872
|
+
def __call__(self) -> T:
|
2873
|
+
return self.fn()
|
2874
|
+
|
2875
|
+
|
2876
|
+
@dc.dataclass(frozen=True)
|
2877
|
+
class Func1(ta.Generic[A0, T]):
|
2878
|
+
fn: ta.Callable[[A0], T]
|
2879
|
+
|
2880
|
+
def __call__(self, a0: A0) -> T:
|
2881
|
+
return self.fn(a0)
|
2882
|
+
|
2883
|
+
|
2884
|
+
@dc.dataclass(frozen=True)
|
2885
|
+
class Func2(ta.Generic[A0, A1, T]):
|
2886
|
+
fn: ta.Callable[[A0, A1], T]
|
2887
|
+
|
2888
|
+
def __call__(self, a0: A0, a1: A1) -> T:
|
2889
|
+
return self.fn(a0, a1)
|
2890
|
+
|
2891
|
+
|
2892
|
+
@dc.dataclass(frozen=True)
|
2893
|
+
class Func3(ta.Generic[A0, A1, A2, T]):
|
2894
|
+
fn: ta.Callable[[A0, A1, A2], T]
|
2895
|
+
|
2896
|
+
def __call__(self, a0: A0, a1: A1, a2: A2) -> T:
|
2897
|
+
return self.fn(a0, a1, a2)
|
2898
|
+
|
2899
|
+
|
2826
2900
|
########################################
|
2827
2901
|
# ../../../omlish/logs/filters.py
|
2828
2902
|
|
@@ -4149,39 +4223,227 @@ def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
|
4149
4223
|
|
4150
4224
|
|
4151
4225
|
########################################
|
4152
|
-
# ../deploy/
|
4226
|
+
# ../deploy/tags.py
|
4153
4227
|
|
4154
4228
|
|
4155
4229
|
##
|
4156
4230
|
|
4157
4231
|
|
4158
|
-
|
4232
|
+
DEPLOY_TAG_SIGIL = '@'
|
4159
4233
|
|
4160
|
-
|
4161
|
-
|
4162
|
-
|
4163
|
-
|
4234
|
+
DEPLOY_TAG_SEPARATOR = '--'
|
4235
|
+
|
4236
|
+
DEPLOY_TAG_DELIMITERS: ta.AbstractSet[str] = frozenset([
|
4237
|
+
DEPLOY_TAG_SEPARATOR,
|
4238
|
+
'.',
|
4239
|
+
])
|
4240
|
+
|
4241
|
+
DEPLOY_TAG_ILLEGAL_STRS: ta.AbstractSet[str] = frozenset([
|
4242
|
+
DEPLOY_TAG_SIGIL,
|
4243
|
+
*DEPLOY_TAG_DELIMITERS,
|
4244
|
+
'/',
|
4245
|
+
])
|
4164
4246
|
|
4165
4247
|
|
4166
4248
|
##
|
4167
4249
|
|
4168
4250
|
|
4169
4251
|
@dc.dataclass(frozen=True)
|
4170
|
-
class
|
4171
|
-
|
4172
|
-
tag: DeployTag
|
4252
|
+
class DeployTag(abc.ABC): # noqa
|
4253
|
+
s: str
|
4173
4254
|
|
4174
4255
|
def __post_init__(self) -> None:
|
4175
|
-
|
4176
|
-
|
4177
|
-
|
4256
|
+
check.not_in(abc.ABC, type(self).__bases__)
|
4257
|
+
check.non_empty_str(self.s)
|
4258
|
+
for ch in DEPLOY_TAG_ILLEGAL_STRS:
|
4259
|
+
check.state(ch not in self.s)
|
4178
4260
|
|
4179
|
-
|
4180
|
-
|
4181
|
-
|
4182
|
-
|
4261
|
+
#
|
4262
|
+
|
4263
|
+
tag_name: ta.ClassVar[str]
|
4264
|
+
tag_kwarg: ta.ClassVar[str]
|
4265
|
+
|
4266
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
4267
|
+
super().__init_subclass__(**kwargs)
|
4268
|
+
|
4269
|
+
if abc.ABC in cls.__bases__:
|
4270
|
+
return
|
4271
|
+
|
4272
|
+
for b in cls.__bases__:
|
4273
|
+
if issubclass(b, DeployTag):
|
4274
|
+
check.in_(abc.ABC, b.__bases__)
|
4275
|
+
|
4276
|
+
check.non_empty_str(tn := cls.tag_name)
|
4277
|
+
check.equal(tn, tn.lower().strip())
|
4278
|
+
check.not_in('_', tn)
|
4279
|
+
|
4280
|
+
check.state(not hasattr(cls, 'tag_kwarg'))
|
4281
|
+
cls.tag_kwarg = tn.replace('-', '_')
|
4282
|
+
|
4283
|
+
|
4284
|
+
##
|
4285
|
+
|
4286
|
+
|
4287
|
+
_DEPLOY_TAGS: ta.Set[ta.Type[DeployTag]] = set()
|
4288
|
+
DEPLOY_TAGS: ta.AbstractSet[ta.Type[DeployTag]] = _DEPLOY_TAGS
|
4289
|
+
|
4290
|
+
_DEPLOY_TAGS_BY_NAME: ta.Dict[str, ta.Type[DeployTag]] = {}
|
4291
|
+
DEPLOY_TAGS_BY_NAME: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_NAME
|
4292
|
+
|
4293
|
+
_DEPLOY_TAGS_BY_KWARG: ta.Dict[str, ta.Type[DeployTag]] = {}
|
4294
|
+
DEPLOY_TAGS_BY_KWARG: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_KWARG
|
4295
|
+
|
4296
|
+
|
4297
|
+
def _register_deploy_tag(cls):
|
4298
|
+
check.not_in(cls.tag_name, _DEPLOY_TAGS_BY_NAME)
|
4299
|
+
check.not_in(cls.tag_kwarg, _DEPLOY_TAGS_BY_KWARG)
|
4300
|
+
|
4301
|
+
_DEPLOY_TAGS.add(cls)
|
4302
|
+
_DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
|
4303
|
+
_DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
|
4304
|
+
|
4305
|
+
return cls
|
4306
|
+
|
4307
|
+
|
4308
|
+
##
|
4309
|
+
|
4310
|
+
|
4311
|
+
@_register_deploy_tag
|
4312
|
+
class DeployTime(DeployTag):
|
4313
|
+
tag_name: ta.ClassVar[str] = 'time'
|
4314
|
+
|
4315
|
+
|
4316
|
+
##
|
4317
|
+
|
4318
|
+
|
4319
|
+
class NameDeployTag(DeployTag, abc.ABC): # noqa
|
4320
|
+
pass
|
4321
|
+
|
4322
|
+
|
4323
|
+
@_register_deploy_tag
|
4324
|
+
class DeployApp(NameDeployTag):
|
4325
|
+
tag_name: ta.ClassVar[str] = 'app'
|
4326
|
+
|
4327
|
+
|
4328
|
+
@_register_deploy_tag
|
4329
|
+
class DeployConf(NameDeployTag):
|
4330
|
+
tag_name: ta.ClassVar[str] = 'conf'
|
4331
|
+
|
4332
|
+
|
4333
|
+
##
|
4334
|
+
|
4335
|
+
|
4336
|
+
class KeyDeployTag(DeployTag, abc.ABC): # noqa
|
4337
|
+
pass
|
4338
|
+
|
4339
|
+
|
4340
|
+
@_register_deploy_tag
|
4341
|
+
class DeployKey(KeyDeployTag):
|
4342
|
+
tag_name: ta.ClassVar[str] = 'deploy-key'
|
4343
|
+
|
4344
|
+
|
4345
|
+
@_register_deploy_tag
|
4346
|
+
class DeployAppKey(KeyDeployTag):
|
4347
|
+
tag_name: ta.ClassVar[str] = 'app-key'
|
4348
|
+
|
4349
|
+
|
4350
|
+
##
|
4351
|
+
|
4352
|
+
|
4353
|
+
class RevDeployTag(DeployTag, abc.ABC): # noqa
|
4354
|
+
pass
|
4355
|
+
|
4356
|
+
|
4357
|
+
@_register_deploy_tag
|
4358
|
+
class DeployAppRev(RevDeployTag):
|
4359
|
+
tag_name: ta.ClassVar[str] = 'app-rev'
|
4360
|
+
|
4361
|
+
|
4362
|
+
##
|
4363
|
+
|
4364
|
+
|
4365
|
+
class DeployTagMap:
|
4366
|
+
def __init__(
|
4367
|
+
self,
|
4368
|
+
*args: DeployTag,
|
4369
|
+
**kwargs: str,
|
4370
|
+
) -> None:
|
4371
|
+
super().__init__()
|
4372
|
+
|
4373
|
+
dct: ta.Dict[ta.Type[DeployTag], DeployTag] = {}
|
4374
|
+
|
4375
|
+
for a in args:
|
4376
|
+
c = type(check.isinstance(a, DeployTag))
|
4377
|
+
check.not_in(c, dct)
|
4378
|
+
dct[c] = a
|
4379
|
+
|
4380
|
+
for k, v in kwargs.items():
|
4381
|
+
c = DEPLOY_TAGS_BY_KWARG[k]
|
4382
|
+
check.not_in(c, dct)
|
4383
|
+
dct[c] = c(v)
|
4384
|
+
|
4385
|
+
self._dct = dct
|
4386
|
+
self._tup = tuple(sorted((type(t).tag_kwarg, t.s) for t in dct.values()))
|
4387
|
+
|
4388
|
+
#
|
4389
|
+
|
4390
|
+
def add(self, *args: ta.Any, **kwargs: ta.Any) -> 'DeployTagMap':
|
4391
|
+
return DeployTagMap(
|
4392
|
+
*self,
|
4393
|
+
*args,
|
4394
|
+
**kwargs,
|
4395
|
+
)
|
4396
|
+
|
4397
|
+
def remove(self, *tags_or_names: ta.Union[ta.Type[DeployTag], str]) -> 'DeployTagMap':
|
4398
|
+
dcs = {
|
4399
|
+
check.issubclass(a, DeployTag) if isinstance(a, type) else DEPLOY_TAGS_BY_NAME[a]
|
4400
|
+
for a in tags_or_names
|
4183
4401
|
}
|
4184
4402
|
|
4403
|
+
return DeployTagMap(*[
|
4404
|
+
t
|
4405
|
+
for t in self._dct.values()
|
4406
|
+
if t not in dcs
|
4407
|
+
])
|
4408
|
+
|
4409
|
+
#
|
4410
|
+
|
4411
|
+
def __repr__(self) -> str:
|
4412
|
+
return f'{self.__class__.__name__}({", ".join(f"{k}={v!r}" for k, v in self._tup)})'
|
4413
|
+
|
4414
|
+
def __hash__(self) -> int:
|
4415
|
+
return hash(self._tup)
|
4416
|
+
|
4417
|
+
def __eq__(self, other: object) -> bool:
|
4418
|
+
if isinstance(other, DeployTagMap):
|
4419
|
+
return self._tup == other._tup
|
4420
|
+
else:
|
4421
|
+
return NotImplemented
|
4422
|
+
|
4423
|
+
#
|
4424
|
+
|
4425
|
+
def __len__(self) -> int:
|
4426
|
+
return len(self._dct)
|
4427
|
+
|
4428
|
+
def __iter__(self) -> ta.Iterator[DeployTag]:
|
4429
|
+
return iter(self._dct.values())
|
4430
|
+
|
4431
|
+
def __getitem__(self, key: ta.Union[ta.Type[DeployTag], str]) -> DeployTag:
|
4432
|
+
if isinstance(key, str):
|
4433
|
+
return self._dct[DEPLOY_TAGS_BY_NAME[key]]
|
4434
|
+
elif isinstance(key, type):
|
4435
|
+
return self._dct[key]
|
4436
|
+
else:
|
4437
|
+
raise TypeError(key)
|
4438
|
+
|
4439
|
+
def __contains__(self, key: ta.Union[ta.Type[DeployTag], str]) -> bool:
|
4440
|
+
if isinstance(key, str):
|
4441
|
+
return DEPLOY_TAGS_BY_NAME[key] in self._dct
|
4442
|
+
elif isinstance(key, type):
|
4443
|
+
return key in self._dct
|
4444
|
+
else:
|
4445
|
+
raise TypeError(key)
|
4446
|
+
|
4185
4447
|
|
4186
4448
|
########################################
|
4187
4449
|
# ../remote/config.py
|
@@ -6634,28 +6896,13 @@ TODO:
|
|
6634
6896
|
##
|
6635
6897
|
|
6636
6898
|
|
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
6899
|
class DeployPathError(Exception):
|
6653
6900
|
pass
|
6654
6901
|
|
6655
6902
|
|
6656
6903
|
class DeployPathRenderable(abc.ABC):
|
6657
6904
|
@abc.abstractmethod
|
6658
|
-
def render(self,
|
6905
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6659
6906
|
raise NotImplementedError
|
6660
6907
|
|
6661
6908
|
|
@@ -6666,26 +6913,30 @@ class DeployPathNamePart(DeployPathRenderable, abc.ABC):
|
|
6666
6913
|
@classmethod
|
6667
6914
|
def parse(cls, s: str) -> 'DeployPathNamePart':
|
6668
6915
|
check.non_empty_str(s)
|
6669
|
-
if s.startswith(
|
6670
|
-
return
|
6671
|
-
elif s in
|
6916
|
+
if s.startswith(DEPLOY_TAG_SIGIL):
|
6917
|
+
return TagDeployPathNamePart(s[1:])
|
6918
|
+
elif s in DEPLOY_TAG_DELIMITERS:
|
6672
6919
|
return DelimiterDeployPathNamePart(s)
|
6673
6920
|
else:
|
6674
6921
|
return ConstDeployPathNamePart(s)
|
6675
6922
|
|
6676
6923
|
|
6677
6924
|
@dc.dataclass(frozen=True)
|
6678
|
-
class
|
6679
|
-
|
6925
|
+
class TagDeployPathNamePart(DeployPathNamePart):
|
6926
|
+
name: str
|
6680
6927
|
|
6681
6928
|
def __post_init__(self) -> None:
|
6682
|
-
check.in_(self.
|
6929
|
+
check.in_(self.name, DEPLOY_TAGS_BY_NAME)
|
6683
6930
|
|
6684
|
-
|
6685
|
-
|
6686
|
-
|
6931
|
+
@property
|
6932
|
+
def tag(self) -> ta.Type[DeployTag]:
|
6933
|
+
return DEPLOY_TAGS_BY_NAME[self.name]
|
6934
|
+
|
6935
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6936
|
+
if tags is not None:
|
6937
|
+
return tags[self.tag].s
|
6687
6938
|
else:
|
6688
|
-
return
|
6939
|
+
return DEPLOY_TAG_SIGIL + self.name
|
6689
6940
|
|
6690
6941
|
|
6691
6942
|
@dc.dataclass(frozen=True)
|
@@ -6693,9 +6944,9 @@ class DelimiterDeployPathNamePart(DeployPathNamePart):
|
|
6693
6944
|
delimiter: str
|
6694
6945
|
|
6695
6946
|
def __post_init__(self) -> None:
|
6696
|
-
check.in_(self.delimiter,
|
6947
|
+
check.in_(self.delimiter, DEPLOY_TAG_DELIMITERS)
|
6697
6948
|
|
6698
|
-
def render(self,
|
6949
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6699
6950
|
return self.delimiter
|
6700
6951
|
|
6701
6952
|
|
@@ -6705,10 +6956,10 @@ class ConstDeployPathNamePart(DeployPathNamePart):
|
|
6705
6956
|
|
6706
6957
|
def __post_init__(self) -> None:
|
6707
6958
|
check.non_empty_str(self.const)
|
6708
|
-
for c in [*
|
6959
|
+
for c in [*DEPLOY_TAG_DELIMITERS, DEPLOY_TAG_SIGIL, '/']:
|
6709
6960
|
check.not_in(c, self.const)
|
6710
6961
|
|
6711
|
-
def render(self,
|
6962
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6712
6963
|
return self.const
|
6713
6964
|
|
6714
6965
|
|
@@ -6723,8 +6974,8 @@ class DeployPathName(DeployPathRenderable):
|
|
6723
6974
|
if len(gl := list(g)) > 1:
|
6724
6975
|
raise DeployPathError(f'May not have consecutive path name part types: {k} {gl}')
|
6725
6976
|
|
6726
|
-
def render(self,
|
6727
|
-
return ''.join(p.render(
|
6977
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6978
|
+
return ''.join(p.render(tags) for p in self.parts)
|
6728
6979
|
|
6729
6980
|
@classmethod
|
6730
6981
|
def parse(cls, s: str) -> 'DeployPathName':
|
@@ -6734,7 +6985,7 @@ class DeployPathName(DeployPathRenderable):
|
|
6734
6985
|
i = 0
|
6735
6986
|
ps = []
|
6736
6987
|
while i < len(s):
|
6737
|
-
ns = [(n, d) for d in
|
6988
|
+
ns = [(n, d) for d in DEPLOY_TAG_DELIMITERS if (n := s.find(d, i)) >= 0]
|
6738
6989
|
if not ns:
|
6739
6990
|
ps.append(s[i:])
|
6740
6991
|
break
|
@@ -6758,8 +7009,8 @@ class DeployPathPart(DeployPathRenderable, abc.ABC): # noqa
|
|
6758
7009
|
def kind(self) -> DeployPathKind:
|
6759
7010
|
raise NotImplementedError
|
6760
7011
|
|
6761
|
-
def render(self,
|
6762
|
-
return self.name.render(
|
7012
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
7013
|
+
return self.name.render(tags) + ('/' if self.kind == 'dir' else '')
|
6763
7014
|
|
6764
7015
|
@classmethod
|
6765
7016
|
def parse(cls, s: str) -> 'DeployPathPart':
|
@@ -6805,20 +7056,20 @@ class DeployPath:
|
|
6805
7056
|
for p in self.parts[:-1]:
|
6806
7057
|
check.equal(p.kind, 'dir')
|
6807
7058
|
|
6808
|
-
|
7059
|
+
@cached_nullary
|
7060
|
+
def tag_indices(self) -> ta.Mapping[ta.Type[DeployTag], ta.Sequence[int]]:
|
7061
|
+
pd: ta.Dict[ta.Type[DeployTag], ta.List[int]] = {}
|
6809
7062
|
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)
|
7063
|
+
if isinstance(np, TagDeployPathNamePart):
|
7064
|
+
pd.setdefault(np.tag, []).append(i)
|
7065
|
+
return pd
|
6815
7066
|
|
6816
7067
|
@property
|
6817
7068
|
def kind(self) -> ta.Literal['file', 'dir']:
|
6818
7069
|
return self.parts[-1].kind
|
6819
7070
|
|
6820
|
-
def render(self,
|
6821
|
-
return ''.join([p.render(
|
7071
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
7072
|
+
return ''.join([p.render(tags) for p in self.parts])
|
6822
7073
|
|
6823
7074
|
@classmethod
|
6824
7075
|
def parse(cls, s: str) -> 'DeployPath':
|
@@ -6842,6 +7093,16 @@ def check_valid_deploy_spec_path(s: str) -> str:
|
|
6842
7093
|
return s
|
6843
7094
|
|
6844
7095
|
|
7096
|
+
class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
|
7097
|
+
@cached_nullary
|
7098
|
+
def _key_str(self) -> str:
|
7099
|
+
return hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8]
|
7100
|
+
|
7101
|
+
@abc.abstractmethod
|
7102
|
+
def key(self) -> KeyDeployTagT:
|
7103
|
+
raise NotImplementedError
|
7104
|
+
|
7105
|
+
|
6845
7106
|
##
|
6846
7107
|
|
6847
7108
|
|
@@ -6887,7 +7148,7 @@ class DeployVenvSpec:
|
|
6887
7148
|
|
6888
7149
|
|
6889
7150
|
@dc.dataclass(frozen=True)
|
6890
|
-
class
|
7151
|
+
class DeployAppConfFile:
|
6891
7152
|
path: str
|
6892
7153
|
body: str
|
6893
7154
|
|
@@ -6899,7 +7160,7 @@ class DeployConfFile:
|
|
6899
7160
|
|
6900
7161
|
|
6901
7162
|
@dc.dataclass(frozen=True)
|
6902
|
-
class
|
7163
|
+
class DeployAppConfLink(abc.ABC): # noqa
|
6903
7164
|
"""
|
6904
7165
|
May be either:
|
6905
7166
|
- @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -6915,11 +7176,11 @@ class DeployConfLink(abc.ABC): # noqa
|
|
6915
7176
|
check.equal(self.src.count('/'), 1)
|
6916
7177
|
|
6917
7178
|
|
6918
|
-
class
|
7179
|
+
class CurrentOnlyDeployAppConfLink(DeployAppConfLink):
|
6919
7180
|
pass
|
6920
7181
|
|
6921
7182
|
|
6922
|
-
class
|
7183
|
+
class AllActiveDeployAppConfLink(DeployAppConfLink):
|
6923
7184
|
pass
|
6924
7185
|
|
6925
7186
|
|
@@ -6927,10 +7188,10 @@ class TagDeployConfLink(DeployConfLink):
|
|
6927
7188
|
|
6928
7189
|
|
6929
7190
|
@dc.dataclass(frozen=True)
|
6930
|
-
class
|
6931
|
-
files: ta.Optional[ta.Sequence[
|
7191
|
+
class DeployAppConfSpec:
|
7192
|
+
files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
|
6932
7193
|
|
6933
|
-
links: ta.Optional[ta.Sequence[
|
7194
|
+
links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
|
6934
7195
|
|
6935
7196
|
def __post_init__(self) -> None:
|
6936
7197
|
if self.files:
|
@@ -6944,18 +7205,37 @@ class DeployConfSpec:
|
|
6944
7205
|
|
6945
7206
|
|
6946
7207
|
@dc.dataclass(frozen=True)
|
6947
|
-
class
|
7208
|
+
class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
|
6948
7209
|
app: DeployApp
|
6949
7210
|
|
6950
7211
|
git: DeployGitSpec
|
6951
7212
|
|
6952
7213
|
venv: ta.Optional[DeployVenvSpec] = None
|
6953
7214
|
|
6954
|
-
conf: ta.Optional[
|
7215
|
+
conf: ta.Optional[DeployAppConfSpec] = None
|
6955
7216
|
|
6956
|
-
@
|
7217
|
+
# @ta.override
|
7218
|
+
def key(self) -> DeployAppKey:
|
7219
|
+
return DeployAppKey(self._key_str())
|
7220
|
+
|
7221
|
+
|
7222
|
+
##
|
7223
|
+
|
7224
|
+
|
7225
|
+
@dc.dataclass(frozen=True)
|
7226
|
+
class DeploySpec(DeploySpecKeyed[DeployKey]):
|
7227
|
+
apps: ta.Sequence[DeployAppSpec]
|
7228
|
+
|
7229
|
+
def __post_init__(self) -> None:
|
7230
|
+
seen: ta.Set[DeployApp] = set()
|
7231
|
+
for a in self.apps:
|
7232
|
+
if a.app in seen:
|
7233
|
+
raise KeyError(a.app)
|
7234
|
+
seen.add(a.app)
|
7235
|
+
|
7236
|
+
# @ta.override
|
6957
7237
|
def key(self) -> DeployKey:
|
6958
|
-
return DeployKey(
|
7238
|
+
return DeployKey(self._key_str())
|
6959
7239
|
|
6960
7240
|
|
6961
7241
|
########################################
|
@@ -7575,18 +7855,18 @@ class DeployConfManager:
|
|
7575
7855
|
|
7576
7856
|
#
|
7577
7857
|
|
7578
|
-
async def
|
7858
|
+
async def _write_app_conf_file(
|
7579
7859
|
self,
|
7580
|
-
|
7581
|
-
|
7860
|
+
acf: DeployAppConfFile,
|
7861
|
+
app_conf_dir: str,
|
7582
7862
|
) -> None:
|
7583
|
-
conf_file = os.path.join(
|
7584
|
-
check.arg(is_path_in_dir(
|
7863
|
+
conf_file = os.path.join(app_conf_dir, acf.path)
|
7864
|
+
check.arg(is_path_in_dir(app_conf_dir, conf_file))
|
7585
7865
|
|
7586
7866
|
os.makedirs(os.path.dirname(conf_file), exist_ok=True)
|
7587
7867
|
|
7588
7868
|
with open(conf_file, 'w') as f: # noqa
|
7589
|
-
f.write(
|
7869
|
+
f.write(acf.body)
|
7590
7870
|
|
7591
7871
|
#
|
7592
7872
|
|
@@ -7595,15 +7875,18 @@ class DeployConfManager:
|
|
7595
7875
|
link_src: str
|
7596
7876
|
link_dst: str
|
7597
7877
|
|
7598
|
-
|
7878
|
+
_UNIQUE_LINK_NAME_STR = '@app--@time--@app-key'
|
7879
|
+
_UNIQUE_LINK_NAME = DeployPath.parse(_UNIQUE_LINK_NAME_STR)
|
7880
|
+
|
7881
|
+
def _compute_app_conf_link_dst(
|
7599
7882
|
self,
|
7600
|
-
link:
|
7601
|
-
|
7602
|
-
|
7603
|
-
|
7883
|
+
link: DeployAppConfLink,
|
7884
|
+
tags: DeployTagMap,
|
7885
|
+
app_conf_dir: str,
|
7886
|
+
conf_link_dir: str,
|
7604
7887
|
) -> _ComputedConfLink:
|
7605
|
-
link_src = os.path.join(
|
7606
|
-
check.arg(is_path_in_dir(
|
7888
|
+
link_src = os.path.join(app_conf_dir, link.src)
|
7889
|
+
check.arg(is_path_in_dir(app_conf_dir, link_src))
|
7607
7890
|
|
7608
7891
|
#
|
7609
7892
|
|
@@ -7618,7 +7901,7 @@ class DeployConfManager:
|
|
7618
7901
|
d, f = os.path.split(link.src)
|
7619
7902
|
# TODO: check filename :|
|
7620
7903
|
link_dst_pfx = d + '/'
|
7621
|
-
link_dst_sfx =
|
7904
|
+
link_dst_sfx = DEPLOY_TAG_SEPARATOR + f
|
7622
7905
|
|
7623
7906
|
else: # noqa
|
7624
7907
|
# @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -7632,10 +7915,10 @@ class DeployConfManager:
|
|
7632
7915
|
|
7633
7916
|
#
|
7634
7917
|
|
7635
|
-
if isinstance(link,
|
7636
|
-
link_dst_mid = str(
|
7637
|
-
elif isinstance(link,
|
7638
|
-
link_dst_mid =
|
7918
|
+
if isinstance(link, CurrentOnlyDeployAppConfLink):
|
7919
|
+
link_dst_mid = str(tags[DeployApp].s)
|
7920
|
+
elif isinstance(link, AllActiveDeployAppConfLink):
|
7921
|
+
link_dst_mid = self._UNIQUE_LINK_NAME.render(tags)
|
7639
7922
|
else:
|
7640
7923
|
raise TypeError(link)
|
7641
7924
|
|
@@ -7646,7 +7929,7 @@ class DeployConfManager:
|
|
7646
7929
|
link_dst_mid,
|
7647
7930
|
link_dst_sfx,
|
7648
7931
|
])
|
7649
|
-
link_dst = os.path.join(
|
7932
|
+
link_dst = os.path.join(conf_link_dir, link_dst_name)
|
7650
7933
|
|
7651
7934
|
return DeployConfManager._ComputedConfLink(
|
7652
7935
|
is_dir=is_dir,
|
@@ -7654,24 +7937,24 @@ class DeployConfManager:
|
|
7654
7937
|
link_dst=link_dst,
|
7655
7938
|
)
|
7656
7939
|
|
7657
|
-
async def
|
7940
|
+
async def _make_app_conf_link(
|
7658
7941
|
self,
|
7659
|
-
link:
|
7660
|
-
|
7661
|
-
|
7662
|
-
|
7942
|
+
link: DeployAppConfLink,
|
7943
|
+
tags: DeployTagMap,
|
7944
|
+
app_conf_dir: str,
|
7945
|
+
conf_link_dir: str,
|
7663
7946
|
) -> None:
|
7664
|
-
comp = self.
|
7947
|
+
comp = self._compute_app_conf_link_dst(
|
7665
7948
|
link,
|
7666
|
-
|
7667
|
-
|
7668
|
-
|
7949
|
+
tags,
|
7950
|
+
app_conf_dir,
|
7951
|
+
conf_link_dir,
|
7669
7952
|
)
|
7670
7953
|
|
7671
7954
|
#
|
7672
7955
|
|
7673
|
-
check.arg(is_path_in_dir(
|
7674
|
-
check.arg(is_path_in_dir(
|
7956
|
+
check.arg(is_path_in_dir(app_conf_dir, comp.link_src))
|
7957
|
+
check.arg(is_path_in_dir(conf_link_dir, comp.link_dst))
|
7675
7958
|
|
7676
7959
|
if comp.is_dir:
|
7677
7960
|
check.arg(os.path.isdir(comp.link_src))
|
@@ -7689,27 +7972,27 @@ class DeployConfManager:
|
|
7689
7972
|
|
7690
7973
|
#
|
7691
7974
|
|
7692
|
-
async def
|
7975
|
+
async def write_app_conf(
|
7693
7976
|
self,
|
7694
|
-
spec:
|
7695
|
-
|
7696
|
-
|
7697
|
-
|
7977
|
+
spec: DeployAppConfSpec,
|
7978
|
+
tags: DeployTagMap,
|
7979
|
+
app_conf_dir: str,
|
7980
|
+
conf_link_dir: str,
|
7698
7981
|
) -> None:
|
7699
|
-
for
|
7700
|
-
await self.
|
7701
|
-
|
7702
|
-
|
7982
|
+
for acf in spec.files or []:
|
7983
|
+
await self._write_app_conf_file(
|
7984
|
+
acf,
|
7985
|
+
app_conf_dir,
|
7703
7986
|
)
|
7704
7987
|
|
7705
7988
|
#
|
7706
7989
|
|
7707
7990
|
for link in spec.links or []:
|
7708
|
-
await self.
|
7991
|
+
await self._make_app_conf_link(
|
7709
7992
|
link,
|
7710
|
-
|
7711
|
-
|
7712
|
-
|
7993
|
+
tags,
|
7994
|
+
app_conf_dir,
|
7995
|
+
conf_link_dir,
|
7713
7996
|
)
|
7714
7997
|
|
7715
7998
|
|
@@ -9312,19 +9595,6 @@ def bind_commands(
|
|
9312
9595
|
# ../deploy/apps.py
|
9313
9596
|
|
9314
9597
|
|
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
9598
|
class DeployAppManager(DeployPathOwner):
|
9329
9599
|
def __init__(
|
9330
9600
|
self,
|
@@ -9345,32 +9615,27 @@ class DeployAppManager(DeployPathOwner):
|
|
9345
9615
|
|
9346
9616
|
#
|
9347
9617
|
|
9348
|
-
|
9349
|
-
|
9350
|
-
|
9351
|
-
_CONF_TAG_DIR_STR = 'tags/conf/@tag--@app/'
|
9352
|
-
_CONF_TAG_DIR = DeployPath.parse(_CONF_TAG_DIR_STR)
|
9618
|
+
_APP_DIR_STR = 'apps/@app/@time--@app-rev--@app-key/'
|
9619
|
+
_APP_DIR = DeployPath.parse(_APP_DIR_STR)
|
9353
9620
|
|
9354
|
-
_DEPLOY_DIR_STR = 'deploys/@
|
9621
|
+
_DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
|
9355
9622
|
_DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
|
9356
9623
|
|
9357
9624
|
_APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
|
9358
|
-
|
9625
|
+
_CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
|
9359
9626
|
|
9360
9627
|
@cached_nullary
|
9361
9628
|
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
9362
9629
|
return {
|
9363
|
-
self.
|
9364
|
-
|
9365
|
-
self._CONF_TAG_DIR,
|
9630
|
+
self._APP_DIR,
|
9366
9631
|
|
9367
9632
|
self._DEPLOY_DIR,
|
9368
9633
|
|
9369
9634
|
self._APP_DEPLOY_LINK,
|
9370
|
-
self.
|
9635
|
+
self._CONF_DEPLOY_DIR,
|
9371
9636
|
|
9372
9637
|
*[
|
9373
|
-
DeployPath.parse(f'{self.
|
9638
|
+
DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
|
9374
9639
|
for sfx in [
|
9375
9640
|
'conf',
|
9376
9641
|
'git',
|
@@ -9383,26 +9648,21 @@ class DeployAppManager(DeployPathOwner):
|
|
9383
9648
|
|
9384
9649
|
async def prepare_app(
|
9385
9650
|
self,
|
9386
|
-
spec:
|
9651
|
+
spec: DeployAppSpec,
|
9652
|
+
tags: DeployTagMap,
|
9387
9653
|
) -> None:
|
9388
|
-
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.git.rev, spec.key()))
|
9389
|
-
|
9390
|
-
#
|
9391
|
-
|
9392
9654
|
deploy_home = check.non_empty_str(self._deploy_home)
|
9393
9655
|
|
9394
9656
|
def build_path(pth: DeployPath) -> str:
|
9395
|
-
return os.path.join(deploy_home, pth.render(
|
9657
|
+
return os.path.join(deploy_home, pth.render(tags))
|
9396
9658
|
|
9397
|
-
|
9398
|
-
conf_tag_dir = build_path(self._CONF_TAG_DIR)
|
9659
|
+
app_dir = build_path(self._APP_DIR)
|
9399
9660
|
deploy_dir = build_path(self._DEPLOY_DIR)
|
9400
9661
|
app_deploy_link = build_path(self._APP_DEPLOY_LINK)
|
9401
|
-
conf_deploy_link_file = build_path(self._CONF_DEPLOY_LINK)
|
9402
9662
|
|
9403
9663
|
#
|
9404
9664
|
|
9405
|
-
os.makedirs(deploy_dir)
|
9665
|
+
os.makedirs(deploy_dir, exist_ok=True)
|
9406
9666
|
|
9407
9667
|
deploying_link = os.path.join(deploy_home, 'deploys/deploying')
|
9408
9668
|
relative_symlink(
|
@@ -9414,9 +9674,9 @@ class DeployAppManager(DeployPathOwner):
|
|
9414
9674
|
|
9415
9675
|
#
|
9416
9676
|
|
9417
|
-
os.makedirs(
|
9677
|
+
os.makedirs(app_dir)
|
9418
9678
|
relative_symlink(
|
9419
|
-
|
9679
|
+
app_dir,
|
9420
9680
|
app_deploy_link,
|
9421
9681
|
target_is_directory=True,
|
9422
9682
|
make_dirs=True,
|
@@ -9424,37 +9684,33 @@ class DeployAppManager(DeployPathOwner):
|
|
9424
9684
|
|
9425
9685
|
#
|
9426
9686
|
|
9427
|
-
os.
|
9428
|
-
|
9429
|
-
conf_tag_dir,
|
9430
|
-
conf_deploy_link_file,
|
9431
|
-
target_is_directory=True,
|
9432
|
-
make_dirs=True,
|
9433
|
-
)
|
9687
|
+
deploy_conf_dir = os.path.join(deploy_dir, 'conf')
|
9688
|
+
os.makedirs(deploy_conf_dir, exist_ok=True)
|
9434
9689
|
|
9435
9690
|
#
|
9436
9691
|
|
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
|
-
|
9692
|
+
# def mirror_symlinks(src: str, dst: str) -> None:
|
9693
|
+
# def mirror_link(lp: str) -> None:
|
9694
|
+
# check.state(os.path.islink(lp))
|
9695
|
+
# shutil.copy2(
|
9696
|
+
# lp,
|
9697
|
+
# os.path.join(dst, os.path.relpath(lp, src)),
|
9698
|
+
# follow_symlinks=False,
|
9699
|
+
# )
|
9700
|
+
#
|
9701
|
+
# for dp, dns, fns in os.walk(src, followlinks=False):
|
9702
|
+
# for fn in fns:
|
9703
|
+
# mirror_link(os.path.join(dp, fn))
|
9704
|
+
#
|
9705
|
+
# for dn in dns:
|
9706
|
+
# dp2 = os.path.join(dp, dn)
|
9707
|
+
# if os.path.islink(dp2):
|
9708
|
+
# mirror_link(dp2)
|
9709
|
+
# else:
|
9710
|
+
# os.makedirs(os.path.join(dst, os.path.relpath(dp2, src)))
|
9456
9711
|
|
9457
9712
|
current_link = os.path.join(deploy_home, 'deploys/current')
|
9713
|
+
|
9458
9714
|
# if os.path.exists(current_link):
|
9459
9715
|
# mirror_symlinks(
|
9460
9716
|
# os.path.join(current_link, 'conf'),
|
@@ -9467,31 +9723,31 @@ class DeployAppManager(DeployPathOwner):
|
|
9467
9723
|
|
9468
9724
|
#
|
9469
9725
|
|
9470
|
-
|
9726
|
+
app_git_dir = os.path.join(app_dir, 'git')
|
9471
9727
|
await self._git.checkout(
|
9472
9728
|
spec.git,
|
9473
|
-
|
9729
|
+
app_git_dir,
|
9474
9730
|
)
|
9475
9731
|
|
9476
9732
|
#
|
9477
9733
|
|
9478
9734
|
if spec.venv is not None:
|
9479
|
-
|
9735
|
+
app_venv_dir = os.path.join(app_dir, 'venv')
|
9480
9736
|
await self._venvs.setup_venv(
|
9481
9737
|
spec.venv,
|
9482
|
-
|
9483
|
-
|
9738
|
+
app_git_dir,
|
9739
|
+
app_venv_dir,
|
9484
9740
|
)
|
9485
9741
|
|
9486
9742
|
#
|
9487
9743
|
|
9488
9744
|
if spec.conf is not None:
|
9489
|
-
|
9490
|
-
await self._conf.
|
9745
|
+
app_conf_dir = os.path.join(app_dir, 'conf')
|
9746
|
+
await self._conf.write_app_conf(
|
9491
9747
|
spec.conf,
|
9492
|
-
|
9493
|
-
|
9494
|
-
|
9748
|
+
tags,
|
9749
|
+
app_conf_dir,
|
9750
|
+
deploy_conf_dir,
|
9495
9751
|
)
|
9496
9752
|
|
9497
9753
|
#
|
@@ -10232,18 +10488,37 @@ class SystemInterpProvider(InterpProvider):
|
|
10232
10488
|
# ../deploy/deploy.py
|
10233
10489
|
|
10234
10490
|
|
10491
|
+
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
10492
|
+
|
10493
|
+
|
10494
|
+
DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
|
10495
|
+
|
10496
|
+
|
10235
10497
|
class DeployManager:
|
10236
10498
|
def __init__(
|
10237
10499
|
self,
|
10238
10500
|
*,
|
10239
10501
|
apps: DeployAppManager,
|
10240
10502
|
paths: DeployPathsManager,
|
10503
|
+
|
10504
|
+
utc_clock: ta.Optional[DeployManagerUtcClock] = None,
|
10241
10505
|
):
|
10242
10506
|
super().__init__()
|
10243
10507
|
|
10244
10508
|
self._apps = apps
|
10245
10509
|
self._paths = paths
|
10246
10510
|
|
10511
|
+
self._utc_clock = utc_clock
|
10512
|
+
|
10513
|
+
def _utc_now(self) -> datetime.datetime:
|
10514
|
+
if self._utc_clock is not None:
|
10515
|
+
return self._utc_clock() # noqa
|
10516
|
+
else:
|
10517
|
+
return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
10518
|
+
|
10519
|
+
def _make_deploy_time(self) -> DeployTime:
|
10520
|
+
return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
|
10521
|
+
|
10247
10522
|
async def run_deploy(
|
10248
10523
|
self,
|
10249
10524
|
spec: DeploySpec,
|
@@ -10252,7 +10527,24 @@ class DeployManager:
|
|
10252
10527
|
|
10253
10528
|
#
|
10254
10529
|
|
10255
|
-
|
10530
|
+
deploy_tags = DeployTagMap(
|
10531
|
+
self._make_deploy_time(),
|
10532
|
+
spec.key(),
|
10533
|
+
)
|
10534
|
+
|
10535
|
+
#
|
10536
|
+
|
10537
|
+
for app in spec.apps:
|
10538
|
+
app_tags = deploy_tags.add(
|
10539
|
+
app.app,
|
10540
|
+
app.key(),
|
10541
|
+
DeployAppRev(app.git.rev),
|
10542
|
+
)
|
10543
|
+
|
10544
|
+
await self._apps.prepare_app(
|
10545
|
+
app,
|
10546
|
+
app_tags,
|
10547
|
+
)
|
10256
10548
|
|
10257
10549
|
|
10258
10550
|
########################################
|