ominfra 0.0.0.dev171__py3-none-any.whl → 0.0.0.dev173__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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/manage/targets/bestpython.sh +6 -9
- ominfra/scripts/manage.py +489 -200
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/RECORD +16 -14
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev171.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
|
@@ -4320,15 +4582,12 @@ BEST_PYTHON_SH = """\
|
|
4320
4582
|
bv=""
|
4321
4583
|
bx=""
|
4322
4584
|
|
4323
|
-
for
|
4324
|
-
x="python$
|
4325
|
-
v=$($x -c "import sys; print(sys.version_info[:])" 2>/dev/null)
|
4326
|
-
if [ $? -eq 0 ]; then
|
4327
|
-
|
4328
|
-
|
4329
|
-
bv=$cv
|
4330
|
-
bx=$x
|
4331
|
-
fi
|
4585
|
+
for v in "" 3 3.{8..13}; do
|
4586
|
+
x="python$v"
|
4587
|
+
v=$($x -c "import sys; print((\\"%02d\\" * 3) % sys.version_info[:3])" 2>/dev/null)
|
4588
|
+
if [ $? -eq 0 ] && ([ -z "$bv" ] || [ "$v" \\> "$bv" ]); then
|
4589
|
+
bv=$v
|
4590
|
+
bx=$x
|
4332
4591
|
fi
|
4333
4592
|
done
|
4334
4593
|
|
@@ -6637,28 +6896,13 @@ TODO:
|
|
6637
6896
|
##
|
6638
6897
|
|
6639
6898
|
|
6640
|
-
DEPLOY_PATH_PLACEHOLDER_SIGIL = '@'
|
6641
|
-
DEPLOY_PATH_PLACEHOLDER_SEPARATOR = '--'
|
6642
|
-
|
6643
|
-
DEPLOY_PATH_PLACEHOLDER_DELIMITERS: ta.AbstractSet[str] = frozenset([
|
6644
|
-
DEPLOY_PATH_PLACEHOLDER_SEPARATOR,
|
6645
|
-
'.',
|
6646
|
-
])
|
6647
|
-
|
6648
|
-
DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
|
6649
|
-
'app',
|
6650
|
-
'tag',
|
6651
|
-
'conf',
|
6652
|
-
])
|
6653
|
-
|
6654
|
-
|
6655
6899
|
class DeployPathError(Exception):
|
6656
6900
|
pass
|
6657
6901
|
|
6658
6902
|
|
6659
6903
|
class DeployPathRenderable(abc.ABC):
|
6660
6904
|
@abc.abstractmethod
|
6661
|
-
def render(self,
|
6905
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6662
6906
|
raise NotImplementedError
|
6663
6907
|
|
6664
6908
|
|
@@ -6669,26 +6913,30 @@ class DeployPathNamePart(DeployPathRenderable, abc.ABC):
|
|
6669
6913
|
@classmethod
|
6670
6914
|
def parse(cls, s: str) -> 'DeployPathNamePart':
|
6671
6915
|
check.non_empty_str(s)
|
6672
|
-
if s.startswith(
|
6673
|
-
return
|
6674
|
-
elif s in
|
6916
|
+
if s.startswith(DEPLOY_TAG_SIGIL):
|
6917
|
+
return TagDeployPathNamePart(s[1:])
|
6918
|
+
elif s in DEPLOY_TAG_DELIMITERS:
|
6675
6919
|
return DelimiterDeployPathNamePart(s)
|
6676
6920
|
else:
|
6677
6921
|
return ConstDeployPathNamePart(s)
|
6678
6922
|
|
6679
6923
|
|
6680
6924
|
@dc.dataclass(frozen=True)
|
6681
|
-
class
|
6682
|
-
|
6925
|
+
class TagDeployPathNamePart(DeployPathNamePart):
|
6926
|
+
name: str
|
6683
6927
|
|
6684
6928
|
def __post_init__(self) -> None:
|
6685
|
-
check.in_(self.
|
6929
|
+
check.in_(self.name, DEPLOY_TAGS_BY_NAME)
|
6686
6930
|
|
6687
|
-
|
6688
|
-
|
6689
|
-
|
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
|
6690
6938
|
else:
|
6691
|
-
return
|
6939
|
+
return DEPLOY_TAG_SIGIL + self.name
|
6692
6940
|
|
6693
6941
|
|
6694
6942
|
@dc.dataclass(frozen=True)
|
@@ -6696,9 +6944,9 @@ class DelimiterDeployPathNamePart(DeployPathNamePart):
|
|
6696
6944
|
delimiter: str
|
6697
6945
|
|
6698
6946
|
def __post_init__(self) -> None:
|
6699
|
-
check.in_(self.delimiter,
|
6947
|
+
check.in_(self.delimiter, DEPLOY_TAG_DELIMITERS)
|
6700
6948
|
|
6701
|
-
def render(self,
|
6949
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6702
6950
|
return self.delimiter
|
6703
6951
|
|
6704
6952
|
|
@@ -6708,10 +6956,10 @@ class ConstDeployPathNamePart(DeployPathNamePart):
|
|
6708
6956
|
|
6709
6957
|
def __post_init__(self) -> None:
|
6710
6958
|
check.non_empty_str(self.const)
|
6711
|
-
for c in [*
|
6959
|
+
for c in [*DEPLOY_TAG_DELIMITERS, DEPLOY_TAG_SIGIL, '/']:
|
6712
6960
|
check.not_in(c, self.const)
|
6713
6961
|
|
6714
|
-
def render(self,
|
6962
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
6715
6963
|
return self.const
|
6716
6964
|
|
6717
6965
|
|
@@ -6726,8 +6974,8 @@ class DeployPathName(DeployPathRenderable):
|
|
6726
6974
|
if len(gl := list(g)) > 1:
|
6727
6975
|
raise DeployPathError(f'May not have consecutive path name part types: {k} {gl}')
|
6728
6976
|
|
6729
|
-
def render(self,
|
6730
|
-
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)
|
6731
6979
|
|
6732
6980
|
@classmethod
|
6733
6981
|
def parse(cls, s: str) -> 'DeployPathName':
|
@@ -6737,7 +6985,7 @@ class DeployPathName(DeployPathRenderable):
|
|
6737
6985
|
i = 0
|
6738
6986
|
ps = []
|
6739
6987
|
while i < len(s):
|
6740
|
-
ns = [(n, d) for d in
|
6988
|
+
ns = [(n, d) for d in DEPLOY_TAG_DELIMITERS if (n := s.find(d, i)) >= 0]
|
6741
6989
|
if not ns:
|
6742
6990
|
ps.append(s[i:])
|
6743
6991
|
break
|
@@ -6761,8 +7009,8 @@ class DeployPathPart(DeployPathRenderable, abc.ABC): # noqa
|
|
6761
7009
|
def kind(self) -> DeployPathKind:
|
6762
7010
|
raise NotImplementedError
|
6763
7011
|
|
6764
|
-
def render(self,
|
6765
|
-
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 '')
|
6766
7014
|
|
6767
7015
|
@classmethod
|
6768
7016
|
def parse(cls, s: str) -> 'DeployPathPart':
|
@@ -6808,20 +7056,20 @@ class DeployPath:
|
|
6808
7056
|
for p in self.parts[:-1]:
|
6809
7057
|
check.equal(p.kind, 'dir')
|
6810
7058
|
|
6811
|
-
|
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]] = {}
|
6812
7062
|
for i, np in enumerate(self.name_parts):
|
6813
|
-
if isinstance(np,
|
6814
|
-
pd.setdefault(
|
6815
|
-
|
6816
|
-
# if 'tag' in pd and 'app' not in pd:
|
6817
|
-
# raise DeployPathError('Tag placeholder in path without app', self)
|
7063
|
+
if isinstance(np, TagDeployPathNamePart):
|
7064
|
+
pd.setdefault(np.tag, []).append(i)
|
7065
|
+
return pd
|
6818
7066
|
|
6819
7067
|
@property
|
6820
7068
|
def kind(self) -> ta.Literal['file', 'dir']:
|
6821
7069
|
return self.parts[-1].kind
|
6822
7070
|
|
6823
|
-
def render(self,
|
6824
|
-
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])
|
6825
7073
|
|
6826
7074
|
@classmethod
|
6827
7075
|
def parse(cls, s: str) -> 'DeployPath':
|
@@ -6845,6 +7093,16 @@ def check_valid_deploy_spec_path(s: str) -> str:
|
|
6845
7093
|
return s
|
6846
7094
|
|
6847
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
|
+
|
6848
7106
|
##
|
6849
7107
|
|
6850
7108
|
|
@@ -6890,7 +7148,7 @@ class DeployVenvSpec:
|
|
6890
7148
|
|
6891
7149
|
|
6892
7150
|
@dc.dataclass(frozen=True)
|
6893
|
-
class
|
7151
|
+
class DeployAppConfFile:
|
6894
7152
|
path: str
|
6895
7153
|
body: str
|
6896
7154
|
|
@@ -6902,7 +7160,7 @@ class DeployConfFile:
|
|
6902
7160
|
|
6903
7161
|
|
6904
7162
|
@dc.dataclass(frozen=True)
|
6905
|
-
class
|
7163
|
+
class DeployAppConfLink(abc.ABC): # noqa
|
6906
7164
|
"""
|
6907
7165
|
May be either:
|
6908
7166
|
- @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -6918,11 +7176,11 @@ class DeployConfLink(abc.ABC): # noqa
|
|
6918
7176
|
check.equal(self.src.count('/'), 1)
|
6919
7177
|
|
6920
7178
|
|
6921
|
-
class
|
7179
|
+
class CurrentOnlyDeployAppConfLink(DeployAppConfLink):
|
6922
7180
|
pass
|
6923
7181
|
|
6924
7182
|
|
6925
|
-
class
|
7183
|
+
class AllActiveDeployAppConfLink(DeployAppConfLink):
|
6926
7184
|
pass
|
6927
7185
|
|
6928
7186
|
|
@@ -6930,10 +7188,10 @@ class TagDeployConfLink(DeployConfLink):
|
|
6930
7188
|
|
6931
7189
|
|
6932
7190
|
@dc.dataclass(frozen=True)
|
6933
|
-
class
|
6934
|
-
files: ta.Optional[ta.Sequence[
|
7191
|
+
class DeployAppConfSpec:
|
7192
|
+
files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
|
6935
7193
|
|
6936
|
-
links: ta.Optional[ta.Sequence[
|
7194
|
+
links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
|
6937
7195
|
|
6938
7196
|
def __post_init__(self) -> None:
|
6939
7197
|
if self.files:
|
@@ -6947,18 +7205,37 @@ class DeployConfSpec:
|
|
6947
7205
|
|
6948
7206
|
|
6949
7207
|
@dc.dataclass(frozen=True)
|
6950
|
-
class
|
7208
|
+
class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
|
6951
7209
|
app: DeployApp
|
6952
7210
|
|
6953
7211
|
git: DeployGitSpec
|
6954
7212
|
|
6955
7213
|
venv: ta.Optional[DeployVenvSpec] = None
|
6956
7214
|
|
6957
|
-
conf: ta.Optional[
|
7215
|
+
conf: ta.Optional[DeployAppConfSpec] = None
|
6958
7216
|
|
6959
|
-
@
|
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
|
6960
7237
|
def key(self) -> DeployKey:
|
6961
|
-
return DeployKey(
|
7238
|
+
return DeployKey(self._key_str())
|
6962
7239
|
|
6963
7240
|
|
6964
7241
|
########################################
|
@@ -7578,18 +7855,18 @@ class DeployConfManager:
|
|
7578
7855
|
|
7579
7856
|
#
|
7580
7857
|
|
7581
|
-
async def
|
7858
|
+
async def _write_app_conf_file(
|
7582
7859
|
self,
|
7583
|
-
|
7584
|
-
|
7860
|
+
acf: DeployAppConfFile,
|
7861
|
+
app_conf_dir: str,
|
7585
7862
|
) -> None:
|
7586
|
-
conf_file = os.path.join(
|
7587
|
-
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))
|
7588
7865
|
|
7589
7866
|
os.makedirs(os.path.dirname(conf_file), exist_ok=True)
|
7590
7867
|
|
7591
7868
|
with open(conf_file, 'w') as f: # noqa
|
7592
|
-
f.write(
|
7869
|
+
f.write(acf.body)
|
7593
7870
|
|
7594
7871
|
#
|
7595
7872
|
|
@@ -7598,15 +7875,18 @@ class DeployConfManager:
|
|
7598
7875
|
link_src: str
|
7599
7876
|
link_dst: str
|
7600
7877
|
|
7601
|
-
|
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(
|
7602
7882
|
self,
|
7603
|
-
link:
|
7604
|
-
|
7605
|
-
|
7606
|
-
|
7883
|
+
link: DeployAppConfLink,
|
7884
|
+
tags: DeployTagMap,
|
7885
|
+
app_conf_dir: str,
|
7886
|
+
conf_link_dir: str,
|
7607
7887
|
) -> _ComputedConfLink:
|
7608
|
-
link_src = os.path.join(
|
7609
|
-
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))
|
7610
7890
|
|
7611
7891
|
#
|
7612
7892
|
|
@@ -7621,7 +7901,7 @@ class DeployConfManager:
|
|
7621
7901
|
d, f = os.path.split(link.src)
|
7622
7902
|
# TODO: check filename :|
|
7623
7903
|
link_dst_pfx = d + '/'
|
7624
|
-
link_dst_sfx =
|
7904
|
+
link_dst_sfx = DEPLOY_TAG_SEPARATOR + f
|
7625
7905
|
|
7626
7906
|
else: # noqa
|
7627
7907
|
# @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -7635,10 +7915,10 @@ class DeployConfManager:
|
|
7635
7915
|
|
7636
7916
|
#
|
7637
7917
|
|
7638
|
-
if isinstance(link,
|
7639
|
-
link_dst_mid = str(
|
7640
|
-
elif isinstance(link,
|
7641
|
-
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)
|
7642
7922
|
else:
|
7643
7923
|
raise TypeError(link)
|
7644
7924
|
|
@@ -7649,7 +7929,7 @@ class DeployConfManager:
|
|
7649
7929
|
link_dst_mid,
|
7650
7930
|
link_dst_sfx,
|
7651
7931
|
])
|
7652
|
-
link_dst = os.path.join(
|
7932
|
+
link_dst = os.path.join(conf_link_dir, link_dst_name)
|
7653
7933
|
|
7654
7934
|
return DeployConfManager._ComputedConfLink(
|
7655
7935
|
is_dir=is_dir,
|
@@ -7657,24 +7937,24 @@ class DeployConfManager:
|
|
7657
7937
|
link_dst=link_dst,
|
7658
7938
|
)
|
7659
7939
|
|
7660
|
-
async def
|
7940
|
+
async def _make_app_conf_link(
|
7661
7941
|
self,
|
7662
|
-
link:
|
7663
|
-
|
7664
|
-
|
7665
|
-
|
7942
|
+
link: DeployAppConfLink,
|
7943
|
+
tags: DeployTagMap,
|
7944
|
+
app_conf_dir: str,
|
7945
|
+
conf_link_dir: str,
|
7666
7946
|
) -> None:
|
7667
|
-
comp = self.
|
7947
|
+
comp = self._compute_app_conf_link_dst(
|
7668
7948
|
link,
|
7669
|
-
|
7670
|
-
|
7671
|
-
|
7949
|
+
tags,
|
7950
|
+
app_conf_dir,
|
7951
|
+
conf_link_dir,
|
7672
7952
|
)
|
7673
7953
|
|
7674
7954
|
#
|
7675
7955
|
|
7676
|
-
check.arg(is_path_in_dir(
|
7677
|
-
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))
|
7678
7958
|
|
7679
7959
|
if comp.is_dir:
|
7680
7960
|
check.arg(os.path.isdir(comp.link_src))
|
@@ -7692,27 +7972,27 @@ class DeployConfManager:
|
|
7692
7972
|
|
7693
7973
|
#
|
7694
7974
|
|
7695
|
-
async def
|
7975
|
+
async def write_app_conf(
|
7696
7976
|
self,
|
7697
|
-
spec:
|
7698
|
-
|
7699
|
-
|
7700
|
-
|
7977
|
+
spec: DeployAppConfSpec,
|
7978
|
+
tags: DeployTagMap,
|
7979
|
+
app_conf_dir: str,
|
7980
|
+
conf_link_dir: str,
|
7701
7981
|
) -> None:
|
7702
|
-
for
|
7703
|
-
await self.
|
7704
|
-
|
7705
|
-
|
7982
|
+
for acf in spec.files or []:
|
7983
|
+
await self._write_app_conf_file(
|
7984
|
+
acf,
|
7985
|
+
app_conf_dir,
|
7706
7986
|
)
|
7707
7987
|
|
7708
7988
|
#
|
7709
7989
|
|
7710
7990
|
for link in spec.links or []:
|
7711
|
-
await self.
|
7991
|
+
await self._make_app_conf_link(
|
7712
7992
|
link,
|
7713
|
-
|
7714
|
-
|
7715
|
-
|
7993
|
+
tags,
|
7994
|
+
app_conf_dir,
|
7995
|
+
conf_link_dir,
|
7716
7996
|
)
|
7717
7997
|
|
7718
7998
|
|
@@ -9315,19 +9595,6 @@ def bind_commands(
|
|
9315
9595
|
# ../deploy/apps.py
|
9316
9596
|
|
9317
9597
|
|
9318
|
-
def make_deploy_tag(
|
9319
|
-
rev: DeployRev,
|
9320
|
-
key: DeployKey,
|
9321
|
-
*,
|
9322
|
-
utcnow: ta.Optional[datetime.datetime] = None,
|
9323
|
-
) -> DeployTag:
|
9324
|
-
if utcnow is None:
|
9325
|
-
utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
9326
|
-
now_fmt = '%Y%m%dT%H%M%SZ'
|
9327
|
-
now_str = utcnow.strftime(now_fmt)
|
9328
|
-
return DeployTag('-'.join([now_str, rev, key]))
|
9329
|
-
|
9330
|
-
|
9331
9598
|
class DeployAppManager(DeployPathOwner):
|
9332
9599
|
def __init__(
|
9333
9600
|
self,
|
@@ -9348,32 +9615,27 @@ class DeployAppManager(DeployPathOwner):
|
|
9348
9615
|
|
9349
9616
|
#
|
9350
9617
|
|
9351
|
-
|
9352
|
-
|
9353
|
-
|
9354
|
-
_CONF_TAG_DIR_STR = 'tags/conf/@tag--@app/'
|
9355
|
-
_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)
|
9356
9620
|
|
9357
|
-
_DEPLOY_DIR_STR = 'deploys/@
|
9621
|
+
_DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
|
9358
9622
|
_DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
|
9359
9623
|
|
9360
9624
|
_APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
|
9361
|
-
|
9625
|
+
_CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
|
9362
9626
|
|
9363
9627
|
@cached_nullary
|
9364
9628
|
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
9365
9629
|
return {
|
9366
|
-
self.
|
9367
|
-
|
9368
|
-
self._CONF_TAG_DIR,
|
9630
|
+
self._APP_DIR,
|
9369
9631
|
|
9370
9632
|
self._DEPLOY_DIR,
|
9371
9633
|
|
9372
9634
|
self._APP_DEPLOY_LINK,
|
9373
|
-
self.
|
9635
|
+
self._CONF_DEPLOY_DIR,
|
9374
9636
|
|
9375
9637
|
*[
|
9376
|
-
DeployPath.parse(f'{self.
|
9638
|
+
DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
|
9377
9639
|
for sfx in [
|
9378
9640
|
'conf',
|
9379
9641
|
'git',
|
@@ -9386,26 +9648,21 @@ class DeployAppManager(DeployPathOwner):
|
|
9386
9648
|
|
9387
9649
|
async def prepare_app(
|
9388
9650
|
self,
|
9389
|
-
spec:
|
9651
|
+
spec: DeployAppSpec,
|
9652
|
+
tags: DeployTagMap,
|
9390
9653
|
) -> None:
|
9391
|
-
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.git.rev, spec.key()))
|
9392
|
-
|
9393
|
-
#
|
9394
|
-
|
9395
9654
|
deploy_home = check.non_empty_str(self._deploy_home)
|
9396
9655
|
|
9397
9656
|
def build_path(pth: DeployPath) -> str:
|
9398
|
-
return os.path.join(deploy_home, pth.render(
|
9657
|
+
return os.path.join(deploy_home, pth.render(tags))
|
9399
9658
|
|
9400
|
-
|
9401
|
-
conf_tag_dir = build_path(self._CONF_TAG_DIR)
|
9659
|
+
app_dir = build_path(self._APP_DIR)
|
9402
9660
|
deploy_dir = build_path(self._DEPLOY_DIR)
|
9403
9661
|
app_deploy_link = build_path(self._APP_DEPLOY_LINK)
|
9404
|
-
conf_deploy_link_file = build_path(self._CONF_DEPLOY_LINK)
|
9405
9662
|
|
9406
9663
|
#
|
9407
9664
|
|
9408
|
-
os.makedirs(deploy_dir)
|
9665
|
+
os.makedirs(deploy_dir, exist_ok=True)
|
9409
9666
|
|
9410
9667
|
deploying_link = os.path.join(deploy_home, 'deploys/deploying')
|
9411
9668
|
relative_symlink(
|
@@ -9417,9 +9674,9 @@ class DeployAppManager(DeployPathOwner):
|
|
9417
9674
|
|
9418
9675
|
#
|
9419
9676
|
|
9420
|
-
os.makedirs(
|
9677
|
+
os.makedirs(app_dir)
|
9421
9678
|
relative_symlink(
|
9422
|
-
|
9679
|
+
app_dir,
|
9423
9680
|
app_deploy_link,
|
9424
9681
|
target_is_directory=True,
|
9425
9682
|
make_dirs=True,
|
@@ -9427,37 +9684,33 @@ class DeployAppManager(DeployPathOwner):
|
|
9427
9684
|
|
9428
9685
|
#
|
9429
9686
|
|
9430
|
-
os.
|
9431
|
-
|
9432
|
-
conf_tag_dir,
|
9433
|
-
conf_deploy_link_file,
|
9434
|
-
target_is_directory=True,
|
9435
|
-
make_dirs=True,
|
9436
|
-
)
|
9687
|
+
deploy_conf_dir = os.path.join(deploy_dir, 'conf')
|
9688
|
+
os.makedirs(deploy_conf_dir, exist_ok=True)
|
9437
9689
|
|
9438
9690
|
#
|
9439
9691
|
|
9440
|
-
def mirror_symlinks(src: str, dst: str) -> None:
|
9441
|
-
|
9442
|
-
|
9443
|
-
|
9444
|
-
|
9445
|
-
|
9446
|
-
|
9447
|
-
|
9448
|
-
|
9449
|
-
|
9450
|
-
|
9451
|
-
|
9452
|
-
|
9453
|
-
|
9454
|
-
|
9455
|
-
|
9456
|
-
|
9457
|
-
|
9458
|
-
|
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)))
|
9459
9711
|
|
9460
9712
|
current_link = os.path.join(deploy_home, 'deploys/current')
|
9713
|
+
|
9461
9714
|
# if os.path.exists(current_link):
|
9462
9715
|
# mirror_symlinks(
|
9463
9716
|
# os.path.join(current_link, 'conf'),
|
@@ -9470,31 +9723,31 @@ class DeployAppManager(DeployPathOwner):
|
|
9470
9723
|
|
9471
9724
|
#
|
9472
9725
|
|
9473
|
-
|
9726
|
+
app_git_dir = os.path.join(app_dir, 'git')
|
9474
9727
|
await self._git.checkout(
|
9475
9728
|
spec.git,
|
9476
|
-
|
9729
|
+
app_git_dir,
|
9477
9730
|
)
|
9478
9731
|
|
9479
9732
|
#
|
9480
9733
|
|
9481
9734
|
if spec.venv is not None:
|
9482
|
-
|
9735
|
+
app_venv_dir = os.path.join(app_dir, 'venv')
|
9483
9736
|
await self._venvs.setup_venv(
|
9484
9737
|
spec.venv,
|
9485
|
-
|
9486
|
-
|
9738
|
+
app_git_dir,
|
9739
|
+
app_venv_dir,
|
9487
9740
|
)
|
9488
9741
|
|
9489
9742
|
#
|
9490
9743
|
|
9491
9744
|
if spec.conf is not None:
|
9492
|
-
|
9493
|
-
await self._conf.
|
9745
|
+
app_conf_dir = os.path.join(app_dir, 'conf')
|
9746
|
+
await self._conf.write_app_conf(
|
9494
9747
|
spec.conf,
|
9495
|
-
|
9496
|
-
|
9497
|
-
|
9748
|
+
tags,
|
9749
|
+
app_conf_dir,
|
9750
|
+
deploy_conf_dir,
|
9498
9751
|
)
|
9499
9752
|
|
9500
9753
|
#
|
@@ -10235,18 +10488,37 @@ class SystemInterpProvider(InterpProvider):
|
|
10235
10488
|
# ../deploy/deploy.py
|
10236
10489
|
|
10237
10490
|
|
10491
|
+
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
10492
|
+
|
10493
|
+
|
10494
|
+
DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
|
10495
|
+
|
10496
|
+
|
10238
10497
|
class DeployManager:
|
10239
10498
|
def __init__(
|
10240
10499
|
self,
|
10241
10500
|
*,
|
10242
10501
|
apps: DeployAppManager,
|
10243
10502
|
paths: DeployPathsManager,
|
10503
|
+
|
10504
|
+
utc_clock: ta.Optional[DeployManagerUtcClock] = None,
|
10244
10505
|
):
|
10245
10506
|
super().__init__()
|
10246
10507
|
|
10247
10508
|
self._apps = apps
|
10248
10509
|
self._paths = paths
|
10249
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
|
+
|
10250
10522
|
async def run_deploy(
|
10251
10523
|
self,
|
10252
10524
|
spec: DeploySpec,
|
@@ -10255,7 +10527,24 @@ class DeployManager:
|
|
10255
10527
|
|
10256
10528
|
#
|
10257
10529
|
|
10258
|
-
|
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
|
+
)
|
10259
10548
|
|
10260
10549
|
|
10261
10550
|
########################################
|