lean-runtime 2.9.0__tar.gz → 2.9.2__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.9.0 → lean_runtime-2.9.2}/CHANGELOG.md +19 -0
- {lean_runtime-2.9.0/lean_runtime.egg-info → lean_runtime-2.9.2}/PKG-INFO +1 -1
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/cli.md +4 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/environments.md +6 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/portable-copies.md +9 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/cli.py +105 -18
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/console.py +12 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/environments.py +4 -10
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/errors.py +25 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/header_cache.py +6 -1
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/health.py +13 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/models.py +2 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/oci.py +122 -22
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/programs.py +2 -6
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/project_sharing.py +3 -1
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/resolver.py +5 -2
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/runtime.py +38 -11
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/schema_resources.py +1 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/shared_projects.py +5 -2
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/store.py +122 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/toolchains.py +30 -1
- {lean_runtime-2.9.0 → lean_runtime-2.9.2/lean_runtime.egg-info}/PKG-INFO +1 -1
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime.egg-info/SOURCES.txt +1 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/pyproject.toml +1 -1
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/cleanup-v1.schema.json +7 -3
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/execution-v1.schema.json +1 -1
- lean_runtime-2.9.2/schemas/publication-v1.schema.json +72 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_bundles.py +130 -2
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_cli.py +77 -2
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_console.py +12 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_events_health.py +1 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_projects.py +7 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_schema_resources.py +1 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_schemas.py +58 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_store.py +35 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/LICENSE +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/MANIFEST.in +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/README.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/environments.toml +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/core-v4.32.2.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/leancert-v4.30.0.5.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/leancert-v4.31.0.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/leancert-v4.32.2.4.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/leancert-v4.33.0.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/mathlib-v4.30.0.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/mathlib-v4.31.0.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/mathlib-v4.32.2.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/catalog/locks/mathlib-v4.33.0.lock.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/compatibility/README.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/compatibility/lake-artifact-cache-4.33.0.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/compatibility/mathlib-4.32.2.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/compatibility/mathlib-4.32.2.toml +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/compatibility/mathlib-4.33.0.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/compatibility/mathlib-4.33.0.toml +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/architecture.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/captures.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/case-study-v1.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/development.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/getting-started.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/index.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/lake-artifact-cache-design.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/local-projects.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/project-publishing.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/python-api.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/ready-programs.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/standalone-files.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/trust-and-limitations.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/docs/v1-precision.md +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/examples/mathlib.toml +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/__init__.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/__main__.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/_git.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/_paths.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/backends.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/bundles.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/capsules.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/comparison.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/decisions.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/diagnostics.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/__init__.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/analyzer.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/api.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/candidate.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/catalog.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/catalog_build.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/catalog_cli.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/catalog_manifest.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/data/catalog.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/defaults.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/engine.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/errors.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/module_inventory.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/planner.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/policy.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/probe.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/py.typed +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/result.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/schema_resources.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/schemas/catalog-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/schemas/plan-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/discovery/schemas/result-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/events.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/facade.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/frontmatter.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/identifier_resolver.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/lake.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/lake_cache.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/lockfiles.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/locking.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/matrix.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/oci_protocol.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/packs.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/policies.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/profiling.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/project_execution.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/projects.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/publisher_verification.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/py.typed +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/references.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/run_cli.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/serialization.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/specs.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/timings.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/toolchain_oci.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/toolchain_slim.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/verification.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime/wire.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime.egg-info/dependency_links.txt +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime.egg-info/entry_points.txt +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime.egg-info/requires.txt +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/lean_runtime.egg-info/top_level.txt +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/mkdocs.yml +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/comparison-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/inspect-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/matrix-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/profile-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/schemas/verify-v1.schema.json +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/scripts/announcement_gate.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/scripts/lake_cache_gate.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/scripts/registry_preflight.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/scripts/run_compatibility.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/scripts/run_v1_case_study.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/scripts/smoke_wheel.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/setup.cfg +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/conftest.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/e2e/test_real_discovery.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_analyzer.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_catalog.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_catalog_build.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_discovery_schema_resources.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_engine.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_planner.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/discovery/test_policy.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_backend.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_capsules.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_diagnostics.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_environment_integration.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_facade.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_frontmatter.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_git.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_header_cache.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_identifier_resolver.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_interactive.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_lake_cache.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_lockfiles.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_models.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_multifile.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_oci_protocol.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_packs.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_paths.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_prebuilt_policy.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_programs.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_project_publish.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_references.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_run_cli.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_runtime.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_signatures.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_specs.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_toolchain_oci.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_toolchain_slim.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_toolchains.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_v1_precision.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_verification_inventory.py +0 -0
- {lean_runtime-2.9.0 → lean_runtime-2.9.2}/tests/test_verification_probe.py +0 -0
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2.9.2 - 2026-08-16
|
|
6
|
+
|
|
7
|
+
- Keep one fail-closed publication authentication session from push preflight through
|
|
8
|
+
upload, propagate explicit publication timeouts to credential and registry I/O,
|
|
9
|
+
and report credential-provider failures without labeling them anonymous.
|
|
10
|
+
- Use logical project names during atomic initialization, show compact TTY-only check
|
|
11
|
+
progress, cache Lean executable identities across invocations, and report check
|
|
12
|
+
command-preparation timing.
|
|
13
|
+
- Track disposable execution and resolution workspaces with process-held leases;
|
|
14
|
+
include their footprint in storage reporting and reclaim abandoned workspaces with
|
|
15
|
+
`clean` while protecting active work.
|
|
16
|
+
|
|
17
|
+
## 2.9.1 - 2026-08-15
|
|
18
|
+
|
|
19
|
+
- Make GHCR credential discovery a single bounded GitHub CLI status read so a
|
|
20
|
+
transient identity lookup cannot silently downgrade an authenticated account
|
|
21
|
+
to anonymous; add a validated `lean-runtime.publication/v1` envelope behind
|
|
22
|
+
`publish environment --json`.
|
|
23
|
+
|
|
5
24
|
## 2.9.0 - 2026-08-15
|
|
6
25
|
|
|
7
26
|
- Make environment publication fail closed: verify registry push access before
|
|
@@ -78,6 +78,7 @@ lean-runtime copy save research-stack --output research-stack.lean-environment
|
|
|
78
78
|
lean-runtime --home /tmp/fresh copy open research-stack.lean-environment --name research-stack
|
|
79
79
|
lean-runtime publish environment environment.lock.json --publish-to ghcr.io/owner/lean-environments
|
|
80
80
|
lean-runtime publish environment --publish-to ghcr.io/owner/lean-environments --check-access
|
|
81
|
+
lean-runtime publish environment --publish-to ghcr.io/owner/lean-environments --check-access --json
|
|
81
82
|
lean-runtime check research-stack Main.lean --json
|
|
82
83
|
lean-runtime inspect research-stack --packages
|
|
83
84
|
lean-runtime environments
|
|
@@ -214,6 +215,9 @@ lean-runtime check research-stack Main.lean --include Support/Defs.lean
|
|
|
214
215
|
```
|
|
215
216
|
|
|
216
217
|
Resolution and materialization print structured lifecycle progress to stderr.
|
|
218
|
+
One-shot checks show a compact in-place status on interactive terminals while Lean
|
|
219
|
+
is working. Redirected output, `--json`, and `--quiet` remain free of human progress;
|
|
220
|
+
`--timings` separates command preparation from Lean execution.
|
|
217
221
|
Pass global `--quiet` before the subcommand to suppress it.
|
|
218
222
|
|
|
219
223
|
Pass global `--timings` before the subcommand for stable phase timing output. Machine-readable
|
|
@@ -93,6 +93,12 @@ Short-lived execution leases prevent deletion during cloning without
|
|
|
93
93
|
serializing concurrent checks. Locks and source snapshots remain retained in
|
|
94
94
|
the current store schema.
|
|
95
95
|
|
|
96
|
+
Disposable execution and dependency-resolution workspaces carry their own
|
|
97
|
+
process-held ownership leases. `storage` includes them under `Scratch`, and
|
|
98
|
+
`clean` previews and reclaims abandoned workspaces while retaining every
|
|
99
|
+
workspace with a live owner. Legacy scratch directories created before ownership
|
|
100
|
+
metadata are removed only by an explicit `clean --execute` after the safety grace.
|
|
101
|
+
|
|
96
102
|
Sparse downloads are stored once by artifact digest under the shared module
|
|
97
103
|
CAS and hardlinked into environment projections when the filesystem permits.
|
|
98
104
|
`lean-runtime storage` reports this category separately. Because it is shared,
|
|
@@ -121,6 +121,9 @@ lean-runtime publish environment \
|
|
|
121
121
|
--check-access
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
Add `--json` for the versioned `lean-runtime.publication/v1` success or failure
|
|
125
|
+
envelope. The process exit code retains the classification below.
|
|
126
|
+
|
|
124
127
|
For GHCR, credentials are selected in this order: the
|
|
125
128
|
`LEAN_RUNTIME_REGISTRY_USERNAME` / `LEAN_RUNTIME_REGISTRY_PASSWORD` pair, an
|
|
126
129
|
authenticated GitHub CLI session, then anonymous access. The access report names
|
|
@@ -132,6 +135,12 @@ run:
|
|
|
132
135
|
gh auth refresh -s write:packages,read:packages
|
|
133
136
|
```
|
|
134
137
|
|
|
138
|
+
Credential discovery is fail-closed: a configured provider that times out or
|
|
139
|
+
returns an unusable token stops publication before any registry request. The same
|
|
140
|
+
verified credential is retained from preflight through upload; it is not rediscovered
|
|
141
|
+
after a long environment build. An explicitly supplied publication `--timeout`
|
|
142
|
+
also applies to credential discovery and registry network operations.
|
|
143
|
+
|
|
135
144
|
Publish the current platform after ensuring the lock:
|
|
136
145
|
|
|
137
146
|
```bash
|
|
@@ -8,12 +8,13 @@ import os
|
|
|
8
8
|
import re
|
|
9
9
|
import sys
|
|
10
10
|
import time
|
|
11
|
+
from dataclasses import replace
|
|
11
12
|
from datetime import datetime
|
|
12
13
|
from importlib.metadata import version as distribution_version
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Any, cast
|
|
15
16
|
|
|
16
|
-
from .console import styler_for
|
|
17
|
+
from .console import ConsoleRenderer, styler_for
|
|
17
18
|
from .environments import ExecutionCapture
|
|
18
19
|
from .errors import (
|
|
19
20
|
LeanRuntimeError,
|
|
@@ -55,6 +56,7 @@ def _schema_for(command: str) -> str:
|
|
|
55
56
|
"matrix": "lean-runtime.matrix/v1",
|
|
56
57
|
"clean": "lean-runtime.cleanup/v1",
|
|
57
58
|
"inspect": "lean-runtime.inspect/v1",
|
|
59
|
+
"build-and-publish": "lean-runtime.publication/v1",
|
|
58
60
|
}.get(command, "lean-runtime.execution/v1")
|
|
59
61
|
|
|
60
62
|
|
|
@@ -88,6 +90,7 @@ def _render_storage(status: StoreStatus) -> None:
|
|
|
88
90
|
("Project packages", status.project_packages, status.project_packages_bytes),
|
|
89
91
|
("Toolchains", None, status.toolchains_bytes),
|
|
90
92
|
("Executions", status.executions, status.executions_bytes),
|
|
93
|
+
("Scratch", status.scratch_workspaces, status.scratch_bytes),
|
|
91
94
|
)
|
|
92
95
|
for label, count, bytes_used in rows:
|
|
93
96
|
counted = f"{count:>5}" if count is not None else " "
|
|
@@ -138,7 +141,11 @@ def _render_doctor(report: DoctorReport) -> None:
|
|
|
138
141
|
print(f"{symbols[check.status]} {check.name}: {check.message}")
|
|
139
142
|
|
|
140
143
|
|
|
141
|
-
def _render_cleanup(
|
|
144
|
+
def _render_cleanup(
|
|
145
|
+
environments: CleanupReport,
|
|
146
|
+
downloads: DownloadCleanupReport | None,
|
|
147
|
+
scratch: CleanupReport | None = None,
|
|
148
|
+
) -> None:
|
|
142
149
|
style = styler_for(sys.stdout)
|
|
143
150
|
retained_note = style.dim(
|
|
144
151
|
f"Retained {len(environments.retained)} environment(s) (named, recent, or in use)."
|
|
@@ -168,17 +175,36 @@ def _render_cleanup(environments: CleanupReport, downloads: DownloadCleanupRepor
|
|
|
168
175
|
)
|
|
169
176
|
else:
|
|
170
177
|
print("No unreferenced cached downloads to remove.")
|
|
178
|
+
if scratch is not None:
|
|
179
|
+
if scratch.candidates:
|
|
180
|
+
anything = True
|
|
181
|
+
print(
|
|
182
|
+
style.bold(
|
|
183
|
+
f"Would remove {len(scratch.candidates)} abandoned workspace(s) · "
|
|
184
|
+
f"{format_byte_size(scratch.candidate_bytes)}"
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
else:
|
|
188
|
+
print("No abandoned workspaces to remove.")
|
|
171
189
|
print(retained_note)
|
|
172
190
|
if anything:
|
|
173
191
|
print()
|
|
174
192
|
print("This was a preview. Re-run with --execute to delete.")
|
|
175
193
|
return
|
|
176
194
|
reclaimed = environments.reclaimed_bytes + (downloads.reclaimed_bytes if downloads else 0)
|
|
177
|
-
|
|
195
|
+
if scratch is not None:
|
|
196
|
+
reclaimed += scratch.reclaimed_bytes
|
|
197
|
+
removed = (
|
|
198
|
+
len(environments.removed)
|
|
199
|
+
+ (len(downloads.removed) if downloads else 0)
|
|
200
|
+
+ (len(scratch.removed) if scratch else 0)
|
|
201
|
+
)
|
|
178
202
|
if removed:
|
|
179
203
|
parts = [f"{len(environments.removed)} environment(s)"]
|
|
180
204
|
if downloads is not None:
|
|
181
205
|
parts.append(f"{len(downloads.removed)} cached download(s)")
|
|
206
|
+
if scratch is not None:
|
|
207
|
+
parts.append(f"{len(scratch.removed)} abandoned workspace(s)")
|
|
182
208
|
print(
|
|
183
209
|
style.green(f"Removed {' and '.join(parts)} · reclaimed {format_byte_size(reclaimed)}")
|
|
184
210
|
)
|
|
@@ -289,11 +315,15 @@ def _print_publication_failure(exc: PublicationError) -> None:
|
|
|
289
315
|
file=sys.stderr,
|
|
290
316
|
)
|
|
291
317
|
print(f" {exc}", file=sys.stderr)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
f"
|
|
295
|
-
|
|
296
|
-
|
|
318
|
+
if exc.attempted_provider is not None:
|
|
319
|
+
print(" Authentication: unavailable (source: none)", file=sys.stderr)
|
|
320
|
+
print(f" Attempted provider: {exc.attempted_provider}", file=sys.stderr)
|
|
321
|
+
else:
|
|
322
|
+
identity = exc.username or "anonymous"
|
|
323
|
+
print(
|
|
324
|
+
f" Authentication: {identity} (source: {exc.credential_source})",
|
|
325
|
+
file=sys.stderr,
|
|
326
|
+
)
|
|
297
327
|
if exc.partial:
|
|
298
328
|
print(
|
|
299
329
|
" No verified final release was produced; immutable platform content may exist.",
|
|
@@ -435,11 +465,19 @@ Run `lean-runtime COMMAND --help` for examples and options.""",
|
|
|
435
465
|
publish_environment.add_argument("--publish-to", required=True)
|
|
436
466
|
publish_environment.add_argument("--tag", action="append", default=[])
|
|
437
467
|
publish_environment.add_argument("--name")
|
|
438
|
-
publish_environment.add_argument(
|
|
468
|
+
publish_environment.add_argument(
|
|
469
|
+
"--timeout",
|
|
470
|
+
type=float,
|
|
471
|
+
default=None,
|
|
472
|
+
help="override timeouts for credential, registry, hydration, and build operations",
|
|
473
|
+
)
|
|
439
474
|
publish_environment.add_argument("--platform-only", action="store_true")
|
|
440
475
|
publish_environment.add_argument("--accelerate", action="store_true")
|
|
441
476
|
publish_environment.add_argument("--sign", action="store_true")
|
|
442
477
|
publish_environment.add_argument("--attest", action="store_true")
|
|
478
|
+
publish_environment.add_argument(
|
|
479
|
+
"--json", action="store_true", help="emit a versioned JSON envelope"
|
|
480
|
+
)
|
|
443
481
|
publish_environment.add_argument(
|
|
444
482
|
"--check-access",
|
|
445
483
|
action="store_true",
|
|
@@ -507,8 +545,8 @@ Run `lean-runtime COMMAND --help` for examples and options.""",
|
|
|
507
545
|
push.add_argument(
|
|
508
546
|
"--timeout",
|
|
509
547
|
type=float,
|
|
510
|
-
default=
|
|
511
|
-
help="
|
|
548
|
+
default=None,
|
|
549
|
+
help="override timeouts for credential, registry, hydration, and build operations",
|
|
512
550
|
)
|
|
513
551
|
push.add_argument(
|
|
514
552
|
"--platform-only",
|
|
@@ -526,6 +564,7 @@ Run `lean-runtime COMMAND --help` for examples and options.""",
|
|
|
526
564
|
push.add_argument(
|
|
527
565
|
"--attest", action="store_true", help="publish a signed source/probe attestation"
|
|
528
566
|
)
|
|
567
|
+
push.add_argument("--json", action="store_true", help="emit a versioned JSON envelope")
|
|
529
568
|
push.add_argument(
|
|
530
569
|
"--check-access",
|
|
531
570
|
action="store_true",
|
|
@@ -928,6 +967,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
928
967
|
args.mathlib = None if args.core else args.mathlib_version
|
|
929
968
|
operation_started = time.monotonic()
|
|
930
969
|
display_path: str | None = None
|
|
970
|
+
renderer = ConsoleRenderer(
|
|
971
|
+
mode="quiet" if args.quiet or getattr(args, "json", False) else None,
|
|
972
|
+
verbose=args.verbose,
|
|
973
|
+
)
|
|
931
974
|
try:
|
|
932
975
|
if args.command == "completion":
|
|
933
976
|
print(_completion_script(args.shell), end="")
|
|
@@ -942,7 +985,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
942
985
|
)
|
|
943
986
|
runtime = Runtime(
|
|
944
987
|
home=args.home,
|
|
945
|
-
on_event=
|
|
988
|
+
on_event=renderer,
|
|
946
989
|
availability=selected_availability,
|
|
947
990
|
libraries=args.libraries,
|
|
948
991
|
max_download_bytes=selected_download_limit,
|
|
@@ -1185,16 +1228,26 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1185
1228
|
_json(environment.inspect().to_dict())
|
|
1186
1229
|
return 0
|
|
1187
1230
|
if args.command == "build-and-publish":
|
|
1188
|
-
|
|
1231
|
+
timeout_override = args.timeout
|
|
1232
|
+
publisher = runtime.begin_publication(
|
|
1233
|
+
args.publish_to,
|
|
1234
|
+
auth_timeout=timeout_override if timeout_override is not None else 10,
|
|
1235
|
+
registry_timeout=timeout_override if timeout_override is not None else 30,
|
|
1236
|
+
)
|
|
1237
|
+
access = publisher.check_access()
|
|
1189
1238
|
if args.check_access:
|
|
1190
|
-
_json(
|
|
1239
|
+
_json(
|
|
1240
|
+
envelope(_schema_for(args.command), ok=True, data=access.to_dict())
|
|
1241
|
+
if args.json
|
|
1242
|
+
else access.to_dict()
|
|
1243
|
+
)
|
|
1191
1244
|
return 0
|
|
1192
1245
|
if args.lock is None:
|
|
1193
1246
|
raise ValueError("publish environment requires LOCK unless --check-access is used")
|
|
1194
1247
|
environment = runtime.open_exact(
|
|
1195
1248
|
EnvironmentLock.load(args.lock),
|
|
1196
1249
|
name=args.name,
|
|
1197
|
-
build_timeout=
|
|
1250
|
+
build_timeout=timeout_override if timeout_override is not None else 1800,
|
|
1198
1251
|
accelerate=args.accelerate,
|
|
1199
1252
|
)
|
|
1200
1253
|
publication = runtime.publish_environment(
|
|
@@ -1204,11 +1257,16 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1204
1257
|
finalize=not args.platform_only,
|
|
1205
1258
|
sign=args.sign,
|
|
1206
1259
|
attest=args.attest,
|
|
1260
|
+
publisher=publisher,
|
|
1207
1261
|
).to_dict()
|
|
1208
1262
|
publication["consumer_command"] = (
|
|
1209
1263
|
f"lean-runtime --library {args.publish_to} download {args.lock}"
|
|
1210
1264
|
)
|
|
1211
|
-
_json(
|
|
1265
|
+
_json(
|
|
1266
|
+
envelope(_schema_for(args.command), ok=True, data=publication)
|
|
1267
|
+
if args.json
|
|
1268
|
+
else publication
|
|
1269
|
+
)
|
|
1212
1270
|
return 0
|
|
1213
1271
|
if args.command == "finalize-publication":
|
|
1214
1272
|
descriptors = []
|
|
@@ -1474,10 +1532,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1474
1532
|
if args.include_downloads
|
|
1475
1533
|
else None
|
|
1476
1534
|
)
|
|
1535
|
+
gc_scratch = runtime.clean_scratch(
|
|
1536
|
+
dry_run=not args.execute,
|
|
1537
|
+
minimum_age_seconds=min(args.minimum_age_hours * 3600, 3600),
|
|
1538
|
+
)
|
|
1477
1539
|
if args.json:
|
|
1478
1540
|
gc_payload: dict[str, Any] = {
|
|
1479
1541
|
"environments": gc_report.to_dict(),
|
|
1480
1542
|
"downloaded_files": gc_downloads.to_dict() if gc_downloads else None,
|
|
1543
|
+
"scratch": gc_scratch.to_dict(),
|
|
1481
1544
|
}
|
|
1482
1545
|
_json(
|
|
1483
1546
|
envelope(
|
|
@@ -1487,7 +1550,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1487
1550
|
)
|
|
1488
1551
|
)
|
|
1489
1552
|
else:
|
|
1490
|
-
_render_cleanup(gc_report, gc_downloads)
|
|
1553
|
+
_render_cleanup(gc_report, gc_downloads, gc_scratch)
|
|
1491
1554
|
return 0
|
|
1492
1555
|
if args.command == "check":
|
|
1493
1556
|
if args.across is not None:
|
|
@@ -1577,6 +1640,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1577
1640
|
)
|
|
1578
1641
|
_emit_result(watched_result, False)
|
|
1579
1642
|
time.sleep(args.watch_interval)
|
|
1643
|
+
check_subject = (
|
|
1644
|
+
Path(args.inputs[-1]).name
|
|
1645
|
+
if args.inputs and args.inputs[-1] != "-"
|
|
1646
|
+
else "stdin"
|
|
1647
|
+
if args.inputs
|
|
1648
|
+
else Path(args.project or ".").resolve().name
|
|
1649
|
+
)
|
|
1650
|
+
runtime.events.emit("check.started", "Checking Lean input", subject=check_subject)
|
|
1580
1651
|
if args.project is not None and args.package_refs:
|
|
1581
1652
|
raise ValueError("check cannot combine --project with --with")
|
|
1582
1653
|
if not args.inputs:
|
|
@@ -1659,9 +1730,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1659
1730
|
shared=args.shared,
|
|
1660
1731
|
)
|
|
1661
1732
|
except KeyboardInterrupt:
|
|
1733
|
+
renderer.close()
|
|
1662
1734
|
print("lean-runtime: interrupted", file=sys.stderr)
|
|
1663
1735
|
return 130
|
|
1664
1736
|
except (ResolutionError, MaterializationError) as exc:
|
|
1737
|
+
renderer.close()
|
|
1665
1738
|
details = {
|
|
1666
1739
|
"phase": exc.phase,
|
|
1667
1740
|
"command": list(exc.command),
|
|
@@ -1681,12 +1754,13 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1681
1754
|
_print_operation_failure(exc, verbose=args.verbose)
|
|
1682
1755
|
return 2
|
|
1683
1756
|
except PublicationError as exc:
|
|
1757
|
+
renderer.close()
|
|
1684
1758
|
if getattr(args, "json", False):
|
|
1685
1759
|
_json(
|
|
1686
1760
|
envelope(
|
|
1687
1761
|
_schema_for(args.command),
|
|
1688
1762
|
ok=False,
|
|
1689
|
-
data=
|
|
1763
|
+
data=exc.to_dict(),
|
|
1690
1764
|
errors=[error("publication_failed", str(exc), details=exc.to_dict())],
|
|
1691
1765
|
)
|
|
1692
1766
|
)
|
|
@@ -1694,6 +1768,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1694
1768
|
_print_publication_failure(exc)
|
|
1695
1769
|
return exc.exit_code
|
|
1696
1770
|
except (LeanRuntimeError, OSError, UnicodeError, ValueError, json.JSONDecodeError) as exc:
|
|
1771
|
+
renderer.close()
|
|
1697
1772
|
if getattr(args, "json", False):
|
|
1698
1773
|
_json(
|
|
1699
1774
|
envelope(
|
|
@@ -1706,6 +1781,18 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1706
1781
|
else:
|
|
1707
1782
|
print(f"lean-runtime: {exc}", file=sys.stderr)
|
|
1708
1783
|
return 2
|
|
1784
|
+
if args.command in {"check", "check-file"}:
|
|
1785
|
+
runtime.events.emit("check.completed", "Lean check completed", ok=result.ok)
|
|
1786
|
+
total_ms = round((time.monotonic() - operation_started) * 1000)
|
|
1787
|
+
accounted_ms = sum(timing.duration_ms for timing in result.timings)
|
|
1788
|
+
result = replace(
|
|
1789
|
+
result,
|
|
1790
|
+
timings=(
|
|
1791
|
+
PhaseTiming("command_preparation", max(0, total_ms - accounted_ms)),
|
|
1792
|
+
*result.timings,
|
|
1793
|
+
),
|
|
1794
|
+
)
|
|
1795
|
+
renderer.close()
|
|
1709
1796
|
_emit_result(result, args.json, display_path=display_path)
|
|
1710
1797
|
if args.timings:
|
|
1711
1798
|
print(render_timings(result.timings), file=sys.stderr)
|
|
@@ -215,6 +215,18 @@ class ConsoleRenderer:
|
|
|
215
215
|
def _render_capability_required(self, event: RuntimeEvent) -> None:
|
|
216
216
|
self._print(event.message)
|
|
217
217
|
|
|
218
|
+
def _render_check_started(self, event: RuntimeEvent) -> None:
|
|
219
|
+
if self.mode != "tty":
|
|
220
|
+
return
|
|
221
|
+
subject = str(event.data.get("subject") or "Lean input")
|
|
222
|
+
message = f"Checking {subject}…"
|
|
223
|
+
self.stream.write("\r" + self.style.cyan(message))
|
|
224
|
+
self.stream.flush()
|
|
225
|
+
self._line_length = len(message)
|
|
226
|
+
|
|
227
|
+
def _render_check_completed(self, _event: RuntimeEvent) -> None:
|
|
228
|
+
self._end_progress_line()
|
|
229
|
+
|
|
218
230
|
# -- low-level output -----------------------------------------------
|
|
219
231
|
|
|
220
232
|
def _verbose_line(self, event: RuntimeEvent) -> None:
|
|
@@ -707,14 +707,11 @@ class Environment:
|
|
|
707
707
|
},
|
|
708
708
|
)
|
|
709
709
|
job_parent = self.manager.store.jobs / execution_id
|
|
710
|
-
|
|
710
|
+
workspace_lease = self.manager.store.lease_workspace(job_parent, "interactive-environment")
|
|
711
711
|
instance = job_parent / f"instance-{uuid.uuid4().hex}"
|
|
712
712
|
|
|
713
713
|
def cleanup() -> None:
|
|
714
|
-
|
|
715
|
-
remove_tree(instance)
|
|
716
|
-
with suppress(OSError):
|
|
717
|
-
job_parent.rmdir()
|
|
714
|
+
workspace_lease.close()
|
|
718
715
|
|
|
719
716
|
try:
|
|
720
717
|
with self.manager.store.execution_lease(self.id):
|
|
@@ -844,7 +841,7 @@ class Environment:
|
|
|
844
841
|
},
|
|
845
842
|
)
|
|
846
843
|
job_parent = self.manager.store.jobs / execution_id
|
|
847
|
-
|
|
844
|
+
workspace_lease = self.manager.store.lease_workspace(job_parent, f"environment-{operation}")
|
|
848
845
|
instance = job_parent / f"instance-{uuid.uuid4().hex}"
|
|
849
846
|
try:
|
|
850
847
|
instance_started = time.monotonic()
|
|
@@ -994,10 +991,7 @@ class Environment:
|
|
|
994
991
|
)
|
|
995
992
|
return result
|
|
996
993
|
finally:
|
|
997
|
-
|
|
998
|
-
remove_tree(instance)
|
|
999
|
-
with suppress(OSError):
|
|
1000
|
-
job_parent.rmdir()
|
|
994
|
+
workspace_lease.close()
|
|
1001
995
|
|
|
1002
996
|
def _result(
|
|
1003
997
|
self,
|
|
@@ -81,6 +81,23 @@ class RegistryRequestError(DownloadUnavailable):
|
|
|
81
81
|
self.retryable = retryable
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
class CredentialAcquisitionError(EnvironmentError):
|
|
85
|
+
"""A configured credential provider could not produce usable credentials."""
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
message: str,
|
|
90
|
+
*,
|
|
91
|
+
provider: str,
|
|
92
|
+
failure_kind: str,
|
|
93
|
+
retryable: bool,
|
|
94
|
+
) -> None:
|
|
95
|
+
super().__init__(message)
|
|
96
|
+
self.provider = provider
|
|
97
|
+
self.failure_kind = failure_kind
|
|
98
|
+
self.retryable = retryable
|
|
99
|
+
|
|
100
|
+
|
|
84
101
|
class PublicationError(EnvironmentError):
|
|
85
102
|
"""A required publication did not reach a remotely verified terminal state."""
|
|
86
103
|
|
|
@@ -97,6 +114,8 @@ class PublicationError(EnvironmentError):
|
|
|
97
114
|
credential_source: str = "anonymous",
|
|
98
115
|
username: str | None = None,
|
|
99
116
|
hint: str | None = None,
|
|
117
|
+
attempted_provider: str | None = None,
|
|
118
|
+
auth_failure_kind: str | None = None,
|
|
100
119
|
) -> None:
|
|
101
120
|
super().__init__(message)
|
|
102
121
|
self.phase = phase
|
|
@@ -108,11 +127,15 @@ class PublicationError(EnvironmentError):
|
|
|
108
127
|
self.credential_source = credential_source
|
|
109
128
|
self.username = username
|
|
110
129
|
self.hint = hint
|
|
130
|
+
self.attempted_provider = attempted_provider
|
|
131
|
+
self.auth_failure_kind = auth_failure_kind
|
|
111
132
|
|
|
112
133
|
@property
|
|
113
134
|
def exit_code(self) -> int:
|
|
114
135
|
if self.partial:
|
|
115
136
|
return 5
|
|
137
|
+
if self.phase == "credential_acquisition":
|
|
138
|
+
return 4 if self.retryable else 3
|
|
116
139
|
if self.status_code in {401, 403}:
|
|
117
140
|
return 3
|
|
118
141
|
if self.retryable:
|
|
@@ -130,6 +153,8 @@ class PublicationError(EnvironmentError):
|
|
|
130
153
|
"credential_source": self.credential_source,
|
|
131
154
|
"username": self.username,
|
|
132
155
|
"hint": self.hint,
|
|
156
|
+
"attempted_provider": self.attempted_provider,
|
|
157
|
+
"auth_failure_kind": self.auth_failure_kind,
|
|
133
158
|
"message": str(self),
|
|
134
159
|
}
|
|
135
160
|
|
|
@@ -50,11 +50,14 @@ class LeanHeaderCache:
|
|
|
50
50
|
self.home = home / "header-snapshots"
|
|
51
51
|
self.toolchains = toolchains
|
|
52
52
|
self._support: dict[str, bool] = {}
|
|
53
|
+
self._toolchain_keys: dict[str, str] = {}
|
|
53
54
|
|
|
54
55
|
def _toolchain_key(self, toolchain: str) -> str:
|
|
56
|
+
if toolchain in self._toolchain_keys:
|
|
57
|
+
return self._toolchain_keys[toolchain]
|
|
55
58
|
digest = getattr(self.toolchains, "executable_digest", None)
|
|
56
59
|
executable = str(digest(toolchain, "lean")) if callable(digest) else toolchain
|
|
57
|
-
|
|
60
|
+
key = sha256_id(
|
|
58
61
|
"lean-header",
|
|
59
62
|
{
|
|
60
63
|
"toolchain": toolchain,
|
|
@@ -62,6 +65,8 @@ class LeanHeaderCache:
|
|
|
62
65
|
"platform": platform_compatibility(),
|
|
63
66
|
},
|
|
64
67
|
).removeprefix("lean-header-")
|
|
68
|
+
self._toolchain_keys[toolchain] = key
|
|
69
|
+
return key
|
|
65
70
|
|
|
66
71
|
def supported(self, toolchain: str) -> bool:
|
|
67
72
|
key = self._toolchain_key(toolchain)
|
|
@@ -86,6 +86,18 @@ def diagnose(toolchains: ToolchainManager, store: EnvironmentStore) -> DoctorRep
|
|
|
86
86
|
status = "warning" if staging else "pass"
|
|
87
87
|
message = f"{len(staging)} incomplete staging directories" if staging else "No stale builds"
|
|
88
88
|
checks.append(DoctorCheck("staging", status, message))
|
|
89
|
+
scratch = store.clean_scratch(dry_run=True, minimum_age_seconds=3600)
|
|
90
|
+
if scratch.candidates:
|
|
91
|
+
checks.append(
|
|
92
|
+
DoctorCheck(
|
|
93
|
+
"scratch",
|
|
94
|
+
"warning",
|
|
95
|
+
f"{len(scratch.candidates)} abandoned workspaces use "
|
|
96
|
+
f"{scratch.candidate_bytes // (1024**2)} MiB",
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
checks.append(DoctorCheck("scratch", "pass", "No abandoned workspaces"))
|
|
89
101
|
store_status = store.status()
|
|
90
102
|
cutoff = datetime.now(timezone.utc).timestamp() - 7 * 24 * 3600
|
|
91
103
|
stale = tuple(
|
|
@@ -114,6 +126,7 @@ def repair(toolchains: ToolchainManager, store: EnvironmentStore) -> DoctorRepor
|
|
|
114
126
|
"""Apply the safe remedies represented by doctor checks, then diagnose again."""
|
|
115
127
|
for staging in store.environments.glob(".staging-*"):
|
|
116
128
|
remove_tree(staging)
|
|
129
|
+
store.clean_scratch(dry_run=False, minimum_age_seconds=3600, include_legacy=False)
|
|
117
130
|
with suppress(ToolchainError):
|
|
118
131
|
toolchains.elan_path(bootstrap=True)
|
|
119
132
|
return diagnose(toolchains, store)
|
|
@@ -19,6 +19,7 @@ TimingPhase = Literal[
|
|
|
19
19
|
"environment_open",
|
|
20
20
|
"instance_creation",
|
|
21
21
|
"input_staging",
|
|
22
|
+
"command_preparation",
|
|
22
23
|
"execution",
|
|
23
24
|
"result_collection",
|
|
24
25
|
"result_publication",
|
|
@@ -38,6 +39,7 @@ TIMING_PHASES = frozenset(
|
|
|
38
39
|
"environment_open",
|
|
39
40
|
"instance_creation",
|
|
40
41
|
"input_staging",
|
|
42
|
+
"command_preparation",
|
|
41
43
|
"execution",
|
|
42
44
|
"result_collection",
|
|
43
45
|
"result_publication",
|