omdev 0.0.0.dev211__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/.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)}'))
         
     |