ominfra 0.0.0.dev189__py3-none-any.whl → 0.0.0.dev191__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/__about__.py +1 -0
- ominfra/manage/deploy/apps.py +109 -88
- ominfra/manage/deploy/commands.py +1 -1
- ominfra/manage/deploy/conf/manager.py +82 -17
- ominfra/manage/deploy/conf/specs.py +9 -0
- ominfra/manage/deploy/deploy.py +294 -1
- ominfra/manage/deploy/inject.py +4 -6
- ominfra/manage/deploy/nginx.py +8 -0
- ominfra/manage/deploy/paths/paths.py +5 -1
- ominfra/manage/deploy/specs.py +21 -1
- ominfra/manage/deploy/systemd.py +131 -0
- ominfra/manage/deploy/tags.py +1 -1
- ominfra/manage/deploy/venvs.py +8 -6
- ominfra/scripts/manage.py +733 -160
- ominfra/systemd.py +49 -0
- {ominfra-0.0.0.dev189.dist-info → ominfra-0.0.0.dev191.dist-info}/METADATA +4 -3
- {ominfra-0.0.0.dev189.dist-info → ominfra-0.0.0.dev191.dist-info}/RECORD +21 -19
- ominfra/manage/deploy/driver.py +0 -62
- {ominfra-0.0.0.dev189.dist-info → ominfra-0.0.0.dev191.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev189.dist-info → ominfra-0.0.0.dev191.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev189.dist-info → ominfra-0.0.0.dev191.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev189.dist-info → ominfra-0.0.0.dev191.dist-info}/top_level.txt +0 -0
ominfra/manage/deploy/deploy.py
CHANGED
@@ -1,10 +1,33 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
import datetime
|
3
|
+
import os.path
|
3
4
|
import typing as ta
|
4
5
|
|
6
|
+
from omlish.lite.cached import cached_nullary
|
7
|
+
from omlish.lite.check import check
|
8
|
+
from omlish.lite.json import json_dumps_pretty
|
9
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
5
10
|
from omlish.lite.typing import Func0
|
11
|
+
from omlish.lite.typing import Func1
|
12
|
+
from omlish.os.paths import abs_real_path
|
13
|
+
from omlish.os.paths import relative_symlink
|
6
14
|
|
15
|
+
from .apps import DeployAppManager
|
16
|
+
from .conf.manager import DeployConfManager
|
17
|
+
from .paths.manager import DeployPathsManager
|
18
|
+
from .paths.owners import DeployPathOwner
|
19
|
+
from .paths.paths import DeployPath
|
20
|
+
from .specs import DeployAppSpec
|
21
|
+
from .specs import DeploySpec
|
22
|
+
from .systemd import DeploySystemdManager
|
23
|
+
from .tags import DeployApp
|
24
|
+
from .tags import DeployTagMap
|
7
25
|
from .tags import DeployTime
|
26
|
+
from .tmp import DeployHomeAtomics
|
27
|
+
from .types import DeployHome
|
28
|
+
|
29
|
+
|
30
|
+
##
|
8
31
|
|
9
32
|
|
10
33
|
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
@@ -13,17 +36,65 @@ DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
|
13
36
|
DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
|
14
37
|
|
15
38
|
|
16
|
-
class DeployManager:
|
39
|
+
class DeployManager(DeployPathOwner):
|
17
40
|
def __init__(
|
18
41
|
self,
|
19
42
|
*,
|
43
|
+
atomics: DeployHomeAtomics,
|
20
44
|
|
21
45
|
utc_clock: ta.Optional[DeployManagerUtcClock] = None,
|
22
46
|
):
|
23
47
|
super().__init__()
|
24
48
|
|
49
|
+
self._atomics = atomics
|
50
|
+
|
25
51
|
self._utc_clock = utc_clock
|
26
52
|
|
53
|
+
#
|
54
|
+
|
55
|
+
# Home current link just points to CURRENT_DEPLOY_LINK, and is intended for user convenience.
|
56
|
+
HOME_CURRENT_LINK = DeployPath.parse('current')
|
57
|
+
|
58
|
+
DEPLOYS_DIR = DeployPath.parse('deploys/')
|
59
|
+
|
60
|
+
# Authoritative current symlink is not in deploy-home, just to prevent accidental corruption.
|
61
|
+
CURRENT_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}current')
|
62
|
+
DEPLOYING_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}deploying')
|
63
|
+
|
64
|
+
DEPLOY_DIR = DeployPath.parse(f'{DEPLOYS_DIR}@time--@deploy-key/')
|
65
|
+
DEPLOY_SPEC_FILE = DeployPath.parse(f'{DEPLOY_DIR}spec.json')
|
66
|
+
|
67
|
+
APPS_DEPLOY_DIR = DeployPath.parse(f'{DEPLOY_DIR}apps/')
|
68
|
+
APP_DEPLOY_LINK = DeployPath.parse(f'{APPS_DEPLOY_DIR}@app')
|
69
|
+
|
70
|
+
CONFS_DEPLOY_DIR = DeployPath.parse(f'{DEPLOY_DIR}conf/')
|
71
|
+
CONF_DEPLOY_DIR = DeployPath.parse(f'{CONFS_DEPLOY_DIR}@conf/')
|
72
|
+
|
73
|
+
@cached_nullary
|
74
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
75
|
+
return {
|
76
|
+
self.DEPLOYS_DIR,
|
77
|
+
|
78
|
+
self.CURRENT_DEPLOY_LINK,
|
79
|
+
self.DEPLOYING_DEPLOY_LINK,
|
80
|
+
|
81
|
+
self.DEPLOY_DIR,
|
82
|
+
self.DEPLOY_SPEC_FILE,
|
83
|
+
|
84
|
+
self.APPS_DEPLOY_DIR,
|
85
|
+
self.APP_DEPLOY_LINK,
|
86
|
+
|
87
|
+
self.CONFS_DEPLOY_DIR,
|
88
|
+
self.CONF_DEPLOY_DIR,
|
89
|
+
}
|
90
|
+
|
91
|
+
#
|
92
|
+
|
93
|
+
def render_path(self, home: DeployHome, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
|
94
|
+
return os.path.join(check.non_empty_str(home), pth.render(tags))
|
95
|
+
|
96
|
+
#
|
97
|
+
|
27
98
|
def _utc_now(self) -> datetime.datetime:
|
28
99
|
if self._utc_clock is not None:
|
29
100
|
return self._utc_clock() # noqa
|
@@ -32,3 +103,225 @@ class DeployManager:
|
|
32
103
|
|
33
104
|
def make_deploy_time(self) -> DeployTime:
|
34
105
|
return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
|
106
|
+
|
107
|
+
#
|
108
|
+
|
109
|
+
def make_home_current_link(self, home: DeployHome) -> None:
|
110
|
+
home_current_link = os.path.join(check.non_empty_str(home), self.HOME_CURRENT_LINK.render())
|
111
|
+
current_deploy_link = os.path.join(check.non_empty_str(home), self.CURRENT_DEPLOY_LINK.render())
|
112
|
+
with self._atomics(home).begin_atomic_path_swap( # noqa
|
113
|
+
'file',
|
114
|
+
home_current_link,
|
115
|
+
auto_commit=True,
|
116
|
+
) as dst_swap:
|
117
|
+
os.unlink(dst_swap.tmp_path)
|
118
|
+
os.symlink(
|
119
|
+
os.path.relpath(current_deploy_link, os.path.dirname(dst_swap.dst_path)),
|
120
|
+
dst_swap.tmp_path,
|
121
|
+
)
|
122
|
+
|
123
|
+
|
124
|
+
##
|
125
|
+
|
126
|
+
|
127
|
+
class DeployDriverFactory(Func1[DeploySpec, ta.ContextManager['DeployDriver']]):
|
128
|
+
pass
|
129
|
+
|
130
|
+
|
131
|
+
class DeployDriver:
|
132
|
+
def __init__(
|
133
|
+
self,
|
134
|
+
*,
|
135
|
+
spec: DeploySpec,
|
136
|
+
home: DeployHome,
|
137
|
+
time: DeployTime,
|
138
|
+
|
139
|
+
deploys: DeployManager,
|
140
|
+
paths: DeployPathsManager,
|
141
|
+
apps: DeployAppManager,
|
142
|
+
conf: DeployConfManager,
|
143
|
+
systemd: DeploySystemdManager,
|
144
|
+
|
145
|
+
msh: ObjMarshalerManager,
|
146
|
+
) -> None:
|
147
|
+
super().__init__()
|
148
|
+
|
149
|
+
self._spec = spec
|
150
|
+
self._home = home
|
151
|
+
self._time = time
|
152
|
+
|
153
|
+
self._deploys = deploys
|
154
|
+
self._paths = paths
|
155
|
+
self._apps = apps
|
156
|
+
self._conf = conf
|
157
|
+
self._systemd = systemd
|
158
|
+
|
159
|
+
self._msh = msh
|
160
|
+
|
161
|
+
#
|
162
|
+
|
163
|
+
@property
|
164
|
+
def tags(self) -> DeployTagMap:
|
165
|
+
return DeployTagMap(
|
166
|
+
self._time,
|
167
|
+
self._spec.key(),
|
168
|
+
)
|
169
|
+
|
170
|
+
def render_path(self, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
|
171
|
+
return os.path.join(self._home, pth.render(tags if tags is not None else self.tags))
|
172
|
+
|
173
|
+
@property
|
174
|
+
def dir(self) -> str:
|
175
|
+
return self.render_path(self._deploys.DEPLOY_DIR)
|
176
|
+
|
177
|
+
#
|
178
|
+
|
179
|
+
async def drive_deploy(self) -> None:
|
180
|
+
spec_json = json_dumps_pretty(self._msh.marshal_obj(self._spec))
|
181
|
+
|
182
|
+
#
|
183
|
+
|
184
|
+
das: ta.Set[DeployApp] = {a.app for a in self._spec.apps}
|
185
|
+
las: ta.Set[DeployApp] = set(self._spec.app_links.apps)
|
186
|
+
if (ras := das & las):
|
187
|
+
raise RuntimeError(f'Must not specify apps as both deploy and link: {sorted(a.s for a in ras)}')
|
188
|
+
|
189
|
+
#
|
190
|
+
|
191
|
+
self._paths.validate_deploy_paths()
|
192
|
+
|
193
|
+
#
|
194
|
+
|
195
|
+
os.makedirs(self.dir)
|
196
|
+
|
197
|
+
#
|
198
|
+
|
199
|
+
spec_file = self.render_path(self._deploys.DEPLOY_SPEC_FILE)
|
200
|
+
with open(spec_file, 'w') as f: # noqa
|
201
|
+
f.write(spec_json)
|
202
|
+
|
203
|
+
#
|
204
|
+
|
205
|
+
deploying_link = self.render_path(self._deploys.DEPLOYING_DEPLOY_LINK)
|
206
|
+
current_link = self.render_path(self._deploys.CURRENT_DEPLOY_LINK)
|
207
|
+
|
208
|
+
#
|
209
|
+
|
210
|
+
if os.path.exists(deploying_link):
|
211
|
+
os.unlink(deploying_link)
|
212
|
+
relative_symlink(
|
213
|
+
self.dir,
|
214
|
+
deploying_link,
|
215
|
+
target_is_directory=True,
|
216
|
+
make_dirs=True,
|
217
|
+
)
|
218
|
+
|
219
|
+
#
|
220
|
+
|
221
|
+
for md in [
|
222
|
+
self._deploys.APPS_DEPLOY_DIR,
|
223
|
+
self._deploys.CONFS_DEPLOY_DIR,
|
224
|
+
]:
|
225
|
+
os.makedirs(self.render_path(md))
|
226
|
+
|
227
|
+
#
|
228
|
+
|
229
|
+
if not self._spec.app_links.exclude_unspecified:
|
230
|
+
cad = abs_real_path(os.path.join(current_link, 'apps'))
|
231
|
+
if os.path.exists(cad):
|
232
|
+
for d in os.listdir(cad):
|
233
|
+
if (da := DeployApp(d)) not in das:
|
234
|
+
las.add(da)
|
235
|
+
|
236
|
+
for la in self._spec.app_links.apps:
|
237
|
+
await self._drive_app_link(
|
238
|
+
la,
|
239
|
+
current_link,
|
240
|
+
)
|
241
|
+
|
242
|
+
for app in self._spec.apps:
|
243
|
+
await self._drive_app_deploy(
|
244
|
+
app,
|
245
|
+
)
|
246
|
+
|
247
|
+
#
|
248
|
+
|
249
|
+
os.replace(deploying_link, current_link)
|
250
|
+
|
251
|
+
#
|
252
|
+
|
253
|
+
await self._systemd.sync_systemd(
|
254
|
+
self._spec.systemd,
|
255
|
+
self._home,
|
256
|
+
os.path.join(self.dir, 'conf', 'systemd'), # FIXME
|
257
|
+
)
|
258
|
+
|
259
|
+
#
|
260
|
+
|
261
|
+
self._deploys.make_home_current_link(self._home)
|
262
|
+
|
263
|
+
#
|
264
|
+
|
265
|
+
async def _drive_app_deploy(self, app: DeployAppSpec) -> None:
|
266
|
+
pa = await self._apps.prepare_app(
|
267
|
+
app,
|
268
|
+
self._home,
|
269
|
+
self.tags,
|
270
|
+
)
|
271
|
+
|
272
|
+
#
|
273
|
+
|
274
|
+
app_link = self.render_path(self._deploys.APP_DEPLOY_LINK, pa.tags)
|
275
|
+
relative_symlink(
|
276
|
+
pa.dir,
|
277
|
+
app_link,
|
278
|
+
target_is_directory=True,
|
279
|
+
make_dirs=True,
|
280
|
+
)
|
281
|
+
|
282
|
+
#
|
283
|
+
|
284
|
+
await self._drive_app_configure(pa)
|
285
|
+
|
286
|
+
async def _drive_app_link(
|
287
|
+
self,
|
288
|
+
app: DeployApp,
|
289
|
+
current_link: str,
|
290
|
+
) -> None:
|
291
|
+
app_link = os.path.join(abs_real_path(current_link), 'apps', app.s)
|
292
|
+
check.state(os.path.islink(app_link))
|
293
|
+
|
294
|
+
app_dir = abs_real_path(app_link)
|
295
|
+
check.state(os.path.isdir(app_dir))
|
296
|
+
|
297
|
+
#
|
298
|
+
|
299
|
+
pa = await self._apps.prepare_app_link(
|
300
|
+
self.tags,
|
301
|
+
app_dir,
|
302
|
+
)
|
303
|
+
|
304
|
+
#
|
305
|
+
|
306
|
+
relative_symlink(
|
307
|
+
app_dir,
|
308
|
+
os.path.join(self.dir, 'apps', app.s),
|
309
|
+
target_is_directory=True,
|
310
|
+
)
|
311
|
+
|
312
|
+
#
|
313
|
+
|
314
|
+
await self._drive_app_configure(pa)
|
315
|
+
|
316
|
+
async def _drive_app_configure(
|
317
|
+
self,
|
318
|
+
pa: DeployAppManager.PreparedApp,
|
319
|
+
) -> None:
|
320
|
+
deploy_conf_dir = self.render_path(self._deploys.CONFS_DEPLOY_DIR)
|
321
|
+
if pa.spec.conf is not None:
|
322
|
+
await self._conf.link_app_conf(
|
323
|
+
pa.spec.conf,
|
324
|
+
pa.tags,
|
325
|
+
check.non_empty_str(pa.conf_dir),
|
326
|
+
deploy_conf_dir,
|
327
|
+
)
|
ominfra/manage/deploy/inject.py
CHANGED
@@ -16,15 +16,16 @@ from .commands import DeployCommand
|
|
16
16
|
from .commands import DeployCommandExecutor
|
17
17
|
from .conf.inject import bind_deploy_conf
|
18
18
|
from .config import DeployConfig
|
19
|
+
from .deploy import DeployDriver
|
20
|
+
from .deploy import DeployDriverFactory
|
19
21
|
from .deploy import DeployManager
|
20
|
-
from .driver import DeployDriver
|
21
|
-
from .driver import DeployDriverFactory
|
22
22
|
from .git import DeployGitManager
|
23
23
|
from .inject_ import bind_deploy_manager
|
24
24
|
from .interp import InterpCommand
|
25
25
|
from .interp import InterpCommandExecutor
|
26
26
|
from .paths.inject import bind_deploy_paths
|
27
27
|
from .specs import DeploySpec
|
28
|
+
from .systemd import DeploySystemdManager
|
28
29
|
from .tags import DeployTime
|
29
30
|
from .tmp import DeployHomeAtomics
|
30
31
|
from .tmp import DeployTmpManager
|
@@ -101,13 +102,10 @@ def bind_deploy(
|
|
101
102
|
|
102
103
|
lst.extend([
|
103
104
|
bind_deploy_manager(DeployAppManager),
|
104
|
-
|
105
105
|
bind_deploy_manager(DeployGitManager),
|
106
|
-
|
107
106
|
bind_deploy_manager(DeployManager),
|
108
|
-
|
107
|
+
bind_deploy_manager(DeploySystemdManager),
|
109
108
|
bind_deploy_manager(DeployTmpManager),
|
110
|
-
|
111
109
|
bind_deploy_manager(DeployVenvManager),
|
112
110
|
])
|
113
111
|
|
@@ -33,6 +33,10 @@ class DeployPathError(Exception):
|
|
33
33
|
|
34
34
|
|
35
35
|
class DeployPathRenderable(abc.ABC):
|
36
|
+
@cached_nullary
|
37
|
+
def __str__(self) -> str:
|
38
|
+
return self.render(None)
|
39
|
+
|
36
40
|
@abc.abstractmethod
|
37
41
|
def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
|
38
42
|
raise NotImplementedError
|
@@ -174,7 +178,7 @@ class FileDeployPathPart(DeployPathPart):
|
|
174
178
|
|
175
179
|
|
176
180
|
@dc.dataclass(frozen=True)
|
177
|
-
class DeployPath:
|
181
|
+
class DeployPath(DeployPathRenderable):
|
178
182
|
parts: ta.Sequence[DeployPathPart]
|
179
183
|
|
180
184
|
@property
|
ominfra/manage/deploy/specs.py
CHANGED
@@ -91,6 +91,22 @@ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
|
|
91
91
|
return DeployAppKey(self._key_str())
|
92
92
|
|
93
93
|
|
94
|
+
@dc.dataclass(frozen=True)
|
95
|
+
class DeployAppLinksSpec:
|
96
|
+
apps: ta.Sequence[DeployApp] = ()
|
97
|
+
|
98
|
+
exclude_unspecified: bool = False
|
99
|
+
|
100
|
+
|
101
|
+
##
|
102
|
+
|
103
|
+
|
104
|
+
@dc.dataclass(frozen=True)
|
105
|
+
class DeploySystemdSpec:
|
106
|
+
# ~/.config/systemd/user/
|
107
|
+
unit_dir: ta.Optional[str] = None
|
108
|
+
|
109
|
+
|
94
110
|
##
|
95
111
|
|
96
112
|
|
@@ -98,7 +114,11 @@ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
|
|
98
114
|
class DeploySpec(DeploySpecKeyed[DeployKey]):
|
99
115
|
home: DeployHome
|
100
116
|
|
101
|
-
apps: ta.Sequence[DeployAppSpec]
|
117
|
+
apps: ta.Sequence[DeployAppSpec] = ()
|
118
|
+
|
119
|
+
app_links: DeployAppLinksSpec = DeployAppLinksSpec()
|
120
|
+
|
121
|
+
systemd: ta.Optional[DeploySystemdSpec] = None
|
102
122
|
|
103
123
|
def __post_init__(self) -> None:
|
104
124
|
check.non_empty_str(self.home)
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
"""
|
3
|
+
TODO:
|
4
|
+
- verify - systemd-analyze
|
5
|
+
- sudo loginctl enable-linger "$USER"
|
6
|
+
- idemp kill services that shouldn't be running, start ones that should
|
7
|
+
- ideally only those defined by links to deploy home
|
8
|
+
- ominfra.systemd / x.sd_orphans
|
9
|
+
"""
|
10
|
+
import os.path
|
11
|
+
import sys
|
12
|
+
import typing as ta
|
13
|
+
|
14
|
+
from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
15
|
+
from omlish.lite.check import check
|
16
|
+
from omlish.os.paths import abs_real_path
|
17
|
+
from omlish.os.paths import is_path_in_dir
|
18
|
+
|
19
|
+
from .specs import DeploySystemdSpec
|
20
|
+
from .tmp import DeployHomeAtomics
|
21
|
+
from .types import DeployHome
|
22
|
+
|
23
|
+
|
24
|
+
class DeploySystemdManager:
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
*,
|
28
|
+
atomics: DeployHomeAtomics,
|
29
|
+
) -> None:
|
30
|
+
super().__init__()
|
31
|
+
|
32
|
+
self._atomics = atomics
|
33
|
+
|
34
|
+
def _scan_link_dir(
|
35
|
+
self,
|
36
|
+
d: str,
|
37
|
+
*,
|
38
|
+
strict: bool = False,
|
39
|
+
) -> ta.Dict[str, str]:
|
40
|
+
o: ta.Dict[str, str] = {}
|
41
|
+
for f in os.listdir(d):
|
42
|
+
fp = os.path.join(d, f)
|
43
|
+
if strict:
|
44
|
+
check.state(os.path.islink(fp))
|
45
|
+
o[f] = abs_real_path(fp)
|
46
|
+
return o
|
47
|
+
|
48
|
+
async def sync_systemd(
|
49
|
+
self,
|
50
|
+
spec: ta.Optional[DeploySystemdSpec],
|
51
|
+
home: DeployHome,
|
52
|
+
conf_dir: str,
|
53
|
+
) -> None:
|
54
|
+
check.non_empty_str(home)
|
55
|
+
|
56
|
+
if not spec:
|
57
|
+
return
|
58
|
+
|
59
|
+
#
|
60
|
+
|
61
|
+
if not (ud := spec.unit_dir):
|
62
|
+
return
|
63
|
+
|
64
|
+
ud = abs_real_path(os.path.expanduser(ud))
|
65
|
+
|
66
|
+
os.makedirs(ud, exist_ok=True)
|
67
|
+
|
68
|
+
#
|
69
|
+
|
70
|
+
uld = {
|
71
|
+
n: p
|
72
|
+
for n, p in self._scan_link_dir(ud).items()
|
73
|
+
if is_path_in_dir(home, p)
|
74
|
+
}
|
75
|
+
|
76
|
+
if os.path.exists(conf_dir):
|
77
|
+
cld = self._scan_link_dir(conf_dir, strict=True)
|
78
|
+
else:
|
79
|
+
cld = {}
|
80
|
+
|
81
|
+
#
|
82
|
+
|
83
|
+
ns = sorted(set(uld) | set(cld))
|
84
|
+
|
85
|
+
for n in ns:
|
86
|
+
cl = cld.get(n)
|
87
|
+
if cl is None:
|
88
|
+
os.unlink(os.path.join(ud, n))
|
89
|
+
else:
|
90
|
+
with self._atomics(home).begin_atomic_path_swap( # noqa
|
91
|
+
'file',
|
92
|
+
os.path.join(ud, n),
|
93
|
+
auto_commit=True,
|
94
|
+
skip_root_dir_check=True,
|
95
|
+
) as dst_swap:
|
96
|
+
os.unlink(dst_swap.tmp_path)
|
97
|
+
os.symlink(
|
98
|
+
os.path.relpath(cl, os.path.dirname(dst_swap.dst_path)),
|
99
|
+
dst_swap.tmp_path,
|
100
|
+
)
|
101
|
+
|
102
|
+
#
|
103
|
+
|
104
|
+
if sys.platform == 'linux':
|
105
|
+
async def reload() -> None:
|
106
|
+
await asyncio_subprocesses.check_call('systemctl', '--user', 'daemon-reload')
|
107
|
+
|
108
|
+
await reload()
|
109
|
+
|
110
|
+
num_deleted = 0
|
111
|
+
for n in ns:
|
112
|
+
if n.endswith('.service'):
|
113
|
+
cl = cld.get(n)
|
114
|
+
ul = uld.get(n)
|
115
|
+
if cl is not None:
|
116
|
+
if ul is None:
|
117
|
+
cs = ['enable', 'start']
|
118
|
+
else:
|
119
|
+
cs = ['restart']
|
120
|
+
else: # noqa
|
121
|
+
if ul is not None:
|
122
|
+
cs = ['stop']
|
123
|
+
num_deleted += 1
|
124
|
+
else:
|
125
|
+
cs = []
|
126
|
+
|
127
|
+
for c in cs:
|
128
|
+
await asyncio_subprocesses.check_call('systemctl', '--user', c, n)
|
129
|
+
|
130
|
+
if num_deleted:
|
131
|
+
await reload()
|
ominfra/manage/deploy/tags.py
CHANGED
ominfra/manage/deploy/venvs.py
CHANGED
@@ -5,6 +5,7 @@ TODO:
|
|
5
5
|
- share more code with pyproject?
|
6
6
|
"""
|
7
7
|
import os.path
|
8
|
+
import shutil
|
8
9
|
|
9
10
|
from omdev.interp.default import get_default_interp_resolver
|
10
11
|
from omdev.interp.types import InterpSpecifier
|
@@ -12,14 +13,12 @@ from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
|
12
13
|
from omlish.lite.check import check
|
13
14
|
|
14
15
|
from .specs import DeployVenvSpec
|
15
|
-
from .types import DeployHome
|
16
16
|
|
17
17
|
|
18
18
|
class DeployVenvManager:
|
19
19
|
async def setup_venv(
|
20
20
|
self,
|
21
21
|
spec: DeployVenvSpec,
|
22
|
-
home: DeployHome,
|
23
22
|
git_dir: str,
|
24
23
|
venv_dir: str,
|
25
24
|
) -> None:
|
@@ -46,9 +45,12 @@ class DeployVenvManager:
|
|
46
45
|
|
47
46
|
if os.path.isfile(reqs_txt):
|
48
47
|
if spec.use_uv:
|
49
|
-
|
50
|
-
|
48
|
+
if shutil.which('uv') is not None:
|
49
|
+
pip_cmd = ['uv', 'pip']
|
50
|
+
else:
|
51
|
+
await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
|
52
|
+
pip_cmd = [venv_exe, '-m', 'uv', 'pip']
|
51
53
|
else:
|
52
|
-
pip_cmd = ['-m', 'pip']
|
54
|
+
pip_cmd = [venv_exe, '-m', 'pip']
|
53
55
|
|
54
|
-
await asyncio_subprocesses.check_call(
|
56
|
+
await asyncio_subprocesses.check_call(*pip_cmd, 'install', '-r', reqs_txt, cwd=venv_dir)
|