ominfra 0.0.0.dev171__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/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
|
########################################
|