engineering-platform 2.3.101__tar.gz → 2.3.102__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.
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/PKG-INFO +1 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/pyproject.toml +2 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_admission.py +27 -5
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_composition.py +44 -11
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_plan.py +19 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_preparation.py +54 -3
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/platform_version.py +1 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_readiness.py +23 -6
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/providers.py +22 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/server.py +27 -8
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/server_console_services.py +9 -6
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/system_installation_topology.py +277 -6
- engineering_platform-2.3.102/src/engineering_platform/system_instance_provisioner.py +1049 -0
- engineering_platform-2.3.102/src/engineering_platform/system_provider_context.py +260 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/system_server_service.py +338 -8
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/templates/workspace-config.json +1 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform.egg-info/PKG-INFO +1 -1
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform.egg-info/SOURCES.txt +2 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform.egg-info/entry_points.txt +1 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/README.md +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/setup.cfg +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/__init__.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/__main__.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/agent_state.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/agent_trust.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/dashboard.css +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/dashboard.js +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/assurance_scope.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/capability_preflight.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/capability_review.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/central_data_transfer.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/central_database.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/central_operational_reset.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/central_store_migration.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/codex_capacity.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/codex_chat.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/codex_observability.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/component_lock.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/component_logging.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/console_presentation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/console_route_ownership.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/contracts/__init__.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/contracts/ep_consumer.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/contracts/models.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/contracts/projection.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/dashboard_browser_validation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/dashboard_state.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/dashboard_supervisor.swift +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/dashboard_translation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/dependabot_producer.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/development_profile.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/drift_diagnostics.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/emergency_recovery.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/engineering_memory.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/ep_consumer_credentials.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/evidence_projection.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_activity.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_context.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_errors.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_evidence.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_executor.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_finalization.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_host.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_host_evidence.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_lease.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_lifecycle.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_models.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_readiness.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_reporting.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_repository.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_timeout_policy.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_timing.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/execution_transaction.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/external_producer_binding.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/file_inbox.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/forensic_attribution.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/forensic_attribution_v2.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/forensic_delta.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/golden_scenario.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/host_admin.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/host_preflight.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_relocation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_activation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_backup.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_executor.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_migration.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/installation_update_operation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/investigation_ledger.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/legacy_inbox_migration.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/legacy_installation_adoption.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/lifecycle_worker.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/live_status.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/local_api.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/local_api_keychain.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/local_repository_binding.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/managed_autonomy.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/managed_codex_runtime.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/managed_workspace_readiness.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/managed_workspace_recovery.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/merge_delegation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/operational_installation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/operational_installation_lock.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/operational_installation_record.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/owner_credential_recovery.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/parity_context.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/parity_lifecycle_dispatcher.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/platform_admin.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/platform_api.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/platform_bootstrap.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/platform_components.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/pr_check_repair.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/pr_evidence_backfill.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/producer.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/product_installation_readback.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/project_agent.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/project_agent_service.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/project_topology.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/prompt_history.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_context.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_context_benchmark.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_context_scope.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_interruption.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_process_identity.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_recovery.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/provider_usage.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/qualification.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/qualification_runtime.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/recommendation_handoff.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/reconciliation_adoption.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/release_operation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/report_analysis.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/repository_attachment.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/repository_handoff.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/resources.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/reviewer_evidence.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/revision_binding.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/server_relay.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/server_service.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/status_model.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/status_reconciliation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/storage.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/submission_cli.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/submission_intake.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/submission_service.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/telemetry.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/telemetry_contract.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/telemetry_export.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/telemetry_metrics.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/validation_identity.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/validation_profile.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/version_preparation_delivery.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/workspace_preflight.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/worktree_provenance.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/worktree_tooling.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform.egg-info/top_level.txt +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/tests/test_adaptive_action_sizing_design.py +0 -0
- {engineering_platform-2.3.101 → engineering_platform-2.3.102}/tests/test_role_task_model_policy_design.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "engineering-platform"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.102"
|
|
8
8
|
description = "Local-first Engineering Platform execution operations runtime"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.14,<3.15"
|
|
@@ -20,6 +20,7 @@ engineering-platform = "engineering_platform.submission_cli:main"
|
|
|
20
20
|
engineering-project-agent = "engineering_platform.project_agent:main"
|
|
21
21
|
engineering-reconciliation-adopt = "engineering_platform.reconciliation_adoption:main"
|
|
22
22
|
engineering-platform-maintenance = "engineering_platform.central_operational_reset:main"
|
|
23
|
+
engineering-platform-system-provisioner = "engineering_platform.system_instance_provisioner:main"
|
|
23
24
|
|
|
24
25
|
[tool.setuptools]
|
|
25
26
|
package-dir = {"" = "src"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bootstrap_contract": "2026.12",
|
|
3
3
|
"checkpoint_format": 1,
|
|
4
|
-
"dashboard_version": "2.3.
|
|
4
|
+
"dashboard_version": "2.3.102",
|
|
5
5
|
"handoff_protocol": 1,
|
|
6
6
|
"memory_format": 2,
|
|
7
7
|
"minimum_codex_cli": "0.146.0",
|
|
8
8
|
"inbox_protocol": 1,
|
|
9
|
-
"platform_version": "2.3.
|
|
9
|
+
"platform_version": "2.3.102",
|
|
10
10
|
"report_format": 2,
|
|
11
|
-
"runner_version": "2.3.
|
|
11
|
+
"runner_version": "2.3.102",
|
|
12
12
|
"status_model": 1,
|
|
13
13
|
"storage_schema": 45,
|
|
14
|
-
"watcher_version": "2.3.
|
|
14
|
+
"watcher_version": "2.3.102"
|
|
15
15
|
}
|
|
@@ -13,6 +13,7 @@ import json
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
import subprocess
|
|
15
15
|
from typing import Mapping
|
|
16
|
+
from typing import Callable
|
|
16
17
|
|
|
17
18
|
from . import (
|
|
18
19
|
installation_update_activation,
|
|
@@ -39,6 +40,9 @@ class InstallationUpdateAdmissionError(ValueError):
|
|
|
39
40
|
"""The exact prepared candidate cannot enter EP pre-cleanup execution."""
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
ServiceInterpreterResolver = Callable[[Path], Path | None]
|
|
44
|
+
|
|
45
|
+
|
|
42
46
|
@dataclass(frozen=True)
|
|
43
47
|
class ExecutionAdmission:
|
|
44
48
|
"""Durable, token-free proof for exactly one non-operational candidate."""
|
|
@@ -208,10 +212,15 @@ def _require_activated_service(
|
|
|
208
212
|
candidate: PreparedUpdateCandidate,
|
|
209
213
|
*,
|
|
210
214
|
service_home: Path | None = None,
|
|
215
|
+
service_interpreter: ServiceInterpreterResolver | None = None,
|
|
211
216
|
) -> None:
|
|
212
217
|
"""Keep cleanup bound to the exact activated service and data root."""
|
|
213
218
|
try:
|
|
214
|
-
selected =
|
|
219
|
+
selected = (
|
|
220
|
+
service_interpreter(Path(plan.data_root))
|
|
221
|
+
if service_interpreter is not None
|
|
222
|
+
else server_service.configured_interpreter(Path(plan.data_root), home=service_home)
|
|
223
|
+
)
|
|
215
224
|
except server_service.ServerServiceError as error:
|
|
216
225
|
raise InstallationUpdateAdmissionError(
|
|
217
226
|
"activated service does not bind the admitted candidate"
|
|
@@ -338,6 +347,7 @@ def admitted_candidate(
|
|
|
338
347
|
*,
|
|
339
348
|
runner: object = subprocess.run,
|
|
340
349
|
service_home: Path | None = None,
|
|
350
|
+
service_interpreter: ServiceInterpreterResolver | None = None,
|
|
341
351
|
) -> PreparedUpdateCandidate:
|
|
342
352
|
"""Reopen the one candidate already admitted for a resumable update.
|
|
343
353
|
|
|
@@ -397,7 +407,10 @@ def admitted_candidate(
|
|
|
397
407
|
raise InstallationUpdateAdmissionError("execution admission does not bind the exact prepared candidate")
|
|
398
408
|
if state in {"VERIFIED", "CLEANUP_PENDING", "COMPLETE"}:
|
|
399
409
|
_require_retained_candidate_package(candidate, runner=runner)
|
|
400
|
-
_require_activated_service(
|
|
410
|
+
_require_activated_service(
|
|
411
|
+
plan, candidate, service_home=service_home,
|
|
412
|
+
service_interpreter=service_interpreter,
|
|
413
|
+
)
|
|
401
414
|
if admission.schema_version == 3:
|
|
402
415
|
if plan.legacy_adoption is None:
|
|
403
416
|
raise InstallationUpdateAdmissionError("execution admission legacy baseline is unavailable")
|
|
@@ -426,17 +439,26 @@ def admitted_candidate(
|
|
|
426
439
|
_require_pre_activation_provenance(plan, admission, snapshot)
|
|
427
440
|
elif record != expected:
|
|
428
441
|
raise InstallationUpdateAdmissionError("activated installation does not bind the admitted legacy candidate")
|
|
429
|
-
elif
|
|
442
|
+
elif (
|
|
443
|
+
service_interpreter(Path(plan.data_root)) if service_interpreter is not None
|
|
444
|
+
else server_service.configured_interpreter(Path(plan.data_root), home=service_home)
|
|
445
|
+
) != Path(candidate.interpreter):
|
|
430
446
|
raise InstallationUpdateAdmissionError("activated service does not bind the admitted legacy candidate")
|
|
431
447
|
elif state == "ACTIVATED":
|
|
432
448
|
if record != expected:
|
|
433
449
|
raise InstallationUpdateAdmissionError("activated installation does not bind the admitted legacy candidate")
|
|
434
|
-
if
|
|
450
|
+
if (
|
|
451
|
+
service_interpreter(Path(plan.data_root)) if service_interpreter is not None
|
|
452
|
+
else server_service.configured_interpreter(Path(plan.data_root), home=service_home)
|
|
453
|
+
) != Path(candidate.interpreter):
|
|
435
454
|
raise InstallationUpdateAdmissionError("activated service does not bind the admitted legacy candidate")
|
|
436
455
|
elif state in {"VERIFIED", "CLEANUP_PENDING", "COMPLETE"}:
|
|
437
456
|
if record != expected and not _finalized_replacement(record, expected):
|
|
438
457
|
raise InstallationUpdateAdmissionError("activated installation does not bind the admitted legacy candidate")
|
|
439
|
-
if
|
|
458
|
+
if (
|
|
459
|
+
service_interpreter(Path(plan.data_root)) if service_interpreter is not None
|
|
460
|
+
else server_service.configured_interpreter(Path(plan.data_root), home=service_home)
|
|
461
|
+
) != Path(candidate.interpreter):
|
|
440
462
|
raise InstallationUpdateAdmissionError("activated service does not bind the admitted legacy candidate")
|
|
441
463
|
else:
|
|
442
464
|
raise InstallationUpdateAdmissionError("execution admission operation is invalid")
|
|
@@ -32,6 +32,10 @@ from .installation_update_plan import InstallationUpdatePlan
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
MigrationRunner = Callable[..., subprocess.CompletedProcess[str]]
|
|
35
|
+
ActivationAction = Callable[
|
|
36
|
+
[InstallationUpdatePlan, Path, Mapping[str, object] | None],
|
|
37
|
+
Mapping[str, object],
|
|
38
|
+
]
|
|
35
39
|
|
|
36
40
|
|
|
37
41
|
class InstallationUpdateCompositionError(ValueError):
|
|
@@ -52,6 +56,8 @@ class InstallationUpdateOperationalActions:
|
|
|
52
56
|
quiesce: EvidenceAction
|
|
53
57
|
verify: EvidenceAction
|
|
54
58
|
quiesce_preflight: EvidenceAction | None = None
|
|
59
|
+
activate: ActivationAction | None = None
|
|
60
|
+
service_interpreter: Callable[[Path], Path | None] | None = None
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
def _evidence(value: Mapping[str, object], step: str) -> dict[str, object]:
|
|
@@ -72,11 +78,13 @@ def _admitted_target(
|
|
|
72
78
|
admission: ExecutionAdmission,
|
|
73
79
|
runner: MigrationRunner,
|
|
74
80
|
service_home: Path | None = None,
|
|
81
|
+
service_interpreter: Callable[[Path], Path | None] | None = None,
|
|
75
82
|
) -> Path:
|
|
76
83
|
"""Resolve only the durable OI-4c candidate launcher for this operation."""
|
|
77
84
|
try:
|
|
78
85
|
candidate = installation_update_admission.admitted_candidate(
|
|
79
86
|
plan, admission, runner=runner, service_home=service_home,
|
|
87
|
+
service_interpreter=service_interpreter,
|
|
80
88
|
)
|
|
81
89
|
except installation_update_admission.InstallationUpdateAdmissionError as error:
|
|
82
90
|
raise InstallationUpdateCompositionError("installation update execution admission is invalid") from error
|
|
@@ -158,9 +166,13 @@ def _bound_target(
|
|
|
158
166
|
admission: ExecutionAdmission,
|
|
159
167
|
runner: MigrationRunner,
|
|
160
168
|
service_home: Path | None = None,
|
|
169
|
+
service_interpreter: Callable[[Path], Path | None] | None = None,
|
|
161
170
|
) -> Path:
|
|
162
171
|
"""Require a recovery attempt to keep the target proven at inventory."""
|
|
163
|
-
target = _admitted_target(
|
|
172
|
+
target = _admitted_target(
|
|
173
|
+
plan, admission=admission, runner=runner, service_home=service_home,
|
|
174
|
+
service_interpreter=service_interpreter,
|
|
175
|
+
)
|
|
164
176
|
try:
|
|
165
177
|
events = status(Path(plan.data_root), plan.operation_id)["events"]
|
|
166
178
|
except InstallationUpdateOperationError as error:
|
|
@@ -184,10 +196,12 @@ def _verify(
|
|
|
184
196
|
action: EvidenceAction,
|
|
185
197
|
runner: MigrationRunner,
|
|
186
198
|
service_home: Path | None = None,
|
|
199
|
+
service_interpreter: Callable[[Path], Path | None] | None = None,
|
|
187
200
|
) -> Mapping[str, object]:
|
|
188
201
|
"""Keep verification bound to the runtime activated from the inventoried path."""
|
|
189
202
|
expected = _bound_target(
|
|
190
203
|
plan, admission=admission, runner=runner, service_home=service_home,
|
|
204
|
+
service_interpreter=service_interpreter,
|
|
191
205
|
)
|
|
192
206
|
try:
|
|
193
207
|
record = operational_installation_record.load(Path(plan.data_root))
|
|
@@ -226,6 +240,7 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
226
240
|
# repeats the same durable lookup while that executor owns the lock.
|
|
227
241
|
admitted_target = _admitted_target(
|
|
228
242
|
plan, admission=admission, runner=migration_runner, service_home=activation_home,
|
|
243
|
+
service_interpreter=actions.service_interpreter,
|
|
229
244
|
)
|
|
230
245
|
pre_activation_record = _admitted_pre_activation_record(admission)
|
|
231
246
|
try:
|
|
@@ -246,6 +261,7 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
246
261
|
_admitted_target(
|
|
247
262
|
bound_plan, admission=admission, runner=migration_runner,
|
|
248
263
|
service_home=activation_home,
|
|
264
|
+
service_interpreter=actions.service_interpreter,
|
|
249
265
|
)
|
|
250
266
|
return action(bound_plan)
|
|
251
267
|
return invoke
|
|
@@ -255,6 +271,7 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
255
271
|
_admitted_target(
|
|
256
272
|
bound_plan, admission=admission, runner=migration_runner,
|
|
257
273
|
service_home=activation_home,
|
|
274
|
+
service_interpreter=actions.service_interpreter,
|
|
258
275
|
)
|
|
259
276
|
_evidence(actions.quiesce(bound_plan), "retained quiescence")
|
|
260
277
|
return action(bound_plan)
|
|
@@ -264,6 +281,7 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
264
281
|
target = _bound_target(
|
|
265
282
|
bound_plan, admission=admission, runner=migration_runner,
|
|
266
283
|
service_home=activation_home,
|
|
284
|
+
service_interpreter=actions.service_interpreter,
|
|
267
285
|
)
|
|
268
286
|
_evidence(actions.quiesce(bound_plan), "retained quiescence")
|
|
269
287
|
return installation_update_migration.migrate(
|
|
@@ -276,6 +294,7 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
276
294
|
target=_admitted_target(
|
|
277
295
|
bound_plan, admission=admission, runner=migration_runner,
|
|
278
296
|
service_home=activation_home,
|
|
297
|
+
service_interpreter=actions.service_interpreter,
|
|
279
298
|
),
|
|
280
299
|
admission=admission,
|
|
281
300
|
action=actions.inventory,
|
|
@@ -284,16 +303,29 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
284
303
|
quiesce=guarded(actions.quiesce),
|
|
285
304
|
backup=after_quiescence(installation_update_backup.backup),
|
|
286
305
|
migrate=migrate,
|
|
287
|
-
activate=lambda bound_plan:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
306
|
+
activate=lambda bound_plan: (
|
|
307
|
+
actions.activate(
|
|
308
|
+
bound_plan,
|
|
309
|
+
_bound_target(
|
|
310
|
+
bound_plan, admission=admission, runner=migration_runner,
|
|
311
|
+
service_home=activation_home,
|
|
312
|
+
service_interpreter=actions.service_interpreter,
|
|
313
|
+
),
|
|
314
|
+
pre_activation_record,
|
|
315
|
+
)
|
|
316
|
+
if actions.activate is not None
|
|
317
|
+
else installation_update_activation.activate(
|
|
318
|
+
bound_plan,
|
|
319
|
+
interpreter=_bound_target(
|
|
320
|
+
bound_plan, admission=admission, runner=migration_runner,
|
|
321
|
+
service_home=activation_home,
|
|
322
|
+
service_interpreter=actions.service_interpreter,
|
|
323
|
+
),
|
|
324
|
+
pre_activation_record=pre_activation_record,
|
|
325
|
+
home=activation_home,
|
|
326
|
+
runner=activation_runner,
|
|
327
|
+
package_runner=migration_runner,
|
|
328
|
+
)
|
|
297
329
|
),
|
|
298
330
|
verify=lambda bound_plan: _verify(
|
|
299
331
|
bound_plan,
|
|
@@ -301,6 +333,7 @@ def compose(plan: InstallationUpdatePlan, *, admission: ExecutionAdmission,
|
|
|
301
333
|
action=actions.verify,
|
|
302
334
|
runner=migration_runner,
|
|
303
335
|
service_home=activation_home,
|
|
336
|
+
service_interpreter=actions.service_interpreter,
|
|
304
337
|
),
|
|
305
338
|
expected_activation=expected_activation,
|
|
306
339
|
quiesce_preflight=(
|
|
@@ -33,6 +33,7 @@ class InstallationUpdatePlan:
|
|
|
33
33
|
cleanup_targets: tuple[str, ...]
|
|
34
34
|
steps: tuple[str, ...]
|
|
35
35
|
legacy_adoption: dict[str, object] | None = None
|
|
36
|
+
target_runtime_venv: str | None = None
|
|
36
37
|
|
|
37
38
|
def payload(self) -> dict[str, object]:
|
|
38
39
|
value = asdict(self)
|
|
@@ -41,6 +42,8 @@ class InstallationUpdatePlan:
|
|
|
41
42
|
# non-empty legacy evidence is always explicit and immutable.
|
|
42
43
|
if value["legacy_adoption"] is None:
|
|
43
44
|
del value["legacy_adoption"]
|
|
45
|
+
if value["target_runtime_venv"] is None:
|
|
46
|
+
del value["target_runtime_venv"]
|
|
44
47
|
return value
|
|
45
48
|
|
|
46
49
|
|
|
@@ -72,7 +75,8 @@ def verify_exact_artifact(plan: InstallationUpdatePlan) -> dict[str, str]:
|
|
|
72
75
|
|
|
73
76
|
|
|
74
77
|
def prepare(data_root: Path, *, operation_id: str, artifact: Path, target_version: str,
|
|
75
|
-
target_digest: str, target_source_revision: str
|
|
78
|
+
target_digest: str, target_source_revision: str,
|
|
79
|
+
target_runtime_venv: Path | None = None) -> InstallationUpdatePlan:
|
|
76
80
|
"""Bind an update to one record and wheel without changing the machine.
|
|
77
81
|
|
|
78
82
|
Callers must execute this returned, exact plan under the EP installation
|
|
@@ -121,6 +125,19 @@ def prepare(data_root: Path, *, operation_id: str, artifact: Path, target_versio
|
|
|
121
125
|
if _digest(exact_artifact) != target_digest:
|
|
122
126
|
raise InstallationUpdatePlanError("target artifact does not match the requested digest")
|
|
123
127
|
operation_root = root / "operations" / operation_id
|
|
128
|
+
runtime_venv: str | None = None
|
|
129
|
+
if target_runtime_venv is not None:
|
|
130
|
+
candidate = Path(target_runtime_venv).expanduser()
|
|
131
|
+
slot = "sha256-" + target_digest.removeprefix("sha256:")
|
|
132
|
+
if (
|
|
133
|
+
not candidate.is_absolute()
|
|
134
|
+
or candidate.name != "venv"
|
|
135
|
+
or candidate.parent.name != slot
|
|
136
|
+
or candidate.parent.parent.name != "slots"
|
|
137
|
+
or candidate.parent.parent.parent.name != "runtimes"
|
|
138
|
+
):
|
|
139
|
+
raise InstallationUpdatePlanError("target runtime slot is invalid")
|
|
140
|
+
runtime_venv = str(candidate.absolute())
|
|
124
141
|
cleanup = tuple(str(path) for path in (
|
|
125
142
|
operation_root / "build", operation_root / "download", operation_root / "pip-cache",
|
|
126
143
|
))
|
|
@@ -133,4 +150,5 @@ def prepare(data_root: Path, *, operation_id: str, artifact: Path, target_versio
|
|
|
133
150
|
steps=("INSTALLATION_LOCK", "INVENTORY_AND_COMPATIBILITY", "EXACT_ARTIFACT",
|
|
134
151
|
"QUIESCE", "BACKUP_AND_MIGRATION", "ACTIVATE", "VERIFY", "CLEANUP"),
|
|
135
152
|
legacy_adoption=legacy,
|
|
153
|
+
target_runtime_venv=runtime_venv,
|
|
136
154
|
)
|
|
@@ -130,7 +130,11 @@ def _paths(plan: InstallationUpdatePlan) -> _OperationPaths:
|
|
|
130
130
|
# the staged filename itself PEP 427-compatible so isolated ``pip`` can
|
|
131
131
|
# install it without relying on an original temporary filename.
|
|
132
132
|
staged = download / f"engineering_platform-{plan.target_version}-py3-none-any.whl"
|
|
133
|
-
candidate =
|
|
133
|
+
candidate = (
|
|
134
|
+
Path(plan.target_runtime_venv).expanduser().absolute()
|
|
135
|
+
if plan.target_runtime_venv is not None
|
|
136
|
+
else operation / _CANDIDATE_DIRECTORY
|
|
137
|
+
)
|
|
134
138
|
paths = _OperationPaths(
|
|
135
139
|
root=root,
|
|
136
140
|
operation=operation,
|
|
@@ -145,11 +149,12 @@ def _paths(plan: InstallationUpdatePlan) -> _OperationPaths:
|
|
|
145
149
|
("update operation", paths.operation),
|
|
146
150
|
("operation download directory", paths.download),
|
|
147
151
|
("staged operation artifact", paths.staged_artifact),
|
|
148
|
-
("candidate runtime", paths.candidate),
|
|
149
152
|
("operation pip cache", paths.pip_cache),
|
|
150
153
|
("candidate runtime marker", paths.marker),
|
|
151
154
|
):
|
|
152
155
|
_contained(root, path, label)
|
|
156
|
+
if plan.target_runtime_venv is None:
|
|
157
|
+
_contained(root, paths.candidate, "candidate runtime")
|
|
153
158
|
return paths
|
|
154
159
|
|
|
155
160
|
|
|
@@ -250,13 +255,53 @@ def _ensure_marker(plan: InstallationUpdatePlan, paths: _OperationPaths, staged:
|
|
|
250
255
|
_read_marker(plan, paths, staged)
|
|
251
256
|
return
|
|
252
257
|
if paths.candidate.exists() or paths.candidate.is_symlink():
|
|
253
|
-
|
|
258
|
+
if plan.target_runtime_venv is None:
|
|
259
|
+
raise InstallationUpdatePreparationError("candidate runtime exists without operation identity")
|
|
260
|
+
_read_runtime_slot_receipt(plan, paths)
|
|
254
261
|
try:
|
|
255
262
|
_write_marker(paths.marker, expected)
|
|
256
263
|
except OSError as error:
|
|
257
264
|
raise InstallationUpdatePreparationError("candidate runtime marker could not be retained") from error
|
|
258
265
|
|
|
259
266
|
|
|
267
|
+
def _runtime_slot_receipt(plan: InstallationUpdatePlan, paths: _OperationPaths) -> dict[str, object]:
|
|
268
|
+
return {
|
|
269
|
+
"schema_version": 1,
|
|
270
|
+
"version": plan.target_version,
|
|
271
|
+
"artifact_digest": plan.target_digest,
|
|
272
|
+
"source_revision": plan.target_source_revision,
|
|
273
|
+
"venv": str(paths.candidate),
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _runtime_slot_receipt_path(paths: _OperationPaths) -> Path:
|
|
278
|
+
return paths.candidate.parent / "runtime-slot.json"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _read_runtime_slot_receipt(plan: InstallationUpdatePlan, paths: _OperationPaths) -> None:
|
|
282
|
+
path = _runtime_slot_receipt_path(paths)
|
|
283
|
+
if path.is_symlink() or not path.is_file():
|
|
284
|
+
raise InstallationUpdatePreparationError("shared runtime slot lacks exact artifact identity")
|
|
285
|
+
try:
|
|
286
|
+
observed = json.loads(path.read_text(encoding="utf-8"))
|
|
287
|
+
except (OSError, json.JSONDecodeError) as error:
|
|
288
|
+
raise InstallationUpdatePreparationError("shared runtime slot identity is unreadable") from error
|
|
289
|
+
if observed != _runtime_slot_receipt(plan, paths):
|
|
290
|
+
raise InstallationUpdatePreparationError("shared runtime slot identity mismatch")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _ensure_runtime_slot_receipt(plan: InstallationUpdatePlan, paths: _OperationPaths) -> None:
|
|
294
|
+
if plan.target_runtime_venv is None:
|
|
295
|
+
return
|
|
296
|
+
path = _runtime_slot_receipt_path(paths)
|
|
297
|
+
expected = _runtime_slot_receipt(plan, paths)
|
|
298
|
+
if path.exists() or path.is_symlink():
|
|
299
|
+
_read_runtime_slot_receipt(plan, paths)
|
|
300
|
+
return
|
|
301
|
+
_write_marker(path, expected)
|
|
302
|
+
_read_runtime_slot_receipt(plan, paths)
|
|
303
|
+
|
|
304
|
+
|
|
260
305
|
def _verified_candidate(
|
|
261
306
|
plan: InstallationUpdatePlan,
|
|
262
307
|
paths: _OperationPaths,
|
|
@@ -345,6 +390,10 @@ def staged_execution_plan(
|
|
|
345
390
|
target_version=plan.target_version,
|
|
346
391
|
target_digest=plan.target_digest,
|
|
347
392
|
target_source_revision=plan.target_source_revision,
|
|
393
|
+
target_runtime_venv=(
|
|
394
|
+
None if plan.target_runtime_venv is None
|
|
395
|
+
else Path(plan.target_runtime_venv)
|
|
396
|
+
),
|
|
348
397
|
)
|
|
349
398
|
except installation_update_plan.InstallationUpdatePlanError as error:
|
|
350
399
|
raise InstallationUpdatePreparationError("staged candidate cannot be rebound to the registered installation") from error
|
|
@@ -352,6 +401,7 @@ def staged_execution_plan(
|
|
|
352
401
|
"operation_id", "installation_id", "data_root", "current_version",
|
|
353
402
|
"current_digest", "target_version", "target_digest",
|
|
354
403
|
"target_source_revision", "cleanup_targets", "steps", "legacy_adoption",
|
|
404
|
+
"target_runtime_venv",
|
|
355
405
|
)
|
|
356
406
|
if any(getattr(rebound, field) != getattr(plan, field) for field in unchanged):
|
|
357
407
|
raise InstallationUpdatePreparationError("registered installation changed before staged candidate binding")
|
|
@@ -517,6 +567,7 @@ def prepare_candidate(
|
|
|
517
567
|
_ensure_marker(plan, paths, staged)
|
|
518
568
|
_create_or_recover_venv(paths, builder=builder, runner=runner)
|
|
519
569
|
package = _install_or_verify_candidate(plan, paths, staged=staged, runner=runner)
|
|
570
|
+
_ensure_runtime_slot_receipt(plan, paths)
|
|
520
571
|
return PreparedUpdateCandidate(
|
|
521
572
|
operation_id=plan.operation_id,
|
|
522
573
|
installation_id=plan.installation_id,
|
|
@@ -12,7 +12,7 @@ from .storage import ENGINEERING_STORAGE_SCHEMA_VERSION
|
|
|
12
12
|
|
|
13
13
|
SEMVER = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$")
|
|
14
14
|
CONTRACT = re.compile(r"^(\d{4})\.(0[1-9]|1[0-2])$")
|
|
15
|
-
CURRENT_PLATFORM_VERSION = "2.3.
|
|
15
|
+
CURRENT_PLATFORM_VERSION = "2.3.102"
|
|
16
16
|
MANIFEST_FIELDS = frozenset(
|
|
17
17
|
{
|
|
18
18
|
"platform_version",
|
|
@@ -6,18 +6,29 @@ retries authentication on behalf of an execution.
|
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import os
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
import re
|
|
11
12
|
import shutil
|
|
12
13
|
import subprocess
|
|
13
14
|
|
|
14
|
-
from .providers import
|
|
15
|
+
from .providers import (
|
|
16
|
+
CodexCliProvider, LocalProcessProvider, codex_cli_executable,
|
|
17
|
+
github_cli_executable, MANAGED_GITHUB_CLI_EXECUTABLE_ENVIRONMENT,
|
|
18
|
+
)
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
_VERSION = re.compile(r"\b\d+(?:\.\d+)+(?:[-+][0-9A-Za-z.]+)?\b")
|
|
18
22
|
CODEX_READINESS_TIMEOUT_SECONDS = 2
|
|
19
23
|
|
|
20
24
|
|
|
25
|
+
def _github_executable() -> str | None:
|
|
26
|
+
"""Keep the legacy PATH observer while honoring a pinned system context."""
|
|
27
|
+
if MANAGED_GITHUB_CLI_EXECUTABLE_ENVIRONMENT in os.environ:
|
|
28
|
+
return github_cli_executable()
|
|
29
|
+
return shutil.which("gh")
|
|
30
|
+
|
|
31
|
+
|
|
21
32
|
def _classify(result: subprocess.CompletedProcess[str] | None) -> str:
|
|
22
33
|
if result is None:
|
|
23
34
|
return "CHECK_FAILED"
|
|
@@ -58,7 +69,7 @@ def runtime_details(root: Path) -> dict[str, dict[str, str]]:
|
|
|
58
69
|
codex_version = _version(CodexCliProvider().command("--version", timeout=CODEX_READINESS_TIMEOUT_SECONDS)) if codex_path else ""
|
|
59
70
|
except (OSError, subprocess.TimeoutExpired):
|
|
60
71
|
codex_version = ""
|
|
61
|
-
github_path =
|
|
72
|
+
github_path = _github_executable() or ""
|
|
62
73
|
try:
|
|
63
74
|
github_version = _version(LocalProcessProvider().execute(root, (github_path, "--version"))) if github_path else ""
|
|
64
75
|
except OSError:
|
|
@@ -98,7 +109,7 @@ def host_status(root: Path, *, require_github: bool = True) -> dict[str, dict[st
|
|
|
98
109
|
}
|
|
99
110
|
if not require_github:
|
|
100
111
|
return result
|
|
101
|
-
github_path =
|
|
112
|
+
github_path = _github_executable()
|
|
102
113
|
if github_path is None:
|
|
103
114
|
result["github"] = {"provider": "GITHUB", "state": "UNAVAILABLE"}
|
|
104
115
|
return result
|
|
@@ -117,11 +128,12 @@ def status(root: Path, *, require_github: bool = True) -> dict[str, dict[str, st
|
|
|
117
128
|
result = host_status(root, require_github=False)
|
|
118
129
|
if not require_github:
|
|
119
130
|
return result
|
|
120
|
-
|
|
131
|
+
github_path = _github_executable()
|
|
132
|
+
if github_path is None:
|
|
121
133
|
result["github"] = {"provider": "GITHUB", "state": "UNAVAILABLE"}
|
|
122
134
|
return result
|
|
123
135
|
try:
|
|
124
|
-
github_result = LocalProcessProvider().execute(root, (
|
|
136
|
+
github_result = LocalProcessProvider().execute(root, (github_path, "auth", "status", "--hostname", "github.com"))
|
|
125
137
|
except OSError:
|
|
126
138
|
github_result = None
|
|
127
139
|
github_state = _classify(github_result)
|
|
@@ -130,8 +142,13 @@ def status(root: Path, *, require_github: bool = True) -> dict[str, dict[str, st
|
|
|
130
142
|
# `gh repo view --json` uses GitHub's GraphQL quota. Readiness only
|
|
131
143
|
# needs a cheap repository-access proof, so use the REST endpoint
|
|
132
144
|
# and avoid turning an exhausted GraphQL quota into a login repair.
|
|
145
|
+
repository_executable = (
|
|
146
|
+
github_path
|
|
147
|
+
if MANAGED_GITHUB_CLI_EXECUTABLE_ENVIRONMENT in os.environ
|
|
148
|
+
else "gh"
|
|
149
|
+
)
|
|
133
150
|
repository_result = LocalProcessProvider().execute(
|
|
134
|
-
root, (
|
|
151
|
+
root, (repository_executable, "api", "repos/{owner}/{repo}", "--jq", ".full_name")
|
|
135
152
|
)
|
|
136
153
|
except OSError:
|
|
137
154
|
repository_result = None
|
{engineering_platform-2.3.101 → engineering_platform-2.3.102}/src/engineering_platform/providers.py
RENAMED
|
@@ -347,6 +347,7 @@ class PrivateRemoteAccessProvider(Protocol):
|
|
|
347
347
|
|
|
348
348
|
|
|
349
349
|
MANAGED_CODEX_CLI_PREFIX_ENVIRONMENT = "EP_MANAGED_CODEX_CLI_PREFIX"
|
|
350
|
+
MANAGED_GITHUB_CLI_EXECUTABLE_ENVIRONMENT = "EP_GITHUB_CLI_EXECUTABLE"
|
|
350
351
|
|
|
351
352
|
|
|
352
353
|
def default_engineering_platform_codex_cli_prefix() -> Path:
|
|
@@ -380,6 +381,23 @@ def codex_cli_executable() -> str | None:
|
|
|
380
381
|
return None
|
|
381
382
|
|
|
382
383
|
|
|
384
|
+
def github_cli_executable() -> str | None:
|
|
385
|
+
"""Select an instance-owned gh executable when the service pins one.
|
|
386
|
+
|
|
387
|
+
User-owned Project Agents retain their ordinary user/PATH behavior. A
|
|
388
|
+
system Server LaunchDaemon always receives the environment variable and
|
|
389
|
+
therefore fails closed instead of falling back when its owned executable
|
|
390
|
+
is missing or substituted.
|
|
391
|
+
"""
|
|
392
|
+
configured = os.environ.get(MANAGED_GITHUB_CLI_EXECUTABLE_ENVIRONMENT)
|
|
393
|
+
if configured is not None:
|
|
394
|
+
candidate = Path(configured).expanduser()
|
|
395
|
+
if candidate.is_absolute() and candidate.is_file() and os.access(candidate, os.X_OK):
|
|
396
|
+
return str(candidate.absolute())
|
|
397
|
+
return None
|
|
398
|
+
return shutil.which("gh")
|
|
399
|
+
|
|
400
|
+
|
|
383
401
|
class ProviderOutputLimitExceeded(RuntimeError):
|
|
384
402
|
"""An advisory provider invocation exceeded its retained output budget."""
|
|
385
403
|
|
|
@@ -576,7 +594,10 @@ class GitHubProvider:
|
|
|
576
594
|
return ProviderStatus("github", "configured", qualified, remote.stdout.strip() if qualified else "GitHub origin unavailable")
|
|
577
595
|
|
|
578
596
|
def github(self, *args: str) -> str:
|
|
579
|
-
|
|
597
|
+
executable = github_cli_executable()
|
|
598
|
+
if executable is None:
|
|
599
|
+
raise RuntimeError("Engineering Platform GitHub CLI is unavailable")
|
|
600
|
+
completed = subprocess.run((executable, *args), text=True, capture_output=True, check=False)
|
|
580
601
|
if completed.returncode:
|
|
581
602
|
raise RuntimeError(completed.stderr.strip() or "GitHub provider command failed")
|
|
582
603
|
return completed.stdout.strip()
|