omdev 0.0.0.dev289__py3-none-any.whl → 0.0.0.dev290__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/ci/github/api/__init__.py +0 -0
- omdev/ci/github/{client.py → api/clients.py} +172 -209
- omdev/ci/github/api/v1/__init__.py +0 -0
- omdev/ci/github/{api.py → api/v1/api.py} +5 -87
- omdev/ci/github/api/v1/client.py +171 -0
- omdev/ci/github/api/v2/__init__.py +0 -0
- omdev/ci/github/api/v2/api.py +148 -0
- omdev/ci/github/api/v2/client.py +166 -0
- omdev/ci/github/cache.py +14 -3
- omdev/ci/github/cli.py +1 -1
- omdev/scripts/ci.py +663 -288
- {omdev-0.0.0.dev289.dist-info → omdev-0.0.0.dev290.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev289.dist-info → omdev-0.0.0.dev290.dist-info}/RECORD +17 -11
- {omdev-0.0.0.dev289.dist-info → omdev-0.0.0.dev290.dist-info}/WHEEL +1 -1
- {omdev-0.0.0.dev289.dist-info → omdev-0.0.0.dev290.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev289.dist-info → omdev-0.0.0.dev290.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev289.dist-info → omdev-0.0.0.dev290.dist-info}/top_level.txt +0 -0
omdev/scripts/ci.py
CHANGED
@@ -103,6 +103,10 @@ TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.Default'], ta.Iterable['Timeo
|
|
103
103
|
# ../../omlish/sockets/addresses.py
|
104
104
|
SocketAddress = ta.Any
|
105
105
|
|
106
|
+
# github/api/v2/api.py
|
107
|
+
GithubCacheServiceV2RequestT = ta.TypeVar('GithubCacheServiceV2RequestT')
|
108
|
+
GithubCacheServiceV2ResponseT = ta.TypeVar('GithubCacheServiceV2ResponseT')
|
109
|
+
|
106
110
|
# ../../omlish/argparse/cli.py
|
107
111
|
ArgparseCmdFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
108
112
|
|
@@ -418,6 +422,31 @@ class DockerPortRelay:
|
|
418
422
|
]
|
419
423
|
|
420
424
|
|
425
|
+
########################################
|
426
|
+
# ../../../omlish/http/urllib.py
|
427
|
+
|
428
|
+
|
429
|
+
##
|
430
|
+
|
431
|
+
|
432
|
+
class NonRaisingUrllibErrorProcessor(urllib.request.HTTPErrorProcessor):
|
433
|
+
"""
|
434
|
+
https://stackoverflow.com/a/74844056
|
435
|
+
|
436
|
+
Usage:
|
437
|
+
|
438
|
+
opener = urllib.request.build_opener(NonRaisingUrllibErrorProcessor)
|
439
|
+
with opener.open(req) as resp:
|
440
|
+
...
|
441
|
+
"""
|
442
|
+
|
443
|
+
def http_response(self, request, response):
|
444
|
+
return response
|
445
|
+
|
446
|
+
def https_response(self, request, response):
|
447
|
+
return response
|
448
|
+
|
449
|
+
|
421
450
|
########################################
|
422
451
|
# ../../../omlish/http/versions.py
|
423
452
|
|
@@ -1985,7 +2014,7 @@ def read_docker_tar_image_id(tar_file: str) -> str:
|
|
1985
2014
|
|
1986
2015
|
|
1987
2016
|
########################################
|
1988
|
-
# ../github/api.py
|
2017
|
+
# ../github/api/v1/api.py
|
1989
2018
|
"""
|
1990
2019
|
export FILE_SIZE=$(stat --format="%s" $FILE)
|
1991
2020
|
|
@@ -2028,6 +2057,11 @@ curl -s \
|
|
2028
2057
|
|
2029
2058
|
|
2030
2059
|
class GithubCacheServiceV1:
|
2060
|
+
def __new__(cls, *args, **kwargs): # noqa
|
2061
|
+
raise TypeError
|
2062
|
+
|
2063
|
+
#
|
2064
|
+
|
2031
2065
|
API_VERSION = '6.0-preview.1'
|
2032
2066
|
|
2033
2067
|
@classmethod
|
@@ -2098,14 +2132,54 @@ class GithubCacheServiceV1:
|
|
2098
2132
|
cache_size: ta.Optional[int]
|
2099
2133
|
|
2100
2134
|
|
2135
|
+
########################################
|
2136
|
+
# ../github/api/v2/api.py
|
2137
|
+
"""
|
2138
|
+
https://github.com/tonistiigi/go-actions-cache/blob/3e9a6642607fd6e4d5d4fdab7c91fe8bf4c36a25/cache_v2.go
|
2139
|
+
|
2140
|
+
==
|
2141
|
+
|
2142
|
+
curl -s \
|
2143
|
+
-X POST \
|
2144
|
+
"${ACTIONS_RESULTS_URL}twirp/github.actions.results.api.v1.CacheService/CreateCacheEntry" \
|
2145
|
+
-H 'Content-Type: application/json' \
|
2146
|
+
-H "Authorization: Bearer $ACTIONS_RUNTIME_TOKEN" \
|
2147
|
+
-d '{"key": "foo", "version": "0000000000000000000000000000000000000000000000000000000000000001" }' \
|
2148
|
+
| jq .
|
2149
|
+
|
2150
|
+
curl -s \
|
2151
|
+
-X POST \
|
2152
|
+
"${ACTIONS_RESULTS_URL}twirp/github.actions.results.api.v1.CacheService/GetCacheEntryDownloadURL" \
|
2153
|
+
-H 'Content-Type: application/json' \
|
2154
|
+
-H "Authorization: Bearer $ACTIONS_RUNTIME_TOKEN" \
|
2155
|
+
-d '{"key": "foo", "restoreKeys": [], "version": "0000000000000000000000000000000000000000000000000000000000000001" }' \
|
2156
|
+
| jq .
|
2157
|
+
|
2158
|
+
""" # noqa
|
2159
|
+
|
2160
|
+
|
2161
|
+
##
|
2162
|
+
|
2163
|
+
|
2101
2164
|
class GithubCacheServiceV2:
|
2165
|
+
def __new__(cls, *args, **kwargs): # noqa
|
2166
|
+
raise TypeError
|
2167
|
+
|
2168
|
+
#
|
2169
|
+
|
2102
2170
|
SERVICE_NAME = 'github.actions.results.api.v1.CacheService'
|
2103
2171
|
|
2172
|
+
@classmethod
|
2173
|
+
def get_service_url(cls, base_url: str) -> str:
|
2174
|
+
return f'{base_url.rstrip("/")}/twirp/{cls.SERVICE_NAME}'
|
2175
|
+
|
2176
|
+
#
|
2177
|
+
|
2104
2178
|
@dc.dataclass(frozen=True)
|
2105
|
-
class Method:
|
2179
|
+
class Method(ta.Generic[GithubCacheServiceV2RequestT, GithubCacheServiceV2ResponseT]):
|
2106
2180
|
name: str
|
2107
|
-
request:
|
2108
|
-
response:
|
2181
|
+
request: ta.Type[GithubCacheServiceV2RequestT]
|
2182
|
+
response: ta.Type[GithubCacheServiceV2ResponseT]
|
2109
2183
|
|
2110
2184
|
#
|
2111
2185
|
|
@@ -2124,6 +2198,8 @@ class GithubCacheServiceV2:
|
|
2124
2198
|
repository_id: int
|
2125
2199
|
scope: ta.Sequence['GithubCacheServiceV2.CacheScope']
|
2126
2200
|
|
2201
|
+
VERSION_LENGTH: int = 64
|
2202
|
+
|
2127
2203
|
#
|
2128
2204
|
|
2129
2205
|
@dc.dataclass(frozen=True)
|
@@ -2132,12 +2208,18 @@ class GithubCacheServiceV2:
|
|
2132
2208
|
version: str
|
2133
2209
|
metadata: ta.Optional['GithubCacheServiceV2.CacheMetadata'] = None
|
2134
2210
|
|
2211
|
+
def __post_init__(self) -> None:
|
2212
|
+
check.equal(len(self.version), GithubCacheServiceV2.VERSION_LENGTH)
|
2213
|
+
|
2135
2214
|
@dc.dataclass(frozen=True)
|
2136
2215
|
class CreateCacheEntryResponse:
|
2137
2216
|
ok: bool
|
2138
2217
|
signed_upload_url: str
|
2139
2218
|
|
2140
|
-
CREATE_CACHE_ENTRY_METHOD
|
2219
|
+
CREATE_CACHE_ENTRY_METHOD: Method[
|
2220
|
+
CreateCacheEntryRequest,
|
2221
|
+
CreateCacheEntryResponse,
|
2222
|
+
] = Method(
|
2141
2223
|
'CreateCacheEntry',
|
2142
2224
|
CreateCacheEntryRequest,
|
2143
2225
|
CreateCacheEntryResponse,
|
@@ -2157,7 +2239,10 @@ class GithubCacheServiceV2:
|
|
2157
2239
|
ok: bool
|
2158
2240
|
entry_id: str
|
2159
2241
|
|
2160
|
-
FINALIZE_CACHE_ENTRY_METHOD
|
2242
|
+
FINALIZE_CACHE_ENTRY_METHOD: Method[
|
2243
|
+
FinalizeCacheEntryUploadRequest,
|
2244
|
+
FinalizeCacheEntryUploadResponse,
|
2245
|
+
] = Method(
|
2161
2246
|
'FinalizeCacheEntryUpload',
|
2162
2247
|
FinalizeCacheEntryUploadRequest,
|
2163
2248
|
FinalizeCacheEntryUploadResponse,
|
@@ -2178,7 +2263,10 @@ class GithubCacheServiceV2:
|
|
2178
2263
|
signed_download_url: str
|
2179
2264
|
matched_key: str
|
2180
2265
|
|
2181
|
-
GET_CACHE_ENTRY_DOWNLOAD_URL_METHOD
|
2266
|
+
GET_CACHE_ENTRY_DOWNLOAD_URL_METHOD: Method[
|
2267
|
+
GetCacheEntryDownloadUrlRequest,
|
2268
|
+
GetCacheEntryDownloadUrlResponse,
|
2269
|
+
] = Method(
|
2182
2270
|
'GetCacheEntryDownloadURL',
|
2183
2271
|
GetCacheEntryDownloadUrlRequest,
|
2184
2272
|
GetCacheEntryDownloadUrlResponse,
|
@@ -5978,13 +6066,14 @@ class FileCacheDataCache(DataCache):
|
|
5978
6066
|
|
5979
6067
|
|
5980
6068
|
########################################
|
5981
|
-
# ../github/
|
6069
|
+
# ../github/api/clients.py
|
5982
6070
|
|
5983
6071
|
|
5984
6072
|
##
|
5985
6073
|
|
5986
6074
|
|
5987
6075
|
class GithubCacheClient(abc.ABC):
|
6076
|
+
@dc.dataclass(frozen=True)
|
5988
6077
|
class Entry(abc.ABC): # noqa
|
5989
6078
|
pass
|
5990
6079
|
|
@@ -6007,18 +6096,21 @@ class GithubCacheClient(abc.ABC):
|
|
6007
6096
|
##
|
6008
6097
|
|
6009
6098
|
|
6010
|
-
class
|
6011
|
-
BASE_URL_ENV_VAR = register_github_env_var('ACTIONS_CACHE_URL')
|
6099
|
+
class BaseGithubCacheClient(GithubCacheClient, abc.ABC):
|
6012
6100
|
AUTH_TOKEN_ENV_VAR = register_github_env_var('ACTIONS_RUNTIME_TOKEN') # noqa
|
6013
6101
|
|
6014
6102
|
KEY_SUFFIX_ENV_VAR = register_github_env_var('GITHUB_RUN_ID')
|
6015
6103
|
|
6104
|
+
DEFAULT_CONCURRENCY = 4
|
6105
|
+
DEFAULT_CHUNK_SIZE = 64 * 1024 * 1024
|
6106
|
+
|
6016
6107
|
#
|
6017
6108
|
|
6018
6109
|
def __init__(
|
6019
6110
|
self,
|
6020
6111
|
*,
|
6021
|
-
|
6112
|
+
service_url: str,
|
6113
|
+
|
6022
6114
|
auth_token: ta.Optional[str] = None,
|
6023
6115
|
|
6024
6116
|
key_prefix: ta.Optional[str] = None,
|
@@ -6027,14 +6119,15 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6027
6119
|
cache_version: int = CI_CACHE_VERSION,
|
6028
6120
|
|
6029
6121
|
loop: ta.Optional[asyncio.AbstractEventLoop] = None,
|
6122
|
+
|
6123
|
+
concurrency: int = DEFAULT_CONCURRENCY,
|
6124
|
+
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
6030
6125
|
) -> None:
|
6031
6126
|
super().__init__()
|
6032
6127
|
|
6033
6128
|
#
|
6034
6129
|
|
6035
|
-
|
6036
|
-
base_url = check.non_empty_str(self.BASE_URL_ENV_VAR())
|
6037
|
-
self._service_url = GithubCacheServiceV1.get_service_url(base_url)
|
6130
|
+
self._service_url = check.non_empty_str(service_url)
|
6038
6131
|
|
6039
6132
|
if auth_token is None:
|
6040
6133
|
auth_token = self.AUTH_TOKEN_ENV_VAR()
|
@@ -6056,7 +6149,16 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6056
6149
|
|
6057
6150
|
self._given_loop = loop
|
6058
6151
|
|
6059
|
-
|
6152
|
+
#
|
6153
|
+
|
6154
|
+
check.arg(concurrency > 0)
|
6155
|
+
self._concurrency = concurrency
|
6156
|
+
|
6157
|
+
check.arg(chunk_size > 0)
|
6158
|
+
self._chunk_size = chunk_size
|
6159
|
+
|
6160
|
+
##
|
6161
|
+
# misc
|
6060
6162
|
|
6061
6163
|
def _get_loop(self) -> asyncio.AbstractEventLoop:
|
6062
6164
|
if (loop := self._given_loop) is not None:
|
@@ -6065,21 +6167,25 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6065
6167
|
|
6066
6168
|
#
|
6067
6169
|
|
6068
|
-
def
|
6170
|
+
def _load_json_bytes(self, b: ta.Optional[bytes]) -> ta.Optional[ta.Any]:
|
6171
|
+
if not b:
|
6172
|
+
return None
|
6173
|
+
return json.loads(b.decode('utf-8-sig'))
|
6174
|
+
|
6175
|
+
##
|
6176
|
+
# requests
|
6177
|
+
|
6178
|
+
def _build_request_headers(
|
6069
6179
|
self,
|
6070
6180
|
headers: ta.Optional[ta.Mapping[str, str]] = None,
|
6071
6181
|
*,
|
6182
|
+
no_auth: bool = False,
|
6072
6183
|
content_type: ta.Optional[str] = None,
|
6073
6184
|
json_content: bool = False,
|
6074
6185
|
) -> ta.Dict[str, str]:
|
6075
|
-
dct = {
|
6076
|
-
'Accept': ';'.join([
|
6077
|
-
'application/json',
|
6078
|
-
f'api-version={GithubCacheServiceV1.API_VERSION}',
|
6079
|
-
]),
|
6080
|
-
}
|
6186
|
+
dct = {}
|
6081
6187
|
|
6082
|
-
if (auth_token := self._auth_token):
|
6188
|
+
if not no_auth and (auth_token := self._auth_token):
|
6083
6189
|
dct['Authorization'] = f'Bearer {auth_token}'
|
6084
6190
|
|
6085
6191
|
if content_type is None and json_content:
|
@@ -6094,19 +6200,13 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6094
6200
|
|
6095
6201
|
#
|
6096
6202
|
|
6097
|
-
def
|
6098
|
-
if not b:
|
6099
|
-
return None
|
6100
|
-
return json.loads(b.decode('utf-8-sig'))
|
6101
|
-
|
6102
|
-
#
|
6103
|
-
|
6104
|
-
async def send_url_request(
|
6203
|
+
async def _send_urllib_request(
|
6105
6204
|
self,
|
6106
6205
|
req: urllib.request.Request,
|
6107
6206
|
) -> ta.Tuple[http.client.HTTPResponse, ta.Optional[bytes]]:
|
6108
6207
|
def run_sync():
|
6109
|
-
|
6208
|
+
opener = urllib.request.build_opener(NonRaisingUrllibErrorProcessor)
|
6209
|
+
with opener.open(req) as resp: # noqa
|
6110
6210
|
body = resp.read()
|
6111
6211
|
return (resp, body)
|
6112
6212
|
|
@@ -6122,18 +6222,32 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6122
6222
|
def __str__(self) -> str:
|
6123
6223
|
return repr(self)
|
6124
6224
|
|
6125
|
-
async def
|
6225
|
+
async def _send_request(
|
6126
6226
|
self,
|
6127
|
-
path: str,
|
6128
6227
|
*,
|
6228
|
+
url: ta.Optional[str] = None,
|
6229
|
+
path: ta.Optional[str] = None,
|
6230
|
+
|
6129
6231
|
method: ta.Optional[str] = None,
|
6232
|
+
|
6130
6233
|
headers: ta.Optional[ta.Mapping[str, str]] = None,
|
6234
|
+
no_auth: bool = False,
|
6131
6235
|
content_type: ta.Optional[str] = None,
|
6236
|
+
|
6132
6237
|
content: ta.Optional[bytes] = None,
|
6133
6238
|
json_content: ta.Optional[ta.Any] = None,
|
6239
|
+
|
6134
6240
|
success_status_codes: ta.Optional[ta.Container[int]] = None,
|
6241
|
+
|
6242
|
+
retry_status_codes: ta.Optional[ta.Container[int]] = None,
|
6243
|
+
num_retries: int = 0,
|
6244
|
+
retry_sleep: ta.Optional[float] = None,
|
6135
6245
|
) -> ta.Optional[ta.Any]:
|
6136
|
-
url
|
6246
|
+
if url is not None and path is not None:
|
6247
|
+
raise RuntimeError('Must not pass both url and path')
|
6248
|
+
elif path is not None:
|
6249
|
+
url = f'{self._service_url}/{path}'
|
6250
|
+
url = check.non_empty_str(url)
|
6137
6251
|
|
6138
6252
|
if content is not None and json_content is not None:
|
6139
6253
|
raise RuntimeError('Must not pass both content and json_content')
|
@@ -6146,33 +6260,52 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6146
6260
|
if method is None:
|
6147
6261
|
method = 'POST' if content is not None else 'GET'
|
6148
6262
|
|
6263
|
+
headers = self._build_request_headers(
|
6264
|
+
headers,
|
6265
|
+
no_auth=no_auth,
|
6266
|
+
content_type=content_type,
|
6267
|
+
json_content=header_json_content,
|
6268
|
+
)
|
6269
|
+
|
6149
6270
|
#
|
6150
6271
|
|
6151
|
-
|
6152
|
-
|
6153
|
-
|
6154
|
-
|
6155
|
-
headers,
|
6156
|
-
|
6157
|
-
|
6158
|
-
),
|
6159
|
-
data=content,
|
6160
|
-
)
|
6272
|
+
for n in itertools.count():
|
6273
|
+
req = urllib.request.Request( # noqa
|
6274
|
+
url,
|
6275
|
+
method=method,
|
6276
|
+
headers=headers,
|
6277
|
+
data=content,
|
6278
|
+
)
|
6161
6279
|
|
6162
|
-
|
6280
|
+
resp, body = await self._send_urllib_request(req)
|
6163
6281
|
|
6164
|
-
|
6282
|
+
#
|
6165
6283
|
|
6166
|
-
|
6167
|
-
|
6168
|
-
|
6169
|
-
|
6170
|
-
|
6171
|
-
|
6284
|
+
if success_status_codes is not None:
|
6285
|
+
is_success = resp.status in success_status_codes
|
6286
|
+
else:
|
6287
|
+
is_success = (200 <= resp.status < 300)
|
6288
|
+
if is_success:
|
6289
|
+
return self._load_json_bytes(body)
|
6290
|
+
|
6291
|
+
#
|
6172
6292
|
|
6173
|
-
|
6293
|
+
log.debug(f'Request to url {url} got unsuccessful status code {resp.status}') # noqa
|
6174
6294
|
|
6175
|
-
|
6295
|
+
if not (
|
6296
|
+
retry_status_codes is not None and
|
6297
|
+
resp.status in retry_status_codes and
|
6298
|
+
n < num_retries
|
6299
|
+
):
|
6300
|
+
raise self.ServiceRequestError(resp.status, body)
|
6301
|
+
|
6302
|
+
if retry_sleep is not None:
|
6303
|
+
await asyncio.sleep(retry_sleep)
|
6304
|
+
|
6305
|
+
raise RuntimeError('Unreachable')
|
6306
|
+
|
6307
|
+
##
|
6308
|
+
# keys
|
6176
6309
|
|
6177
6310
|
KEY_PART_SEPARATOR = '---'
|
6178
6311
|
|
@@ -6183,73 +6316,8 @@ class GithubCacheServiceV1BaseClient(GithubCacheClient, abc.ABC):
|
|
6183
6316
|
('' if partial_suffix else self._key_suffix),
|
6184
6317
|
])
|
6185
6318
|
|
6186
|
-
|
6187
|
-
|
6188
|
-
@dc.dataclass(frozen=True)
|
6189
|
-
class Entry(GithubCacheClient.Entry):
|
6190
|
-
artifact: GithubCacheServiceV1.ArtifactCacheEntry
|
6191
|
-
|
6192
|
-
def get_entry_url(self, entry: GithubCacheClient.Entry) -> ta.Optional[str]:
|
6193
|
-
entry1 = check.isinstance(entry, self.Entry)
|
6194
|
-
return entry1.artifact.archive_location
|
6195
|
-
|
6196
|
-
#
|
6197
|
-
|
6198
|
-
def build_get_entry_url_path(self, *keys: str) -> str:
|
6199
|
-
qp = dict(
|
6200
|
-
keys=','.join(urllib.parse.quote_plus(k) for k in keys),
|
6201
|
-
version=str(self._cache_version),
|
6202
|
-
)
|
6203
|
-
|
6204
|
-
return '?'.join([
|
6205
|
-
'cache',
|
6206
|
-
'&'.join([
|
6207
|
-
f'{k}={v}'
|
6208
|
-
for k, v in qp.items()
|
6209
|
-
]),
|
6210
|
-
])
|
6211
|
-
|
6212
|
-
GET_ENTRY_SUCCESS_STATUS_CODES = (200, 204)
|
6213
|
-
|
6214
|
-
|
6215
|
-
##
|
6216
|
-
|
6217
|
-
|
6218
|
-
class GithubCacheServiceV1Client(GithubCacheServiceV1BaseClient):
|
6219
|
-
DEFAULT_CONCURRENCY = 4
|
6220
|
-
|
6221
|
-
DEFAULT_CHUNK_SIZE = 32 * 1024 * 1024
|
6222
|
-
|
6223
|
-
def __init__(
|
6224
|
-
self,
|
6225
|
-
*,
|
6226
|
-
concurrency: int = DEFAULT_CONCURRENCY,
|
6227
|
-
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
6228
|
-
**kwargs: ta.Any,
|
6229
|
-
) -> None:
|
6230
|
-
super().__init__(**kwargs)
|
6231
|
-
|
6232
|
-
check.arg(concurrency > 0)
|
6233
|
-
self._concurrency = concurrency
|
6234
|
-
|
6235
|
-
check.arg(chunk_size > 0)
|
6236
|
-
self._chunk_size = chunk_size
|
6237
|
-
|
6238
|
-
#
|
6239
|
-
|
6240
|
-
async def get_entry(self, key: str) -> ta.Optional[GithubCacheServiceV1BaseClient.Entry]:
|
6241
|
-
obj = await self.send_service_request(
|
6242
|
-
self.build_get_entry_url_path(self.fix_key(key, partial_suffix=True)),
|
6243
|
-
)
|
6244
|
-
if obj is None:
|
6245
|
-
return None
|
6246
|
-
|
6247
|
-
return self.Entry(GithubCacheServiceV1.dataclass_from_json(
|
6248
|
-
GithubCacheServiceV1.ArtifactCacheEntry,
|
6249
|
-
obj,
|
6250
|
-
))
|
6251
|
-
|
6252
|
-
#
|
6319
|
+
##
|
6320
|
+
# downloading
|
6253
6321
|
|
6254
6322
|
@dc.dataclass(frozen=True)
|
6255
6323
|
class _DownloadChunk:
|
@@ -6267,7 +6335,7 @@ class GithubCacheServiceV1Client(GithubCacheServiceV1BaseClient):
|
|
6267
6335
|
},
|
6268
6336
|
)
|
6269
6337
|
|
6270
|
-
_, buf_ = await self.
|
6338
|
+
_, buf_ = await self._send_urllib_request(req)
|
6271
6339
|
|
6272
6340
|
buf = check.not_none(buf_)
|
6273
6341
|
check.equal(len(buf), chunk.size)
|
@@ -6310,7 +6378,7 @@ class GithubCacheServiceV1Client(GithubCacheServiceV1BaseClient):
|
|
6310
6378
|
#
|
6311
6379
|
# status_code = check.isinstance(curl_res['response_code'], int)
|
6312
6380
|
#
|
6313
|
-
# if not (200 <= status_code
|
6381
|
+
# if not (200 <= status_code < 300):
|
6314
6382
|
# raise RuntimeError(f'Curl chunk download {chunk} failed: {curl_res}')
|
6315
6383
|
|
6316
6384
|
async def _download_file_chunk(self, chunk: _DownloadChunk) -> None:
|
@@ -6322,11 +6390,17 @@ class GithubCacheServiceV1Client(GithubCacheServiceV1BaseClient):
|
|
6322
6390
|
):
|
6323
6391
|
await self._download_file_chunk_urllib(chunk)
|
6324
6392
|
|
6325
|
-
async def
|
6326
|
-
|
6327
|
-
|
6393
|
+
async def _download_file_chunks(
|
6394
|
+
self,
|
6395
|
+
*,
|
6396
|
+
key: str,
|
6397
|
+
url: str,
|
6398
|
+
out_file: str,
|
6399
|
+
) -> None:
|
6400
|
+
check.non_empty_str(key)
|
6401
|
+
check.non_empty_str(url)
|
6328
6402
|
|
6329
|
-
head_resp, _ = await self.
|
6403
|
+
head_resp, _ = await self._send_urllib_request(urllib.request.Request( # noqa
|
6330
6404
|
url,
|
6331
6405
|
method='HEAD',
|
6332
6406
|
))
|
@@ -6355,74 +6429,68 @@ class GithubCacheServiceV1Client(GithubCacheServiceV1BaseClient):
|
|
6355
6429
|
|
6356
6430
|
await asyncio_wait_concurrent(download_tasks, self._concurrency)
|
6357
6431
|
|
6358
|
-
|
6359
|
-
|
6360
|
-
with log_timing_context(
|
6361
|
-
'Downloading github cache '
|
6362
|
-
f'key {entry1.artifact.cache_key} '
|
6363
|
-
f'version {entry1.artifact.cache_version} '
|
6364
|
-
f'to {out_file}',
|
6365
|
-
):
|
6366
|
-
await self._download_file(entry1, out_file)
|
6432
|
+
##
|
6433
|
+
# uploading
|
6367
6434
|
|
6368
|
-
|
6435
|
+
@dc.dataclass(frozen=True)
|
6436
|
+
class _UploadChunk:
|
6437
|
+
url: str
|
6438
|
+
key: str
|
6439
|
+
in_file: str
|
6440
|
+
offset: int
|
6441
|
+
size: int
|
6369
6442
|
|
6370
|
-
|
6371
|
-
|
6372
|
-
key: str,
|
6373
|
-
cache_id: int,
|
6374
|
-
in_file: str,
|
6375
|
-
offset: int,
|
6376
|
-
size: int,
|
6377
|
-
) -> None:
|
6378
|
-
with log_timing_context(
|
6379
|
-
f'Uploading github cache {key} '
|
6380
|
-
f'file {in_file} '
|
6381
|
-
f'chunk {offset} - {offset + size}',
|
6382
|
-
):
|
6383
|
-
with open(in_file, 'rb') as f: # noqa
|
6384
|
-
f.seek(offset)
|
6385
|
-
buf = f.read(size)
|
6443
|
+
UPLOAD_CHUNK_NUM_RETRIES = 10
|
6444
|
+
UPLOAD_CHUNK_RETRY_SLEEP = .5
|
6386
6445
|
|
6387
|
-
|
6446
|
+
async def _upload_file_chunk_(self, chunk: _UploadChunk) -> None:
|
6447
|
+
with open(chunk.in_file, 'rb') as f: # noqa
|
6448
|
+
f.seek(chunk.offset)
|
6449
|
+
buf = f.read(chunk.size)
|
6388
6450
|
|
6389
|
-
|
6390
|
-
f'caches/{cache_id}',
|
6391
|
-
method='PATCH',
|
6392
|
-
content_type='application/octet-stream',
|
6393
|
-
headers={
|
6394
|
-
'Content-Range': f'bytes {offset}-{offset + size - 1}/*',
|
6395
|
-
},
|
6396
|
-
content=buf,
|
6397
|
-
success_status_codes=[204],
|
6398
|
-
)
|
6451
|
+
check.equal(len(buf), chunk.size)
|
6399
6452
|
|
6400
|
-
|
6401
|
-
|
6453
|
+
await self._send_request(
|
6454
|
+
url=chunk.url,
|
6402
6455
|
|
6403
|
-
|
6456
|
+
method='PATCH',
|
6404
6457
|
|
6405
|
-
|
6458
|
+
headers={
|
6459
|
+
'Content-Range': f'bytes {chunk.offset}-{chunk.offset + chunk.size - 1}/*',
|
6460
|
+
},
|
6461
|
+
no_auth=True,
|
6462
|
+
content_type='application/octet-stream',
|
6406
6463
|
|
6407
|
-
|
6464
|
+
content=buf,
|
6408
6465
|
|
6409
|
-
|
6410
|
-
|
6411
|
-
|
6412
|
-
|
6413
|
-
|
6414
|
-
reserve_resp_obj = await self.send_service_request(
|
6415
|
-
'caches',
|
6416
|
-
json_content=GithubCacheServiceV1.dataclass_to_json(reserve_req),
|
6417
|
-
success_status_codes=[201],
|
6418
|
-
)
|
6419
|
-
reserve_resp = GithubCacheServiceV1.dataclass_from_json( # noqa
|
6420
|
-
GithubCacheServiceV1.ReserveCacheResponse,
|
6421
|
-
reserve_resp_obj,
|
6466
|
+
success_status_codes=[204],
|
6467
|
+
|
6468
|
+
# retry_status_codes=[405],
|
6469
|
+
num_retries=self.UPLOAD_CHUNK_NUM_RETRIES,
|
6470
|
+
retry_sleep=self.UPLOAD_CHUNK_RETRY_SLEEP,
|
6422
6471
|
)
|
6423
|
-
cache_id = check.isinstance(reserve_resp.cache_id, int)
|
6424
6472
|
|
6425
|
-
|
6473
|
+
async def _upload_file_chunk(self, chunk: _UploadChunk) -> None:
|
6474
|
+
with log_timing_context(
|
6475
|
+
f'Uploading github cache {chunk.key} '
|
6476
|
+
f'file {chunk.in_file} '
|
6477
|
+
f'chunk {chunk.offset} - {chunk.offset + chunk.size}',
|
6478
|
+
):
|
6479
|
+
await self._upload_file_chunk_(chunk)
|
6480
|
+
|
6481
|
+
async def _upload_file_chunks(
|
6482
|
+
self,
|
6483
|
+
*,
|
6484
|
+
in_file: str,
|
6485
|
+
url: str,
|
6486
|
+
key: str,
|
6487
|
+
|
6488
|
+
file_size: ta.Optional[int] = None,
|
6489
|
+
) -> None:
|
6490
|
+
check.state(os.path.isfile(in_file))
|
6491
|
+
|
6492
|
+
if file_size is None:
|
6493
|
+
file_size = os.stat(in_file).st_size
|
6426
6494
|
|
6427
6495
|
#
|
6428
6496
|
|
@@ -6431,34 +6499,16 @@ class GithubCacheServiceV1Client(GithubCacheServiceV1BaseClient):
|
|
6431
6499
|
for i in range((file_size // chunk_size) + (1 if file_size % chunk_size else 0)):
|
6432
6500
|
offset = i * chunk_size
|
6433
6501
|
size = min(chunk_size, file_size - offset)
|
6434
|
-
upload_tasks.append(self._upload_file_chunk(
|
6435
|
-
|
6436
|
-
|
6437
|
-
in_file,
|
6438
|
-
offset,
|
6439
|
-
size,
|
6440
|
-
))
|
6502
|
+
upload_tasks.append(self._upload_file_chunk(self._UploadChunk(
|
6503
|
+
url=url,
|
6504
|
+
key=key,
|
6505
|
+
in_file=in_file,
|
6506
|
+
offset=offset,
|
6507
|
+
size=size,
|
6508
|
+
)))
|
6441
6509
|
|
6442
6510
|
await asyncio_wait_concurrent(upload_tasks, self._concurrency)
|
6443
6511
|
|
6444
|
-
#
|
6445
|
-
|
6446
|
-
commit_req = GithubCacheServiceV1.CommitCacheRequest(
|
6447
|
-
size=file_size,
|
6448
|
-
)
|
6449
|
-
await self.send_service_request(
|
6450
|
-
f'caches/{cache_id}',
|
6451
|
-
json_content=GithubCacheServiceV1.dataclass_to_json(commit_req),
|
6452
|
-
success_status_codes=[204],
|
6453
|
-
)
|
6454
|
-
|
6455
|
-
async def upload_file(self, key: str, in_file: str) -> None:
|
6456
|
-
with log_timing_context(
|
6457
|
-
f'Uploading github cache file {os.path.basename(in_file)} '
|
6458
|
-
f'key {key}',
|
6459
|
-
):
|
6460
|
-
await self._upload_file(key, in_file)
|
6461
|
-
|
6462
6512
|
|
6463
6513
|
########################################
|
6464
6514
|
# ../../dataserver/targets.py
|
@@ -7708,119 +7758,318 @@ def subprocess_maybe_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
|
|
7708
7758
|
|
7709
7759
|
|
7710
7760
|
########################################
|
7711
|
-
# ../github/
|
7761
|
+
# ../github/api/v1/client.py
|
7712
7762
|
|
7713
7763
|
|
7714
7764
|
##
|
7715
7765
|
|
7716
7766
|
|
7717
|
-
class
|
7718
|
-
|
7719
|
-
class Config:
|
7720
|
-
pass
|
7767
|
+
class GithubCacheServiceV1Client(BaseGithubCacheClient):
|
7768
|
+
BASE_URL_ENV_VAR = register_github_env_var('ACTIONS_CACHE_URL')
|
7721
7769
|
|
7722
7770
|
def __init__(
|
7723
7771
|
self,
|
7724
|
-
config: Config = Config(),
|
7725
7772
|
*,
|
7726
|
-
|
7727
|
-
version: ta.Optional[CacheVersion] = None,
|
7773
|
+
base_url: ta.Optional[str] = None,
|
7728
7774
|
|
7729
|
-
|
7775
|
+
**kwargs: ta.Any,
|
7730
7776
|
) -> None:
|
7777
|
+
if base_url is None:
|
7778
|
+
base_url = check.non_empty_str(self.BASE_URL_ENV_VAR())
|
7779
|
+
service_url = GithubCacheServiceV1.get_service_url(base_url)
|
7780
|
+
|
7731
7781
|
super().__init__(
|
7732
|
-
|
7782
|
+
service_url=service_url,
|
7783
|
+
**kwargs,
|
7733
7784
|
)
|
7734
7785
|
|
7735
|
-
|
7786
|
+
#
|
7736
7787
|
|
7737
|
-
|
7738
|
-
|
7739
|
-
|
7740
|
-
|
7741
|
-
|
7788
|
+
def _build_request_headers(
|
7789
|
+
self,
|
7790
|
+
headers: ta.Optional[ta.Mapping[str, str]] = None,
|
7791
|
+
**kwargs: ta.Any,
|
7792
|
+
) -> ta.Dict[str, str]:
|
7793
|
+
return super()._build_request_headers(
|
7794
|
+
{
|
7795
|
+
'Accept': ';'.join([
|
7796
|
+
'application/json',
|
7797
|
+
f'api-version={GithubCacheServiceV1.API_VERSION}',
|
7798
|
+
]),
|
7799
|
+
**(headers or {}),
|
7800
|
+
},
|
7801
|
+
**kwargs,
|
7802
|
+
)
|
7742
7803
|
|
7743
|
-
|
7804
|
+
#
|
7805
|
+
|
7806
|
+
@dc.dataclass(frozen=True)
|
7807
|
+
class Entry(GithubCacheClient.Entry):
|
7808
|
+
artifact: GithubCacheServiceV1.ArtifactCacheEntry
|
7809
|
+
|
7810
|
+
def get_entry_url(self, entry: GithubCacheClient.Entry) -> ta.Optional[str]:
|
7811
|
+
entry1 = check.isinstance(entry, self.Entry)
|
7812
|
+
return entry1.artifact.archive_location
|
7744
7813
|
|
7745
7814
|
#
|
7746
7815
|
|
7747
|
-
|
7748
|
-
|
7749
|
-
|
7750
|
-
|
7816
|
+
def _build_get_entry_url_path(self, *keys: str) -> str:
|
7817
|
+
qp = dict(
|
7818
|
+
keys=','.join(urllib.parse.quote_plus(k) for k in keys),
|
7819
|
+
version=str(self._cache_version),
|
7820
|
+
)
|
7751
7821
|
|
7752
|
-
|
7822
|
+
return '?'.join([
|
7823
|
+
'cache',
|
7824
|
+
'&'.join([
|
7825
|
+
f'{k}={v}'
|
7826
|
+
for k, v in qp.items()
|
7827
|
+
]),
|
7828
|
+
])
|
7829
|
+
|
7830
|
+
GET_ENTRY_SUCCESS_STATUS_CODES = (200, 204)
|
7831
|
+
|
7832
|
+
#
|
7833
|
+
|
7834
|
+
async def get_entry(self, key: str) -> ta.Optional[GithubCacheClient.Entry]:
|
7835
|
+
obj = await self._send_request(
|
7836
|
+
path=self._build_get_entry_url_path(self.fix_key(key, partial_suffix=True)),
|
7837
|
+
)
|
7838
|
+
if obj is None:
|
7753
7839
|
return None
|
7754
7840
|
|
7755
|
-
|
7756
|
-
|
7757
|
-
|
7841
|
+
return self.Entry(GithubCacheServiceV1.dataclass_from_json(
|
7842
|
+
GithubCacheServiceV1.ArtifactCacheEntry,
|
7843
|
+
obj,
|
7844
|
+
))
|
7758
7845
|
|
7759
|
-
|
7846
|
+
#
|
7760
7847
|
|
7761
|
-
|
7848
|
+
async def download_file(self, entry: GithubCacheClient.Entry, out_file: str) -> None:
|
7849
|
+
entry1 = check.isinstance(entry, self.Entry)
|
7850
|
+
with log_timing_context(
|
7851
|
+
'Downloading github cache '
|
7852
|
+
f'key {entry1.artifact.cache_key} '
|
7853
|
+
f'version {entry1.artifact.cache_version} '
|
7854
|
+
f'to {out_file}',
|
7855
|
+
):
|
7856
|
+
await self._download_file_chunks(
|
7857
|
+
key=check.non_empty_str(entry1.artifact.cache_key),
|
7858
|
+
url=check.non_empty_str(entry1.artifact.archive_location),
|
7859
|
+
out_file=out_file,
|
7860
|
+
)
|
7762
7861
|
|
7763
|
-
|
7862
|
+
#
|
7863
|
+
|
7864
|
+
async def _upload_file(self, key: str, in_file: str) -> None:
|
7865
|
+
fixed_key = self.fix_key(key)
|
7866
|
+
|
7867
|
+
check.state(os.path.isfile(in_file))
|
7868
|
+
file_size = os.stat(in_file).st_size
|
7869
|
+
|
7870
|
+
#
|
7871
|
+
|
7872
|
+
reserve_req = GithubCacheServiceV1.ReserveCacheRequest(
|
7873
|
+
key=fixed_key,
|
7874
|
+
cache_size=file_size,
|
7875
|
+
version=str(self._cache_version),
|
7876
|
+
)
|
7877
|
+
reserve_resp_obj = await self._send_request(
|
7878
|
+
path='caches',
|
7879
|
+
json_content=GithubCacheServiceV1.dataclass_to_json(reserve_req),
|
7880
|
+
success_status_codes=[201],
|
7881
|
+
)
|
7882
|
+
reserve_resp = GithubCacheServiceV1.dataclass_from_json( # noqa
|
7883
|
+
GithubCacheServiceV1.ReserveCacheResponse,
|
7884
|
+
reserve_resp_obj,
|
7885
|
+
)
|
7886
|
+
cache_id = check.isinstance(reserve_resp.cache_id, int)
|
7887
|
+
|
7888
|
+
log.debug(f'Github cache file {os.path.basename(in_file)} got id {cache_id}') # noqa
|
7889
|
+
|
7890
|
+
#
|
7891
|
+
|
7892
|
+
url = f'{self._service_url}/caches/{cache_id}'
|
7893
|
+
|
7894
|
+
await self._upload_file_chunks(
|
7895
|
+
in_file=in_file,
|
7896
|
+
url=url,
|
7897
|
+
key=fixed_key,
|
7898
|
+
file_size=file_size,
|
7899
|
+
)
|
7900
|
+
|
7901
|
+
#
|
7902
|
+
|
7903
|
+
commit_req = GithubCacheServiceV1.CommitCacheRequest(
|
7904
|
+
size=file_size,
|
7905
|
+
)
|
7906
|
+
await self._send_request(
|
7907
|
+
path=f'caches/{cache_id}',
|
7908
|
+
json_content=GithubCacheServiceV1.dataclass_to_json(commit_req),
|
7909
|
+
success_status_codes=[204],
|
7910
|
+
)
|
7911
|
+
|
7912
|
+
async def upload_file(self, key: str, in_file: str) -> None:
|
7913
|
+
with log_timing_context(
|
7914
|
+
f'Uploading github cache file {os.path.basename(in_file)} '
|
7915
|
+
f'key {key}',
|
7916
|
+
):
|
7917
|
+
await self._upload_file(key, in_file)
|
7918
|
+
|
7919
|
+
|
7920
|
+
########################################
|
7921
|
+
# ../github/api/v2/client.py
|
7922
|
+
|
7923
|
+
|
7924
|
+
##
|
7925
|
+
|
7926
|
+
|
7927
|
+
class GithubCacheServiceV2Client(BaseGithubCacheClient):
|
7928
|
+
BASE_URL_ENV_VAR = register_github_env_var('ACTIONS_RESULTS_URL')
|
7929
|
+
|
7930
|
+
def __init__(
|
7764
7931
|
self,
|
7765
|
-
key: str,
|
7766
|
-
file_path: str,
|
7767
7932
|
*,
|
7768
|
-
|
7769
|
-
|
7770
|
-
|
7771
|
-
|
7772
|
-
|
7773
|
-
|
7933
|
+
base_url: ta.Optional[str] = None,
|
7934
|
+
|
7935
|
+
**kwargs: ta.Any,
|
7936
|
+
) -> None:
|
7937
|
+
if base_url is None:
|
7938
|
+
base_url = check.non_empty_str(self.BASE_URL_ENV_VAR())
|
7939
|
+
service_url = GithubCacheServiceV2.get_service_url(base_url)
|
7940
|
+
|
7941
|
+
super().__init__(
|
7942
|
+
service_url=service_url,
|
7943
|
+
**kwargs,
|
7774
7944
|
)
|
7775
7945
|
|
7776
|
-
|
7946
|
+
#
|
7777
7947
|
|
7778
|
-
|
7948
|
+
async def _send_method_request(
|
7949
|
+
self,
|
7950
|
+
method: GithubCacheServiceV2.Method[
|
7951
|
+
GithubCacheServiceV2RequestT,
|
7952
|
+
GithubCacheServiceV2ResponseT,
|
7953
|
+
],
|
7954
|
+
request: GithubCacheServiceV2RequestT,
|
7955
|
+
**kwargs: ta.Any,
|
7956
|
+
) -> ta.Optional[GithubCacheServiceV2ResponseT]:
|
7957
|
+
obj = await self._send_request(
|
7958
|
+
path=method.name,
|
7959
|
+
json_content=dc.asdict(request), # type: ignore[call-overload]
|
7960
|
+
**kwargs,
|
7961
|
+
)
|
7962
|
+
|
7963
|
+
if obj is None:
|
7964
|
+
return None
|
7965
|
+
return method.response(**obj)
|
7779
7966
|
|
7780
7967
|
#
|
7781
7968
|
|
7782
|
-
|
7783
|
-
|
7784
|
-
|
7785
|
-
|
7969
|
+
@dc.dataclass(frozen=True)
|
7970
|
+
class Entry(GithubCacheClient.Entry):
|
7971
|
+
request: GithubCacheServiceV2.GetCacheEntryDownloadUrlRequest
|
7972
|
+
response: GithubCacheServiceV2.GetCacheEntryDownloadUrlResponse
|
7786
7973
|
|
7787
|
-
|
7974
|
+
def __post_init__(self) -> None:
|
7975
|
+
check.state(self.response.ok)
|
7976
|
+
check.non_empty_str(self.response.signed_download_url)
|
7977
|
+
|
7978
|
+
def get_entry_url(self, entry: GithubCacheClient.Entry) -> ta.Optional[str]:
|
7979
|
+
entry2 = check.isinstance(entry, self.Entry)
|
7980
|
+
return check.non_empty_str(entry2.response.signed_download_url)
|
7981
|
+
|
7982
|
+
#
|
7983
|
+
|
7984
|
+
async def get_entry(self, key: str) -> ta.Optional[GithubCacheClient.Entry]:
|
7985
|
+
version = str(self._cache_version).zfill(GithubCacheServiceV2.VERSION_LENGTH)
|
7986
|
+
|
7987
|
+
req = GithubCacheServiceV2.GetCacheEntryDownloadUrlRequest(
|
7988
|
+
key=self.fix_key(key),
|
7989
|
+
restore_keys=[self.fix_key(key, partial_suffix=True)],
|
7990
|
+
version=version,
|
7991
|
+
)
|
7992
|
+
|
7993
|
+
resp = await self._send_method_request(
|
7994
|
+
GithubCacheServiceV2.GET_CACHE_ENTRY_DOWNLOAD_URL_METHOD,
|
7995
|
+
req,
|
7996
|
+
)
|
7997
|
+
if resp is None or not resp.ok:
|
7788
7998
|
return None
|
7789
7999
|
|
7790
|
-
return
|
8000
|
+
return self.Entry(
|
8001
|
+
request=req,
|
8002
|
+
response=resp,
|
8003
|
+
)
|
7791
8004
|
|
7792
|
-
|
7793
|
-
await FileCacheDataCache(self).put_data(key, data)
|
8005
|
+
#
|
7794
8006
|
|
8007
|
+
async def download_file(self, entry: GithubCacheClient.Entry, out_file: str) -> None:
|
8008
|
+
entry2 = check.isinstance(entry, self.Entry)
|
8009
|
+
with log_timing_context(
|
8010
|
+
'Downloading github cache '
|
8011
|
+
f'key {entry2.response.matched_key} '
|
8012
|
+
f'version {entry2.request.version} '
|
8013
|
+
f'to {out_file}',
|
8014
|
+
):
|
8015
|
+
await self._download_file_chunks(
|
8016
|
+
key=check.non_empty_str(entry2.response.matched_key),
|
8017
|
+
url=check.non_empty_str(entry2.response.signed_download_url),
|
8018
|
+
out_file=out_file,
|
8019
|
+
)
|
7795
8020
|
|
7796
|
-
|
7797
|
-
# ../github/cli.py
|
7798
|
-
"""
|
7799
|
-
See:
|
7800
|
-
- https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28
|
7801
|
-
"""
|
8021
|
+
#
|
7802
8022
|
|
8023
|
+
async def _upload_file(self, key: str, in_file: str) -> None:
|
8024
|
+
fixed_key = self.fix_key(key)
|
7803
8025
|
|
7804
|
-
|
7805
|
-
|
7806
|
-
def list_referenced_env_vars(self) -> None:
|
7807
|
-
print('\n'.join(sorted(ev.k for ev in GITHUB_ENV_VARS)))
|
8026
|
+
check.state(os.path.isfile(in_file))
|
8027
|
+
file_size = os.stat(in_file).st_size
|
7808
8028
|
|
7809
|
-
|
7810
|
-
argparse_arg('key'),
|
7811
|
-
)
|
7812
|
-
async def get_cache_entry(self) -> None:
|
7813
|
-
client = GithubCacheServiceV1Client()
|
7814
|
-
entry = await client.get_entry(self.args.key)
|
7815
|
-
if entry is None:
|
7816
|
-
return
|
7817
|
-
print(json_dumps_pretty(dc.asdict(entry))) # noqa
|
8029
|
+
#
|
7818
8030
|
|
7819
|
-
|
7820
|
-
|
7821
|
-
|
7822
|
-
|
7823
|
-
|
8031
|
+
version = str(self._cache_version).zfill(GithubCacheServiceV2.VERSION_LENGTH)
|
8032
|
+
|
8033
|
+
reserve_resp = check.not_none(await self._send_method_request(
|
8034
|
+
GithubCacheServiceV2.CREATE_CACHE_ENTRY_METHOD, # type: ignore[arg-type]
|
8035
|
+
GithubCacheServiceV2.CreateCacheEntryRequest(
|
8036
|
+
key=fixed_key,
|
8037
|
+
version=version,
|
8038
|
+
),
|
8039
|
+
))
|
8040
|
+
check.state(reserve_resp.ok)
|
8041
|
+
|
8042
|
+
log.debug(f'Github cache file {os.path.basename(in_file)} upload reserved for file size {file_size}') # noqa
|
8043
|
+
|
8044
|
+
#
|
8045
|
+
|
8046
|
+
await self._upload_file_chunks(
|
8047
|
+
in_file=in_file,
|
8048
|
+
url=reserve_resp.signed_upload_url,
|
8049
|
+
key=fixed_key,
|
8050
|
+
file_size=file_size,
|
8051
|
+
)
|
8052
|
+
|
8053
|
+
#
|
8054
|
+
|
8055
|
+
commit_resp = check.not_none(await self._send_method_request(
|
8056
|
+
GithubCacheServiceV2.FINALIZE_CACHE_ENTRY_METHOD, # type: ignore[arg-type]
|
8057
|
+
GithubCacheServiceV2.FinalizeCacheEntryUploadRequest(
|
8058
|
+
key=key,
|
8059
|
+
size_bytes=file_size,
|
8060
|
+
version=version,
|
8061
|
+
),
|
8062
|
+
))
|
8063
|
+
check.state(commit_resp.ok)
|
8064
|
+
|
8065
|
+
log.debug(f'Github cache file {os.path.basename(in_file)} upload complete, entry id {commit_resp.entry_id}') # noqa
|
8066
|
+
|
8067
|
+
async def upload_file(self, key: str, in_file: str) -> None:
|
8068
|
+
with log_timing_context(
|
8069
|
+
f'Uploading github cache file {os.path.basename(in_file)} '
|
8070
|
+
f'key {key}',
|
8071
|
+
):
|
8072
|
+
await self._upload_file(key, in_file)
|
7824
8073
|
|
7825
8074
|
|
7826
8075
|
########################################
|
@@ -9619,20 +9868,129 @@ async def build_cache_served_docker_image_data_server_routes(
|
|
9619
9868
|
|
9620
9869
|
|
9621
9870
|
########################################
|
9622
|
-
# ../github/
|
9871
|
+
# ../github/cache.py
|
9623
9872
|
|
9624
9873
|
|
9625
9874
|
##
|
9626
9875
|
|
9627
9876
|
|
9628
|
-
|
9629
|
-
|
9630
|
-
|
9631
|
-
|
9632
|
-
inj.bind(FileCache, to_key=GithubCache),
|
9633
|
-
]
|
9877
|
+
class GithubCache(FileCache, DataCache):
|
9878
|
+
@dc.dataclass(frozen=True)
|
9879
|
+
class Config:
|
9880
|
+
pass
|
9634
9881
|
|
9635
|
-
|
9882
|
+
DEFAULT_CLIENT_VERSION: ta.ClassVar[int] = 2
|
9883
|
+
|
9884
|
+
DEFAULT_CLIENTS_BY_VERSION: ta.ClassVar[ta.Mapping[int, ta.Callable[..., GithubCacheClient]]] = {
|
9885
|
+
1: GithubCacheServiceV1Client,
|
9886
|
+
2: GithubCacheServiceV2Client,
|
9887
|
+
}
|
9888
|
+
|
9889
|
+
def __init__(
|
9890
|
+
self,
|
9891
|
+
config: Config = Config(),
|
9892
|
+
*,
|
9893
|
+
client: ta.Optional[GithubCacheClient] = None,
|
9894
|
+
default_client_version: ta.Optional[int] = None,
|
9895
|
+
|
9896
|
+
version: ta.Optional[CacheVersion] = None,
|
9897
|
+
|
9898
|
+
local: DirectoryFileCache,
|
9899
|
+
) -> None:
|
9900
|
+
super().__init__(
|
9901
|
+
version=version,
|
9902
|
+
)
|
9903
|
+
|
9904
|
+
self._config = config
|
9905
|
+
|
9906
|
+
if client is None:
|
9907
|
+
client_cls = self.DEFAULT_CLIENTS_BY_VERSION[default_client_version or self.DEFAULT_CLIENT_VERSION]
|
9908
|
+
client = client_cls(
|
9909
|
+
cache_version=self._version,
|
9910
|
+
)
|
9911
|
+
self._client: GithubCacheClient = client
|
9912
|
+
|
9913
|
+
self._local = local
|
9914
|
+
|
9915
|
+
#
|
9916
|
+
|
9917
|
+
async def get_file(self, key: str) -> ta.Optional[str]:
|
9918
|
+
local_file = self._local.get_cache_file_path(key)
|
9919
|
+
if os.path.exists(local_file):
|
9920
|
+
return local_file
|
9921
|
+
|
9922
|
+
if (entry := await self._client.get_entry(key)) is None:
|
9923
|
+
return None
|
9924
|
+
|
9925
|
+
tmp_file = self._local.format_incomplete_file(local_file)
|
9926
|
+
with unlinking_if_exists(tmp_file):
|
9927
|
+
await self._client.download_file(entry, tmp_file)
|
9928
|
+
|
9929
|
+
os.replace(tmp_file, local_file)
|
9930
|
+
|
9931
|
+
return local_file
|
9932
|
+
|
9933
|
+
async def put_file(
|
9934
|
+
self,
|
9935
|
+
key: str,
|
9936
|
+
file_path: str,
|
9937
|
+
*,
|
9938
|
+
steal: bool = False,
|
9939
|
+
) -> str:
|
9940
|
+
cache_file_path = await self._local.put_file(
|
9941
|
+
key,
|
9942
|
+
file_path,
|
9943
|
+
steal=steal,
|
9944
|
+
)
|
9945
|
+
|
9946
|
+
await self._client.upload_file(key, cache_file_path)
|
9947
|
+
|
9948
|
+
return cache_file_path
|
9949
|
+
|
9950
|
+
#
|
9951
|
+
|
9952
|
+
async def get_data(self, key: str) -> ta.Optional[DataCache.Data]:
|
9953
|
+
local_file = self._local.get_cache_file_path(key)
|
9954
|
+
if os.path.exists(local_file):
|
9955
|
+
return DataCache.FileData(local_file)
|
9956
|
+
|
9957
|
+
if (entry := await self._client.get_entry(key)) is None:
|
9958
|
+
return None
|
9959
|
+
|
9960
|
+
return DataCache.UrlData(check.non_empty_str(self._client.get_entry_url(entry)))
|
9961
|
+
|
9962
|
+
async def put_data(self, key: str, data: DataCache.Data) -> None:
|
9963
|
+
await FileCacheDataCache(self).put_data(key, data)
|
9964
|
+
|
9965
|
+
|
9966
|
+
########################################
|
9967
|
+
# ../github/cli.py
|
9968
|
+
"""
|
9969
|
+
See:
|
9970
|
+
- https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28
|
9971
|
+
"""
|
9972
|
+
|
9973
|
+
|
9974
|
+
class GithubCli(ArgparseCli):
|
9975
|
+
@argparse_cmd()
|
9976
|
+
def list_referenced_env_vars(self) -> None:
|
9977
|
+
print('\n'.join(sorted(ev.k for ev in GITHUB_ENV_VARS)))
|
9978
|
+
|
9979
|
+
@argparse_cmd(
|
9980
|
+
argparse_arg('key'),
|
9981
|
+
)
|
9982
|
+
async def get_cache_entry(self) -> None:
|
9983
|
+
client = GithubCacheServiceV1Client()
|
9984
|
+
entry = await client.get_entry(self.args.key)
|
9985
|
+
if entry is None:
|
9986
|
+
return
|
9987
|
+
print(json_dumps_pretty(dc.asdict(entry))) # noqa
|
9988
|
+
|
9989
|
+
@argparse_cmd(
|
9990
|
+
argparse_arg('repository-id'),
|
9991
|
+
)
|
9992
|
+
def list_cache_entries(self) -> None:
|
9993
|
+
raise NotImplementedError
|
9636
9994
|
|
9637
9995
|
|
9638
9996
|
########################################
|
@@ -10325,6 +10683,23 @@ subprocesses = Subprocesses()
|
|
10325
10683
|
SubprocessRun._DEFAULT_SUBPROCESSES = subprocesses # noqa
|
10326
10684
|
|
10327
10685
|
|
10686
|
+
########################################
|
10687
|
+
# ../github/inject.py
|
10688
|
+
|
10689
|
+
|
10690
|
+
##
|
10691
|
+
|
10692
|
+
|
10693
|
+
def bind_github() -> InjectorBindings:
|
10694
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10695
|
+
inj.bind(GithubCache, singleton=True),
|
10696
|
+
inj.bind(DataCache, to_key=GithubCache),
|
10697
|
+
inj.bind(FileCache, to_key=GithubCache),
|
10698
|
+
]
|
10699
|
+
|
10700
|
+
return inj.as_bindings(*lst)
|
10701
|
+
|
10702
|
+
|
10328
10703
|
########################################
|
10329
10704
|
# ../requirements.py
|
10330
10705
|
"""
|