omdev 0.0.0.dev211__py3-none-any.whl → 0.0.0.dev213__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- omdev/.manifests.json +15 -1
- omdev/__about__.py +0 -4
- omdev/amalg/gen.py +2 -3
- omdev/amalg/imports.py +4 -5
- omdev/amalg/manifests.py +7 -10
- omdev/amalg/resources.py +24 -27
- omdev/amalg/srcfiles.py +7 -10
- omdev/amalg/strip.py +4 -5
- omdev/amalg/types.py +1 -1
- omdev/amalg/typing.py +9 -8
- 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 +22 -10
- omdev/ci/compose.py +30 -56
- 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/ci/requirements.py +2 -2
- omdev/git/shallow.py +1 -1
- omdev/scripts/ci.py +948 -451
- omdev/scripts/interp.py +23 -0
- omdev/scripts/pyproject.py +23 -0
- omdev/tokens/__init__.py +0 -0
- omdev/tokens/all.py +35 -0
- omdev/tokens/tokenizert.py +215 -0
- omdev/{tokens.py → tokens/utils.py} +6 -12
- omdev/tools/mkenv.py +131 -0
- omdev/tools/mkrelimp.py +4 -6
- {omdev-0.0.0.dev211.dist-info → omdev-0.0.0.dev213.dist-info}/METADATA +2 -5
- {omdev-0.0.0.dev211.dist-info → omdev-0.0.0.dev213.dist-info}/RECORD +38 -33
- {omdev-0.0.0.dev211.dist-info → omdev-0.0.0.dev213.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev211.dist-info → omdev-0.0.0.dev213.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev211.dist-info → omdev-0.0.0.dev213.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev211.dist-info → omdev-0.0.0.dev213.dist-info}/top_level.txt +0 -0
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
|
|
@@ -112,10 +115,16 @@ class CiCli(ArgparseCli):
|
|
112
115
|
check.state(os.path.isfile(docker_file))
|
113
116
|
|
114
117
|
if compose_file is None:
|
115
|
-
compose_file = find_alt_file(
|
116
|
-
'
|
117
|
-
|
118
|
-
|
118
|
+
compose_file = find_alt_file(*[
|
119
|
+
f'{f}.{x}'
|
120
|
+
for f in [
|
121
|
+
'docker/docker-compose',
|
122
|
+
'docker/compose',
|
123
|
+
'docker-compose',
|
124
|
+
'compose',
|
125
|
+
]
|
126
|
+
for x in ['yaml', 'yml']
|
127
|
+
])
|
119
128
|
check.state(os.path.isfile(compose_file))
|
120
129
|
|
121
130
|
if not requirements_txts:
|
@@ -151,14 +160,14 @@ class CiCli(ArgparseCli):
|
|
151
160
|
|
152
161
|
#
|
153
162
|
|
154
|
-
with Ci(
|
163
|
+
async with Ci(
|
155
164
|
Ci.Config(
|
156
165
|
project_dir=project_dir,
|
157
166
|
|
158
167
|
docker_file=docker_file,
|
159
168
|
|
160
169
|
compose_file=compose_file,
|
161
|
-
service=service,
|
170
|
+
service=self.args.service,
|
162
171
|
|
163
172
|
requirements_txts=requirements_txts,
|
164
173
|
|
@@ -167,12 +176,15 @@ class CiCli(ArgparseCli):
|
|
167
176
|
'python3 -m pytest -svv test.py',
|
168
177
|
])),
|
169
178
|
|
170
|
-
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,
|
171
183
|
),
|
172
184
|
file_cache=file_cache,
|
173
185
|
shell_cache=shell_cache,
|
174
186
|
) as ci:
|
175
|
-
ci.run()
|
187
|
+
await ci.run()
|
176
188
|
|
177
189
|
|
178
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',
|
@@ -200,7 +171,10 @@ class DockerComposeRun(ExitStacked):
|
|
200
171
|
'-f', compose_file,
|
201
172
|
'run',
|
202
173
|
'--rm',
|
203
|
-
*itertools.chain.from_iterable(
|
174
|
+
*itertools.chain.from_iterable(
|
175
|
+
['-e', k]
|
176
|
+
for k in (self._cfg.cmd.env or [])
|
177
|
+
),
|
204
178
|
*(self._cfg.run_options or []),
|
205
179
|
self._cfg.service,
|
206
180
|
'sh', '-c', shlex.quote(self._cfg.cmd.s),
|
@@ -208,7 +182,7 @@ class DockerComposeRun(ExitStacked):
|
|
208
182
|
|
209
183
|
run_cmd = dc.replace(self._cfg.cmd, s=sh_cmd)
|
210
184
|
|
211
|
-
run_cmd.run(
|
212
|
-
|
185
|
+
await run_cmd.run(
|
186
|
+
asyncio_subprocesses.check_call,
|
213
187
|
**self._subprocess_kwargs,
|
214
188
|
)
|
omdev/ci/docker.py
CHANGED
@@ -13,9 +13,9 @@ import shlex
|
|
13
13
|
import tarfile
|
14
14
|
import typing as ta
|
15
15
|
|
16
|
+
from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
16
17
|
from omlish.lite.check import check
|
17
18
|
from omlish.lite.contextmanagers import defer
|
18
|
-
from omlish.subprocesses import subprocesses
|
19
19
|
|
20
20
|
from .shell import ShellCmd
|
21
21
|
from .utils import make_temp_file
|
@@ -60,8 +60,8 @@ def read_docker_tar_image_id(tar_file: str) -> str:
|
|
60
60
|
##
|
61
61
|
|
62
62
|
|
63
|
-
def is_docker_image_present(image: str) -> bool:
|
64
|
-
out =
|
63
|
+
async def is_docker_image_present(image: str) -> bool:
|
64
|
+
out = await asyncio_subprocesses.check_output(
|
65
65
|
'docker',
|
66
66
|
'images',
|
67
67
|
'--format', 'json',
|
@@ -76,55 +76,74 @@ def is_docker_image_present(image: str) -> bool:
|
|
76
76
|
return True
|
77
77
|
|
78
78
|
|
79
|
-
def pull_docker_image(
|
79
|
+
async def pull_docker_image(
|
80
80
|
image: str,
|
81
81
|
) -> None:
|
82
|
-
|
82
|
+
await asyncio_subprocesses.check_call(
|
83
83
|
'docker',
|
84
84
|
'pull',
|
85
85
|
image,
|
86
86
|
)
|
87
87
|
|
88
88
|
|
89
|
-
def build_docker_image(
|
89
|
+
async def build_docker_image(
|
90
90
|
docker_file: str,
|
91
91
|
*,
|
92
|
+
tag: ta.Optional[str] = None,
|
92
93
|
cwd: ta.Optional[str] = None,
|
93
94
|
) -> str:
|
94
95
|
id_file = make_temp_file()
|
95
96
|
with defer(lambda: os.unlink(id_file)):
|
96
|
-
|
97
|
+
await asyncio_subprocesses.check_call(
|
97
98
|
'docker',
|
98
99
|
'build',
|
99
100
|
'-f', os.path.abspath(docker_file),
|
100
101
|
'--iidfile', id_file,
|
101
102
|
'--squash',
|
103
|
+
*(['--tag', tag] if tag is not None else []),
|
102
104
|
'.',
|
103
105
|
**(dict(cwd=cwd) if cwd is not None else {}),
|
104
106
|
)
|
105
107
|
|
106
|
-
with open(id_file) as f:
|
108
|
+
with open(id_file) as f: # noqa
|
107
109
|
image_id = check.single(f.read().strip().splitlines()).strip()
|
108
110
|
|
109
111
|
return image_id
|
110
112
|
|
111
113
|
|
114
|
+
async def tag_docker_image(image: str, tag: str) -> None:
|
115
|
+
await asyncio_subprocesses.check_call(
|
116
|
+
'docker',
|
117
|
+
'tag',
|
118
|
+
image,
|
119
|
+
tag,
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
async def delete_docker_tag(tag: str) -> None:
|
124
|
+
await asyncio_subprocesses.check_call(
|
125
|
+
'docker',
|
126
|
+
'rmi',
|
127
|
+
tag,
|
128
|
+
)
|
129
|
+
|
130
|
+
|
112
131
|
##
|
113
132
|
|
114
133
|
|
115
|
-
def save_docker_tar_cmd(
|
134
|
+
async def save_docker_tar_cmd(
|
116
135
|
image: str,
|
117
136
|
output_cmd: ShellCmd,
|
118
137
|
) -> None:
|
119
138
|
cmd = dc.replace(output_cmd, s=f'docker save {image} | {output_cmd.s}')
|
120
|
-
cmd.run(
|
139
|
+
await cmd.run(asyncio_subprocesses.check_call)
|
121
140
|
|
122
141
|
|
123
|
-
def save_docker_tar(
|
142
|
+
async def save_docker_tar(
|
124
143
|
image: str,
|
125
144
|
tar_file: str,
|
126
145
|
) -> None:
|
127
|
-
return save_docker_tar_cmd(
|
146
|
+
return await save_docker_tar_cmd(
|
128
147
|
image,
|
129
148
|
ShellCmd(f'cat > {shlex.quote(tar_file)}'),
|
130
149
|
)
|
@@ -133,19 +152,19 @@ def save_docker_tar(
|
|
133
152
|
#
|
134
153
|
|
135
154
|
|
136
|
-
def load_docker_tar_cmd(
|
155
|
+
async def load_docker_tar_cmd(
|
137
156
|
input_cmd: ShellCmd,
|
138
157
|
) -> str:
|
139
158
|
cmd = dc.replace(input_cmd, s=f'{input_cmd.s} | docker load')
|
140
159
|
|
141
|
-
out = cmd.run(
|
160
|
+
out = (await cmd.run(asyncio_subprocesses.check_output)).decode()
|
142
161
|
|
143
162
|
line = check.single(out.strip().splitlines())
|
144
163
|
loaded = line.partition(':')[2].strip()
|
145
164
|
return loaded
|
146
165
|
|
147
166
|
|
148
|
-
def load_docker_tar(
|
167
|
+
async def load_docker_tar(
|
149
168
|
tar_file: str,
|
150
169
|
) -> str:
|
151
|
-
return load_docker_tar_cmd(ShellCmd(f'cat {shlex.quote(tar_file)}'))
|
170
|
+
return await load_docker_tar_cmd(ShellCmd(f'cat {shlex.quote(tar_file)}'))
|