omdev 0.0.0.dev212__py3-none-any.whl → 0.0.0.dev213__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.
- omdev/cc/cdeps.py +34 -1
- omdev/cc/cdeps.toml +19 -2
- omdev/cc/cli.py +13 -1
- omdev/ci/ci.py +71 -48
- omdev/ci/cli.py +12 -6
- omdev/ci/compose.py +26 -55
- omdev/ci/docker.py +35 -16
- omdev/ci/github/cache.py +153 -184
- omdev/ci/github/cacheapi.py +1 -1
- omdev/ci/github/cli.py +2 -2
- omdev/ci/github/curl.py +209 -0
- omdev/git/shallow.py +1 -1
- omdev/scripts/ci.py +936 -448
- omdev/scripts/interp.py +23 -0
- omdev/scripts/pyproject.py +23 -0
- omdev/tokens/tokenizert.py +1 -3
- {omdev-0.0.0.dev212.dist-info → omdev-0.0.0.dev213.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev212.dist-info → omdev-0.0.0.dev213.dist-info}/RECORD +22 -21
- {omdev-0.0.0.dev212.dist-info → omdev-0.0.0.dev213.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev212.dist-info → omdev-0.0.0.dev213.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev212.dist-info → omdev-0.0.0.dev213.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev212.dist-info → omdev-0.0.0.dev213.dist-info}/top_level.txt +0 -0
omdev/cc/cdeps.py
CHANGED
@@ -5,6 +5,7 @@ import typing as ta
|
|
5
5
|
from omlish import cached
|
6
6
|
from omlish import lang
|
7
7
|
from omlish import marshal as msh
|
8
|
+
from omlish.configs import all as configs
|
8
9
|
|
9
10
|
|
10
11
|
@dc.dataclass(frozen=True)
|
@@ -20,19 +21,51 @@ class Cdep:
|
|
20
21
|
|
21
22
|
#
|
22
23
|
|
24
|
+
sources: ta.Sequence[str] | None = None
|
23
25
|
include: ta.Sequence[str] | None = None
|
24
26
|
|
25
27
|
#
|
26
28
|
|
27
29
|
@dc.dataclass(frozen=True)
|
28
30
|
class Cmake:
|
29
|
-
|
31
|
+
@dc.dataclass(frozen=True)
|
32
|
+
class FetchContent:
|
33
|
+
url: str | None = None
|
34
|
+
|
35
|
+
@dc.dataclass(frozen=True)
|
36
|
+
class Git:
|
37
|
+
repository: str | None = None
|
38
|
+
tag: str | None = None
|
39
|
+
|
40
|
+
git: Git | None = None
|
41
|
+
|
42
|
+
fetch_content: FetchContent | None = None
|
30
43
|
|
31
44
|
cmake: Cmake | None = None
|
32
45
|
|
33
46
|
|
47
|
+
def process_marshaled_cdep(obj: ta.Any) -> ta.Any:
|
48
|
+
obj = configs.processing.matched_rewrite(
|
49
|
+
lambda s: s if isinstance(s, str) else ''.join(s),
|
50
|
+
obj,
|
51
|
+
('sources', None),
|
52
|
+
('include', None),
|
53
|
+
)
|
54
|
+
|
55
|
+
return obj
|
56
|
+
|
57
|
+
|
34
58
|
@cached.function
|
35
59
|
def load_cdeps() -> ta.Mapping[str, Cdep]:
|
36
60
|
src = lang.get_relative_resources(globals=globals())['cdeps.toml'].read_text()
|
37
61
|
dct = tomllib.loads(src)
|
62
|
+
|
63
|
+
dct = {
|
64
|
+
**dct,
|
65
|
+
'deps': {
|
66
|
+
k: process_marshaled_cdep(v)
|
67
|
+
for k, v in dct.get('deps', {}).items()
|
68
|
+
},
|
69
|
+
}
|
70
|
+
|
38
71
|
return msh.unmarshal(dct.get('deps', {}), ta.Mapping[str, Cdep]) # type: ignore
|
omdev/cc/cdeps.toml
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
[deps.httplib]
|
2
|
+
include = ['.']
|
3
|
+
|
4
|
+
[deps.httplib.git]
|
5
|
+
url = 'https://github.com/yhirose/cpp-httplib'
|
6
|
+
rev = 'a7bc00e3307fecdb4d67545e93be7b88cfb1e186'
|
7
|
+
subtrees = ['httplib.h']
|
8
|
+
|
9
|
+
[deps.httplib.cmake.fetch_content.git]
|
10
|
+
repository = 'https://github.com/yhirose/cpp-httplib.git'
|
11
|
+
tag = 'a7bc00e3307fecdb4d67545e93be7b88cfb1e186'
|
12
|
+
|
13
|
+
|
14
|
+
#
|
15
|
+
|
1
16
|
[deps.json]
|
2
17
|
include = ['single_include']
|
3
18
|
|
@@ -6,8 +21,9 @@ url = 'https://github.com/nlohmann/json'
|
|
6
21
|
rev = '9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03'
|
7
22
|
subtrees = ['single_include']
|
8
23
|
|
9
|
-
[deps.json.cmake]
|
10
|
-
|
24
|
+
[deps.json.cmake.fetch_content]
|
25
|
+
url = 'https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz'
|
26
|
+
|
11
27
|
|
12
28
|
#
|
13
29
|
|
@@ -19,6 +35,7 @@ url = 'https://github.com/pybind/pybind11'
|
|
19
35
|
rev = '945e251a6ce7273058b36214d94415e9c9530b8e'
|
20
36
|
subtrees = ['include']
|
21
37
|
|
38
|
+
|
22
39
|
#
|
23
40
|
|
24
41
|
[deps.ut]
|
omdev/cc/cli.py
CHANGED
@@ -18,6 +18,8 @@ Freestanding options:
|
|
18
18
|
TODO:
|
19
19
|
- cext interop
|
20
20
|
- gen cmake
|
21
|
+
- fix CFLAGS/CCFLAGS/CPPFLAGS/CXXFLAGS
|
22
|
+
- jit-gen cmake mode? multi-src builds
|
21
23
|
"""
|
22
24
|
import os
|
23
25
|
import shlex
|
@@ -35,6 +37,7 @@ from .. import magic
|
|
35
37
|
from ..cache import data as dcache
|
36
38
|
from .cdeps import Cdep
|
37
39
|
from .cdeps import load_cdeps
|
40
|
+
from .cdeps import process_marshaled_cdep
|
38
41
|
|
39
42
|
|
40
43
|
class Cli(ap.Cli):
|
@@ -64,16 +67,22 @@ class Cli(ap.Cli):
|
|
64
67
|
if src_magic.key == '@omlish-cdeps':
|
65
68
|
for dep in check.isinstance(src_magic.prepared, ta.Sequence):
|
66
69
|
if isinstance(dep, ta.Mapping):
|
67
|
-
dep =
|
70
|
+
dep = process_marshaled_cdep(dep)
|
71
|
+
dep = msh.unmarshal(dep, Cdep)
|
68
72
|
else:
|
69
73
|
dep = load_cdeps()[check.isinstance(dep, str)]
|
70
74
|
|
75
|
+
if dep.sources:
|
76
|
+
# TODO
|
77
|
+
raise NotImplementedError
|
78
|
+
|
71
79
|
dep_spec = dcache.GitSpec(
|
72
80
|
url=dep.git.url,
|
73
81
|
rev=dep.git.rev,
|
74
82
|
subtrees=dep.git.subtrees,
|
75
83
|
)
|
76
84
|
dep_dir = dcache.default().get(dep_spec)
|
85
|
+
|
77
86
|
for dep_inc in dep.include or []:
|
78
87
|
inc_dir = os.path.join(dep_dir, dep_inc)
|
79
88
|
check.state(os.path.isdir(inc_dir))
|
@@ -96,6 +105,9 @@ class Cli(ap.Cli):
|
|
96
105
|
if cflags := os.environ.get('CFLAGS'):
|
97
106
|
sh_parts.append(cflags) # Explicitly shell-unquoted
|
98
107
|
|
108
|
+
if ldflags := os.environ.get('LDFLAGS'):
|
109
|
+
sh_parts.append(ldflags) # Explicitly shell-unquoted
|
110
|
+
|
99
111
|
sh_parts.extend([
|
100
112
|
'-std=c++20',
|
101
113
|
shlex.quote(os.path.abspath(src_file)),
|
omdev/ci/ci.py
CHANGED
@@ -7,9 +7,10 @@ import tarfile
|
|
7
7
|
import tempfile
|
8
8
|
import typing as ta
|
9
9
|
|
10
|
+
from omlish.lite.cached import async_cached_nullary
|
10
11
|
from omlish.lite.cached import cached_nullary
|
11
12
|
from omlish.lite.check import check
|
12
|
-
from omlish.lite.contextmanagers import
|
13
|
+
from omlish.lite.contextmanagers import AsyncExitStacked
|
13
14
|
from omlish.lite.contextmanagers import defer
|
14
15
|
|
15
16
|
from .cache import FileCache
|
@@ -22,13 +23,14 @@ from .docker import is_docker_image_present
|
|
22
23
|
from .docker import load_docker_tar_cmd
|
23
24
|
from .docker import pull_docker_image
|
24
25
|
from .docker import save_docker_tar_cmd
|
26
|
+
from .docker import tag_docker_image
|
25
27
|
from .requirements import build_requirements_hash
|
26
28
|
from .requirements import download_requirements
|
27
29
|
from .shell import ShellCmd
|
28
30
|
from .utils import log_timing_context
|
29
31
|
|
30
32
|
|
31
|
-
class Ci(
|
33
|
+
class Ci(AsyncExitStacked):
|
32
34
|
FILE_NAME_HASH_LEN = 16
|
33
35
|
|
34
36
|
@dc.dataclass(frozen=True)
|
@@ -45,6 +47,9 @@ class Ci(ExitStacked):
|
|
45
47
|
requirements_txts: ta.Optional[ta.Sequence[str]] = None
|
46
48
|
|
47
49
|
always_pull: bool = False
|
50
|
+
always_build: bool = False
|
51
|
+
|
52
|
+
no_dependencies: bool = False
|
48
53
|
|
49
54
|
def __post_init__(self) -> None:
|
50
55
|
check.not_isinstance(self.requirements_txts, str)
|
@@ -64,7 +69,7 @@ class Ci(ExitStacked):
|
|
64
69
|
|
65
70
|
#
|
66
71
|
|
67
|
-
def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
|
72
|
+
async def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
|
68
73
|
if self._shell_cache is None:
|
69
74
|
return None
|
70
75
|
|
@@ -74,9 +79,9 @@ class Ci(ExitStacked):
|
|
74
79
|
|
75
80
|
get_cache_cmd = dc.replace(get_cache_cmd, s=f'{get_cache_cmd.s} | zstd -cd --long') # noqa
|
76
81
|
|
77
|
-
return load_docker_tar_cmd(get_cache_cmd)
|
82
|
+
return await load_docker_tar_cmd(get_cache_cmd)
|
78
83
|
|
79
|
-
def _save_cache_docker_image(self, key: str, image: str) -> None:
|
84
|
+
async def _save_cache_docker_image(self, key: str, image: str) -> None:
|
80
85
|
if self._shell_cache is None:
|
81
86
|
return
|
82
87
|
|
@@ -85,12 +90,12 @@ class Ci(ExitStacked):
|
|
85
90
|
|
86
91
|
put_cache_cmd = dc.replace(put_cache_cmd, s=f'zstd | {put_cache_cmd.s}')
|
87
92
|
|
88
|
-
save_docker_tar_cmd(image, put_cache_cmd)
|
93
|
+
await save_docker_tar_cmd(image, put_cache_cmd)
|
89
94
|
|
90
95
|
#
|
91
96
|
|
92
|
-
def _load_docker_image(self, image: str) -> None:
|
93
|
-
if not self._cfg.always_pull and is_docker_image_present(image):
|
97
|
+
async def _load_docker_image(self, image: str) -> None:
|
98
|
+
if not self._cfg.always_pull and (await is_docker_image_present(image)):
|
94
99
|
return
|
95
100
|
|
96
101
|
dep_suffix = image
|
@@ -98,63 +103,79 @@ class Ci(ExitStacked):
|
|
98
103
|
dep_suffix = dep_suffix.replace(c, '-')
|
99
104
|
|
100
105
|
cache_key = f'docker-{dep_suffix}'
|
101
|
-
if self._load_cache_docker_image(cache_key) is not None:
|
106
|
+
if (await self._load_cache_docker_image(cache_key)) is not None:
|
102
107
|
return
|
103
108
|
|
104
|
-
pull_docker_image(image)
|
109
|
+
await pull_docker_image(image)
|
105
110
|
|
106
|
-
self._save_cache_docker_image(cache_key, image)
|
111
|
+
await self._save_cache_docker_image(cache_key, image)
|
107
112
|
|
108
|
-
def load_docker_image(self, image: str) -> None:
|
113
|
+
async def load_docker_image(self, image: str) -> None:
|
109
114
|
with log_timing_context(f'Load docker image: {image}'):
|
110
|
-
self._load_docker_image(image)
|
115
|
+
await self._load_docker_image(image)
|
111
116
|
|
112
|
-
@
|
113
|
-
def load_compose_service_dependencies(self) -> None:
|
117
|
+
@async_cached_nullary
|
118
|
+
async def load_compose_service_dependencies(self) -> None:
|
114
119
|
deps = get_compose_service_dependencies(
|
115
120
|
self._cfg.compose_file,
|
116
121
|
self._cfg.service,
|
117
122
|
)
|
118
123
|
|
119
124
|
for dep_image in deps.values():
|
120
|
-
self.load_docker_image(dep_image)
|
125
|
+
await self.load_docker_image(dep_image)
|
121
126
|
|
122
127
|
#
|
123
128
|
|
124
|
-
|
125
|
-
|
129
|
+
@cached_nullary
|
130
|
+
def docker_file_hash(self) -> str:
|
131
|
+
return build_docker_file_hash(self._cfg.docker_file)[:self.FILE_NAME_HASH_LEN]
|
132
|
+
|
133
|
+
async def _resolve_ci_image(self) -> str:
|
134
|
+
cache_key = f'ci-{self.docker_file_hash()}'
|
135
|
+
image_tag = f'{self._cfg.service}:{cache_key}'
|
136
|
+
|
137
|
+
if not self._cfg.always_build and (await is_docker_image_present(image_tag)):
|
138
|
+
return image_tag
|
126
139
|
|
127
|
-
cache_key
|
128
|
-
|
129
|
-
|
140
|
+
if (cache_image_id := await self._load_cache_docker_image(cache_key)) is not None:
|
141
|
+
await tag_docker_image(
|
142
|
+
cache_image_id,
|
143
|
+
image_tag,
|
144
|
+
)
|
145
|
+
return image_tag
|
130
146
|
|
131
|
-
image_id = build_docker_image(
|
147
|
+
image_id = await build_docker_image(
|
132
148
|
self._cfg.docker_file,
|
149
|
+
tag=image_tag,
|
133
150
|
cwd=self._cfg.project_dir,
|
134
151
|
)
|
135
152
|
|
136
|
-
self._save_cache_docker_image(cache_key, image_id)
|
153
|
+
await self._save_cache_docker_image(cache_key, image_id)
|
137
154
|
|
138
|
-
return
|
155
|
+
return image_tag
|
139
156
|
|
140
|
-
@
|
141
|
-
def resolve_ci_image(self) -> str:
|
157
|
+
@async_cached_nullary
|
158
|
+
async def resolve_ci_image(self) -> str:
|
142
159
|
with log_timing_context('Resolve ci image') as ltc:
|
143
|
-
image_id = self._resolve_ci_image()
|
160
|
+
image_id = await self._resolve_ci_image()
|
144
161
|
ltc.set_description(f'Resolve ci image: {image_id}')
|
145
162
|
return image_id
|
146
163
|
|
147
164
|
#
|
148
165
|
|
149
|
-
|
150
|
-
|
166
|
+
@cached_nullary
|
167
|
+
def requirements_txts(self) -> ta.Sequence[str]:
|
168
|
+
return [
|
151
169
|
os.path.join(self._cfg.project_dir, rf)
|
152
170
|
for rf in check.not_none(self._cfg.requirements_txts)
|
153
171
|
]
|
154
172
|
|
155
|
-
|
173
|
+
@cached_nullary
|
174
|
+
def requirements_hash(self) -> str:
|
175
|
+
return build_requirements_hash(self.requirements_txts())[:self.FILE_NAME_HASH_LEN]
|
156
176
|
|
157
|
-
|
177
|
+
async def _resolve_requirements_dir(self) -> str:
|
178
|
+
tar_file_key = f'requirements-{self.docker_file_hash()}-{self.requirements_hash()}'
|
158
179
|
tar_file_name = f'{tar_file_key}.tar'
|
159
180
|
|
160
181
|
temp_dir = tempfile.mkdtemp()
|
@@ -170,9 +191,9 @@ class Ci(ExitStacked):
|
|
170
191
|
os.makedirs(temp_requirements_dir)
|
171
192
|
|
172
193
|
download_requirements(
|
173
|
-
self.resolve_ci_image(),
|
194
|
+
await self.resolve_ci_image(),
|
174
195
|
temp_requirements_dir,
|
175
|
-
requirements_txts,
|
196
|
+
self.requirements_txts(),
|
176
197
|
)
|
177
198
|
|
178
199
|
if self._file_cache is not None:
|
@@ -189,16 +210,16 @@ class Ci(ExitStacked):
|
|
189
210
|
|
190
211
|
return temp_requirements_dir
|
191
212
|
|
192
|
-
@
|
193
|
-
def resolve_requirements_dir(self) -> str:
|
213
|
+
@async_cached_nullary
|
214
|
+
async def resolve_requirements_dir(self) -> str:
|
194
215
|
with log_timing_context('Resolve requirements dir') as ltc:
|
195
|
-
requirements_dir = self._resolve_requirements_dir()
|
216
|
+
requirements_dir = await self._resolve_requirements_dir()
|
196
217
|
ltc.set_description(f'Resolve requirements dir: {requirements_dir}')
|
197
218
|
return requirements_dir
|
198
219
|
|
199
220
|
#
|
200
221
|
|
201
|
-
def _run_compose_(self) -> None:
|
222
|
+
async def _run_compose_(self) -> None:
|
202
223
|
setup_cmds = [
|
203
224
|
'pip install --root-user-action ignore --find-links /requirements --no-index uv',
|
204
225
|
(
|
@@ -216,34 +237,36 @@ class Ci(ExitStacked):
|
|
216
237
|
|
217
238
|
#
|
218
239
|
|
219
|
-
with DockerComposeRun(DockerComposeRun.Config(
|
240
|
+
async with DockerComposeRun(DockerComposeRun.Config(
|
220
241
|
compose_file=self._cfg.compose_file,
|
221
242
|
service=self._cfg.service,
|
222
243
|
|
223
|
-
image=self.resolve_ci_image(),
|
244
|
+
image=await self.resolve_ci_image(),
|
224
245
|
|
225
246
|
cmd=ci_cmd,
|
226
247
|
|
227
248
|
run_options=[
|
228
249
|
'-v', f'{os.path.abspath(self._cfg.project_dir)}:/project',
|
229
|
-
'-v', f'{os.path.abspath(self.resolve_requirements_dir())}:/requirements',
|
250
|
+
'-v', f'{os.path.abspath(await self.resolve_requirements_dir())}:/requirements',
|
230
251
|
],
|
231
252
|
|
232
253
|
cwd=self._cfg.project_dir,
|
254
|
+
|
255
|
+
no_dependencies=self._cfg.no_dependencies,
|
233
256
|
)) as ci_compose_run:
|
234
|
-
ci_compose_run.run()
|
257
|
+
await ci_compose_run.run()
|
235
258
|
|
236
|
-
def _run_compose(self) -> None:
|
259
|
+
async def _run_compose(self) -> None:
|
237
260
|
with log_timing_context('Run compose'):
|
238
|
-
self._run_compose_()
|
261
|
+
await self._run_compose_()
|
239
262
|
|
240
263
|
#
|
241
264
|
|
242
|
-
def run(self) -> None:
|
243
|
-
self.load_compose_service_dependencies()
|
265
|
+
async def run(self) -> None:
|
266
|
+
await self.load_compose_service_dependencies()
|
244
267
|
|
245
|
-
self.resolve_ci_image()
|
268
|
+
await self.resolve_ci_image()
|
246
269
|
|
247
|
-
self.resolve_requirements_dir()
|
270
|
+
await self.resolve_requirements_dir()
|
248
271
|
|
249
|
-
self._run_compose()
|
272
|
+
await self._run_compose()
|
omdev/ci/cli.py
CHANGED
@@ -76,18 +76,21 @@ class CiCli(ArgparseCli):
|
|
76
76
|
argparse_arg('--docker-file'),
|
77
77
|
argparse_arg('--compose-file'),
|
78
78
|
argparse_arg('-r', '--requirements-txt', action='append'),
|
79
|
+
|
79
80
|
argparse_arg('--github-cache', action='store_true'),
|
80
81
|
argparse_arg('--cache-dir'),
|
82
|
+
|
81
83
|
argparse_arg('--always-pull', action='store_true'),
|
84
|
+
argparse_arg('--always-build', action='store_true'),
|
85
|
+
|
86
|
+
argparse_arg('--no-dependencies', action='store_true'),
|
82
87
|
)
|
83
88
|
async def run(self) -> None:
|
84
89
|
project_dir = self.args.project_dir
|
85
90
|
docker_file = self.args.docker_file
|
86
91
|
compose_file = self.args.compose_file
|
87
|
-
service = self.args.service
|
88
92
|
requirements_txts = self.args.requirements_txt
|
89
93
|
cache_dir = self.args.cache_dir
|
90
|
-
always_pull = self.args.always_pull
|
91
94
|
|
92
95
|
#
|
93
96
|
|
@@ -157,14 +160,14 @@ class CiCli(ArgparseCli):
|
|
157
160
|
|
158
161
|
#
|
159
162
|
|
160
|
-
with Ci(
|
163
|
+
async with Ci(
|
161
164
|
Ci.Config(
|
162
165
|
project_dir=project_dir,
|
163
166
|
|
164
167
|
docker_file=docker_file,
|
165
168
|
|
166
169
|
compose_file=compose_file,
|
167
|
-
service=service,
|
170
|
+
service=self.args.service,
|
168
171
|
|
169
172
|
requirements_txts=requirements_txts,
|
170
173
|
|
@@ -173,12 +176,15 @@ class CiCli(ArgparseCli):
|
|
173
176
|
'python3 -m pytest -svv test.py',
|
174
177
|
])),
|
175
178
|
|
176
|
-
always_pull=always_pull,
|
179
|
+
always_pull=self.args.always_pull,
|
180
|
+
always_build=self.args.always_build,
|
181
|
+
|
182
|
+
no_dependencies=self.args.no_dependencies,
|
177
183
|
),
|
178
184
|
file_cache=file_cache,
|
179
185
|
shell_cache=shell_cache,
|
180
186
|
) as ci:
|
181
|
-
ci.run()
|
187
|
+
await ci.run()
|
182
188
|
|
183
189
|
|
184
190
|
async def _async_main() -> ta.Optional[int]:
|
omdev/ci/compose.py
CHANGED
@@ -11,12 +11,13 @@ import os.path
|
|
11
11
|
import shlex
|
12
12
|
import typing as ta
|
13
13
|
|
14
|
+
from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
14
15
|
from omlish.lite.cached import cached_nullary
|
15
16
|
from omlish.lite.check import check
|
16
|
-
from omlish.lite.contextmanagers import
|
17
|
+
from omlish.lite.contextmanagers import AsyncExitStacked
|
18
|
+
from omlish.lite.contextmanagers import adefer
|
17
19
|
from omlish.lite.contextmanagers import defer
|
18
20
|
from omlish.lite.json import json_dumps_pretty
|
19
|
-
from omlish.subprocesses import subprocesses
|
20
21
|
|
21
22
|
from .shell import ShellCmd
|
22
23
|
from .utils import make_temp_file
|
@@ -46,7 +47,7 @@ def get_compose_service_dependencies(
|
|
46
47
|
##
|
47
48
|
|
48
49
|
|
49
|
-
class DockerComposeRun(
|
50
|
+
class DockerComposeRun(AsyncExitStacked):
|
50
51
|
@dc.dataclass(frozen=True)
|
51
52
|
class Config:
|
52
53
|
compose_file: str
|
@@ -64,6 +65,7 @@ class DockerComposeRun(ExitStacked):
|
|
64
65
|
|
65
66
|
#
|
66
67
|
|
68
|
+
no_dependencies: bool = False
|
67
69
|
no_dependency_cleanup: bool = False
|
68
70
|
|
69
71
|
#
|
@@ -82,40 +84,6 @@ class DockerComposeRun(ExitStacked):
|
|
82
84
|
|
83
85
|
#
|
84
86
|
|
85
|
-
@property
|
86
|
-
def image_tag(self) -> str:
|
87
|
-
pfx = 'sha256:'
|
88
|
-
if (image := self._cfg.image).startswith(pfx):
|
89
|
-
image = image[len(pfx):]
|
90
|
-
|
91
|
-
return f'{self._cfg.service}:{image}'
|
92
|
-
|
93
|
-
@cached_nullary
|
94
|
-
def tag_image(self) -> str:
|
95
|
-
image_tag = self.image_tag
|
96
|
-
|
97
|
-
subprocesses.check_call(
|
98
|
-
'docker',
|
99
|
-
'tag',
|
100
|
-
self._cfg.image,
|
101
|
-
image_tag,
|
102
|
-
**self._subprocess_kwargs,
|
103
|
-
)
|
104
|
-
|
105
|
-
def delete_tag() -> None:
|
106
|
-
subprocesses.check_call(
|
107
|
-
'docker',
|
108
|
-
'rmi',
|
109
|
-
image_tag,
|
110
|
-
**self._subprocess_kwargs,
|
111
|
-
)
|
112
|
-
|
113
|
-
self._enter_context(defer(delete_tag)) # noqa
|
114
|
-
|
115
|
-
return image_tag
|
116
|
-
|
117
|
-
#
|
118
|
-
|
119
87
|
def _rewrite_compose_dct(self, in_dct: ta.Dict[str, ta.Any]) -> ta.Dict[str, ta.Any]:
|
120
88
|
out = dict(in_dct)
|
121
89
|
|
@@ -129,7 +97,7 @@ class DockerComposeRun(ExitStacked):
|
|
129
97
|
in_service: dict = in_services[self._cfg.service]
|
130
98
|
out_services[self._cfg.service] = out_service = dict(in_service)
|
131
99
|
|
132
|
-
out_service['image'] = self.
|
100
|
+
out_service['image'] = self._cfg.image
|
133
101
|
|
134
102
|
for k in ['build', 'platform']:
|
135
103
|
if k in out_service:
|
@@ -142,16 +110,21 @@ class DockerComposeRun(ExitStacked):
|
|
142
110
|
|
143
111
|
#
|
144
112
|
|
145
|
-
|
113
|
+
if not self._cfg.no_dependencies:
|
114
|
+
depends_on = in_service.get('depends_on', [])
|
146
115
|
|
147
|
-
|
148
|
-
|
149
|
-
|
116
|
+
for dep_service, in_dep_service_dct in list(in_services.items()):
|
117
|
+
if dep_service not in depends_on:
|
118
|
+
continue
|
150
119
|
|
151
|
-
|
152
|
-
|
120
|
+
out_dep_service: dict = dict(in_dep_service_dct)
|
121
|
+
out_services[dep_service] = out_dep_service
|
153
122
|
|
154
|
-
|
123
|
+
out_dep_service['ports'] = []
|
124
|
+
|
125
|
+
else:
|
126
|
+
out_service['depends_on'] = []
|
127
|
+
out_service['links'] = []
|
155
128
|
|
156
129
|
#
|
157
130
|
|
@@ -177,22 +150,20 @@ class DockerComposeRun(ExitStacked):
|
|
177
150
|
|
178
151
|
#
|
179
152
|
|
180
|
-
def _cleanup_dependencies(self) -> None:
|
181
|
-
|
153
|
+
async def _cleanup_dependencies(self) -> None:
|
154
|
+
await asyncio_subprocesses.check_call(
|
182
155
|
'docker',
|
183
156
|
'compose',
|
184
157
|
'-f', self.rewrite_compose_file(),
|
185
158
|
'down',
|
186
159
|
)
|
187
160
|
|
188
|
-
def run(self) -> None:
|
189
|
-
self.tag_image()
|
190
|
-
|
161
|
+
async def run(self) -> None:
|
191
162
|
compose_file = self.rewrite_compose_file()
|
192
163
|
|
193
|
-
with contextlib.
|
194
|
-
if not self._cfg.no_dependency_cleanup:
|
195
|
-
es.
|
164
|
+
async with contextlib.AsyncExitStack() as es:
|
165
|
+
if not (self._cfg.no_dependencies or self._cfg.no_dependency_cleanup):
|
166
|
+
await es.enter_async_context(adefer(self._cleanup_dependencies)) # noqa
|
196
167
|
|
197
168
|
sh_cmd = ' '.join([
|
198
169
|
'docker',
|
@@ -211,7 +182,7 @@ class DockerComposeRun(ExitStacked):
|
|
211
182
|
|
212
183
|
run_cmd = dc.replace(self._cfg.cmd, s=sh_cmd)
|
213
184
|
|
214
|
-
run_cmd.run(
|
215
|
-
|
185
|
+
await run_cmd.run(
|
186
|
+
asyncio_subprocesses.check_call,
|
216
187
|
**self._subprocess_kwargs,
|
217
188
|
)
|