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/github/cache.py
    CHANGED
    
    | 
         @@ -1,14 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # ruff: noqa: UP006 UP007
         
     | 
| 
       2 
2 
     | 
    
         
             
            # @omlish-lite
         
     | 
| 
      
 3 
     | 
    
         
            +
            import abc
         
     | 
| 
       3 
4 
     | 
    
         
             
            import dataclasses as dc
         
     | 
| 
       4 
     | 
    
         
            -
            import json
         
     | 
| 
       5 
5 
     | 
    
         
             
            import os
         
     | 
| 
       6 
6 
     | 
    
         
             
            import shlex
         
     | 
| 
       7 
7 
     | 
    
         
             
            import typing as ta
         
     | 
| 
      
 8 
     | 
    
         
            +
            import urllib.parse
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
            from omlish.lite.check import check
         
     | 
| 
       10 
     | 
    
         
            -
            from omlish.lite.contextmanagers import defer
         
     | 
| 
       11 
     | 
    
         
            -
            from omlish.lite.json import json_dumps_compact
         
     | 
| 
       12 
11 
     | 
    
         
             
            from omlish.subprocesses import subprocesses
         
     | 
| 
       13 
12 
     | 
    
         | 
| 
       14 
13 
     | 
    
         
             
            from ..cache import DirectoryFileCache
         
     | 
| 
         @@ -16,256 +15,168 @@ from ..cache import ShellCache 
     | 
|
| 
       16 
15 
     | 
    
         
             
            from ..shell import ShellCmd
         
     | 
| 
       17 
16 
     | 
    
         
             
            from ..utils import make_temp_file
         
     | 
| 
       18 
17 
     | 
    
         
             
            from .cacheapi import GithubCacheServiceV1
         
     | 
| 
      
 18 
     | 
    
         
            +
            from .curl import GithubServiceCurlClient
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            ##
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
            class  
     | 
| 
      
 24 
     | 
    
         
            +
            class GithubCacheShellClient(abc.ABC):
         
     | 
| 
      
 25 
     | 
    
         
            +
                class Entry(abc.ABC):  # noqa
         
     | 
| 
      
 26 
     | 
    
         
            +
                    pass
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                @abc.abstractmethod
         
     | 
| 
      
 29 
     | 
    
         
            +
                def run_get_entry(self, key: str) -> ta.Optional[Entry]:
         
     | 
| 
      
 30 
     | 
    
         
            +
                    raise NotImplementedError
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                @abc.abstractmethod
         
     | 
| 
      
 33 
     | 
    
         
            +
                def download_get_entry(self, entry: Entry, out_file: str) -> None:
         
     | 
| 
      
 34 
     | 
    
         
            +
                    raise NotImplementedError
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                @abc.abstractmethod
         
     | 
| 
      
 37 
     | 
    
         
            +
                def upload_cache_entry(self, key: str, in_file: str) -> None:
         
     | 
| 
      
 38 
     | 
    
         
            +
                    raise NotImplementedError
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            #
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            class GithubCacheServiceV1ShellClient(GithubCacheShellClient):
         
     | 
| 
       25 
45 
     | 
    
         
             
                BASE_URL_ENV_KEY = 'ACTIONS_CACHE_URL'
         
     | 
| 
       26 
46 
     | 
    
         
             
                AUTH_TOKEN_ENV_KEY = 'ACTIONS_RUNTIME_TOKEN'  # noqa
         
     | 
| 
       27 
47 
     | 
    
         | 
| 
      
 48 
     | 
    
         
            +
                KEY_SUFFIX_ENV_KEY = 'GITHUB_RUN_ID'
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                CACHE_VERSION: ta.ClassVar[int] = 1
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                #
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
       28 
54 
     | 
    
         
             
                def __init__(
         
     | 
| 
       29 
55 
     | 
    
         
             
                        self,
         
     | 
| 
       30 
56 
     | 
    
         
             
                        *,
         
     | 
| 
       31 
57 
     | 
    
         
             
                        base_url: ta.Optional[str] = None,
         
     | 
| 
       32 
58 
     | 
    
         
             
                        auth_token: ta.Optional[str] = None,
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                        key_prefix: ta.Optional[str] = None,
         
     | 
| 
      
 61 
     | 
    
         
            +
                        key_suffix: ta.Optional[str] = None,
         
     | 
| 
       33 
62 
     | 
    
         
             
                ) -> None:
         
     | 
| 
       34 
63 
     | 
    
         
             
                    super().__init__()
         
     | 
| 
       35 
64 
     | 
    
         | 
| 
      
 65 
     | 
    
         
            +
                    #
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
       36 
67 
     | 
    
         
             
                    if base_url is None:
         
     | 
| 
       37 
68 
     | 
    
         
             
                        base_url = os.environ[self.BASE_URL_ENV_KEY]
         
     | 
| 
       38 
     | 
    
         
            -
                     
     | 
| 
      
 69 
     | 
    
         
            +
                    service_url = GithubCacheServiceV1.get_service_url(base_url)
         
     | 
| 
       39 
70 
     | 
    
         | 
| 
       40 
71 
     | 
    
         
             
                    if auth_token is None:
         
     | 
| 
       41 
72 
     | 
    
         
             
                        auth_token = os.environ.get(self.AUTH_TOKEN_ENV_KEY)
         
     | 
| 
       42 
     | 
    
         
            -
                    self._auth_token = auth_token
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                    self._service_url = GithubCacheServiceV1.get_service_url(self._base_url)
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                #
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                _MISSING = object()
         
     | 
| 
       49 
73 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                         
     | 
| 
       52 
     | 
    
         
            -
                         
     | 
| 
       53 
     | 
    
         
            -
                         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                ) -> ta.Dict[str, str]:
         
     | 
| 
       56 
     | 
    
         
            -
                    dct = {
         
     | 
| 
       57 
     | 
    
         
            -
                        'Accept': f'application/json;api-version={GithubCacheServiceV1.API_VERSION}',
         
     | 
| 
       58 
     | 
    
         
            -
                    }
         
     | 
| 
      
 74 
     | 
    
         
            +
                    self._curl = GithubServiceCurlClient(
         
     | 
| 
      
 75 
     | 
    
         
            +
                        service_url,
         
     | 
| 
      
 76 
     | 
    
         
            +
                        auth_token,
         
     | 
| 
      
 77 
     | 
    
         
            +
                        api_version=GithubCacheServiceV1.API_VERSION,
         
     | 
| 
      
 78 
     | 
    
         
            +
                    )
         
     | 
| 
       59 
79 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                     
     | 
| 
       61 
     | 
    
         
            -
                        auth_token = self._auth_token
         
     | 
| 
       62 
     | 
    
         
            -
                    if auth_token:
         
     | 
| 
       63 
     | 
    
         
            -
                        dct['Authorization'] = f'Bearer {auth_token}'
         
     | 
| 
      
 80 
     | 
    
         
            +
                    #
         
     | 
| 
       64 
81 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
                     
     | 
| 
       66 
     | 
    
         
            -
                        dct['Content-Type'] = content_type
         
     | 
| 
      
 82 
     | 
    
         
            +
                    self._key_prefix = key_prefix
         
     | 
| 
       67 
83 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
                     
     | 
| 
      
 84 
     | 
    
         
            +
                    if key_suffix is None:
         
     | 
| 
      
 85 
     | 
    
         
            +
                        key_suffix = os.environ[self.KEY_SUFFIX_ENV_KEY]
         
     | 
| 
      
 86 
     | 
    
         
            +
                    self._key_suffix = check.non_empty_str(key_suffix)
         
     | 
| 
       69 
87 
     | 
    
         | 
| 
       70 
88 
     | 
    
         
             
                #
         
     | 
| 
       71 
89 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
                 
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                def build_curl_cmd(
         
     | 
| 
       75 
     | 
    
         
            -
                        self,
         
     | 
| 
       76 
     | 
    
         
            -
                        method: str,
         
     | 
| 
       77 
     | 
    
         
            -
                        url: str,
         
     | 
| 
       78 
     | 
    
         
            -
                        *,
         
     | 
| 
       79 
     | 
    
         
            -
                        json_content: bool = False,
         
     | 
| 
       80 
     | 
    
         
            -
                        content_type: ta.Optional[str] = None,
         
     | 
| 
       81 
     | 
    
         
            -
                ) -> ShellCmd:
         
     | 
| 
       82 
     | 
    
         
            -
                    if content_type is None and json_content:
         
     | 
| 
       83 
     | 
    
         
            -
                        content_type = 'application/json'
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
                    env = {}
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
                    header_auth_token: ta.Optional[str]
         
     | 
| 
       88 
     | 
    
         
            -
                    if self._auth_token:
         
     | 
| 
       89 
     | 
    
         
            -
                        env[self.HEADER_AUTH_TOKEN_ENV_KEY] = self._auth_token
         
     | 
| 
       90 
     | 
    
         
            -
                        header_auth_token = f'${self.HEADER_AUTH_TOKEN_ENV_KEY}'
         
     | 
| 
       91 
     | 
    
         
            -
                    else:
         
     | 
| 
       92 
     | 
    
         
            -
                        header_auth_token = None
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                    hdrs = self.build_headers(
         
     | 
| 
       95 
     | 
    
         
            -
                        auth_token=header_auth_token,
         
     | 
| 
       96 
     | 
    
         
            -
                        content_type=content_type,
         
     | 
| 
       97 
     | 
    
         
            -
                    )
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                    url = f'{self._service_url}/{url}'
         
     | 
| 
      
 90 
     | 
    
         
            +
                KEY_PART_SEPARATOR = '--'
         
     | 
| 
       100 
91 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
                         
     | 
| 
       104 
     | 
    
         
            -
                         
     | 
| 
       105 
     | 
    
         
            -
                         
     | 
| 
       106 
     | 
    
         
            -
                        *[f'-H "{k}: {v}"' for k, v in hdrs.items()],
         
     | 
| 
      
 92 
     | 
    
         
            +
                def fix_key(self, s: str) -> str:
         
     | 
| 
      
 93 
     | 
    
         
            +
                    return self.KEY_PART_SEPARATOR.join([
         
     | 
| 
      
 94 
     | 
    
         
            +
                        *([self._key_prefix] if self._key_prefix else []),
         
     | 
| 
      
 95 
     | 
    
         
            +
                        s,
         
     | 
| 
      
 96 
     | 
    
         
            +
                        self._key_suffix,
         
     | 
| 
       107 
97 
     | 
    
         
             
                    ])
         
     | 
| 
       108 
98 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
                    return ShellCmd(
         
     | 
| 
       110 
     | 
    
         
            -
                        cmd,
         
     | 
| 
       111 
     | 
    
         
            -
                        env=env,
         
     | 
| 
       112 
     | 
    
         
            -
                    )
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                def build_post_json_curl_cmd(
         
     | 
| 
       115 
     | 
    
         
            -
                        self,
         
     | 
| 
       116 
     | 
    
         
            -
                        url: str,
         
     | 
| 
       117 
     | 
    
         
            -
                        obj: ta.Any,
         
     | 
| 
       118 
     | 
    
         
            -
                        **kwargs: ta.Any,
         
     | 
| 
       119 
     | 
    
         
            -
                ) -> ShellCmd:
         
     | 
| 
       120 
     | 
    
         
            -
                    curl_cmd = self.build_curl_cmd(
         
     | 
| 
       121 
     | 
    
         
            -
                        'POST',
         
     | 
| 
       122 
     | 
    
         
            -
                        url,
         
     | 
| 
       123 
     | 
    
         
            -
                        json_content=True,
         
     | 
| 
       124 
     | 
    
         
            -
                        **kwargs,
         
     | 
| 
       125 
     | 
    
         
            -
                    )
         
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
                    obj_json = json_dumps_compact(obj)
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                    return dc.replace(curl_cmd, s=f'{curl_cmd.s} -d {shlex.quote(obj_json)}')
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
99 
     | 
    
         
             
                #
         
     | 
| 
       132 
100 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
                @dc.dataclass()
         
     | 
| 
       134 
     | 
    
         
            -
                class CurlError(RuntimeError):
         
     | 
| 
       135 
     | 
    
         
            -
                    status_code: int
         
     | 
| 
       136 
     | 
    
         
            -
                    body: ta.Optional[bytes]
         
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
                    def __str__(self) -> str:
         
     | 
| 
       139 
     | 
    
         
            -
                        return repr(self)
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
101 
     | 
    
         
             
                @dc.dataclass(frozen=True)
         
     | 
| 
       142 
     | 
    
         
            -
                class  
     | 
| 
       143 
     | 
    
         
            -
                     
     | 
| 
       144 
     | 
    
         
            -
                    body: ta.Optional[bytes]
         
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
                    def as_error(self) -> 'GithubV1CacheShellClient.CurlError':
         
     | 
| 
       147 
     | 
    
         
            -
                        return GithubV1CacheShellClient.CurlError(
         
     | 
| 
       148 
     | 
    
         
            -
                            status_code=self.status_code,
         
     | 
| 
       149 
     | 
    
         
            -
                            body=self.body,
         
     | 
| 
       150 
     | 
    
         
            -
                        )
         
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
                def run_curl_cmd(
         
     | 
| 
       153 
     | 
    
         
            -
                        self,
         
     | 
| 
       154 
     | 
    
         
            -
                        cmd: ShellCmd,
         
     | 
| 
       155 
     | 
    
         
            -
                        *,
         
     | 
| 
       156 
     | 
    
         
            -
                        raise_: bool = False,
         
     | 
| 
       157 
     | 
    
         
            -
                ) -> CurlResult:
         
     | 
| 
       158 
     | 
    
         
            -
                    out_file = make_temp_file()
         
     | 
| 
       159 
     | 
    
         
            -
                    with defer(lambda: os.unlink(out_file)):
         
     | 
| 
       160 
     | 
    
         
            -
                        run_cmd = dc.replace(cmd, s=f"{cmd.s} -o {out_file} -w '%{{json}}'")
         
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
                        out_json_bytes = run_cmd.run(subprocesses.check_output)
         
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
                        out_json = json.loads(out_json_bytes.decode())
         
     | 
| 
       165 
     | 
    
         
            -
                        status_code = check.isinstance(out_json['response_code'], int)
         
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
       167 
     | 
    
         
            -
                        with open(out_file, 'rb') as f:
         
     | 
| 
       168 
     | 
    
         
            -
                            body = f.read()
         
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
                        result = self.CurlResult(
         
     | 
| 
       171 
     | 
    
         
            -
                            status_code=status_code,
         
     | 
| 
       172 
     | 
    
         
            -
                            body=body,
         
     | 
| 
       173 
     | 
    
         
            -
                        )
         
     | 
| 
       174 
     | 
    
         
            -
             
     | 
| 
       175 
     | 
    
         
            -
                    if raise_ and (500 <= status_code <= 600):
         
     | 
| 
       176 
     | 
    
         
            -
                        raise result.as_error()
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                    return result
         
     | 
| 
       179 
     | 
    
         
            -
             
     | 
| 
       180 
     | 
    
         
            -
                def run_json_curl_cmd(
         
     | 
| 
       181 
     | 
    
         
            -
                        self,
         
     | 
| 
       182 
     | 
    
         
            -
                        cmd: ShellCmd,
         
     | 
| 
       183 
     | 
    
         
            -
                        *,
         
     | 
| 
       184 
     | 
    
         
            -
                        success_status_codes: ta.Optional[ta.Container[int]] = None,
         
     | 
| 
       185 
     | 
    
         
            -
                ) -> ta.Optional[ta.Any]:
         
     | 
| 
       186 
     | 
    
         
            -
                    result = self.run_curl_cmd(cmd, raise_=True)
         
     | 
| 
       187 
     | 
    
         
            -
             
     | 
| 
       188 
     | 
    
         
            -
                    if success_status_codes is not None:
         
     | 
| 
       189 
     | 
    
         
            -
                        is_success = result.status_code in success_status_codes
         
     | 
| 
       190 
     | 
    
         
            -
                    else:
         
     | 
| 
       191 
     | 
    
         
            -
                        is_success = 200 <= result.status_code < 300
         
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
     | 
    
         
            -
                    if is_success:
         
     | 
| 
       194 
     | 
    
         
            -
                        if not (body := result.body):
         
     | 
| 
       195 
     | 
    
         
            -
                            return None
         
     | 
| 
       196 
     | 
    
         
            -
                        return json.loads(body.decode('utf-8-sig'))
         
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
                    elif result.status_code == 404:
         
     | 
| 
       199 
     | 
    
         
            -
                        return None
         
     | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
       201 
     | 
    
         
            -
                    else:
         
     | 
| 
       202 
     | 
    
         
            -
                        raise result.as_error()
         
     | 
| 
      
 102 
     | 
    
         
            +
                class Entry(GithubCacheShellClient.Entry):
         
     | 
| 
      
 103 
     | 
    
         
            +
                    artifact: GithubCacheServiceV1.ArtifactCacheEntry
         
     | 
| 
       203 
104 
     | 
    
         | 
| 
       204 
105 
     | 
    
         
             
                #
         
     | 
| 
       205 
106 
     | 
    
         | 
| 
       206 
107 
     | 
    
         
             
                def build_get_entry_curl_cmd(self, key: str) -> ShellCmd:
         
     | 
| 
       207 
     | 
    
         
            -
                     
     | 
| 
      
 108 
     | 
    
         
            +
                    fixed_key = self.fix_key(key)
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    qp = dict(
         
     | 
| 
      
 111 
     | 
    
         
            +
                        keys=fixed_key,
         
     | 
| 
      
 112 
     | 
    
         
            +
                        version=str(self.CACHE_VERSION),
         
     | 
| 
      
 113 
     | 
    
         
            +
                    )
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    return self._curl.build_cmd(
         
     | 
| 
       208 
116 
     | 
    
         
             
                        'GET',
         
     | 
| 
       209 
     | 
    
         
            -
                         
     | 
| 
      
 117 
     | 
    
         
            +
                        shlex.quote('?'.join([
         
     | 
| 
      
 118 
     | 
    
         
            +
                            'cache',
         
     | 
| 
      
 119 
     | 
    
         
            +
                            '&'.join([
         
     | 
| 
      
 120 
     | 
    
         
            +
                                f'{k}={urllib.parse.quote_plus(v)}'
         
     | 
| 
      
 121 
     | 
    
         
            +
                                for k, v in qp.items()
         
     | 
| 
      
 122 
     | 
    
         
            +
                            ]),
         
     | 
| 
      
 123 
     | 
    
         
            +
                        ])),
         
     | 
| 
       210 
124 
     | 
    
         
             
                    )
         
     | 
| 
       211 
125 
     | 
    
         | 
| 
       212 
     | 
    
         
            -
                def run_get_entry(self, key: str) -> ta.Optional[ 
     | 
| 
       213 
     | 
    
         
            -
                     
     | 
| 
      
 126 
     | 
    
         
            +
                def run_get_entry(self, key: str) -> ta.Optional[Entry]:
         
     | 
| 
      
 127 
     | 
    
         
            +
                    fixed_key = self.fix_key(key)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    curl_cmd = self.build_get_entry_curl_cmd(fixed_key)
         
     | 
| 
       214 
129 
     | 
    
         | 
| 
       215 
     | 
    
         
            -
                    obj = self. 
     | 
| 
      
 130 
     | 
    
         
            +
                    obj = self._curl.run_json_cmd(
         
     | 
| 
       216 
131 
     | 
    
         
             
                        curl_cmd,
         
     | 
| 
       217 
132 
     | 
    
         
             
                        success_status_codes=[200, 204],
         
     | 
| 
       218 
133 
     | 
    
         
             
                    )
         
     | 
| 
       219 
134 
     | 
    
         
             
                    if obj is None:
         
     | 
| 
       220 
135 
     | 
    
         
             
                        return None
         
     | 
| 
       221 
136 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
                    return GithubCacheServiceV1.dataclass_from_json(
         
     | 
| 
      
 137 
     | 
    
         
            +
                    return self.Entry(GithubCacheServiceV1.dataclass_from_json(
         
     | 
| 
       223 
138 
     | 
    
         
             
                        GithubCacheServiceV1.ArtifactCacheEntry,
         
     | 
| 
       224 
139 
     | 
    
         
             
                        obj,
         
     | 
| 
       225 
     | 
    
         
            -
                    )
         
     | 
| 
      
 140 
     | 
    
         
            +
                    ))
         
     | 
| 
       226 
141 
     | 
    
         | 
| 
       227 
142 
     | 
    
         
             
                #
         
     | 
| 
       228 
143 
     | 
    
         | 
| 
       229 
     | 
    
         
            -
                def build_download_get_entry_cmd(
         
     | 
| 
       230 
     | 
    
         
            -
                        self,
         
     | 
| 
       231 
     | 
    
         
            -
                        entry: GithubCacheServiceV1.ArtifactCacheEntry,
         
     | 
| 
       232 
     | 
    
         
            -
                        out_file: str,
         
     | 
| 
       233 
     | 
    
         
            -
                ) -> ShellCmd:
         
     | 
| 
      
 144 
     | 
    
         
            +
                def build_download_get_entry_cmd(self, entry: Entry, out_file: str) -> ShellCmd:
         
     | 
| 
       234 
145 
     | 
    
         
             
                    return ShellCmd(' '.join([
         
     | 
| 
       235 
146 
     | 
    
         
             
                        'aria2c',
         
     | 
| 
       236 
147 
     | 
    
         
             
                        '-x', '4',
         
     | 
| 
       237 
148 
     | 
    
         
             
                        '-o', out_file,
         
     | 
| 
       238 
     | 
    
         
            -
                        check.non_empty_str(entry.archive_location),
         
     | 
| 
      
 149 
     | 
    
         
            +
                        check.non_empty_str(entry.artifact.archive_location),
         
     | 
| 
       239 
150 
     | 
    
         
             
                    ]))
         
     | 
| 
       240 
151 
     | 
    
         | 
| 
       241 
     | 
    
         
            -
                def download_get_entry(
         
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
                        entry 
     | 
| 
       244 
     | 
    
         
            -
                        out_file 
     | 
| 
       245 
     | 
    
         
            -
             
     | 
| 
       246 
     | 
    
         
            -
                    dl_cmd = self.build_download_get_entry_cmd(entry, out_file)
         
     | 
| 
      
 152 
     | 
    
         
            +
                def download_get_entry(self, entry: GithubCacheShellClient.Entry, out_file: str) -> None:
         
     | 
| 
      
 153 
     | 
    
         
            +
                    dl_cmd = self.build_download_get_entry_cmd(
         
     | 
| 
      
 154 
     | 
    
         
            +
                        check.isinstance(entry, GithubCacheServiceV1ShellClient.Entry),
         
     | 
| 
      
 155 
     | 
    
         
            +
                        out_file,
         
     | 
| 
      
 156 
     | 
    
         
            +
                    )
         
     | 
| 
       247 
157 
     | 
    
         
             
                    dl_cmd.run(subprocesses.check_call)
         
     | 
| 
       248 
158 
     | 
    
         | 
| 
       249 
159 
     | 
    
         
             
                #
         
     | 
| 
       250 
160 
     | 
    
         | 
| 
       251 
     | 
    
         
            -
                def upload_cache_entry(
         
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
       253 
     | 
    
         
            -
             
     | 
| 
       254 
     | 
    
         
            -
                        in_file: str,
         
     | 
| 
       255 
     | 
    
         
            -
                ) -> None:
         
     | 
| 
      
 161 
     | 
    
         
            +
                def upload_cache_entry(self, key: str, in_file: str) -> None:
         
     | 
| 
      
 162 
     | 
    
         
            +
                    fixed_key = self.fix_key(key)
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
       256 
164 
     | 
    
         
             
                    check.state(os.path.isfile(in_file))
         
     | 
| 
       257 
165 
     | 
    
         | 
| 
       258 
166 
     | 
    
         
             
                    file_size = os.stat(in_file).st_size
         
     | 
| 
       259 
167 
     | 
    
         | 
| 
      
 168 
     | 
    
         
            +
                    #
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
       260 
170 
     | 
    
         
             
                    reserve_req = GithubCacheServiceV1.ReserveCacheRequest(
         
     | 
| 
       261 
     | 
    
         
            -
                        key= 
     | 
| 
      
 171 
     | 
    
         
            +
                        key=fixed_key,
         
     | 
| 
       262 
172 
     | 
    
         
             
                        cache_size=file_size,
         
     | 
| 
      
 173 
     | 
    
         
            +
                        version=str(self.CACHE_VERSION),
         
     | 
| 
       263 
174 
     | 
    
         
             
                    )
         
     | 
| 
       264 
     | 
    
         
            -
                    reserve_cmd = self. 
     | 
| 
      
 175 
     | 
    
         
            +
                    reserve_cmd = self._curl.build_post_json_cmd(
         
     | 
| 
       265 
176 
     | 
    
         
             
                        'caches',
         
     | 
| 
       266 
177 
     | 
    
         
             
                        GithubCacheServiceV1.dataclass_to_json(reserve_req),
         
     | 
| 
       267 
178 
     | 
    
         
             
                    )
         
     | 
| 
       268 
     | 
    
         
            -
                    reserve_resp_obj: ta.Any = check.not_none(self. 
     | 
| 
      
 179 
     | 
    
         
            +
                    reserve_resp_obj: ta.Any = check.not_none(self._curl.run_json_cmd(
         
     | 
| 
       269 
180 
     | 
    
         
             
                        reserve_cmd,
         
     | 
| 
       270 
181 
     | 
    
         
             
                        success_status_codes=[201],
         
     | 
| 
       271 
182 
     | 
    
         
             
                    ))
         
     | 
| 
         @@ -273,8 +184,66 @@ class GithubV1CacheShellClient: 
     | 
|
| 
       273 
184 
     | 
    
         
             
                        GithubCacheServiceV1.ReserveCacheResponse,
         
     | 
| 
       274 
185 
     | 
    
         
             
                        reserve_resp_obj,
         
     | 
| 
       275 
186 
     | 
    
         
             
                    )
         
     | 
| 
      
 187 
     | 
    
         
            +
                    cache_id = check.isinstance(reserve_resp.cache_id, int)
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                    #
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                    tmp_file = make_temp_file()
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                    print(f'{file_size=}')
         
     | 
| 
      
 194 
     | 
    
         
            +
                    num_written = 0
         
     | 
| 
      
 195 
     | 
    
         
            +
                    chunk_size = 32 * 1024 * 1024
         
     | 
| 
      
 196 
     | 
    
         
            +
                    for i in range((file_size // chunk_size) + (1 if file_size % chunk_size else 0)):
         
     | 
| 
      
 197 
     | 
    
         
            +
                        ofs = i * chunk_size
         
     | 
| 
      
 198 
     | 
    
         
            +
                        sz = min(chunk_size, file_size - ofs)
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                        patch_cmd = self._curl.build_cmd(
         
     | 
| 
      
 201 
     | 
    
         
            +
                            'PATCH',
         
     | 
| 
      
 202 
     | 
    
         
            +
                            f'caches/{cache_id}',
         
     | 
| 
      
 203 
     | 
    
         
            +
                            content_type='application/octet-stream',
         
     | 
| 
      
 204 
     | 
    
         
            +
                            headers={
         
     | 
| 
      
 205 
     | 
    
         
            +
                                'Content-Range': f'bytes {ofs}-{ofs + sz - 1}/*',
         
     | 
| 
      
 206 
     | 
    
         
            +
                            },
         
     | 
| 
      
 207 
     | 
    
         
            +
                        )
         
     | 
| 
       276 
208 
     | 
    
         | 
| 
       277 
     | 
    
         
            -
             
     | 
| 
      
 209 
     | 
    
         
            +
                        #
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                        # patch_data_cmd = dc.replace(patch_cmd, s=' | '.join([
         
     | 
| 
      
 212 
     | 
    
         
            +
                        #     f'dd if={in_file} bs={chunk_size} skip={i} count=1 status=none',
         
     | 
| 
      
 213 
     | 
    
         
            +
                        #     f'{patch_cmd.s} --data-binary -',
         
     | 
| 
      
 214 
     | 
    
         
            +
                        # ]))
         
     | 
| 
      
 215 
     | 
    
         
            +
                        # print(f'{patch_data_cmd.s=}')
         
     | 
| 
      
 216 
     | 
    
         
            +
                        # patch_result = self._curl.run_cmd(patch_data_cmd, raise_=True)
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
                        #
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                        with open(in_file, 'rb') as f:
         
     | 
| 
      
 221 
     | 
    
         
            +
                            f.seek(ofs)
         
     | 
| 
      
 222 
     | 
    
         
            +
                            buf = f.read(sz)
         
     | 
| 
      
 223 
     | 
    
         
            +
                        with open(tmp_file, 'wb') as f:
         
     | 
| 
      
 224 
     | 
    
         
            +
                            f.write(buf)
         
     | 
| 
      
 225 
     | 
    
         
            +
                        num_written += len(buf)
         
     | 
| 
      
 226 
     | 
    
         
            +
                        print(f'{num_written=}')
         
     | 
| 
      
 227 
     | 
    
         
            +
                        patch_data_cmd = dc.replace(patch_cmd, s=f'{patch_cmd.s} --data-binary @{tmp_file}')
         
     | 
| 
      
 228 
     | 
    
         
            +
                        print(f'{patch_data_cmd.s=}')
         
     | 
| 
      
 229 
     | 
    
         
            +
                        patch_result = self._curl.run_cmd(patch_data_cmd, raise_=True)
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
                        #
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
                        check.equal(patch_result.status_code, 204)
         
     | 
| 
      
 234 
     | 
    
         
            +
                        ofs += sz
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                    #
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                    commit_req = GithubCacheServiceV1.CommitCacheRequest(
         
     | 
| 
      
 239 
     | 
    
         
            +
                        size=file_size,
         
     | 
| 
      
 240 
     | 
    
         
            +
                    )
         
     | 
| 
      
 241 
     | 
    
         
            +
                    commit_cmd = self._curl.build_post_json_cmd(
         
     | 
| 
      
 242 
     | 
    
         
            +
                        f'caches/{cache_id}',
         
     | 
| 
      
 243 
     | 
    
         
            +
                        GithubCacheServiceV1.dataclass_to_json(commit_req),
         
     | 
| 
      
 244 
     | 
    
         
            +
                    )
         
     | 
| 
      
 245 
     | 
    
         
            +
                    commit_result = self._curl.run_cmd(commit_cmd, raise_=True)
         
     | 
| 
      
 246 
     | 
    
         
            +
                    check.equal(commit_result.status_code, 204)
         
     | 
| 
       278 
247 
     | 
    
         | 
| 
       279 
248 
     | 
    
         | 
| 
       280 
249 
     | 
    
         
             
            ##
         
     | 
| 
         @@ -285,15 +254,15 @@ class GithubShellCache(ShellCache): 
     | 
|
| 
       285 
254 
     | 
    
         
             
                        self,
         
     | 
| 
       286 
255 
     | 
    
         
             
                        dir: str,  # noqa
         
     | 
| 
       287 
256 
     | 
    
         
             
                        *,
         
     | 
| 
       288 
     | 
    
         
            -
                        client: ta.Optional[ 
     | 
| 
      
 257 
     | 
    
         
            +
                        client: ta.Optional[GithubCacheShellClient] = None,
         
     | 
| 
       289 
258 
     | 
    
         
             
                ) -> None:
         
     | 
| 
       290 
259 
     | 
    
         
             
                    super().__init__()
         
     | 
| 
       291 
260 
     | 
    
         | 
| 
       292 
261 
     | 
    
         
             
                    self._dir = check.not_none(dir)
         
     | 
| 
       293 
262 
     | 
    
         | 
| 
       294 
263 
     | 
    
         
             
                    if client is None:
         
     | 
| 
       295 
     | 
    
         
            -
                        client =  
     | 
| 
       296 
     | 
    
         
            -
                    self._client = client
         
     | 
| 
      
 264 
     | 
    
         
            +
                        client = GithubCacheServiceV1ShellClient()
         
     | 
| 
      
 265 
     | 
    
         
            +
                    self._client: GithubCacheShellClient = client
         
     | 
| 
       297 
266 
     | 
    
         | 
| 
       298 
267 
     | 
    
         
             
                    self._local = DirectoryFileCache(self._dir)
         
     | 
| 
       299 
268 
     | 
    
         | 
    
        omdev/ci/github/cacheapi.py
    CHANGED
    
    
    
        omdev/ci/github/cli.py
    CHANGED
    
    | 
         @@ -11,7 +11,7 @@ from omlish.argparse.cli import argparse_arg 
     | 
|
| 
       11 
11 
     | 
    
         
             
            from omlish.argparse.cli import argparse_cmd
         
     | 
| 
       12 
12 
     | 
    
         
             
            from omlish.lite.json import json_dumps_pretty
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
            from .cache import  
     | 
| 
      
 14 
     | 
    
         
            +
            from .cache import GithubCacheServiceV1ShellClient
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
            class GithubCli(ArgparseCli):
         
     | 
| 
         @@ -19,7 +19,7 @@ class GithubCli(ArgparseCli): 
     | 
|
| 
       19 
19 
     | 
    
         
             
                    argparse_arg('key'),
         
     | 
| 
       20 
20 
     | 
    
         
             
                )
         
     | 
| 
       21 
21 
     | 
    
         
             
                def get_cache_entry(self) -> None:
         
     | 
| 
       22 
     | 
    
         
            -
                    shell_client =  
     | 
| 
      
 22 
     | 
    
         
            +
                    shell_client = GithubCacheServiceV1ShellClient()
         
     | 
| 
       23 
23 
     | 
    
         
             
                    entry = shell_client.run_get_entry(self.args.key)
         
     | 
| 
       24 
24 
     | 
    
         
             
                    if entry is None:
         
     | 
| 
       25 
25 
     | 
    
         
             
                        return
         
     | 
    
        omdev/ci/github/curl.py
    ADDED
    
    | 
         @@ -0,0 +1,209 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # ruff: noqa: UP006 UP007
         
     | 
| 
      
 2 
     | 
    
         
            +
            # @omlish-lite
         
     | 
| 
      
 3 
     | 
    
         
            +
            import dataclasses as dc
         
     | 
| 
      
 4 
     | 
    
         
            +
            import json
         
     | 
| 
      
 5 
     | 
    
         
            +
            import os
         
     | 
| 
      
 6 
     | 
    
         
            +
            import shlex
         
     | 
| 
      
 7 
     | 
    
         
            +
            import typing as ta
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            from omlish.lite.check import check
         
     | 
| 
      
 10 
     | 
    
         
            +
            from omlish.lite.contextmanagers import defer
         
     | 
| 
      
 11 
     | 
    
         
            +
            from omlish.lite.json import json_dumps_compact
         
     | 
| 
      
 12 
     | 
    
         
            +
            from omlish.subprocesses import subprocesses
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            from ..shell import ShellCmd
         
     | 
| 
      
 15 
     | 
    
         
            +
            from ..utils import make_temp_file
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            ##
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            class GithubServiceCurlClient:
         
     | 
| 
      
 22 
     | 
    
         
            +
                def __init__(
         
     | 
| 
      
 23 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 24 
     | 
    
         
            +
                        service_url: str,
         
     | 
| 
      
 25 
     | 
    
         
            +
                        auth_token: ta.Optional[str] = None,
         
     | 
| 
      
 26 
     | 
    
         
            +
                        *,
         
     | 
| 
      
 27 
     | 
    
         
            +
                        api_version: ta.Optional[str] = None,
         
     | 
| 
      
 28 
     | 
    
         
            +
                ) -> None:
         
     | 
| 
      
 29 
     | 
    
         
            +
                    super().__init__()
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    self._service_url = check.non_empty_str(service_url)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    self._auth_token = auth_token
         
     | 
| 
      
 33 
     | 
    
         
            +
                    self._api_version = api_version
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                #
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                _MISSING = object()
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def build_headers(
         
     | 
| 
      
 40 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 41 
     | 
    
         
            +
                        headers: ta.Optional[ta.Mapping[str, str]] = None,
         
     | 
| 
      
 42 
     | 
    
         
            +
                        *,
         
     | 
| 
      
 43 
     | 
    
         
            +
                        auth_token: ta.Any = _MISSING,
         
     | 
| 
      
 44 
     | 
    
         
            +
                        content_type: ta.Optional[str] = None,
         
     | 
| 
      
 45 
     | 
    
         
            +
                ) -> ta.Dict[str, str]:
         
     | 
| 
      
 46 
     | 
    
         
            +
                    dct = {
         
     | 
| 
      
 47 
     | 
    
         
            +
                        'Accept': ';'.join([
         
     | 
| 
      
 48 
     | 
    
         
            +
                            'application/json',
         
     | 
| 
      
 49 
     | 
    
         
            +
                            *([f'api-version={self._api_version}'] if self._api_version else []),
         
     | 
| 
      
 50 
     | 
    
         
            +
                        ]),
         
     | 
| 
      
 51 
     | 
    
         
            +
                    }
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    if auth_token is self._MISSING:
         
     | 
| 
      
 54 
     | 
    
         
            +
                        auth_token = self._auth_token
         
     | 
| 
      
 55 
     | 
    
         
            +
                    if auth_token:
         
     | 
| 
      
 56 
     | 
    
         
            +
                        dct['Authorization'] = f'Bearer {auth_token}'
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    if content_type is not None:
         
     | 
| 
      
 59 
     | 
    
         
            +
                        dct['Content-Type'] = content_type
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    if headers:
         
     | 
| 
      
 62 
     | 
    
         
            +
                        dct.update(headers)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    return dct
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                #
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                HEADER_AUTH_TOKEN_ENV_KEY_PREFIX = '_GITHUB_SERVICE_AUTH_TOKEN'  # noqa
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                @property
         
     | 
| 
      
 71 
     | 
    
         
            +
                def header_auth_token_env_key(self) -> str:
         
     | 
| 
      
 72 
     | 
    
         
            +
                    return f'{self.HEADER_AUTH_TOKEN_ENV_KEY_PREFIX}_{id(self)}'
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def build_cmd(
         
     | 
| 
      
 75 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 76 
     | 
    
         
            +
                        method: str,
         
     | 
| 
      
 77 
     | 
    
         
            +
                        url: str,
         
     | 
| 
      
 78 
     | 
    
         
            +
                        *,
         
     | 
| 
      
 79 
     | 
    
         
            +
                        json_content: bool = False,
         
     | 
| 
      
 80 
     | 
    
         
            +
                        content_type: ta.Optional[str] = None,
         
     | 
| 
      
 81 
     | 
    
         
            +
                        headers: ta.Optional[ta.Dict[str, str]] = None,
         
     | 
| 
      
 82 
     | 
    
         
            +
                ) -> ShellCmd:
         
     | 
| 
      
 83 
     | 
    
         
            +
                    if content_type is None and json_content:
         
     | 
| 
      
 84 
     | 
    
         
            +
                        content_type = 'application/json'
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    env = {}
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    header_auth_token: ta.Optional[str]
         
     | 
| 
      
 89 
     | 
    
         
            +
                    if self._auth_token:
         
     | 
| 
      
 90 
     | 
    
         
            +
                        header_env_key = self.header_auth_token_env_key
         
     | 
| 
      
 91 
     | 
    
         
            +
                        env[header_env_key] = self._auth_token
         
     | 
| 
      
 92 
     | 
    
         
            +
                        header_auth_token = f'${header_env_key}'
         
     | 
| 
      
 93 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 94 
     | 
    
         
            +
                        header_auth_token = None
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                    built_hdrs = self.build_headers(
         
     | 
| 
      
 97 
     | 
    
         
            +
                        headers,
         
     | 
| 
      
 98 
     | 
    
         
            +
                        auth_token=header_auth_token,
         
     | 
| 
      
 99 
     | 
    
         
            +
                        content_type=content_type,
         
     | 
| 
      
 100 
     | 
    
         
            +
                    )
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    url = f'{self._service_url}/{url}'
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    cmd = ' '.join([
         
     | 
| 
      
 105 
     | 
    
         
            +
                        'curl',
         
     | 
| 
      
 106 
     | 
    
         
            +
                        '-s',
         
     | 
| 
      
 107 
     | 
    
         
            +
                        '-X', method,
         
     | 
| 
      
 108 
     | 
    
         
            +
                        url,
         
     | 
| 
      
 109 
     | 
    
         
            +
                        *[f'-H "{k}: {v}"' for k, v in built_hdrs.items()],
         
     | 
| 
      
 110 
     | 
    
         
            +
                    ])
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                    return ShellCmd(
         
     | 
| 
      
 113 
     | 
    
         
            +
                        cmd,
         
     | 
| 
      
 114 
     | 
    
         
            +
                        env=env,
         
     | 
| 
      
 115 
     | 
    
         
            +
                    )
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                def build_post_json_cmd(
         
     | 
| 
      
 118 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 119 
     | 
    
         
            +
                        url: str,
         
     | 
| 
      
 120 
     | 
    
         
            +
                        obj: ta.Any,
         
     | 
| 
      
 121 
     | 
    
         
            +
                        **kwargs: ta.Any,
         
     | 
| 
      
 122 
     | 
    
         
            +
                ) -> ShellCmd:
         
     | 
| 
      
 123 
     | 
    
         
            +
                    curl_cmd = self.build_cmd(
         
     | 
| 
      
 124 
     | 
    
         
            +
                        'POST',
         
     | 
| 
      
 125 
     | 
    
         
            +
                        url,
         
     | 
| 
      
 126 
     | 
    
         
            +
                        json_content=True,
         
     | 
| 
      
 127 
     | 
    
         
            +
                        **kwargs,
         
     | 
| 
      
 128 
     | 
    
         
            +
                    )
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                    obj_json = json_dumps_compact(obj)
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    return dc.replace(curl_cmd, s=f'{curl_cmd.s} -d {shlex.quote(obj_json)}')
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                #
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                @dc.dataclass()
         
     | 
| 
      
 137 
     | 
    
         
            +
                class Error(RuntimeError):
         
     | 
| 
      
 138 
     | 
    
         
            +
                    status_code: int
         
     | 
| 
      
 139 
     | 
    
         
            +
                    body: ta.Optional[bytes]
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    def __str__(self) -> str:
         
     | 
| 
      
 142 
     | 
    
         
            +
                        return repr(self)
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                @dc.dataclass(frozen=True)
         
     | 
| 
      
 145 
     | 
    
         
            +
                class Result:
         
     | 
| 
      
 146 
     | 
    
         
            +
                    status_code: int
         
     | 
| 
      
 147 
     | 
    
         
            +
                    body: ta.Optional[bytes]
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                    def as_error(self) -> 'GithubServiceCurlClient.Error':
         
     | 
| 
      
 150 
     | 
    
         
            +
                        return GithubServiceCurlClient.Error(
         
     | 
| 
      
 151 
     | 
    
         
            +
                            status_code=self.status_code,
         
     | 
| 
      
 152 
     | 
    
         
            +
                            body=self.body,
         
     | 
| 
      
 153 
     | 
    
         
            +
                        )
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                def run_cmd(
         
     | 
| 
      
 156 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 157 
     | 
    
         
            +
                        cmd: ShellCmd,
         
     | 
| 
      
 158 
     | 
    
         
            +
                        *,
         
     | 
| 
      
 159 
     | 
    
         
            +
                        raise_: bool = False,
         
     | 
| 
      
 160 
     | 
    
         
            +
                        **subprocess_kwargs: ta.Any,
         
     | 
| 
      
 161 
     | 
    
         
            +
                ) -> Result:
         
     | 
| 
      
 162 
     | 
    
         
            +
                    out_file = make_temp_file()
         
     | 
| 
      
 163 
     | 
    
         
            +
                    with defer(lambda: os.unlink(out_file)):
         
     | 
| 
      
 164 
     | 
    
         
            +
                        run_cmd = dc.replace(cmd, s=f"{cmd.s} -o {out_file} -w '%{{json}}'")
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                        out_json_bytes = run_cmd.run(
         
     | 
| 
      
 167 
     | 
    
         
            +
                            subprocesses.check_output,
         
     | 
| 
      
 168 
     | 
    
         
            +
                            **subprocess_kwargs,
         
     | 
| 
      
 169 
     | 
    
         
            +
                        )
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                        out_json = json.loads(out_json_bytes.decode())
         
     | 
| 
      
 172 
     | 
    
         
            +
                        status_code = check.isinstance(out_json['response_code'], int)
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                        with open(out_file, 'rb') as f:
         
     | 
| 
      
 175 
     | 
    
         
            +
                            body = f.read()
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                        result = self.Result(
         
     | 
| 
      
 178 
     | 
    
         
            +
                            status_code=status_code,
         
     | 
| 
      
 179 
     | 
    
         
            +
                            body=body,
         
     | 
| 
      
 180 
     | 
    
         
            +
                        )
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                    if raise_ and (500 <= status_code <= 600):
         
     | 
| 
      
 183 
     | 
    
         
            +
                        raise result.as_error()
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                    return result
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                def run_json_cmd(
         
     | 
| 
      
 188 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 189 
     | 
    
         
            +
                        cmd: ShellCmd,
         
     | 
| 
      
 190 
     | 
    
         
            +
                        *,
         
     | 
| 
      
 191 
     | 
    
         
            +
                        success_status_codes: ta.Optional[ta.Container[int]] = None,
         
     | 
| 
      
 192 
     | 
    
         
            +
                ) -> ta.Optional[ta.Any]:
         
     | 
| 
      
 193 
     | 
    
         
            +
                    result = self.run_cmd(cmd, raise_=True)
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                    if success_status_codes is not None:
         
     | 
| 
      
 196 
     | 
    
         
            +
                        is_success = result.status_code in success_status_codes
         
     | 
| 
      
 197 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 198 
     | 
    
         
            +
                        is_success = 200 <= result.status_code < 300
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                    if is_success:
         
     | 
| 
      
 201 
     | 
    
         
            +
                        if not (body := result.body):
         
     | 
| 
      
 202 
     | 
    
         
            +
                            return None
         
     | 
| 
      
 203 
     | 
    
         
            +
                        return json.loads(body.decode('utf-8-sig'))
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                    elif result.status_code == 404:
         
     | 
| 
      
 206 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 209 
     | 
    
         
            +
                        raise result.as_error()
         
     | 
    
        omdev/ci/requirements.py
    CHANGED
    
    
    
        omdev/git/shallow.py
    CHANGED