lean-runtime 2.0.6__tar.gz → 2.0.7__tar.gz
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.
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/CHANGELOG.md +6 -0
- {lean_runtime-2.0.6/lean_runtime.egg-info → lean_runtime-2.0.7}/PKG-INFO +1 -1
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/oci.py +66 -44
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/run_cli.py +1 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7/lean_runtime.egg-info}/PKG-INFO +1 -1
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/pyproject.toml +1 -1
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_bundles.py +52 -2
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/LICENSE +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/MANIFEST.in +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/README.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/catalog/environments.toml +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/catalog/locks/core-v4.32.2.lock.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/catalog/locks/mathlib-v4.30.0.lock.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/catalog/locks/mathlib-v4.31.0.lock.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/catalog/locks/mathlib-v4.32.2.lock.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/compatibility/README.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/compatibility/mathlib-4.32.2.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/compatibility/mathlib-4.32.2.toml +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/architecture.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/captures.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/case-study-v1.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/cli.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/development.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/environments.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/getting-started.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/index.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/local-projects.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/portable-copies.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/python-api.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/ready-programs.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/standalone-files.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/trust-and-limitations.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/docs/v1-precision.md +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/examples/mathlib.toml +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/__init__.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/__main__.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/_git.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/_paths.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/backends.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/bundles.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/cli.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/comparison.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/decisions.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/diagnostics.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/__init__.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/analyzer.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/api.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/candidate.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/catalog.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/catalog_build.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/catalog_cli.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/catalog_manifest.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/data/catalog.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/defaults.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/engine.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/errors.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/module_inventory.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/planner.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/policy.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/probe.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/py.typed +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/result.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schema_resources.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schemas/catalog-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schemas/plan-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schemas/result-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/environments.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/errors.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/events.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/facade.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/frontmatter.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/health.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/lake.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/lockfiles.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/locking.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/matrix.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/models.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/policies.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/profiling.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/programs.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/projects.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/publisher_verification.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/py.typed +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/references.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/resolver.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/runtime.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/schema_resources.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/serialization.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/specs.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/store.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/timings.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/toolchains.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/verification.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/wire.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime.egg-info/SOURCES.txt +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime.egg-info/dependency_links.txt +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime.egg-info/entry_points.txt +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime.egg-info/requires.txt +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime.egg-info/top_level.txt +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/mkdocs.yml +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/cleanup-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/comparison-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/execution-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/inspect-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/matrix-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/profile-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/schemas/verify-v1.schema.json +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/scripts/run_compatibility.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/scripts/run_v1_case_study.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/scripts/smoke_wheel.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/setup.cfg +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/conftest.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/e2e/test_real_discovery.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_analyzer.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_catalog.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_catalog_build.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_discovery_schema_resources.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_engine.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_planner.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_policy.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_backend.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_cli.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_diagnostics.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_environment_integration.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_events_health.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_facade.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_frontmatter.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_git.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_interactive.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_lockfiles.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_multifile.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_paths.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_prebuilt_policy.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_programs.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_projects.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_references.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_run_cli.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_runtime.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_schema_resources.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_schemas.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_signatures.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_specs.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_store.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_toolchains.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_v1_precision.py +0 -0
- {lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/test_verification_inventory.py +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2.0.7
|
|
6
|
+
|
|
7
|
+
- Retry an OCI layer once from a clean partial after size or SHA-256 integrity
|
|
8
|
+
verification fails, bypassing intermediary caches while preserving strict
|
|
9
|
+
digest validation and emitting structured retry diagnostics.
|
|
10
|
+
|
|
5
11
|
## 2.0.6
|
|
6
12
|
|
|
7
13
|
- Preserve the highest-ranked Lean compiler rejection when automatic discovery
|
|
@@ -32,6 +32,7 @@ _BEARER_PARAMETER = re.compile(r'([a-zA-Z]+)="([^"]*)"')
|
|
|
32
32
|
_DIGEST = re.compile(r"sha256:([0-9a-f]{64})")
|
|
33
33
|
_ACCEPT = ", ".join((INDEX_MEDIA_TYPE, MANIFEST_MEDIA_TYPE))
|
|
34
34
|
DEFAULT_ENVIRONMENT_LIBRARIES = ("oci://ghcr.io/alerad/lean-runtime-cache",)
|
|
35
|
+
_BLOB_INTEGRITY_ATTEMPTS = 2
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class SignatureVerifier(Protocol):
|
|
@@ -215,52 +216,73 @@ class OCIRegistryClient:
|
|
|
215
216
|
return destination
|
|
216
217
|
destination.unlink()
|
|
217
218
|
temporary = destination.with_name(f".{destination.name}.partial")
|
|
218
|
-
|
|
219
|
-
temporary.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
response = self._request(request)
|
|
238
|
-
if offset and response.status != 206:
|
|
239
|
-
offset = 0
|
|
240
|
-
written = 0
|
|
241
|
-
observed = hashlib.sha256()
|
|
219
|
+
for attempt in range(1, _BLOB_INTEGRITY_ATTEMPTS + 1):
|
|
220
|
+
if temporary.exists() and temporary.stat().st_size > size:
|
|
221
|
+
temporary.unlink()
|
|
222
|
+
offset = temporary.stat().st_size if temporary.exists() else 0
|
|
223
|
+
request = urllib.request.Request(self._url(f"blobs/{digest}"))
|
|
224
|
+
if offset:
|
|
225
|
+
request.add_header("Range", f"bytes={offset}-")
|
|
226
|
+
if attempt > 1:
|
|
227
|
+
request.add_header("Cache-Control", "no-cache")
|
|
228
|
+
events.emit(
|
|
229
|
+
"library.layer_download_started",
|
|
230
|
+
"Downloading OCI blob",
|
|
231
|
+
digest=digest,
|
|
232
|
+
size=size,
|
|
233
|
+
attempt=attempt,
|
|
234
|
+
resumed_bytes=offset,
|
|
235
|
+
)
|
|
236
|
+
observed = hashlib.sha256()
|
|
237
|
+
written = offset
|
|
242
238
|
if offset:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
with temporary.open("rb") as existing:
|
|
240
|
+
for chunk in iter(lambda: existing.read(1024 * 1024), b""):
|
|
241
|
+
observed.update(chunk)
|
|
242
|
+
try:
|
|
243
|
+
response = self._request(request)
|
|
244
|
+
if offset and response.status != 206:
|
|
245
|
+
offset = 0
|
|
246
|
+
written = 0
|
|
247
|
+
observed = hashlib.sha256()
|
|
248
|
+
if offset:
|
|
249
|
+
content_range = response.headers.get("Content-Range", "")
|
|
250
|
+
if not content_range.startswith(f"bytes {offset}-"):
|
|
251
|
+
response.close()
|
|
252
|
+
temporary.unlink(missing_ok=True)
|
|
253
|
+
raise EnvironmentError("OCI registry returned an invalid byte range")
|
|
254
|
+
mode = "ab" if offset else "wb"
|
|
255
|
+
with response, temporary.open(mode) as output:
|
|
256
|
+
while chunk := response.read(1024 * 1024):
|
|
257
|
+
written += len(chunk)
|
|
258
|
+
if written > size:
|
|
259
|
+
raise EnvironmentError("OCI blob exceeds its declared size")
|
|
260
|
+
observed.update(chunk)
|
|
261
|
+
output.write(chunk)
|
|
262
|
+
observed_digest = "sha256:" + observed.hexdigest()
|
|
263
|
+
if written == size and observed_digest == digest:
|
|
264
|
+
temporary.replace(destination)
|
|
265
|
+
break
|
|
257
266
|
temporary.unlink(missing_ok=True)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
267
|
+
if attempt == _BLOB_INTEGRITY_ATTEMPTS:
|
|
268
|
+
raise EnvironmentError(
|
|
269
|
+
"downloaded OCI blob failed digest verification "
|
|
270
|
+
f"after {attempt} attempts (expected {digest}, got "
|
|
271
|
+
f"{observed_digest}, bytes {written}/{size})"
|
|
272
|
+
)
|
|
273
|
+
events.emit(
|
|
274
|
+
"library.layer_download_retry",
|
|
275
|
+
"Retrying OCI blob after integrity verification failed",
|
|
276
|
+
digest=digest,
|
|
277
|
+
attempt=attempt + 1,
|
|
278
|
+
observed_digest=observed_digest,
|
|
279
|
+
downloaded_bytes=written,
|
|
280
|
+
expected_bytes=size,
|
|
281
|
+
)
|
|
282
|
+
except urllib.error.HTTPError as exc:
|
|
283
|
+
raise DownloadUnavailable(f"OCI blob download failed: HTTP {exc.code}") from exc
|
|
284
|
+
except OSError as exc:
|
|
285
|
+
raise DownloadUnavailable(f"OCI blob download failed: {exc}") from exc
|
|
264
286
|
return destination
|
|
265
287
|
|
|
266
288
|
def blob_exists(self, digest: str) -> bool:
|
|
@@ -34,6 +34,7 @@ def _progress(event: RuntimeEvent) -> None:
|
|
|
34
34
|
"package_reference.started": "Resolving dependency",
|
|
35
35
|
"library.lookup": "Looking for a cached environment",
|
|
36
36
|
"library.layer_download_started": "Downloading cached environment",
|
|
37
|
+
"library.layer_download_retry": "Retrying cached-environment download",
|
|
37
38
|
"environment.build_started": "Building environment",
|
|
38
39
|
"environment.cache_hit": "Using local environment",
|
|
39
40
|
}
|
|
@@ -18,8 +18,14 @@ import pytest
|
|
|
18
18
|
from lean_runtime import EnvironmentError, EnvironmentLock, LockedPackage, Runtime
|
|
19
19
|
from lean_runtime.bundles import _extract_layer, _oci_archive
|
|
20
20
|
from lean_runtime.environments import ENVIRONMENT_SCHEMA
|
|
21
|
-
from lean_runtime.
|
|
22
|
-
from lean_runtime.
|
|
21
|
+
from lean_runtime.events import EventEmitter
|
|
22
|
+
from lean_runtime.oci import OCIEnvironmentPublisher, OCIRegistryClient, OCIRepository
|
|
23
|
+
from lean_runtime.store import (
|
|
24
|
+
EnvironmentStore,
|
|
25
|
+
environment_identity,
|
|
26
|
+
platform_record,
|
|
27
|
+
source_snapshot_digest,
|
|
28
|
+
)
|
|
23
29
|
|
|
24
30
|
|
|
25
31
|
def _git_package(path: Path) -> tuple[str, str]:
|
|
@@ -403,6 +409,50 @@ def test_transparent_authenticated_oci_pull_and_blob_reuse(tmp_path: Path) -> No
|
|
|
403
409
|
thread.join(timeout=5)
|
|
404
410
|
|
|
405
411
|
|
|
412
|
+
def test_oci_blob_integrity_failure_retries_once_from_scratch(tmp_path: Path) -> None:
|
|
413
|
+
data = b"verified layer contents"
|
|
414
|
+
digest = "sha256:" + hashlib.sha256(data).hexdigest()
|
|
415
|
+
requests = 0
|
|
416
|
+
cache_controls: list[str | None] = []
|
|
417
|
+
|
|
418
|
+
class Handler(http.server.BaseHTTPRequestHandler):
|
|
419
|
+
def do_GET(self) -> None:
|
|
420
|
+
nonlocal requests
|
|
421
|
+
requests += 1
|
|
422
|
+
cache_controls.append(self.headers.get("Cache-Control"))
|
|
423
|
+
response = b"x" * len(data) if requests == 1 else data
|
|
424
|
+
self.send_response(200)
|
|
425
|
+
self.send_header("Content-Length", str(len(response)))
|
|
426
|
+
self.end_headers()
|
|
427
|
+
self.wfile.write(response)
|
|
428
|
+
|
|
429
|
+
def log_message(self, _format: str, *_args: object) -> None:
|
|
430
|
+
pass
|
|
431
|
+
|
|
432
|
+
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Handler)
|
|
433
|
+
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
434
|
+
thread.start()
|
|
435
|
+
events = []
|
|
436
|
+
try:
|
|
437
|
+
repository = OCIRepository.parse(f"oci+http://127.0.0.1:{server.server_port}/owner/cache")
|
|
438
|
+
path = OCIRegistryClient(repository).download_blob(
|
|
439
|
+
{"digest": digest, "size": len(data)},
|
|
440
|
+
EnvironmentStore(tmp_path / "consumer"),
|
|
441
|
+
EventEmitter(events.append),
|
|
442
|
+
)
|
|
443
|
+
assert path.read_bytes() == data
|
|
444
|
+
assert requests == 2
|
|
445
|
+
assert cache_controls == [None, "no-cache"]
|
|
446
|
+
assert [event.kind for event in events].count("library.layer_download_retry") == 1
|
|
447
|
+
starts = [event for event in events if event.kind == "library.layer_download_started"]
|
|
448
|
+
assert [event.data["attempt"] for event in starts] == [1, 2]
|
|
449
|
+
assert starts[1].data["resumed_bytes"] == 0
|
|
450
|
+
finally:
|
|
451
|
+
server.shutdown()
|
|
452
|
+
server.server_close()
|
|
453
|
+
thread.join(timeout=5)
|
|
454
|
+
|
|
455
|
+
|
|
406
456
|
def test_oci_publisher_uploads_blobs_before_manifests(tmp_path: Path) -> None:
|
|
407
457
|
producer, environment_id, lock = _published_runtime(tmp_path / "producer")
|
|
408
458
|
publisher = OCIEnvironmentPublisher(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schemas/catalog-v1.schema.json
RENAMED
|
File without changes
|
{lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schemas/plan-v1.schema.json
RENAMED
|
File without changes
|
{lean_runtime-2.0.6 → lean_runtime-2.0.7}/lean_runtime/discovery/schemas/result-v1.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lean_runtime-2.0.6 → lean_runtime-2.0.7}/tests/discovery/test_discovery_schema_resources.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|