omdev 0.0.0.dev209__py3-none-any.whl → 0.0.0.dev211__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/.manifests.json +1 -1
- omdev/amalg/main.py +10 -1
- omdev/cc/cdeps.py +38 -0
- omdev/cc/cdeps.toml +4 -1
- omdev/cc/cli.py +2 -25
- omdev/ci/__init__.py +0 -0
- omdev/ci/__main__.py +4 -0
- omdev/ci/cache.py +168 -0
- omdev/ci/ci.py +249 -0
- omdev/ci/cli.py +189 -0
- omdev/ci/compose.py +214 -0
- omdev/ci/docker.py +151 -0
- omdev/ci/github/__init__.py +0 -0
- omdev/ci/github/bootstrap.py +11 -0
- omdev/ci/github/cache.py +355 -0
- omdev/ci/github/cacheapi.py +207 -0
- omdev/ci/github/cli.py +39 -0
- omdev/ci/requirements.py +80 -0
- omdev/ci/shell.py +42 -0
- omdev/ci/utils.py +81 -0
- omdev/interp/cli.py +0 -1
- omdev/interp/providers/system.py +2 -1
- omdev/pycharm/cli.py +1 -1
- omdev/pyproject/cli.py +0 -1
- omdev/revisions.py +0 -1
- omdev/scripts/ci.py +3435 -0
- omdev/scripts/interp.py +41 -25
- omdev/scripts/pyproject.py +41 -25
- omdev/tools/pawk/pawk.py +0 -1
- {omdev-0.0.0.dev209.dist-info → omdev-0.0.0.dev211.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev209.dist-info → omdev-0.0.0.dev211.dist-info}/RECORD +35 -18
- {omdev-0.0.0.dev209.dist-info → omdev-0.0.0.dev211.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev209.dist-info → omdev-0.0.0.dev211.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev209.dist-info → omdev-0.0.0.dev211.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev209.dist-info → omdev-0.0.0.dev211.dist-info}/top_level.txt +0 -0
omdev/.manifests.json
CHANGED
omdev/amalg/main.py
CHANGED
@@ -28,6 +28,7 @@ Targets:
|
|
28
28
|
import argparse
|
29
29
|
import logging
|
30
30
|
import os.path
|
31
|
+
import stat
|
31
32
|
import typing as ta
|
32
33
|
|
33
34
|
from omlish import check
|
@@ -60,7 +61,15 @@ def _gen_one(
|
|
60
61
|
if output_path is not None:
|
61
62
|
with open(output_path, 'w') as f:
|
62
63
|
f.write(src)
|
63
|
-
|
64
|
+
|
65
|
+
src_mode = os.stat(input_path).st_mode
|
66
|
+
out_mode = (
|
67
|
+
src_mode
|
68
|
+
| (stat.S_IXUSR if src_mode & stat.S_IRUSR else 0)
|
69
|
+
| (stat.S_IXGRP if src_mode & stat.S_IRGRP else 0)
|
70
|
+
| (stat.S_IXOTH if src_mode & stat.S_IROTH else 0)
|
71
|
+
)
|
72
|
+
os.chmod(output_path, out_mode)
|
64
73
|
|
65
74
|
else:
|
66
75
|
print(src)
|
omdev/cc/cdeps.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import tomllib
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from omlish import cached
|
6
|
+
from omlish import lang
|
7
|
+
from omlish import marshal as msh
|
8
|
+
|
9
|
+
|
10
|
+
@dc.dataclass(frozen=True)
|
11
|
+
class Cdep:
|
12
|
+
@dc.dataclass(frozen=True)
|
13
|
+
class Git:
|
14
|
+
url: str
|
15
|
+
rev: str
|
16
|
+
|
17
|
+
subtrees: ta.Sequence[str] | None = None
|
18
|
+
|
19
|
+
git: Git
|
20
|
+
|
21
|
+
#
|
22
|
+
|
23
|
+
include: ta.Sequence[str] | None = None
|
24
|
+
|
25
|
+
#
|
26
|
+
|
27
|
+
@dc.dataclass(frozen=True)
|
28
|
+
class Cmake:
|
29
|
+
fetch_content_url: str | None = None
|
30
|
+
|
31
|
+
cmake: Cmake | None = None
|
32
|
+
|
33
|
+
|
34
|
+
@cached.function
|
35
|
+
def load_cdeps() -> ta.Mapping[str, Cdep]:
|
36
|
+
src = lang.get_relative_resources(globals=globals())['cdeps.toml'].read_text()
|
37
|
+
dct = tomllib.loads(src)
|
38
|
+
return msh.unmarshal(dct.get('deps', {}), ta.Mapping[str, Cdep]) # type: ignore
|
omdev/cc/cdeps.toml
CHANGED
@@ -3,9 +3,12 @@ include = ['single_include']
|
|
3
3
|
|
4
4
|
[deps.json.git]
|
5
5
|
url = 'https://github.com/nlohmann/json'
|
6
|
-
rev = '
|
6
|
+
rev = '9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03'
|
7
7
|
subtrees = ['single_include']
|
8
8
|
|
9
|
+
[deps.json.cmake]
|
10
|
+
fetch_content_url = 'https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz'
|
11
|
+
|
9
12
|
#
|
10
13
|
|
11
14
|
[deps.pybind11]
|
omdev/cc/cli.py
CHANGED
@@ -19,45 +19,22 @@ TODO:
|
|
19
19
|
- cext interop
|
20
20
|
- gen cmake
|
21
21
|
"""
|
22
|
-
import dataclasses as dc
|
23
22
|
import os
|
24
23
|
import shlex
|
25
24
|
import shutil
|
26
25
|
import subprocess
|
27
26
|
import tempfile
|
28
|
-
import tomllib
|
29
27
|
import typing as ta
|
30
28
|
|
31
|
-
from omlish import cached
|
32
29
|
from omlish import check
|
33
|
-
from omlish import lang
|
34
30
|
from omlish import marshal as msh
|
35
31
|
from omlish.argparse import all as ap
|
36
32
|
from omlish.formats import json
|
37
33
|
|
38
34
|
from .. import magic
|
39
35
|
from ..cache import data as dcache
|
40
|
-
|
41
|
-
|
42
|
-
@dc.dataclass(frozen=True)
|
43
|
-
class Cdep:
|
44
|
-
@dc.dataclass(frozen=True)
|
45
|
-
class Git:
|
46
|
-
url: str
|
47
|
-
rev: str
|
48
|
-
|
49
|
-
subtrees: ta.Sequence[str] | None = None
|
50
|
-
|
51
|
-
git: Git
|
52
|
-
|
53
|
-
include: ta.Sequence[str] | None = None
|
54
|
-
|
55
|
-
|
56
|
-
@cached.function
|
57
|
-
def load_cdeps() -> ta.Mapping[str, Cdep]:
|
58
|
-
src = lang.get_relative_resources(globals=globals())['cdeps.toml'].read_text()
|
59
|
-
dct = tomllib.loads(src)
|
60
|
-
return msh.unmarshal(dct.get('deps', {}), ta.Mapping[str, Cdep]) # type: ignore
|
36
|
+
from .cdeps import Cdep
|
37
|
+
from .cdeps import load_cdeps
|
61
38
|
|
62
39
|
|
63
40
|
class Cli(ap.Cli):
|
omdev/ci/__init__.py
ADDED
File without changes
|
omdev/ci/__main__.py
ADDED
omdev/ci/cache.py
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import os.path
|
5
|
+
import shlex
|
6
|
+
import shutil
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from .shell import ShellCmd
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
@abc.abstractmethod
|
16
|
+
class FileCache(abc.ABC):
|
17
|
+
@abc.abstractmethod
|
18
|
+
def get_file(self, key: str) -> ta.Optional[str]:
|
19
|
+
raise NotImplementedError
|
20
|
+
|
21
|
+
@abc.abstractmethod
|
22
|
+
def put_file(self, key: str, file_path: str) -> ta.Optional[str]:
|
23
|
+
raise NotImplementedError
|
24
|
+
|
25
|
+
|
26
|
+
#
|
27
|
+
|
28
|
+
|
29
|
+
class DirectoryFileCache(FileCache):
|
30
|
+
def __init__(self, dir: str) -> None: # noqa
|
31
|
+
super().__init__()
|
32
|
+
|
33
|
+
self._dir = dir
|
34
|
+
|
35
|
+
#
|
36
|
+
|
37
|
+
def get_cache_file_path(
|
38
|
+
self,
|
39
|
+
key: str,
|
40
|
+
*,
|
41
|
+
make_dirs: bool = False,
|
42
|
+
) -> str:
|
43
|
+
if make_dirs:
|
44
|
+
os.makedirs(self._dir, exist_ok=True)
|
45
|
+
return os.path.join(self._dir, key)
|
46
|
+
|
47
|
+
def format_incomplete_file(self, f: str) -> str:
|
48
|
+
return os.path.join(os.path.dirname(f), f'_{os.path.basename(f)}.incomplete')
|
49
|
+
|
50
|
+
#
|
51
|
+
|
52
|
+
def get_file(self, key: str) -> ta.Optional[str]:
|
53
|
+
cache_file_path = self.get_cache_file_path(key)
|
54
|
+
if not os.path.exists(cache_file_path):
|
55
|
+
return None
|
56
|
+
return cache_file_path
|
57
|
+
|
58
|
+
def put_file(self, key: str, file_path: str) -> None:
|
59
|
+
cache_file_path = self.get_cache_file_path(key, make_dirs=True)
|
60
|
+
shutil.copyfile(file_path, cache_file_path)
|
61
|
+
|
62
|
+
|
63
|
+
##
|
64
|
+
|
65
|
+
|
66
|
+
class ShellCache(abc.ABC):
|
67
|
+
@abc.abstractmethod
|
68
|
+
def get_file_cmd(self, key: str) -> ta.Optional[ShellCmd]:
|
69
|
+
raise NotImplementedError
|
70
|
+
|
71
|
+
class PutFileCmdContext(abc.ABC):
|
72
|
+
def __init__(self) -> None:
|
73
|
+
super().__init__()
|
74
|
+
|
75
|
+
self._state: ta.Literal['open', 'committed', 'aborted'] = 'open'
|
76
|
+
|
77
|
+
@property
|
78
|
+
def state(self) -> ta.Literal['open', 'committed', 'aborted']:
|
79
|
+
return self._state
|
80
|
+
|
81
|
+
#
|
82
|
+
|
83
|
+
@property
|
84
|
+
@abc.abstractmethod
|
85
|
+
def cmd(self) -> ShellCmd:
|
86
|
+
raise NotImplementedError
|
87
|
+
|
88
|
+
#
|
89
|
+
|
90
|
+
def __enter__(self):
|
91
|
+
return self
|
92
|
+
|
93
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
94
|
+
if exc_val is None:
|
95
|
+
self.commit()
|
96
|
+
else:
|
97
|
+
self.abort()
|
98
|
+
|
99
|
+
#
|
100
|
+
|
101
|
+
@abc.abstractmethod
|
102
|
+
def _commit(self) -> None:
|
103
|
+
raise NotImplementedError
|
104
|
+
|
105
|
+
def commit(self) -> None:
|
106
|
+
if self._state == 'committed':
|
107
|
+
return
|
108
|
+
elif self._state == 'open':
|
109
|
+
self._commit()
|
110
|
+
self._state = 'committed'
|
111
|
+
else:
|
112
|
+
raise RuntimeError(self._state)
|
113
|
+
|
114
|
+
#
|
115
|
+
|
116
|
+
@abc.abstractmethod
|
117
|
+
def _abort(self) -> None:
|
118
|
+
raise NotImplementedError
|
119
|
+
|
120
|
+
def abort(self) -> None:
|
121
|
+
if self._state == 'aborted':
|
122
|
+
return
|
123
|
+
elif self._state == 'open':
|
124
|
+
self._abort()
|
125
|
+
self._state = 'committed'
|
126
|
+
else:
|
127
|
+
raise RuntimeError(self._state)
|
128
|
+
|
129
|
+
@abc.abstractmethod
|
130
|
+
def put_file_cmd(self, key: str) -> PutFileCmdContext:
|
131
|
+
raise NotImplementedError
|
132
|
+
|
133
|
+
|
134
|
+
#
|
135
|
+
|
136
|
+
|
137
|
+
class DirectoryShellCache(ShellCache):
|
138
|
+
def __init__(self, dfc: DirectoryFileCache) -> None:
|
139
|
+
super().__init__()
|
140
|
+
|
141
|
+
self._dfc = dfc
|
142
|
+
|
143
|
+
def get_file_cmd(self, key: str) -> ta.Optional[ShellCmd]:
|
144
|
+
f = self._dfc.get_file(key)
|
145
|
+
if f is None:
|
146
|
+
return None
|
147
|
+
return ShellCmd(f'cat {shlex.quote(f)}')
|
148
|
+
|
149
|
+
class _PutFileCmdContext(ShellCache.PutFileCmdContext): # noqa
|
150
|
+
def __init__(self, tf: str, f: str) -> None:
|
151
|
+
super().__init__()
|
152
|
+
|
153
|
+
self._tf = tf
|
154
|
+
self._f = f
|
155
|
+
|
156
|
+
@property
|
157
|
+
def cmd(self) -> ShellCmd:
|
158
|
+
return ShellCmd(f'cat > {shlex.quote(self._tf)}')
|
159
|
+
|
160
|
+
def _commit(self) -> None:
|
161
|
+
os.replace(self._tf, self._f)
|
162
|
+
|
163
|
+
def _abort(self) -> None:
|
164
|
+
os.unlink(self._tf)
|
165
|
+
|
166
|
+
def put_file_cmd(self, key: str) -> ShellCache.PutFileCmdContext:
|
167
|
+
f = self._dfc.get_cache_file_path(key, make_dirs=True)
|
168
|
+
return self._PutFileCmdContext(self._dfc.format_incomplete_file(f), f)
|
omdev/ci/ci.py
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import dataclasses as dc
|
4
|
+
import os.path
|
5
|
+
import shutil
|
6
|
+
import tarfile
|
7
|
+
import tempfile
|
8
|
+
import typing as ta
|
9
|
+
|
10
|
+
from omlish.lite.cached import cached_nullary
|
11
|
+
from omlish.lite.check import check
|
12
|
+
from omlish.lite.contextmanagers import ExitStacked
|
13
|
+
from omlish.lite.contextmanagers import defer
|
14
|
+
|
15
|
+
from .cache import FileCache
|
16
|
+
from .cache import ShellCache
|
17
|
+
from .compose import DockerComposeRun
|
18
|
+
from .compose import get_compose_service_dependencies
|
19
|
+
from .docker import build_docker_file_hash
|
20
|
+
from .docker import build_docker_image
|
21
|
+
from .docker import is_docker_image_present
|
22
|
+
from .docker import load_docker_tar_cmd
|
23
|
+
from .docker import pull_docker_image
|
24
|
+
from .docker import save_docker_tar_cmd
|
25
|
+
from .requirements import build_requirements_hash
|
26
|
+
from .requirements import download_requirements
|
27
|
+
from .shell import ShellCmd
|
28
|
+
from .utils import log_timing_context
|
29
|
+
|
30
|
+
|
31
|
+
class Ci(ExitStacked):
|
32
|
+
FILE_NAME_HASH_LEN = 16
|
33
|
+
|
34
|
+
@dc.dataclass(frozen=True)
|
35
|
+
class Config:
|
36
|
+
project_dir: str
|
37
|
+
|
38
|
+
docker_file: str
|
39
|
+
|
40
|
+
compose_file: str
|
41
|
+
service: str
|
42
|
+
|
43
|
+
cmd: ShellCmd
|
44
|
+
|
45
|
+
requirements_txts: ta.Optional[ta.Sequence[str]] = None
|
46
|
+
|
47
|
+
always_pull: bool = False
|
48
|
+
|
49
|
+
def __post_init__(self) -> None:
|
50
|
+
check.not_isinstance(self.requirements_txts, str)
|
51
|
+
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
cfg: Config,
|
55
|
+
*,
|
56
|
+
shell_cache: ta.Optional[ShellCache] = None,
|
57
|
+
file_cache: ta.Optional[FileCache] = None,
|
58
|
+
) -> None:
|
59
|
+
super().__init__()
|
60
|
+
|
61
|
+
self._cfg = cfg
|
62
|
+
self._shell_cache = shell_cache
|
63
|
+
self._file_cache = file_cache
|
64
|
+
|
65
|
+
#
|
66
|
+
|
67
|
+
def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
|
68
|
+
if self._shell_cache is None:
|
69
|
+
return None
|
70
|
+
|
71
|
+
get_cache_cmd = self._shell_cache.get_file_cmd(key)
|
72
|
+
if get_cache_cmd is None:
|
73
|
+
return None
|
74
|
+
|
75
|
+
get_cache_cmd = dc.replace(get_cache_cmd, s=f'{get_cache_cmd.s} | zstd -cd --long') # noqa
|
76
|
+
|
77
|
+
return load_docker_tar_cmd(get_cache_cmd)
|
78
|
+
|
79
|
+
def _save_cache_docker_image(self, key: str, image: str) -> None:
|
80
|
+
if self._shell_cache is None:
|
81
|
+
return
|
82
|
+
|
83
|
+
with self._shell_cache.put_file_cmd(key) as put_cache:
|
84
|
+
put_cache_cmd = put_cache.cmd
|
85
|
+
|
86
|
+
put_cache_cmd = dc.replace(put_cache_cmd, s=f'zstd | {put_cache_cmd.s}')
|
87
|
+
|
88
|
+
save_docker_tar_cmd(image, put_cache_cmd)
|
89
|
+
|
90
|
+
#
|
91
|
+
|
92
|
+
def _load_docker_image(self, image: str) -> None:
|
93
|
+
if not self._cfg.always_pull and is_docker_image_present(image):
|
94
|
+
return
|
95
|
+
|
96
|
+
dep_suffix = image
|
97
|
+
for c in '/:.-_':
|
98
|
+
dep_suffix = dep_suffix.replace(c, '-')
|
99
|
+
|
100
|
+
cache_key = f'docker-{dep_suffix}'
|
101
|
+
if self._load_cache_docker_image(cache_key) is not None:
|
102
|
+
return
|
103
|
+
|
104
|
+
pull_docker_image(image)
|
105
|
+
|
106
|
+
self._save_cache_docker_image(cache_key, image)
|
107
|
+
|
108
|
+
def load_docker_image(self, image: str) -> None:
|
109
|
+
with log_timing_context(f'Load docker image: {image}'):
|
110
|
+
self._load_docker_image(image)
|
111
|
+
|
112
|
+
@cached_nullary
|
113
|
+
def load_compose_service_dependencies(self) -> None:
|
114
|
+
deps = get_compose_service_dependencies(
|
115
|
+
self._cfg.compose_file,
|
116
|
+
self._cfg.service,
|
117
|
+
)
|
118
|
+
|
119
|
+
for dep_image in deps.values():
|
120
|
+
self.load_docker_image(dep_image)
|
121
|
+
|
122
|
+
#
|
123
|
+
|
124
|
+
def _resolve_ci_image(self) -> str:
|
125
|
+
docker_file_hash = build_docker_file_hash(self._cfg.docker_file)[:self.FILE_NAME_HASH_LEN]
|
126
|
+
|
127
|
+
cache_key = f'ci-{docker_file_hash}'
|
128
|
+
if (cache_image_id := self._load_cache_docker_image(cache_key)) is not None:
|
129
|
+
return cache_image_id
|
130
|
+
|
131
|
+
image_id = build_docker_image(
|
132
|
+
self._cfg.docker_file,
|
133
|
+
cwd=self._cfg.project_dir,
|
134
|
+
)
|
135
|
+
|
136
|
+
self._save_cache_docker_image(cache_key, image_id)
|
137
|
+
|
138
|
+
return image_id
|
139
|
+
|
140
|
+
@cached_nullary
|
141
|
+
def resolve_ci_image(self) -> str:
|
142
|
+
with log_timing_context('Resolve ci image') as ltc:
|
143
|
+
image_id = self._resolve_ci_image()
|
144
|
+
ltc.set_description(f'Resolve ci image: {image_id}')
|
145
|
+
return image_id
|
146
|
+
|
147
|
+
#
|
148
|
+
|
149
|
+
def _resolve_requirements_dir(self) -> str:
|
150
|
+
requirements_txts = [
|
151
|
+
os.path.join(self._cfg.project_dir, rf)
|
152
|
+
for rf in check.not_none(self._cfg.requirements_txts)
|
153
|
+
]
|
154
|
+
|
155
|
+
requirements_hash = build_requirements_hash(requirements_txts)[:self.FILE_NAME_HASH_LEN]
|
156
|
+
|
157
|
+
tar_file_key = f'requirements-{requirements_hash}'
|
158
|
+
tar_file_name = f'{tar_file_key}.tar'
|
159
|
+
|
160
|
+
temp_dir = tempfile.mkdtemp()
|
161
|
+
self._enter_context(defer(lambda: shutil.rmtree(temp_dir))) # noqa
|
162
|
+
|
163
|
+
if self._file_cache is not None and (cache_tar_file := self._file_cache.get_file(tar_file_key)):
|
164
|
+
with tarfile.open(cache_tar_file) as tar:
|
165
|
+
tar.extractall(path=temp_dir) # noqa
|
166
|
+
|
167
|
+
return temp_dir
|
168
|
+
|
169
|
+
temp_requirements_dir = os.path.join(temp_dir, 'requirements')
|
170
|
+
os.makedirs(temp_requirements_dir)
|
171
|
+
|
172
|
+
download_requirements(
|
173
|
+
self.resolve_ci_image(),
|
174
|
+
temp_requirements_dir,
|
175
|
+
requirements_txts,
|
176
|
+
)
|
177
|
+
|
178
|
+
if self._file_cache is not None:
|
179
|
+
temp_tar_file = os.path.join(temp_dir, tar_file_name)
|
180
|
+
|
181
|
+
with tarfile.open(temp_tar_file, 'w') as tar:
|
182
|
+
for requirement_file in os.listdir(temp_requirements_dir):
|
183
|
+
tar.add(
|
184
|
+
os.path.join(temp_requirements_dir, requirement_file),
|
185
|
+
arcname=requirement_file,
|
186
|
+
)
|
187
|
+
|
188
|
+
self._file_cache.put_file(os.path.basename(tar_file_key), temp_tar_file)
|
189
|
+
|
190
|
+
return temp_requirements_dir
|
191
|
+
|
192
|
+
@cached_nullary
|
193
|
+
def resolve_requirements_dir(self) -> str:
|
194
|
+
with log_timing_context('Resolve requirements dir') as ltc:
|
195
|
+
requirements_dir = self._resolve_requirements_dir()
|
196
|
+
ltc.set_description(f'Resolve requirements dir: {requirements_dir}')
|
197
|
+
return requirements_dir
|
198
|
+
|
199
|
+
#
|
200
|
+
|
201
|
+
def _run_compose_(self) -> None:
|
202
|
+
setup_cmds = [
|
203
|
+
'pip install --root-user-action ignore --find-links /requirements --no-index uv',
|
204
|
+
(
|
205
|
+
'uv pip install --system --find-links /requirements ' +
|
206
|
+
' '.join(f'-r /project/{rf}' for rf in self._cfg.requirements_txts or [])
|
207
|
+
),
|
208
|
+
]
|
209
|
+
|
210
|
+
#
|
211
|
+
|
212
|
+
ci_cmd = dc.replace(self._cfg.cmd, s=' && '.join([
|
213
|
+
*setup_cmds,
|
214
|
+
f'({self._cfg.cmd.s})',
|
215
|
+
]))
|
216
|
+
|
217
|
+
#
|
218
|
+
|
219
|
+
with DockerComposeRun(DockerComposeRun.Config(
|
220
|
+
compose_file=self._cfg.compose_file,
|
221
|
+
service=self._cfg.service,
|
222
|
+
|
223
|
+
image=self.resolve_ci_image(),
|
224
|
+
|
225
|
+
cmd=ci_cmd,
|
226
|
+
|
227
|
+
run_options=[
|
228
|
+
'-v', f'{os.path.abspath(self._cfg.project_dir)}:/project',
|
229
|
+
'-v', f'{os.path.abspath(self.resolve_requirements_dir())}:/requirements',
|
230
|
+
],
|
231
|
+
|
232
|
+
cwd=self._cfg.project_dir,
|
233
|
+
)) as ci_compose_run:
|
234
|
+
ci_compose_run.run()
|
235
|
+
|
236
|
+
def _run_compose(self) -> None:
|
237
|
+
with log_timing_context('Run compose'):
|
238
|
+
self._run_compose_()
|
239
|
+
|
240
|
+
#
|
241
|
+
|
242
|
+
def run(self) -> None:
|
243
|
+
self.load_compose_service_dependencies()
|
244
|
+
|
245
|
+
self.resolve_ci_image()
|
246
|
+
|
247
|
+
self.resolve_requirements_dir()
|
248
|
+
|
249
|
+
self._run_compose()
|