porringer 0.2.1.dev90__tar.gz → 0.2.1.dev92__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.
- porringer-0.2.1.dev92/PKG-INFO +87 -0
- porringer-0.2.1.dev92/README.md +69 -0
- porringer-0.2.1.dev92/porringer/__init__.py +60 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/api.py +46 -25
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/backend.py +14 -13
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/builder.py +43 -13
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/cache.py +10 -14
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/__init__.py +3 -1
- porringer-0.2.1.dev92/porringer/backend/command/client.py +47 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/action_builder.py +121 -119
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/discovery.py +29 -8
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/execution.py +459 -571
- porringer-0.2.1.dev92/porringer/backend/command/core/inspection.py +387 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/phase.py +8 -21
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/presence.py +27 -25
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/core/resolution.py +129 -82
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/manifest.py +9 -7
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/package.py +105 -131
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/plugin.py +57 -32
- porringer-0.2.1.dev92/porringer/backend/command/profile.py +157 -0
- porringer-0.2.1.dev92/porringer/backend/command/project.py +318 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/command/self.py +4 -1
- porringer-0.2.1.dev92/porringer/backend/command/sync.py +594 -0
- porringer-0.2.1.dev92/porringer/backend/command/tool.py +269 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/resolver.py +4 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/backend/schema.py +6 -3
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/__init__.py +3 -1
- porringer-0.2.1.dev92/porringer/console/command/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/command/cache.py +22 -17
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/command/check.py +10 -7
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/command/download.py +14 -23
- porringer-0.2.1.dev92/porringer/console/command/env.py +276 -0
- porringer-0.2.1.dev92/porringer/console/command/install.py +262 -0
- porringer-0.2.1.dev92/porringer/console/command/open.py +45 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/command/package.py +9 -6
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/command/plugin.py +48 -42
- porringer-0.2.1.dev92/porringer/console/command/preview.py +296 -0
- porringer-0.2.1.dev92/porringer/console/command/schema.py +79 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/command/self.py +10 -7
- porringer-0.2.1.dev92/porringer/console/command/sync.py +377 -0
- porringer-0.2.1.dev92/porringer/console/common.py +50 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/console/entry.py +34 -8
- porringer-0.2.1.dev92/porringer/console/output.py +111 -0
- porringer-0.2.1.dev92/porringer/console/schema.py +56 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/path.py +1 -3
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/__init__.py +5 -2
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/environment.py +164 -48
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/manifest.py +4 -2
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/plugin_manager.py +41 -50
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/project_environment.py +131 -7
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/python_environment.py +21 -9
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/runtime.py +16 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/scm.py +5 -3
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/plugin_schema/tool_based.py +84 -95
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/core/schema.py +85 -18
- porringer-0.2.1.dev92/porringer/core/target.py +100 -0
- porringer-0.2.1.dev92/porringer/plugin/__init__.py +6 -0
- porringer-0.2.1.dev92/porringer/plugin/apt/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/apt/plugin.py +6 -19
- porringer-0.2.1.dev92/porringer/plugin/brew/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/brew/plugin.py +31 -16
- porringer-0.2.1.dev92/porringer/plugin/git/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/git/plugin.py +4 -1
- porringer-0.2.1.dev92/porringer/plugin/npm/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/npm/plugin.py +7 -8
- porringer-0.2.1.dev92/porringer/plugin/npm_project/__init__.py +4 -0
- porringer-0.2.1.dev92/porringer/plugin/npm_project/plugin.py +25 -0
- porringer-0.2.1.dev92/porringer/plugin/pdm/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pdm/plugin.py +10 -4
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pim/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pim/plugin.py +158 -52
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pip/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pip/plugin.py +67 -174
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pipx/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pipx/plugin.py +15 -3
- porringer-0.2.1.dev92/porringer/plugin/pnpm/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pnpm/plugin.py +7 -5
- porringer-0.2.1.dev92/porringer/plugin/pnpm_project/__init__.py +4 -0
- porringer-0.2.1.dev92/porringer/plugin/pnpm_project/plugin.py +29 -0
- porringer-0.2.1.dev92/porringer/plugin/poetry/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/poetry/plugin.py +26 -6
- porringer-0.2.1.dev92/porringer/plugin/pyenv/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/pyenv/plugin.py +17 -11
- porringer-0.2.1.dev92/porringer/plugin/uv/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/uv/plugin.py +4 -1
- porringer-0.2.1.dev92/porringer/plugin/uv_project/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/uv_project/plugin.py +6 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/winget/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/plugin/winget/plugin.py +4 -7
- porringer-0.2.1.dev92/porringer/plugin/yarn_project/__init__.py +4 -0
- porringer-0.2.1.dev92/porringer/plugin/yarn_project/plugin.py +33 -0
- porringer-0.2.1.dev92/porringer/schema/__init__.py +207 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/schema/cache.py +18 -4
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/schema/check.py +18 -5
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/schema/download.py +9 -4
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/schema/execution.py +61 -17
- porringer-0.2.1.dev92/porringer/schema/inspection.py +151 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/schema/manifest.py +15 -70
- porringer-0.2.1.dev92/porringer/schema/observability.py +162 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/schema/plugin.py +5 -2
- porringer-0.2.1.dev92/porringer/schema/profile.py +67 -0
- porringer-0.2.1.dev92/porringer/schema/progress.py +331 -0
- porringer-0.2.1.dev92/porringer/schema/project.py +85 -0
- porringer-0.2.1.dev92/porringer/schema/snapshot.py +26 -0
- porringer-0.2.1.dev92/porringer/schema/tool.py +58 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/mock/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/mock/environment.py +6 -3
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/mock/plugin_manager.py +17 -15
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/mock/project_environment.py +4 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/mock/scm.py +4 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/mock/subprocess.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/pytest/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/pytest/plugin.py +4 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/pytest/shared.py +10 -7
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/pytest/tests.py +90 -12
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/test/pytest/variants.py +6 -3
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/utility/__init__.py +3 -1
- porringer-0.2.1.dev92/porringer/utility/concurrency.py +57 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/utility/download.py +95 -76
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/utility/exception.py +15 -12
- porringer-0.2.1.dev92/porringer/utility/observability.py +456 -0
- porringer-0.2.1.dev92/porringer/utility/tool_environment.py +69 -0
- porringer-0.2.1.dev92/porringer/utility/trace.py +229 -0
- porringer-0.2.1.dev92/porringer/utility/utility.py +307 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/pyproject.toml +33 -32
- porringer-0.2.1.dev92/tests/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/conftest.py +58 -60
- porringer-0.2.1.dev92/tests/fixtures/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/fixtures/api.py +6 -5
- porringer-0.2.1.dev92/tests/fixtures/command_process.py +154 -0
- porringer-0.2.1.dev92/tests/fixtures/disposable_environment.py +180 -0
- porringer-0.2.1.dev92/tests/fixtures/factories.py +134 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/fixtures/http.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/fixtures/mock_plugins.py +7 -71
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/fixtures/packages.py +3 -1
- porringer-0.2.1.dev92/tests/fixtures/smoke_packages.py +117 -0
- porringer-0.2.1.dev92/tests/fixtures/strategies.py +161 -0
- porringer-0.2.1.dev92/tests/fixtures/tool_smoke.py +50 -0
- porringer-0.2.1.dev92/tests/integration/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/integration/plugins/__init__.py +3 -1
- porringer-0.2.1.dev92/tests/integration/plugins/winget/__init__.py +7 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/integration/plugins/winget/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/integration/test_bootstrap_presence.py +120 -0
- porringer-0.2.1.dev92/tests/integration/test_dry_run_acceptance.py +64 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/integration/test_example_bootstrap.py +19 -16
- porringer-0.2.1.dev92/tests/integration/test_example_presence.py +55 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/integration/test_frozen_app_presence.py +36 -32
- porringer-0.2.1.dev92/tests/integration/test_minimal_path.py +49 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/__init__.py +3 -1
- porringer-0.2.1.dev92/tests/unit/plugins/apt/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/apt/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/brew/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/brew/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/git/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/git/test_clone_detection.py +4 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/git/test_scm.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/npm/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/npm/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/npm_project/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/npm_project/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/pdm/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pdm/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/pim/__init__.py +4 -0
- porringer-0.2.1.dev92/tests/unit/plugins/pim/test_environment.py +207 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pip/__init__.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pip/conftest.py +4 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pip/test_auxiliary_tools.py +11 -10
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pip/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/pipx/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pipx/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/pnpm/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pnpm/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/pnpm_project/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pnpm_project/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/poetry/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/poetry/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/pyenv/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/pyenv/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/uv/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/uv/test_environment.py +6 -3
- porringer-0.2.1.dev92/tests/unit/plugins/uv_project/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/uv_project/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/plugins/yarn_project/__init__.py +4 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/plugins/yarn_project/test_environment.py +4 -1
- porringer-0.2.1.dev92/tests/unit/test_action_filtering.py +145 -0
- porringer-0.2.1.dev92/tests/unit/test_action_progress.py +422 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_backend_resolver.py +10 -10
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_bootstrap_cross_platform.py +7 -5
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_cache.py +23 -20
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_check.py +15 -12
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_check_updates.py +8 -64
- porringer-0.2.1.dev92/tests/unit/test_cli.py +331 -0
- porringer-0.2.1.dev92/tests/unit/test_command_plugin.py +312 -0
- porringer-0.2.1.dev92/tests/unit/test_command_process.py +81 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_command_self.py +12 -9
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_concurrency_and_client.py +146 -45
- porringer-0.2.1.dev92/tests/unit/test_console_output.py +109 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_deferred_resolution.py +3 -1
- porringer-0.2.1.dev92/tests/unit/test_disposable_environment.py +75 -0
- porringer-0.2.1.dev92/tests/unit/test_docs_contract.py +87 -0
- porringer-0.2.1.dev92/tests/unit/test_downstream_api.py +330 -0
- porringer-0.2.1.dev92/tests/unit/test_event_loop_safety.py +88 -0
- porringer-0.2.1.dev92/tests/unit/test_example_manifests.py +21 -0
- porringer-0.2.1.dev92/tests/unit/test_extension_list.py +79 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_extras_introspection.py +5 -2
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_frozen_app_detection.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_frozen_presence_resolution.py +3 -1
- porringer-0.2.1.dev92/tests/unit/test_hermeticity.py +32 -0
- porringer-0.2.1.dev92/tests/unit/test_install_cli.py +136 -0
- porringer-0.2.1.dev92/tests/unit/test_manifest_discovery.py +324 -0
- porringer-0.2.1.dev92/tests/unit/test_manifest_inspect.py +180 -0
- porringer-0.2.1.dev92/tests/unit/test_manifest_loading.py +448 -0
- porringer-0.2.1.dev92/tests/unit/test_manifest_schema.py +521 -0
- porringer-0.2.1.dev92/tests/unit/test_manifest_validation.py +192 -0
- porringer-0.2.1.dev92/tests/unit/test_output_schema_roundtrip.py +245 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_package_ref.py +4 -1
- porringer-0.2.1.dev92/tests/unit/test_package_ref_invariants.py +96 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_package_relation.py +26 -11
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_path_sync.py +4 -1
- porringer-0.2.1.dev92/tests/unit/test_pipx_bootstrap_contract.py +108 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_aux_and_errors.py +108 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_commands.py +98 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_dry_run_no_subprocess.py +67 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_lifecycle.py +397 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_manager.py +255 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_manager_presence.py +303 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_manager_upgrade.py +771 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_protocol.py +254 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_runtime.py +881 -0
- porringer-0.2.1.dev92/tests/unit/test_plugin_tags.py +777 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_presence_detection.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_project_directory.py +95 -52
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_project_root.py +34 -28
- porringer-0.2.1.dev92/tests/unit/test_run_command_progress.py +63 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_runtime_context_seeding.py +3 -1
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_runtime_propagation.py +38 -3
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_runtime_updates.py +4 -1
- porringer-0.2.1.dev92/tests/unit/test_smoke_packages.py +27 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_sub_action_progress.py +149 -36
- porringer-0.2.1.dev92/tests/unit/test_sync_inspection.py +278 -0
- porringer-0.2.1.dev92/tests/unit/test_target_resolution.py +56 -0
- porringer-0.2.1.dev92/tests/unit/test_trace.py +162 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_uninstall.py +22 -20
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_update_detection.py +84 -383
- porringer-0.2.1.dev92/tests/unit/test_update_detection_resolution.py +149 -0
- porringer-0.2.1.dev92/tests/unit/test_update_detection_spec.py +88 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/tests/unit/test_upgrade.py +7 -6
- porringer-0.2.1.dev92/tests/unit/update_detection_helpers.py +58 -0
- porringer-0.2.1.dev90/PKG-INFO +0 -54
- porringer-0.2.1.dev90/README.md +0 -38
- porringer-0.2.1.dev90/porringer/__init__.py +0 -21
- porringer-0.2.1.dev90/porringer/backend/command/core/wsl_overlay.py +0 -42
- porringer-0.2.1.dev90/porringer/backend/command/sync.py +0 -482
- porringer-0.2.1.dev90/porringer/console/command/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/console/command/schema.py +0 -58
- porringer-0.2.1.dev90/porringer/console/command/sync.py +0 -534
- porringer-0.2.1.dev90/porringer/console/schema.py +0 -39
- porringer-0.2.1.dev90/porringer/core/transport.py +0 -89
- porringer-0.2.1.dev90/porringer/plugin/__init__.py +0 -4
- porringer-0.2.1.dev90/porringer/plugin/apt/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/brew/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/bun/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/bun/plugin.py +0 -90
- porringer-0.2.1.dev90/porringer/plugin/bun_project/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/bun_project/plugin.py +0 -36
- porringer-0.2.1.dev90/porringer/plugin/deno/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/deno/plugin.py +0 -161
- porringer-0.2.1.dev90/porringer/plugin/deno_project/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/deno_project/plugin.py +0 -64
- porringer-0.2.1.dev90/porringer/plugin/git/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/npm/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/npm_project/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/npm_project/plugin.py +0 -34
- porringer-0.2.1.dev90/porringer/plugin/pdm/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/pnpm/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/pnpm_project/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/pnpm_project/plugin.py +0 -61
- porringer-0.2.1.dev90/porringer/plugin/poetry/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/pyenv/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/uv/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/uv_project/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/wsl/__init__.py +0 -6
- porringer-0.2.1.dev90/porringer/plugin/wsl/transport.py +0 -71
- porringer-0.2.1.dev90/porringer/plugin/wsl/utility.py +0 -122
- porringer-0.2.1.dev90/porringer/plugin/yarn_project/__init__.py +0 -1
- porringer-0.2.1.dev90/porringer/plugin/yarn_project/plugin.py +0 -66
- porringer-0.2.1.dev90/porringer/schema/__init__.py +0 -111
- porringer-0.2.1.dev90/porringer/schema/config.py +0 -14
- porringer-0.2.1.dev90/porringer/schema/progress.py +0 -157
- porringer-0.2.1.dev90/porringer/utility/utility.py +0 -226
- porringer-0.2.1.dev90/tests/__init__.py +0 -5
- porringer-0.2.1.dev90/tests/fixtures/__init__.py +0 -6
- porringer-0.2.1.dev90/tests/fixtures/manifests.py +0 -160
- porringer-0.2.1.dev90/tests/integration/__init__.py +0 -5
- porringer-0.2.1.dev90/tests/integration/plugins/git/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/integration/plugins/git/test_scm.py +0 -20
- porringer-0.2.1.dev90/tests/integration/plugins/pip/__init__.py +0 -5
- porringer-0.2.1.dev90/tests/integration/plugins/pip/test_environment.py +0 -20
- porringer-0.2.1.dev90/tests/integration/plugins/pipx/__init__.py +0 -5
- porringer-0.2.1.dev90/tests/integration/plugins/pipx/test_environment.py +0 -20
- porringer-0.2.1.dev90/tests/integration/plugins/winget/__init__.py +0 -5
- porringer-0.2.1.dev90/tests/integration/test_bare_environment.py +0 -49
- porringer-0.2.1.dev90/tests/integration/test_bootstrap_presence.py +0 -115
- porringer-0.2.1.dev90/tests/integration/test_example_presence.py +0 -42
- porringer-0.2.1.dev90/tests/unit/plugins/apt/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/brew/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/bun/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/bun/test_environment.py +0 -20
- porringer-0.2.1.dev90/tests/unit/plugins/bun_project/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/bun_project/test_environment.py +0 -16
- porringer-0.2.1.dev90/tests/unit/plugins/deno/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/deno/test_environment.py +0 -20
- porringer-0.2.1.dev90/tests/unit/plugins/deno_project/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/deno_project/test_environment.py +0 -16
- porringer-0.2.1.dev90/tests/unit/plugins/git/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/npm/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/npm_project/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/pdm/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/pim/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/pim/test_environment.py +0 -83
- porringer-0.2.1.dev90/tests/unit/plugins/pipx/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/pnpm/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/pnpm_project/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/poetry/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/pyenv/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/uv/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/uv_project/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/plugins/yarn_project/__init__.py +0 -1
- porringer-0.2.1.dev90/tests/unit/test_cli.py +0 -98
- porringer-0.2.1.dev90/tests/unit/test_command_plugin.py +0 -1882
- porringer-0.2.1.dev90/tests/unit/test_event_loop_safety.py +0 -87
- porringer-0.2.1.dev90/tests/unit/test_manifest.py +0 -1461
- porringer-0.2.1.dev90/tests/unit/test_package_filtering.py +0 -194
- porringer-0.2.1.dev90/tests/unit/test_plugin_filtering.py +0 -219
- porringer-0.2.1.dev90/tests/unit/test_plugin_manager.py +0 -1333
- porringer-0.2.1.dev90/tests/unit/test_wsl.py +0 -906
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/LICENSE.md +0 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/py.typed +0 -0
- {porringer-0.2.1.dev90 → porringer-0.2.1.dev92}/porringer/utility/py.typed +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: porringer
|
|
3
|
+
Version: 0.2.1.dev92
|
|
4
|
+
Author-Email: Synodic Software <contact@synodic.software>
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: homepage, https://github.com/synodic/porringer
|
|
7
|
+
Project-URL: repository, https://github.com/synodic/porringer
|
|
8
|
+
Requires-Python: >=3.14
|
|
9
|
+
Requires-Dist: typer[all]>=0.26.8
|
|
10
|
+
Requires-Dist: pydantic>=2.13.4
|
|
11
|
+
Requires-Dist: platformdirs>=4.10.0
|
|
12
|
+
Requires-Dist: userpath>=1.9.2
|
|
13
|
+
Requires-Dist: packaging>=26.2
|
|
14
|
+
Requires-Dist: aiohttp>=3.14.1
|
|
15
|
+
Requires-Dist: stamina>=26.1.0
|
|
16
|
+
Requires-Dist: filelock>=3.29.4
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<picture>
|
|
21
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/synodic/porringer/development/docs/images/porringer-dark.svg">
|
|
22
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/synodic/porringer/development/docs/images/porringer-light.svg">
|
|
23
|
+
<img width=25% alt="Porringer Logo" src="https://raw.githubusercontent.com/synodic/porringer/development/docs/images/porringer-light.svg">
|
|
24
|
+
</picture>
|
|
25
|
+
<br>
|
|
26
|
+
<em>A meta-package manager for all porridges</em>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
# Porringer
|
|
30
|
+
|
|
31
|
+
A CLI and Python API for synchronizing developer environments from a declarative manifest. Porringer reads the packages, tools, runtimes, repositories, and project sync work you want, then delegates the actual work to installed package managers through plugins.
|
|
32
|
+
|
|
33
|
+
[](LICENSE.md)
|
|
34
|
+
[](https://pypi.org/project/porringer/)
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- Manifest-driven setup from `porringer.json` or `[tool.porringer]` in `pyproject.toml`.
|
|
39
|
+
- Plugin-based installers for Python, Node, system packages, project sync, runtimes, and source repositories.
|
|
40
|
+
- Read-only previews with stable action IDs, command previews, diagnostics, and JSON output.
|
|
41
|
+
- Sync strategies for minimal installs, latest allowed versions, or exact declared constraints.
|
|
42
|
+
- JSONL progress streams, replay records, and trace artifacts for downstream clients.
|
|
43
|
+
- Python APIs for inspection, execution, cached projects, profiles, tools, progress events, and client snapshots.
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
Install Porringer into an isolated CLI environment, then preview a manifest before running it:
|
|
48
|
+
|
|
49
|
+
```shell
|
|
50
|
+
pipx install porringer
|
|
51
|
+
porringer preview examples/python-dev
|
|
52
|
+
porringer install examples/python-dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See [Setup](https://synodic.github.io/porringer/setup/) for `uv`, `pip`, and development checkout instructions.
|
|
56
|
+
|
|
57
|
+
## Development
|
|
58
|
+
|
|
59
|
+
Porringer uses [PDM](https://pdm-project.org/en/latest/) for local development. Common tasks are defined in `pyproject.toml` under `[tool.pdm.scripts]`:
|
|
60
|
+
|
|
61
|
+
```shell
|
|
62
|
+
pdm install
|
|
63
|
+
pdm run test
|
|
64
|
+
pdm run generate
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
See [Development](https://synodic.github.io/porringer/development/) for test lanes, traces, smoke tests, and diagnostics.
|
|
68
|
+
|
|
69
|
+
For contribution guidelines, see [CONTRIBUTING.md](https://github.com/synodic/.github/blob/stable/CONTRIBUTING.md).
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
- [Documentation home](https://synodic.github.io/porringer/)
|
|
74
|
+
- [Setup](https://synodic.github.io/porringer/setup/)
|
|
75
|
+
- [Install links](https://synodic.github.io/porringer/links/)
|
|
76
|
+
- [Install command](https://synodic.github.io/porringer/install/)
|
|
77
|
+
- [Preview command](https://synodic.github.io/porringer/preview/)
|
|
78
|
+
- [Check command](https://synodic.github.io/porringer/check/)
|
|
79
|
+
- [Download command](https://synodic.github.io/porringer/download/)
|
|
80
|
+
- [API surface](https://synodic.github.io/porringer/api/)
|
|
81
|
+
- [Development](https://synodic.github.io/porringer/development/)
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
This project is licensed under the MIT License. See [LICENSE.md](LICENSE.md) for details.
|
|
86
|
+
|
|
87
|
+
Copyright © 2025 Synodic Software
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/synodic/porringer/development/docs/images/porringer-dark.svg">
|
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/synodic/porringer/development/docs/images/porringer-light.svg">
|
|
5
|
+
<img width=25% alt="Porringer Logo" src="https://raw.githubusercontent.com/synodic/porringer/development/docs/images/porringer-light.svg">
|
|
6
|
+
</picture>
|
|
7
|
+
<br>
|
|
8
|
+
<em>A meta-package manager for all porridges</em>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
# Porringer
|
|
12
|
+
|
|
13
|
+
A CLI and Python API for synchronizing developer environments from a declarative manifest. Porringer reads the packages, tools, runtimes, repositories, and project sync work you want, then delegates the actual work to installed package managers through plugins.
|
|
14
|
+
|
|
15
|
+
[](LICENSE.md)
|
|
16
|
+
[](https://pypi.org/project/porringer/)
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Manifest-driven setup from `porringer.json` or `[tool.porringer]` in `pyproject.toml`.
|
|
21
|
+
- Plugin-based installers for Python, Node, system packages, project sync, runtimes, and source repositories.
|
|
22
|
+
- Read-only previews with stable action IDs, command previews, diagnostics, and JSON output.
|
|
23
|
+
- Sync strategies for minimal installs, latest allowed versions, or exact declared constraints.
|
|
24
|
+
- JSONL progress streams, replay records, and trace artifacts for downstream clients.
|
|
25
|
+
- Python APIs for inspection, execution, cached projects, profiles, tools, progress events, and client snapshots.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
Install Porringer into an isolated CLI environment, then preview a manifest before running it:
|
|
30
|
+
|
|
31
|
+
```shell
|
|
32
|
+
pipx install porringer
|
|
33
|
+
porringer preview examples/python-dev
|
|
34
|
+
porringer install examples/python-dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
See [Setup](https://synodic.github.io/porringer/setup/) for `uv`, `pip`, and development checkout instructions.
|
|
38
|
+
|
|
39
|
+
## Development
|
|
40
|
+
|
|
41
|
+
Porringer uses [PDM](https://pdm-project.org/en/latest/) for local development. Common tasks are defined in `pyproject.toml` under `[tool.pdm.scripts]`:
|
|
42
|
+
|
|
43
|
+
```shell
|
|
44
|
+
pdm install
|
|
45
|
+
pdm run test
|
|
46
|
+
pdm run generate
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See [Development](https://synodic.github.io/porringer/development/) for test lanes, traces, smoke tests, and diagnostics.
|
|
50
|
+
|
|
51
|
+
For contribution guidelines, see [CONTRIBUTING.md](https://github.com/synodic/.github/blob/stable/CONTRIBUTING.md).
|
|
52
|
+
|
|
53
|
+
## Documentation
|
|
54
|
+
|
|
55
|
+
- [Documentation home](https://synodic.github.io/porringer/)
|
|
56
|
+
- [Setup](https://synodic.github.io/porringer/setup/)
|
|
57
|
+
- [Install links](https://synodic.github.io/porringer/links/)
|
|
58
|
+
- [Install command](https://synodic.github.io/porringer/install/)
|
|
59
|
+
- [Preview command](https://synodic.github.io/porringer/preview/)
|
|
60
|
+
- [Check command](https://synodic.github.io/porringer/check/)
|
|
61
|
+
- [Download command](https://synodic.github.io/porringer/download/)
|
|
62
|
+
- [API surface](https://synodic.github.io/porringer/api/)
|
|
63
|
+
- [Development](https://synodic.github.io/porringer/development/)
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
This project is licensed under the MIT License. See [LICENSE.md](LICENSE.md) for details.
|
|
68
|
+
|
|
69
|
+
Copyright © 2025 Synodic Software
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Public package exports.
|
|
2
|
+
|
|
3
|
+
Public package entry points for Porringer.
|
|
4
|
+
|
|
5
|
+
This package exposes the public API, backend helpers, CLI commands, and
|
|
6
|
+
supporting utilities that applications use when integrating with Porringer.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from importlib.metadata import PackageNotFoundError
|
|
11
|
+
from importlib.metadata import version as _metadata_version
|
|
12
|
+
|
|
13
|
+
from porringer.api import API
|
|
14
|
+
from porringer.backend.command.core.discovery import DiscoveredPlugins
|
|
15
|
+
from porringer.core.plugin_schema.runtime import RuntimeContext
|
|
16
|
+
from porringer.schema.cache import LocalConfiguration
|
|
17
|
+
from porringer.schema.execution import SetupParameters
|
|
18
|
+
from porringer.schema.manifest import SetupManifest
|
|
19
|
+
from porringer.utility.exception import (
|
|
20
|
+
CommandTimeoutError,
|
|
21
|
+
ManifestError,
|
|
22
|
+
ManifestValidationCode,
|
|
23
|
+
NotSupportedError,
|
|
24
|
+
PluginDependencyError,
|
|
25
|
+
PluginError,
|
|
26
|
+
PorringerError,
|
|
27
|
+
ProcessError,
|
|
28
|
+
SetupError,
|
|
29
|
+
UpdateError,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
'API',
|
|
34
|
+
'CommandTimeoutError',
|
|
35
|
+
'DiscoveredPlugins',
|
|
36
|
+
'LocalConfiguration',
|
|
37
|
+
'ManifestError',
|
|
38
|
+
'ManifestValidationCode',
|
|
39
|
+
'NotSupportedError',
|
|
40
|
+
'PluginDependencyError',
|
|
41
|
+
'PluginError',
|
|
42
|
+
'PorringerError',
|
|
43
|
+
'ProcessError',
|
|
44
|
+
'RuntimeContext',
|
|
45
|
+
'SetupError',
|
|
46
|
+
'SetupManifest',
|
|
47
|
+
'SetupParameters',
|
|
48
|
+
'UpdateError',
|
|
49
|
+
'__version__',
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
__version__: str = _metadata_version('porringer')
|
|
54
|
+
except PackageNotFoundError:
|
|
55
|
+
__version__ = '0.0.0'
|
|
56
|
+
|
|
57
|
+
# Register a NullHandler so library consumers can attach their own logging
|
|
58
|
+
# handlers without seeing warnings about an unconfigured logger.
|
|
59
|
+
# See https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library
|
|
60
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Helpers for api.
|
|
2
|
+
|
|
3
|
+
Public API surface for Porringer.
|
|
4
|
+
"""
|
|
2
5
|
|
|
3
6
|
import asyncio
|
|
4
7
|
import logging
|
|
5
8
|
|
|
6
9
|
from porringer.backend.builder import Builder
|
|
7
10
|
from porringer.backend.cache import DirectoryCacheManager
|
|
11
|
+
from porringer.backend.command.client import ClientCommands
|
|
8
12
|
from porringer.backend.command.core.discovery import DiscoveredPlugins, discover_all_plugins
|
|
9
13
|
from porringer.backend.command.package import PackageCommands
|
|
10
14
|
from porringer.backend.command.plugin import PluginCommands
|
|
15
|
+
from porringer.backend.command.profile import ProfileCommands
|
|
16
|
+
from porringer.backend.command.project import ProjectCommands
|
|
11
17
|
from porringer.backend.command.self import check_self_updates
|
|
12
18
|
from porringer.backend.command.sync import SyncCommands
|
|
19
|
+
from porringer.backend.command.tool import ToolCommands
|
|
13
20
|
from porringer.backend.resolver import resolve_configuration
|
|
14
21
|
from porringer.backend.schema import GlobalConfiguration
|
|
22
|
+
from porringer.core.plugin_schema.runtime import RuntimeContext
|
|
15
23
|
from porringer.schema import (
|
|
16
24
|
DownloadParameters,
|
|
17
25
|
DownloadResult,
|
|
@@ -25,19 +33,14 @@ logger = logging.getLogger(__name__)
|
|
|
25
33
|
|
|
26
34
|
|
|
27
35
|
class API:
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
Provides namespace sub-APIs:
|
|
36
|
+
"""Programmatic interface for Porringer's core operations.
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
upgrade, uninstall packages, check for updates).
|
|
36
|
-
* ``api.sync`` — manifest loading, streaming execution.
|
|
37
|
-
* ``api.cache`` — directory registration and validation.
|
|
38
|
+
The class exposes stable sub-APIs for manifest inspection and execution,
|
|
39
|
+
package management, project registration, cached tool operations, and
|
|
40
|
+
profile handling.
|
|
38
41
|
|
|
39
42
|
Cross-cutting helpers live directly on the ``API`` class:
|
|
40
|
-
:meth:`discover_plugins`, :meth:`check_self_updates`,
|
|
43
|
+
:meth:`discover_plugins`, :meth:`check_self_updates`, and
|
|
41
44
|
:meth:`download`.
|
|
42
45
|
"""
|
|
43
46
|
|
|
@@ -46,7 +49,7 @@ class API:
|
|
|
46
49
|
local_configuration: LocalConfiguration,
|
|
47
50
|
global_configuration: GlobalConfiguration | None = None,
|
|
48
51
|
) -> None:
|
|
49
|
-
"""Initializes the API
|
|
52
|
+
"""Initializes the API.
|
|
50
53
|
|
|
51
54
|
Args:
|
|
52
55
|
local_configuration: The local configuration.
|
|
@@ -57,13 +60,17 @@ class API:
|
|
|
57
60
|
|
|
58
61
|
configuration = resolve_configuration(local_configuration, global_configuration)
|
|
59
62
|
|
|
60
|
-
self.
|
|
63
|
+
self._cache = DirectoryCacheManager(configuration.data_directory)
|
|
61
64
|
|
|
62
|
-
self.
|
|
65
|
+
self.extension = PluginCommands()
|
|
63
66
|
self.package = PackageCommands()
|
|
64
|
-
self.sync = SyncCommands(self.
|
|
67
|
+
self.sync = SyncCommands(self._cache)
|
|
68
|
+
self.project = ProjectCommands(self._cache, self.sync)
|
|
69
|
+
self.tool = ToolCommands(self.sync, self.package)
|
|
70
|
+
self.profile = ProfileCommands(self.sync)
|
|
71
|
+
self.client = ClientCommands(self.project, self.tool)
|
|
65
72
|
|
|
66
|
-
#
|
|
73
|
+
# Discover plugins and resolve runtime information.
|
|
67
74
|
|
|
68
75
|
@staticmethod
|
|
69
76
|
async def discover_plugins(
|
|
@@ -76,14 +83,14 @@ class API:
|
|
|
76
83
|
This is the recommended entry-point for GUI callers. It
|
|
77
84
|
returns a :class:`DiscoveredPlugins` object that can be
|
|
78
85
|
forwarded to every subsequent operation
|
|
79
|
-
(``api.sync.
|
|
86
|
+
(``api.sync.inspect``, ``api.sync.run``, ``api.package.list``,
|
|
80
87
|
``api.package.upgrade``, etc.) so that plugin discovery and
|
|
81
88
|
runtime resolution happen exactly once.
|
|
82
89
|
|
|
83
90
|
Args:
|
|
84
91
|
use_cache: Reuse cached entry-point scan metadata when
|
|
85
92
|
``True`` (the default). Pass ``False`` to force a
|
|
86
|
-
fresh scan after installing
|
|
93
|
+
fresh scan after installing or uninstalling extension packages.
|
|
87
94
|
resolve_runtime: When ``True`` (the default), resolve a
|
|
88
95
|
:class:`RuntimeContext` from available
|
|
89
96
|
``RuntimeProvider`` plugins and attach it to the
|
|
@@ -96,15 +103,29 @@ class API:
|
|
|
96
103
|
"""
|
|
97
104
|
plugins = await asyncio.to_thread(discover_all_plugins, use_cache=use_cache)
|
|
98
105
|
if resolve_runtime:
|
|
99
|
-
|
|
100
|
-
logger.debug(
|
|
101
|
-
'discover_plugins: runtime_context=%s',
|
|
102
|
-
{k: str(v) for k, v in plugins.runtime_context.executables.items()}
|
|
103
|
-
if plugins.runtime_context.executables
|
|
104
|
-
else '<empty>',
|
|
105
|
-
)
|
|
106
|
+
await API.resolve_runtime_context(plugins)
|
|
106
107
|
return plugins
|
|
107
108
|
|
|
109
|
+
@staticmethod
|
|
110
|
+
async def resolve_runtime_context(plugins: DiscoveredPlugins) -> RuntimeContext:
|
|
111
|
+
"""Resolve and attach runtime context for previously discovered plugins.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
plugins: A :class:`DiscoveredPlugins` object, usually from
|
|
115
|
+
:meth:`discover_plugins` with ``resolve_runtime=False``.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
The resolved :class:`RuntimeContext` attached to ``plugins``.
|
|
119
|
+
"""
|
|
120
|
+
plugins.runtime_context = await Builder.resolve_runtime_context(plugins.environments)
|
|
121
|
+
logger.debug(
|
|
122
|
+
'resolve_runtime_context: runtime_context=%s',
|
|
123
|
+
{k: str(v) for k, v in plugins.runtime_context.executables.items()}
|
|
124
|
+
if plugins.runtime_context.executables
|
|
125
|
+
else '<empty>',
|
|
126
|
+
)
|
|
127
|
+
return plugins.runtime_context
|
|
128
|
+
|
|
108
129
|
@staticmethod
|
|
109
130
|
async def check_self_updates() -> PackageUpdateInfo:
|
|
110
131
|
"""Check for updates to the Porringer package by querying PyPI.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Public package exports for the backend module.
|
|
2
|
+
|
|
3
|
+
Backend package for Porringer.
|
|
2
4
|
|
|
3
5
|
This package contains modules and utilities for backend processing,
|
|
4
6
|
including configuration resolution, plugin management, and command execution.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
"""Backend
|
|
1
|
+
"""Backend helpers for backend.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Backend resolution for mapping `(kind, ecosystem)` pairs to installer plugins.
|
|
4
|
+
|
|
5
|
+
The `BackendResolver` chooses the plugin that should handle each
|
|
6
|
+
`(PluginKind, ecosystem)` pair declared in a manifest. For example,
|
|
7
|
+
`(PACKAGE, "python")` might resolve to `uv`, `pip`, or another matching
|
|
8
|
+
plugin depending on availability and user preferences.
|
|
7
9
|
"""
|
|
8
10
|
|
|
9
11
|
import logging
|
|
@@ -16,7 +18,7 @@ from porringer.core.schema import Ecosystem, Plugin, PluginKind
|
|
|
16
18
|
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
18
20
|
|
|
19
|
-
#
|
|
21
|
+
# A plugin that participates in backend resolution.
|
|
20
22
|
BackendPlugin = Plugin
|
|
21
23
|
|
|
22
24
|
|
|
@@ -62,19 +64,18 @@ class BackendResolver:
|
|
|
62
64
|
self._preferences = preferences or {}
|
|
63
65
|
self._runtime_context = runtime_context
|
|
64
66
|
|
|
65
|
-
# Index
|
|
67
|
+
# Index registered plugin names by `(kind, ecosystem)` pair.
|
|
66
68
|
self._backend_plugins: dict[tuple[PluginKind, Ecosystem], list[str]] = defaultdict(list)
|
|
67
69
|
for name, plugin in self._all_plugins.items():
|
|
68
70
|
ecosystem = type(plugin).ecosystem()
|
|
69
71
|
if ecosystem is not None:
|
|
70
72
|
self._backend_plugins[(type(plugin).plugin_kind(), ecosystem)].append(name)
|
|
71
73
|
|
|
72
|
-
# Resolve only the pairs the caller
|
|
73
|
-
# *needed_pairs* is ``None
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
# current manifest.
|
|
74
|
+
# Resolve only the pairs the caller needs right away. When
|
|
75
|
+
# *needed_pairs* is ``None``, every registered pair is resolved to
|
|
76
|
+
# preserve the existing behavior. Passing an explicit set avoids
|
|
77
|
+
# noisy "No available plugin" log messages for ecosystems that are
|
|
78
|
+
# registered via entry points but irrelevant to the current manifest.
|
|
78
79
|
resolve_keys = needed_pairs if needed_pairs is not None else set(self._backend_plugins)
|
|
79
80
|
|
|
80
81
|
# Resolve once and cache
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Backend helpers for builder.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Helpers for discovering and constructing plugins from entry points.
|
|
4
|
+
|
|
5
|
+
The builder offers reusable logic for scanning entry-point groups,
|
|
6
|
+
instantiating plugins, and resolving runtime executables without duplicating
|
|
7
|
+
per-plugin-kind boilerplate.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
10
|
import asyncio
|
|
@@ -16,7 +18,12 @@ from packaging.utils import canonicalize_name
|
|
|
16
18
|
from packaging.version import Version
|
|
17
19
|
|
|
18
20
|
from porringer.core.plugin_schema.environment import Environment
|
|
19
|
-
from porringer.core.plugin_schema.runtime import
|
|
21
|
+
from porringer.core.plugin_schema.runtime import (
|
|
22
|
+
DefaultRuntimeExecutableProvider,
|
|
23
|
+
ResolvedRuntime,
|
|
24
|
+
RuntimeContext,
|
|
25
|
+
RuntimeProvider,
|
|
26
|
+
)
|
|
20
27
|
from porringer.core.schema import Distribution, Plugin, PluginDependency, PluginParameters
|
|
21
28
|
|
|
22
29
|
logger = logging.getLogger(__name__)
|
|
@@ -36,12 +43,29 @@ async def _resolve_provider_executable(
|
|
|
36
43
|
env: RuntimeProvider,
|
|
37
44
|
kind: str,
|
|
38
45
|
) -> Path | None:
|
|
39
|
-
"""Resolve a single executable from *env* trying
|
|
46
|
+
"""Resolve a single executable from *env* by trying the fastest route first.
|
|
40
47
|
|
|
41
48
|
Returns the resolved :class:`~pathlib.Path` or ``None`` when no
|
|
42
|
-
executable
|
|
49
|
+
executable can be obtained.
|
|
43
50
|
"""
|
|
44
|
-
#
|
|
51
|
+
# Use the provider's built-in default executable when it is available.
|
|
52
|
+
if isinstance(env, DefaultRuntimeExecutableProvider):
|
|
53
|
+
try:
|
|
54
|
+
executable = await env.default_executable()
|
|
55
|
+
except Exception:
|
|
56
|
+
logger.debug("default_executable() failed for '%s'", name, exc_info=True)
|
|
57
|
+
executable = None
|
|
58
|
+
|
|
59
|
+
if executable is not None:
|
|
60
|
+
logger.debug(
|
|
61
|
+
"Resolved runtime '%s' via provider '%s' default_executable: path=%s",
|
|
62
|
+
kind,
|
|
63
|
+
name,
|
|
64
|
+
executable,
|
|
65
|
+
)
|
|
66
|
+
return executable
|
|
67
|
+
|
|
68
|
+
# Try the provider's preferred default tag next.
|
|
45
69
|
try:
|
|
46
70
|
default = await env.default_tag()
|
|
47
71
|
except Exception:
|
|
@@ -76,7 +100,7 @@ async def _resolve_provider_executable(
|
|
|
76
100
|
name,
|
|
77
101
|
)
|
|
78
102
|
|
|
79
|
-
#
|
|
103
|
+
# Fall back to scanning every available tag.
|
|
80
104
|
try:
|
|
81
105
|
tags = await env.available_tags()
|
|
82
106
|
except Exception:
|
|
@@ -122,7 +146,7 @@ async def _resolve_provider_executable(
|
|
|
122
146
|
|
|
123
147
|
|
|
124
148
|
class Builder:
|
|
125
|
-
"""Helper class for building Porringer projects"""
|
|
149
|
+
"""Helper class for building Porringer projects."""
|
|
126
150
|
|
|
127
151
|
# ------------------------------------------------------------------
|
|
128
152
|
# Generic discovery & construction
|
|
@@ -134,7 +158,7 @@ class Builder:
|
|
|
134
158
|
base_class: type[T],
|
|
135
159
|
*,
|
|
136
160
|
check_dependencies: bool = False,
|
|
137
|
-
) -> list[PluginInformation[T]]:
|
|
161
|
+
) -> tuple[list[PluginInformation[T]], dict[str, str]]:
|
|
138
162
|
"""Search for registered plugins in an entry-point group.
|
|
139
163
|
|
|
140
164
|
Scans `porringer.<group>` for classes that are subclasses of
|
|
@@ -147,9 +171,12 @@ class Builder:
|
|
|
147
171
|
and filters out plugins with unmet required dependencies.
|
|
148
172
|
|
|
149
173
|
Returns:
|
|
150
|
-
A
|
|
174
|
+
A tuple of ``(infos, load_errors)`` where *load_errors* maps
|
|
175
|
+
entry-point name to a human-readable error message for every
|
|
176
|
+
plugin that could not be loaded.
|
|
151
177
|
"""
|
|
152
178
|
plugin_types: list[PluginInformation[T]] = []
|
|
179
|
+
load_errors: dict[str, str] = {}
|
|
153
180
|
|
|
154
181
|
entry_points = list(metadata.entry_points(group=f'porringer.{group}'))
|
|
155
182
|
logger.debug('Entry points for porringer.%s: %s', group, [ep.name for ep in entry_points])
|
|
@@ -159,12 +186,15 @@ class Builder:
|
|
|
159
186
|
loaded_type = entry_point.load()
|
|
160
187
|
except Exception as e:
|
|
161
188
|
logger.warning("Plugin '%s' could not be loaded: %s. Skipping", entry_point.name, e)
|
|
189
|
+
load_errors[entry_point.name] = str(e)
|
|
162
190
|
continue
|
|
163
191
|
|
|
164
192
|
plugin_name = str(canonicalize_name(entry_point.name))
|
|
165
193
|
|
|
166
194
|
if entry_point.dist is None:
|
|
195
|
+
msg = 'plugin is not installed'
|
|
167
196
|
logger.warning("Plugin '%s' is not installed. Skipping", plugin_name)
|
|
197
|
+
load_errors[plugin_name] = msg
|
|
168
198
|
continue
|
|
169
199
|
|
|
170
200
|
if not issubclass(loaded_type, base_class):
|
|
@@ -176,7 +206,7 @@ class Builder:
|
|
|
176
206
|
if check_dependencies:
|
|
177
207
|
plugin_types = Builder._resolve_dependencies(plugin_types)
|
|
178
208
|
|
|
179
|
-
return plugin_types
|
|
209
|
+
return plugin_types, load_errors
|
|
180
210
|
|
|
181
211
|
@staticmethod
|
|
182
212
|
def build_plugin[T: Plugin](info: PluginInformation[T]) -> T:
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Directory cache management for manifest directories."""
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import json
|
|
4
5
|
import logging
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
8
|
+
from filelock import FileLock
|
|
7
9
|
from pydantic import ValidationError
|
|
8
10
|
|
|
9
11
|
from porringer.backend.command.manifest import has_manifest
|
|
@@ -69,32 +71,26 @@ class DirectoryCacheManager:
|
|
|
69
71
|
return self._cache
|
|
70
72
|
|
|
71
73
|
def _save(self) -> None:
|
|
72
|
-
"""Save cache to disk atomically."""
|
|
74
|
+
"""Save cache to disk atomically under a cross-process lock."""
|
|
73
75
|
if self._cache is None:
|
|
74
76
|
return
|
|
75
77
|
|
|
76
78
|
self.data_directory.mkdir(parents=True, exist_ok=True)
|
|
77
79
|
|
|
78
|
-
# Write to temp file then rename for atomic write
|
|
79
80
|
temp_path = self._cache_path.with_suffix('.tmp')
|
|
81
|
+
lock_path = self._cache_path.with_suffix(f'{self._cache_path.suffix}.lock')
|
|
80
82
|
try:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
with FileLock(lock_path):
|
|
84
|
+
temp_path.write_text(self._cache.model_dump_json(indent=2), encoding='utf-8')
|
|
85
|
+
temp_path.replace(self._cache_path)
|
|
83
86
|
logger.debug(f'Saved directory cache to {self._cache_path}')
|
|
84
|
-
except PermissionError as e:
|
|
85
|
-
if temp_path.exists():
|
|
86
|
-
temp_path.unlink()
|
|
87
|
-
logger.error(f'Permission denied writing cache: {e}')
|
|
88
|
-
raise
|
|
89
87
|
except OSError as e:
|
|
90
|
-
if temp_path.exists():
|
|
91
|
-
temp_path.unlink()
|
|
92
88
|
logger.error(f'Failed to write cache: {e}')
|
|
93
89
|
raise
|
|
94
|
-
|
|
90
|
+
finally:
|
|
95
91
|
if temp_path.exists():
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
with contextlib.suppress(OSError):
|
|
93
|
+
temp_path.unlink()
|
|
98
94
|
|
|
99
95
|
@staticmethod
|
|
100
96
|
def _normalize_path(path: Path) -> Path:
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Public package exports for the backend.command module.
|
|
2
|
+
|
|
3
|
+
Command package for Porringer backend.
|
|
2
4
|
|
|
3
5
|
This package contains command modules for backend operations,
|
|
4
6
|
including plugin management and self-update functionalities.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""CLI command implementation for client.
|
|
2
|
+
|
|
3
|
+
Client-oriented aggregate commands.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
|
|
8
|
+
from porringer.backend.command.core.discovery import DiscoveredPlugins, discover_all_plugins
|
|
9
|
+
from porringer.backend.command.core.inspection import plugin_snapshots
|
|
10
|
+
from porringer.backend.command.project import ProjectCommands
|
|
11
|
+
from porringer.backend.command.tool import ToolCommands
|
|
12
|
+
from porringer.schema import ClientSnapshot, InspectionMode, ManagedToolReport
|
|
13
|
+
from porringer.utility.observability import result_status
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ClientCommands:
|
|
17
|
+
"""Aggregate read models for long-lived clients."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, project_commands: ProjectCommands, tool_commands: ToolCommands) -> None:
|
|
20
|
+
"""Initialize client commands."""
|
|
21
|
+
self._project = project_commands
|
|
22
|
+
self._tool = tool_commands
|
|
23
|
+
|
|
24
|
+
async def snapshot(
|
|
25
|
+
self,
|
|
26
|
+
*,
|
|
27
|
+
inspection_mode: InspectionMode = InspectionMode.FAST,
|
|
28
|
+
include_updates: bool = False,
|
|
29
|
+
plugins: DiscoveredPlugins | None = None,
|
|
30
|
+
) -> ClientSnapshot:
|
|
31
|
+
"""Return a low-latency state bundle for GUI or agent clients."""
|
|
32
|
+
shared_plugins = plugins or await asyncio.to_thread(discover_all_plugins, use_cache=True)
|
|
33
|
+
projects = await self._project.inspect_cached(inspection_mode=inspection_mode, plugins=shared_plugins)
|
|
34
|
+
updates: ManagedToolReport | None = None
|
|
35
|
+
if include_updates:
|
|
36
|
+
updates = await self._tool.check_updates(plugins=shared_plugins)
|
|
37
|
+
diagnostics = projects.diagnostics + (updates.diagnostics if updates is not None else ())
|
|
38
|
+
follow_up_actions = projects.follow_up_actions + (updates.follow_up_actions if updates is not None else ())
|
|
39
|
+
return ClientSnapshot(
|
|
40
|
+
status=result_status(projects.success and (updates.success if updates is not None else True), diagnostics),
|
|
41
|
+
inspection_mode=inspection_mode,
|
|
42
|
+
plugins=plugin_snapshots(shared_plugins),
|
|
43
|
+
projects=projects,
|
|
44
|
+
updates=updates,
|
|
45
|
+
diagnostics=diagnostics,
|
|
46
|
+
follow_up_actions=follow_up_actions,
|
|
47
|
+
)
|