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/manage/deploy/apps.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
-
import datetime
|
3
2
|
import os.path
|
4
|
-
import shutil
|
5
3
|
import typing as ta
|
6
4
|
|
7
5
|
from omlish.lite.cached import cached_nullary
|
@@ -12,28 +10,12 @@ from .conf import DeployConfManager
|
|
12
10
|
from .git import DeployGitManager
|
13
11
|
from .paths.owners import DeployPathOwner
|
14
12
|
from .paths.paths import DeployPath
|
15
|
-
from .specs import
|
16
|
-
from .
|
13
|
+
from .specs import DeployAppSpec
|
14
|
+
from .tags import DeployTagMap
|
17
15
|
from .types import DeployHome
|
18
|
-
from .types import DeployKey
|
19
|
-
from .types import DeployRev
|
20
|
-
from .types import DeployTag
|
21
16
|
from .venvs import DeployVenvManager
|
22
17
|
|
23
18
|
|
24
|
-
def make_deploy_tag(
|
25
|
-
rev: DeployRev,
|
26
|
-
key: DeployKey,
|
27
|
-
*,
|
28
|
-
utcnow: ta.Optional[datetime.datetime] = None,
|
29
|
-
) -> DeployTag:
|
30
|
-
if utcnow is None:
|
31
|
-
utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
32
|
-
now_fmt = '%Y%m%dT%H%M%SZ'
|
33
|
-
now_str = utcnow.strftime(now_fmt)
|
34
|
-
return DeployTag('-'.join([now_str, rev, key]))
|
35
|
-
|
36
|
-
|
37
19
|
class DeployAppManager(DeployPathOwner):
|
38
20
|
def __init__(
|
39
21
|
self,
|
@@ -54,32 +36,27 @@ class DeployAppManager(DeployPathOwner):
|
|
54
36
|
|
55
37
|
#
|
56
38
|
|
57
|
-
|
58
|
-
|
39
|
+
_APP_DIR_STR = 'apps/@app/@time--@app-rev--@app-key/'
|
40
|
+
_APP_DIR = DeployPath.parse(_APP_DIR_STR)
|
59
41
|
|
60
|
-
|
61
|
-
_CONF_TAG_DIR = DeployPath.parse(_CONF_TAG_DIR_STR)
|
62
|
-
|
63
|
-
_DEPLOY_DIR_STR = 'deploys/@tag--@app/'
|
42
|
+
_DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
|
64
43
|
_DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
|
65
44
|
|
66
45
|
_APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
|
67
|
-
|
46
|
+
_CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
|
68
47
|
|
69
48
|
@cached_nullary
|
70
49
|
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
71
50
|
return {
|
72
|
-
self.
|
73
|
-
|
74
|
-
self._CONF_TAG_DIR,
|
51
|
+
self._APP_DIR,
|
75
52
|
|
76
53
|
self._DEPLOY_DIR,
|
77
54
|
|
78
55
|
self._APP_DEPLOY_LINK,
|
79
|
-
self.
|
56
|
+
self._CONF_DEPLOY_DIR,
|
80
57
|
|
81
58
|
*[
|
82
|
-
DeployPath.parse(f'{self.
|
59
|
+
DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
|
83
60
|
for sfx in [
|
84
61
|
'conf',
|
85
62
|
'git',
|
@@ -92,26 +69,21 @@ class DeployAppManager(DeployPathOwner):
|
|
92
69
|
|
93
70
|
async def prepare_app(
|
94
71
|
self,
|
95
|
-
spec:
|
72
|
+
spec: DeployAppSpec,
|
73
|
+
tags: DeployTagMap,
|
96
74
|
) -> None:
|
97
|
-
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.git.rev, spec.key()))
|
98
|
-
|
99
|
-
#
|
100
|
-
|
101
75
|
deploy_home = check.non_empty_str(self._deploy_home)
|
102
76
|
|
103
77
|
def build_path(pth: DeployPath) -> str:
|
104
|
-
return os.path.join(deploy_home, pth.render(
|
78
|
+
return os.path.join(deploy_home, pth.render(tags))
|
105
79
|
|
106
|
-
|
107
|
-
conf_tag_dir = build_path(self._CONF_TAG_DIR)
|
80
|
+
app_dir = build_path(self._APP_DIR)
|
108
81
|
deploy_dir = build_path(self._DEPLOY_DIR)
|
109
82
|
app_deploy_link = build_path(self._APP_DEPLOY_LINK)
|
110
|
-
conf_deploy_link_file = build_path(self._CONF_DEPLOY_LINK)
|
111
83
|
|
112
84
|
#
|
113
85
|
|
114
|
-
os.makedirs(deploy_dir)
|
86
|
+
os.makedirs(deploy_dir, exist_ok=True)
|
115
87
|
|
116
88
|
deploying_link = os.path.join(deploy_home, 'deploys/deploying')
|
117
89
|
relative_symlink(
|
@@ -123,9 +95,9 @@ class DeployAppManager(DeployPathOwner):
|
|
123
95
|
|
124
96
|
#
|
125
97
|
|
126
|
-
os.makedirs(
|
98
|
+
os.makedirs(app_dir)
|
127
99
|
relative_symlink(
|
128
|
-
|
100
|
+
app_dir,
|
129
101
|
app_deploy_link,
|
130
102
|
target_is_directory=True,
|
131
103
|
make_dirs=True,
|
@@ -133,37 +105,33 @@ class DeployAppManager(DeployPathOwner):
|
|
133
105
|
|
134
106
|
#
|
135
107
|
|
136
|
-
os.
|
137
|
-
|
138
|
-
conf_tag_dir,
|
139
|
-
conf_deploy_link_file,
|
140
|
-
target_is_directory=True,
|
141
|
-
make_dirs=True,
|
142
|
-
)
|
108
|
+
deploy_conf_dir = os.path.join(deploy_dir, 'conf')
|
109
|
+
os.makedirs(deploy_conf_dir, exist_ok=True)
|
143
110
|
|
144
111
|
#
|
145
112
|
|
146
|
-
def mirror_symlinks(src: str, dst: str) -> None:
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
113
|
+
# def mirror_symlinks(src: str, dst: str) -> None:
|
114
|
+
# def mirror_link(lp: str) -> None:
|
115
|
+
# check.state(os.path.islink(lp))
|
116
|
+
# shutil.copy2(
|
117
|
+
# lp,
|
118
|
+
# os.path.join(dst, os.path.relpath(lp, src)),
|
119
|
+
# follow_symlinks=False,
|
120
|
+
# )
|
121
|
+
#
|
122
|
+
# for dp, dns, fns in os.walk(src, followlinks=False):
|
123
|
+
# for fn in fns:
|
124
|
+
# mirror_link(os.path.join(dp, fn))
|
125
|
+
#
|
126
|
+
# for dn in dns:
|
127
|
+
# dp2 = os.path.join(dp, dn)
|
128
|
+
# if os.path.islink(dp2):
|
129
|
+
# mirror_link(dp2)
|
130
|
+
# else:
|
131
|
+
# os.makedirs(os.path.join(dst, os.path.relpath(dp2, src)))
|
165
132
|
|
166
133
|
current_link = os.path.join(deploy_home, 'deploys/current')
|
134
|
+
|
167
135
|
# if os.path.exists(current_link):
|
168
136
|
# mirror_symlinks(
|
169
137
|
# os.path.join(current_link, 'conf'),
|
@@ -176,31 +144,31 @@ class DeployAppManager(DeployPathOwner):
|
|
176
144
|
|
177
145
|
#
|
178
146
|
|
179
|
-
|
147
|
+
app_git_dir = os.path.join(app_dir, 'git')
|
180
148
|
await self._git.checkout(
|
181
149
|
spec.git,
|
182
|
-
|
150
|
+
app_git_dir,
|
183
151
|
)
|
184
152
|
|
185
153
|
#
|
186
154
|
|
187
155
|
if spec.venv is not None:
|
188
|
-
|
156
|
+
app_venv_dir = os.path.join(app_dir, 'venv')
|
189
157
|
await self._venvs.setup_venv(
|
190
158
|
spec.venv,
|
191
|
-
|
192
|
-
|
159
|
+
app_git_dir,
|
160
|
+
app_venv_dir,
|
193
161
|
)
|
194
162
|
|
195
163
|
#
|
196
164
|
|
197
165
|
if spec.conf is not None:
|
198
|
-
|
199
|
-
await self._conf.
|
166
|
+
app_conf_dir = os.path.join(app_dir, 'conf')
|
167
|
+
await self._conf.write_app_conf(
|
200
168
|
spec.conf,
|
201
|
-
|
202
|
-
|
203
|
-
|
169
|
+
tags,
|
170
|
+
app_conf_dir,
|
171
|
+
deploy_conf_dir,
|
204
172
|
)
|
205
173
|
|
206
174
|
#
|
ominfra/manage/deploy/conf.py
CHANGED
@@ -23,13 +23,15 @@ from omlish.lite.check import check
|
|
23
23
|
from omlish.os.paths import is_path_in_dir
|
24
24
|
from omlish.os.paths import relative_symlink
|
25
25
|
|
26
|
-
from .paths.paths import
|
27
|
-
from .specs import
|
28
|
-
from .specs import
|
29
|
-
from .specs import
|
30
|
-
from .specs import
|
31
|
-
from .specs import
|
32
|
-
from .
|
26
|
+
from .paths.paths import DeployPath
|
27
|
+
from .specs import AllActiveDeployAppConfLink
|
28
|
+
from .specs import CurrentOnlyDeployAppConfLink
|
29
|
+
from .specs import DeployAppConfFile
|
30
|
+
from .specs import DeployAppConfLink
|
31
|
+
from .specs import DeployAppConfSpec
|
32
|
+
from .tags import DEPLOY_TAG_SEPARATOR
|
33
|
+
from .tags import DeployApp
|
34
|
+
from .tags import DeployTagMap
|
33
35
|
from .types import DeployHome
|
34
36
|
|
35
37
|
|
@@ -45,18 +47,18 @@ class DeployConfManager:
|
|
45
47
|
|
46
48
|
#
|
47
49
|
|
48
|
-
async def
|
50
|
+
async def _write_app_conf_file(
|
49
51
|
self,
|
50
|
-
|
51
|
-
|
52
|
+
acf: DeployAppConfFile,
|
53
|
+
app_conf_dir: str,
|
52
54
|
) -> None:
|
53
|
-
conf_file = os.path.join(
|
54
|
-
check.arg(is_path_in_dir(
|
55
|
+
conf_file = os.path.join(app_conf_dir, acf.path)
|
56
|
+
check.arg(is_path_in_dir(app_conf_dir, conf_file))
|
55
57
|
|
56
58
|
os.makedirs(os.path.dirname(conf_file), exist_ok=True)
|
57
59
|
|
58
60
|
with open(conf_file, 'w') as f: # noqa
|
59
|
-
f.write(
|
61
|
+
f.write(acf.body)
|
60
62
|
|
61
63
|
#
|
62
64
|
|
@@ -65,15 +67,18 @@ class DeployConfManager:
|
|
65
67
|
link_src: str
|
66
68
|
link_dst: str
|
67
69
|
|
68
|
-
|
70
|
+
_UNIQUE_LINK_NAME_STR = '@app--@time--@app-key'
|
71
|
+
_UNIQUE_LINK_NAME = DeployPath.parse(_UNIQUE_LINK_NAME_STR)
|
72
|
+
|
73
|
+
def _compute_app_conf_link_dst(
|
69
74
|
self,
|
70
|
-
link:
|
71
|
-
|
72
|
-
|
73
|
-
|
75
|
+
link: DeployAppConfLink,
|
76
|
+
tags: DeployTagMap,
|
77
|
+
app_conf_dir: str,
|
78
|
+
conf_link_dir: str,
|
74
79
|
) -> _ComputedConfLink:
|
75
|
-
link_src = os.path.join(
|
76
|
-
check.arg(is_path_in_dir(
|
80
|
+
link_src = os.path.join(app_conf_dir, link.src)
|
81
|
+
check.arg(is_path_in_dir(app_conf_dir, link_src))
|
77
82
|
|
78
83
|
#
|
79
84
|
|
@@ -88,7 +93,7 @@ class DeployConfManager:
|
|
88
93
|
d, f = os.path.split(link.src)
|
89
94
|
# TODO: check filename :|
|
90
95
|
link_dst_pfx = d + '/'
|
91
|
-
link_dst_sfx =
|
96
|
+
link_dst_sfx = DEPLOY_TAG_SEPARATOR + f
|
92
97
|
|
93
98
|
else: # noqa
|
94
99
|
# @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
|
@@ -102,10 +107,10 @@ class DeployConfManager:
|
|
102
107
|
|
103
108
|
#
|
104
109
|
|
105
|
-
if isinstance(link,
|
106
|
-
link_dst_mid = str(
|
107
|
-
elif isinstance(link,
|
108
|
-
link_dst_mid =
|
110
|
+
if isinstance(link, CurrentOnlyDeployAppConfLink):
|
111
|
+
link_dst_mid = str(tags[DeployApp].s)
|
112
|
+
elif isinstance(link, AllActiveDeployAppConfLink):
|
113
|
+
link_dst_mid = self._UNIQUE_LINK_NAME.render(tags)
|
109
114
|
else:
|
110
115
|
raise TypeError(link)
|
111
116
|
|
@@ -116,7 +121,7 @@ class DeployConfManager:
|
|
116
121
|
link_dst_mid,
|
117
122
|
link_dst_sfx,
|
118
123
|
])
|
119
|
-
link_dst = os.path.join(
|
124
|
+
link_dst = os.path.join(conf_link_dir, link_dst_name)
|
120
125
|
|
121
126
|
return DeployConfManager._ComputedConfLink(
|
122
127
|
is_dir=is_dir,
|
@@ -124,24 +129,24 @@ class DeployConfManager:
|
|
124
129
|
link_dst=link_dst,
|
125
130
|
)
|
126
131
|
|
127
|
-
async def
|
132
|
+
async def _make_app_conf_link(
|
128
133
|
self,
|
129
|
-
link:
|
130
|
-
|
131
|
-
|
132
|
-
|
134
|
+
link: DeployAppConfLink,
|
135
|
+
tags: DeployTagMap,
|
136
|
+
app_conf_dir: str,
|
137
|
+
conf_link_dir: str,
|
133
138
|
) -> None:
|
134
|
-
comp = self.
|
139
|
+
comp = self._compute_app_conf_link_dst(
|
135
140
|
link,
|
136
|
-
|
137
|
-
|
138
|
-
|
141
|
+
tags,
|
142
|
+
app_conf_dir,
|
143
|
+
conf_link_dir,
|
139
144
|
)
|
140
145
|
|
141
146
|
#
|
142
147
|
|
143
|
-
check.arg(is_path_in_dir(
|
144
|
-
check.arg(is_path_in_dir(
|
148
|
+
check.arg(is_path_in_dir(app_conf_dir, comp.link_src))
|
149
|
+
check.arg(is_path_in_dir(conf_link_dir, comp.link_dst))
|
145
150
|
|
146
151
|
if comp.is_dir:
|
147
152
|
check.arg(os.path.isdir(comp.link_src))
|
@@ -159,25 +164,25 @@ class DeployConfManager:
|
|
159
164
|
|
160
165
|
#
|
161
166
|
|
162
|
-
async def
|
167
|
+
async def write_app_conf(
|
163
168
|
self,
|
164
|
-
spec:
|
165
|
-
|
166
|
-
|
167
|
-
|
169
|
+
spec: DeployAppConfSpec,
|
170
|
+
tags: DeployTagMap,
|
171
|
+
app_conf_dir: str,
|
172
|
+
conf_link_dir: str,
|
168
173
|
) -> None:
|
169
|
-
for
|
170
|
-
await self.
|
171
|
-
|
172
|
-
|
174
|
+
for acf in spec.files or []:
|
175
|
+
await self._write_app_conf_file(
|
176
|
+
acf,
|
177
|
+
app_conf_dir,
|
173
178
|
)
|
174
179
|
|
175
180
|
#
|
176
181
|
|
177
182
|
for link in spec.links or []:
|
178
|
-
await self.
|
183
|
+
await self._make_app_conf_link(
|
179
184
|
link,
|
180
|
-
|
181
|
-
|
182
|
-
|
185
|
+
tags,
|
186
|
+
app_conf_dir,
|
187
|
+
conf_link_dir,
|
183
188
|
)
|
ominfra/manage/deploy/deploy.py
CHANGED
@@ -1,7 +1,21 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import datetime
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from omlish.lite.typing import Func0
|
6
|
+
|
2
7
|
from .apps import DeployAppManager
|
3
8
|
from .paths.manager import DeployPathsManager
|
4
9
|
from .specs import DeploySpec
|
10
|
+
from .tags import DeployAppRev
|
11
|
+
from .tags import DeployTagMap
|
12
|
+
from .tags import DeployTime
|
13
|
+
|
14
|
+
|
15
|
+
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
16
|
+
|
17
|
+
|
18
|
+
DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
|
5
19
|
|
6
20
|
|
7
21
|
class DeployManager:
|
@@ -10,12 +24,25 @@ class DeployManager:
|
|
10
24
|
*,
|
11
25
|
apps: DeployAppManager,
|
12
26
|
paths: DeployPathsManager,
|
27
|
+
|
28
|
+
utc_clock: ta.Optional[DeployManagerUtcClock] = None,
|
13
29
|
):
|
14
30
|
super().__init__()
|
15
31
|
|
16
32
|
self._apps = apps
|
17
33
|
self._paths = paths
|
18
34
|
|
35
|
+
self._utc_clock = utc_clock
|
36
|
+
|
37
|
+
def _utc_now(self) -> datetime.datetime:
|
38
|
+
if self._utc_clock is not None:
|
39
|
+
return self._utc_clock() # noqa
|
40
|
+
else:
|
41
|
+
return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
42
|
+
|
43
|
+
def _make_deploy_time(self) -> DeployTime:
|
44
|
+
return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
|
45
|
+
|
19
46
|
async def run_deploy(
|
20
47
|
self,
|
21
48
|
spec: DeploySpec,
|
@@ -24,4 +51,21 @@ class DeployManager:
|
|
24
51
|
|
25
52
|
#
|
26
53
|
|
27
|
-
|
54
|
+
deploy_tags = DeployTagMap(
|
55
|
+
self._make_deploy_time(),
|
56
|
+
spec.key(),
|
57
|
+
)
|
58
|
+
|
59
|
+
#
|
60
|
+
|
61
|
+
for app in spec.apps:
|
62
|
+
app_tags = deploy_tags.add(
|
63
|
+
app.app,
|
64
|
+
app.key(),
|
65
|
+
DeployAppRev(app.git.rev),
|
66
|
+
)
|
67
|
+
|
68
|
+
await self._apps.prepare_app(
|
69
|
+
app,
|
70
|
+
app_tags,
|
71
|
+
)
|
@@ -13,38 +13,28 @@ import dataclasses as dc
|
|
13
13
|
import itertools
|
14
14
|
import typing as ta
|
15
15
|
|
16
|
+
from omlish.lite.cached import cached_nullary
|
16
17
|
from omlish.lite.check import check
|
17
18
|
from omlish.lite.strings import split_keep_delimiter
|
18
19
|
|
19
|
-
from ..
|
20
|
-
from ..
|
20
|
+
from ..tags import DEPLOY_TAG_DELIMITERS
|
21
|
+
from ..tags import DEPLOY_TAG_SIGIL
|
22
|
+
from ..tags import DEPLOY_TAGS_BY_NAME
|
23
|
+
from ..tags import DeployTag
|
24
|
+
from ..tags import DeployTagMap
|
25
|
+
from .types import DeployPathKind
|
21
26
|
|
22
27
|
|
23
28
|
##
|
24
29
|
|
25
30
|
|
26
|
-
DEPLOY_PATH_PLACEHOLDER_SIGIL = '@'
|
27
|
-
DEPLOY_PATH_PLACEHOLDER_SEPARATOR = '--'
|
28
|
-
|
29
|
-
DEPLOY_PATH_PLACEHOLDER_DELIMITERS: ta.AbstractSet[str] = frozenset([
|
30
|
-
DEPLOY_PATH_PLACEHOLDER_SEPARATOR,
|
31
|
-
'.',
|
32
|
-
])
|
33
|
-
|
34
|
-
DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
|
35
|
-
'app',
|
36
|
-
'tag',
|
37
|
-
'conf',
|
38
|
-
])
|
39
|
-
|
40
|
-
|
41
31
|
class DeployPathError(Exception):
|
42
32
|
pass
|
43
33
|
|
44
34
|
|
45
35
|
class DeployPathRenderable(abc.ABC):
|
46
36
|
@abc.abstractmethod
|
47
|
-
def render(self,
|
37
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
48
38
|
raise NotImplementedError
|
49
39
|
|
50
40
|
|
@@ -55,26 +45,30 @@ class DeployPathNamePart(DeployPathRenderable, abc.ABC):
|
|
55
45
|
@classmethod
|
56
46
|
def parse(cls, s: str) -> 'DeployPathNamePart':
|
57
47
|
check.non_empty_str(s)
|
58
|
-
if s.startswith(
|
59
|
-
return
|
60
|
-
elif s in
|
48
|
+
if s.startswith(DEPLOY_TAG_SIGIL):
|
49
|
+
return TagDeployPathNamePart(s[1:])
|
50
|
+
elif s in DEPLOY_TAG_DELIMITERS:
|
61
51
|
return DelimiterDeployPathNamePart(s)
|
62
52
|
else:
|
63
53
|
return ConstDeployPathNamePart(s)
|
64
54
|
|
65
55
|
|
66
56
|
@dc.dataclass(frozen=True)
|
67
|
-
class
|
68
|
-
|
57
|
+
class TagDeployPathNamePart(DeployPathNamePart):
|
58
|
+
name: str
|
69
59
|
|
70
60
|
def __post_init__(self) -> None:
|
71
|
-
check.in_(self.
|
61
|
+
check.in_(self.name, DEPLOY_TAGS_BY_NAME)
|
72
62
|
|
73
|
-
|
74
|
-
|
75
|
-
|
63
|
+
@property
|
64
|
+
def tag(self) -> ta.Type[DeployTag]:
|
65
|
+
return DEPLOY_TAGS_BY_NAME[self.name]
|
66
|
+
|
67
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
68
|
+
if tags is not None:
|
69
|
+
return tags[self.tag].s
|
76
70
|
else:
|
77
|
-
return
|
71
|
+
return DEPLOY_TAG_SIGIL + self.name
|
78
72
|
|
79
73
|
|
80
74
|
@dc.dataclass(frozen=True)
|
@@ -82,9 +76,9 @@ class DelimiterDeployPathNamePart(DeployPathNamePart):
|
|
82
76
|
delimiter: str
|
83
77
|
|
84
78
|
def __post_init__(self) -> None:
|
85
|
-
check.in_(self.delimiter,
|
79
|
+
check.in_(self.delimiter, DEPLOY_TAG_DELIMITERS)
|
86
80
|
|
87
|
-
def render(self,
|
81
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
88
82
|
return self.delimiter
|
89
83
|
|
90
84
|
|
@@ -94,10 +88,10 @@ class ConstDeployPathNamePart(DeployPathNamePart):
|
|
94
88
|
|
95
89
|
def __post_init__(self) -> None:
|
96
90
|
check.non_empty_str(self.const)
|
97
|
-
for c in [*
|
91
|
+
for c in [*DEPLOY_TAG_DELIMITERS, DEPLOY_TAG_SIGIL, '/']:
|
98
92
|
check.not_in(c, self.const)
|
99
93
|
|
100
|
-
def render(self,
|
94
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
101
95
|
return self.const
|
102
96
|
|
103
97
|
|
@@ -112,8 +106,8 @@ class DeployPathName(DeployPathRenderable):
|
|
112
106
|
if len(gl := list(g)) > 1:
|
113
107
|
raise DeployPathError(f'May not have consecutive path name part types: {k} {gl}')
|
114
108
|
|
115
|
-
def render(self,
|
116
|
-
return ''.join(p.render(
|
109
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
110
|
+
return ''.join(p.render(tags) for p in self.parts)
|
117
111
|
|
118
112
|
@classmethod
|
119
113
|
def parse(cls, s: str) -> 'DeployPathName':
|
@@ -123,7 +117,7 @@ class DeployPathName(DeployPathRenderable):
|
|
123
117
|
i = 0
|
124
118
|
ps = []
|
125
119
|
while i < len(s):
|
126
|
-
ns = [(n, d) for d in
|
120
|
+
ns = [(n, d) for d in DEPLOY_TAG_DELIMITERS if (n := s.find(d, i)) >= 0]
|
127
121
|
if not ns:
|
128
122
|
ps.append(s[i:])
|
129
123
|
break
|
@@ -147,8 +141,8 @@ class DeployPathPart(DeployPathRenderable, abc.ABC): # noqa
|
|
147
141
|
def kind(self) -> DeployPathKind:
|
148
142
|
raise NotImplementedError
|
149
143
|
|
150
|
-
def render(self,
|
151
|
-
return self.name.render(
|
144
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
145
|
+
return self.name.render(tags) + ('/' if self.kind == 'dir' else '')
|
152
146
|
|
153
147
|
@classmethod
|
154
148
|
def parse(cls, s: str) -> 'DeployPathPart':
|
@@ -194,20 +188,20 @@ class DeployPath:
|
|
194
188
|
for p in self.parts[:-1]:
|
195
189
|
check.equal(p.kind, 'dir')
|
196
190
|
|
197
|
-
|
191
|
+
@cached_nullary
|
192
|
+
def tag_indices(self) -> ta.Mapping[ta.Type[DeployTag], ta.Sequence[int]]:
|
193
|
+
pd: ta.Dict[ta.Type[DeployTag], ta.List[int]] = {}
|
198
194
|
for i, np in enumerate(self.name_parts):
|
199
|
-
if isinstance(np,
|
200
|
-
pd.setdefault(
|
201
|
-
|
202
|
-
# if 'tag' in pd and 'app' not in pd:
|
203
|
-
# raise DeployPathError('Tag placeholder in path without app', self)
|
195
|
+
if isinstance(np, TagDeployPathNamePart):
|
196
|
+
pd.setdefault(np.tag, []).append(i)
|
197
|
+
return pd
|
204
198
|
|
205
199
|
@property
|
206
200
|
def kind(self) -> ta.Literal['file', 'dir']:
|
207
201
|
return self.parts[-1].kind
|
208
202
|
|
209
|
-
def render(self,
|
210
|
-
return ''.join([p.render(
|
203
|
+
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
204
|
+
return ''.join([p.render(tags) for p in self.parts])
|
211
205
|
|
212
206
|
@classmethod
|
213
207
|
def parse(cls, s: str) -> 'DeployPath':
|