base-cli 0.4.3__tar.gz → 0.5.0__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.
- base_cli-0.5.0/.github/ISSUE_TEMPLATE/bug_report.yml +50 -0
- base_cli-0.5.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- base_cli-0.5.0/.github/ISSUE_TEMPLATE/documentation.yml +25 -0
- base_cli-0.5.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- base_cli-0.5.0/.github/ISSUE_TEMPLATE/support.md +12 -0
- base_cli-0.5.0/.github/ISSUE_TEMPLATE/support.yml +30 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/workflows/compatibility.yml +37 -4
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/workflows/dependency-matrix.yml +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/workflows/docs.yml +6 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/workflows/package.yml +188 -18
- base_cli-0.5.0/.github/workflows/project-intake.yml +309 -0
- base_cli-0.5.0/.github/workflows/tests.yml +201 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/CHANGELOG.md +101 -8
- {base_cli-0.4.3 → base_cli-0.5.0}/CONTRIBUTING.md +7 -10
- {base_cli-0.4.3 → base_cli-0.5.0}/PKG-INFO +120 -36
- {base_cli-0.4.3 → base_cli-0.5.0}/README.md +113 -32
- base_cli-0.5.0/VERSION +1 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/base_manifest.yaml +6 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/README.md +22 -9
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/atlas_click/README.md +2 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/atlas_click/pyproject.toml +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/atlas_click/tests/test_consumer.py +0 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/beacon_typer/README.md +5 -4
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/beacon_typer/pyproject.toml +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/beacon_typer/src/beacon_typer/cli.py +0 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/beacon_typer/tests/test_consumer.py +0 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/cinder_automation/README.md +1 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/cinder_automation/pyproject.toml +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/cinder_automation/src/cinder_automation/cli.py +4 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/cinder_automation/tests/test_consumer.py +0 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/adopter-readiness.md +4 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/adoption-evidence.md +13 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/api-reference.md +32 -32
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/api-stability.md +13 -4
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/cache-ownership-and-layout.md +23 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/compatibility-dashboard.md +2 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/consumer-profiles.md +28 -0
- base_cli-0.5.0/docs/consumer-quickstart.md +92 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/coverage-policy.md +5 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/dependency-support.md +3 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/extensions.md +6 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/index.md +10 -0
- base_cli-0.5.0/docs/integrations.md +85 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/json-contracts.md +68 -4
- base_cli-0.5.0/docs/local-config.md +62 -0
- base_cli-0.5.0/docs/migration-0.5.md +33 -0
- base_cli-0.5.0/docs/optional-output-dependencies.md +78 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/output-contracts.md +11 -0
- base_cli-0.5.0/docs/performance.md +210 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/platform-support.md +30 -5
- base_cli-0.5.0/docs/release-0.5.0-checklist.md +55 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/releasing.md +81 -14
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/security-review.md +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/security-threat-model.md +19 -2
- base_cli-0.5.0/docs/strict-json-consumer.md +93 -0
- base_cli-0.5.0/docs/testing.md +86 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/typer-adapter.md +16 -6
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/automation_observability_app/src/automation_observability_app/cli.py +3 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/minimal_cli/src/minimal_cli/cli.py +3 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typer_app/README.md +6 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typer_app/pyproject.toml +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_app_core.py +71 -14
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_attach.py +12 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_click_compat.py +37 -7
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_lifecycle.py +8 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_lifecycle_install.py +46 -5
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_private_files.py +31 -5
- base_cli-0.5.0/lib/python/base_cli/_run.py +868 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_runtime.py +354 -29
- base_cli-0.5.0/lib/python/base_cli/_stdout_capture.py +117 -0
- base_cli-0.5.0/lib/python/base_cli/_windows_retention.py +100 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/config.py +187 -15
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/context.py +13 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/extensions.py +28 -23
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/inspection.py +3 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/integrations.py +32 -8
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/json_contracts.py +61 -16
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/lifecycle_options.py +20 -1
- base_cli-0.5.0/lib/python/base_cli/logging.py +448 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/output.py +115 -39
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/paths.py +36 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/profile.py +43 -11
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/redaction.py +56 -6
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/typer.py +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli.egg-info/PKG-INFO +120 -36
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli.egg-info/SOURCES.txt +41 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli.egg-info/requires.txt +5 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/pyproject.toml +7 -5
- base_cli-0.5.0/scripts/benchmark_runtime.py +912 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/generate_api_reference.py +41 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/generate_release_metadata.py +20 -10
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/record_compatibility_evidence.py +17 -1
- base_cli-0.5.0/scripts/release_metadata_helpers.py +24 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_changelog.py +88 -1
- base_cli-0.5.0/scripts/validate_consumer_typing.py +48 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_coverage.py +25 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_docs.py +6 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_package_artifact.py +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_release_metadata.py +28 -10
- base_cli-0.5.0/scripts/validate_release_provenance.py +181 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_release_ref.py +8 -2
- base_cli-0.5.0/scripts/verify_release_assets.py +178 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_adversarial_regressions.py +4 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_lifecycle.py +4 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_registration.py +16 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_run.py +75 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_run_metadata.py +185 -3
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_runtime_boundary.py +9 -0
- base_cli-0.5.0/tests/test_batteries_included_config.py +362 -0
- base_cli-0.5.0/tests/test_benchmark_runtime.py +235 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_cleanup_security.py +35 -2
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_click_tree_attachment.py +157 -3
- base_cli-0.5.0/tests/test_community_issue_templates.py +18 -0
- base_cli-0.5.0/tests/test_config_discovery_trust.py +93 -0
- base_cli-0.5.0/tests/test_consumer_quickstart_docs.py +28 -0
- base_cli-0.5.0/tests/test_dependency_documentation.py +29 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_explicit_config_validation.py +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_extensions.py +85 -0
- base_cli-0.5.0/tests/test_framework_config_runtime.py +316 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_generate_compatibility_dashboard.py +3 -3
- base_cli-0.5.0/tests/test_generated_api_reference.py +17 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_integrations.py +68 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_json_contracts.py +205 -2
- base_cli-0.5.0/tests/test_json_descriptor_capture.py +78 -0
- base_cli-0.5.0/tests/test_logger_ownership.py +111 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_logging.py +86 -0
- base_cli-0.5.0/tests/test_logging_hot_path.py +217 -0
- base_cli-0.5.0/tests/test_optional_output_dependencies_docs.py +29 -0
- base_cli-0.5.0/tests/test_optional_yaml_dependency.py +87 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_output.py +125 -0
- base_cli-0.5.0/tests/test_package_workflow.py +38 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_paths.py +36 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_platform_edge_paths.py +89 -2
- base_cli-0.5.0/tests/test_project_intake.py +509 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_public_api.py +1 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_redaction_security.py +84 -0
- base_cli-0.5.0/tests/test_retention_contention.py +93 -0
- base_cli-0.5.0/tests/test_retention_platform.py +61 -0
- base_cli-0.5.0/tests/test_run_app_reentrancy.py +122 -0
- base_cli-0.5.0/tests/test_run_bundle_retention.py +432 -0
- base_cli-0.5.0/tests/test_run_json_preflight.py +227 -0
- base_cli-0.5.0/tests/test_strict_json_consumer_docs.py +27 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_typer_adapter.py +38 -0
- base_cli-0.5.0/tests/test_validate_changelog.py +120 -0
- base_cli-0.5.0/tests/test_validate_consumer_typing.py +34 -0
- base_cli-0.5.0/tests/test_validate_coverage.py +26 -0
- base_cli-0.5.0/tests/test_validate_package_artifact.py +11 -0
- base_cli-0.5.0/tests/test_validate_release_provenance.py +227 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_validate_release_ref.py +4 -0
- base_cli-0.5.0/tests/test_verify_release_assets.py +139 -0
- base_cli-0.5.0/tests/test_windows_retention.py +43 -0
- base_cli-0.4.3/.github/ISSUE_TEMPLATE/support.md +0 -28
- base_cli-0.4.3/.github/workflows/project-intake.yml +0 -255
- base_cli-0.4.3/.github/workflows/tests.yml +0 -147
- base_cli-0.4.3/VERSION +0 -1
- base_cli-0.4.3/docs/integrations.md +0 -54
- base_cli-0.4.3/docs/local-config.md +0 -24
- base_cli-0.4.3/docs/performance.md +0 -33
- base_cli-0.4.3/lib/python/base_cli/_run.py +0 -431
- base_cli-0.4.3/lib/python/base_cli/logging.py +0 -292
- base_cli-0.4.3/scripts/benchmark_runtime.py +0 -114
- base_cli-0.4.3/tests/test_batteries_included_config.py +0 -173
- base_cli-0.4.3/tests/test_benchmark_runtime.py +0 -21
- base_cli-0.4.3/tests/test_optional_yaml_dependency.py +0 -42
- base_cli-0.4.3/tests/test_run_bundle_retention.py +0 -190
- base_cli-0.4.3/tests/test_validate_changelog.py +0 -64
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/base-project.yml +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/pull_request_template.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/workflows/examples.yml +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/.github/workflows/issue-branch-policy.yml +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/LICENSE +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/MANIFEST.in +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/SECURITY.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/atlas_click/src/atlas_click/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/atlas_click/src/atlas_click/cli.py +1 -1
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/beacon_typer/src/beacon_typer/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/cinder_automation/src/cinder_automation/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/compatibility/consumers/manifest.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/framework-choice.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/migration-argparse.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/migration-cement.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/migration-click.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/migration-typer.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/migrations.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/schemas.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/docs/user-config-typing.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/README.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/automation_observability_app/README.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/automation_observability_app/pyproject.toml +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/automation_observability_app/src/automation_observability_app/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/automation_observability_app/tests/test_cli.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/minimal_cli/README.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/minimal_cli/pyproject.toml +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/minimal_cli/src/minimal_cli/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/minimal_cli/tests/test_cli.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/nested_click_app/README.md +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/nested_click_app/pyproject.toml +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/nested_click_app/src/nested_click_app/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/nested_click_app/src/nested_click_app/cli.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/nested_click_app/src/nested_click_app/plugin.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/nested_click_app/tests/test_cli.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typed_consumer.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typer_app/src/typer_app/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typer_app/src/typer_app/cli.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typer_app/tests/test_cli.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/examples/typer_consumer.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/__init__.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_cleanup.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/_dependencies.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/app.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/asyncio_adapter.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/attachment.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/command_filters.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/command_protocol.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/deprecations.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/errors.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/exit_codes.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/experimental.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/history.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/py.typed +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/runtime.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/schemas/v1/command-protocol.schema.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/schemas/v1/error.schema.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/schemas/v1/inspection.schema.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/schemas/v1/log.schema.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/schemas/v1/ndjson.schema.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/schemas/v1/output.schema.json +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli/testing.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli.egg-info/dependency_links.txt +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/lib/python/base_cli.egg-info/top_level.txt +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/generate_compatibility_dashboard.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_consumers.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_contract_fixtures.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_examples.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_installed_package.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/scripts/validate_schemas.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/setup.cfg +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/conftest.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_api_stability.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_dry_run.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_log_retention.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_module_boundaries.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_quiet.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_runtime_errors.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_security_boundaries.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_startup_transaction.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_app_subcommands.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_command_filters.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_command_protocol.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_context_workspace.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_generic_core.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_history.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_inspection.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_invocation_parity.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_lifecycle_options.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_profile.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_security_documentation.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_testing.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_typed_contracts.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_validate_docs.py +0 -0
- {base_cli-0.4.3 → base_cli-0.5.0}/tests/test_validate_schemas.py +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a reproducible base-cli defect
|
|
3
|
+
title: "bug: "
|
|
4
|
+
labels:
|
|
5
|
+
- bug
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: |
|
|
10
|
+
Do not include secrets, personal data, or vulnerability details. Use SECURITY.md for private reports.
|
|
11
|
+
- type: input
|
|
12
|
+
id: version
|
|
13
|
+
attributes:
|
|
14
|
+
label: Version or commit
|
|
15
|
+
description: Include the installed release, package revision, or commit.
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
- type: input
|
|
19
|
+
id: environment
|
|
20
|
+
attributes:
|
|
21
|
+
label: Environment
|
|
22
|
+
description: Include Python, Click/Typer, OS, architecture, and relevant extras.
|
|
23
|
+
validations:
|
|
24
|
+
required: true
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: reproduction
|
|
27
|
+
attributes:
|
|
28
|
+
label: Reproduction
|
|
29
|
+
description: Give the smallest command, fixture, or test that reproduces the problem.
|
|
30
|
+
validations:
|
|
31
|
+
required: true
|
|
32
|
+
- type: textarea
|
|
33
|
+
id: expected
|
|
34
|
+
attributes:
|
|
35
|
+
label: Expected behavior
|
|
36
|
+
validations:
|
|
37
|
+
required: true
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: actual
|
|
40
|
+
attributes:
|
|
41
|
+
label: Actual behavior
|
|
42
|
+
render: text
|
|
43
|
+
validations:
|
|
44
|
+
required: true
|
|
45
|
+
- type: textarea
|
|
46
|
+
id: logs
|
|
47
|
+
attributes:
|
|
48
|
+
label: Relevant logs
|
|
49
|
+
description: Redact credentials, tokens, personal data, and private paths.
|
|
50
|
+
render: text
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Support and adoption questions
|
|
4
|
+
url: https://github.com/orgs/basefoundry/discussions
|
|
5
|
+
about: Ask ecosystem-wide usage and onboarding questions in the canonical public forum.
|
|
6
|
+
- name: Private security report
|
|
7
|
+
url: https://github.com/basefoundry/base-cli/security/advisories/new
|
|
8
|
+
about: Never disclose vulnerabilities or sensitive data in a public issue.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Documentation improvement
|
|
2
|
+
description: Report missing, incorrect, or confusing base-cli documentation
|
|
3
|
+
title: "docs: "
|
|
4
|
+
labels:
|
|
5
|
+
- documentation
|
|
6
|
+
body:
|
|
7
|
+
- type: input
|
|
8
|
+
id: location
|
|
9
|
+
attributes:
|
|
10
|
+
label: Documentation location
|
|
11
|
+
description: Give the URL or repository path.
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: problem
|
|
16
|
+
attributes:
|
|
17
|
+
label: Problem
|
|
18
|
+
description: Explain what a reader cannot understand or do.
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: suggestion
|
|
23
|
+
attributes:
|
|
24
|
+
label: Suggested improvement
|
|
25
|
+
description: Describe the corrected or clearer outcome.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Feature proposal
|
|
2
|
+
description: Propose a user-facing base-cli capability or improvement
|
|
3
|
+
title: "feature: "
|
|
4
|
+
labels:
|
|
5
|
+
- enhancement
|
|
6
|
+
body:
|
|
7
|
+
- type: textarea
|
|
8
|
+
id: problem
|
|
9
|
+
attributes:
|
|
10
|
+
label: User problem
|
|
11
|
+
description: Who is affected, and what are they unable to do today?
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: outcome
|
|
16
|
+
attributes:
|
|
17
|
+
label: Desired outcome
|
|
18
|
+
description: Describe what success looks like without requiring a particular implementation.
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: alternatives
|
|
23
|
+
attributes:
|
|
24
|
+
label: Alternatives and context
|
|
25
|
+
description: Include workarounds, constraints, or compatibility concerns.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Adoption support
|
|
3
|
+
about: Request migration or downstream compatibility help
|
|
4
|
+
title: "[Support] "
|
|
5
|
+
labels: ""
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Use the Support and adoption question form when it is available. Include the
|
|
10
|
+
base-cli version, Python and operating-system versions, a minimal
|
|
11
|
+
reproduction, and redacted evidence. Never include secrets; use SECURITY.md
|
|
12
|
+
for private vulnerability reports.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Support and adoption question
|
|
2
|
+
description: Ask for help using or evaluating base-cli
|
|
3
|
+
title: "support: "
|
|
4
|
+
labels:
|
|
5
|
+
- adoption support
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: |
|
|
10
|
+
For general ecosystem questions, prefer Base Discussions. Do not include secrets or security-sensitive details here.
|
|
11
|
+
- type: input
|
|
12
|
+
id: version
|
|
13
|
+
attributes:
|
|
14
|
+
label: Version or commit
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: environment
|
|
19
|
+
attributes:
|
|
20
|
+
label: Environment
|
|
21
|
+
description: Include Python, Click/Typer, OS, architecture, and relevant extras.
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
- type: textarea
|
|
25
|
+
id: goal
|
|
26
|
+
attributes:
|
|
27
|
+
label: Goal and evidence
|
|
28
|
+
description: What are you trying to do, what did you try, and what happened?
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
@@ -37,6 +37,33 @@ jobs:
|
|
|
37
37
|
- name: Run adapter and Beacon compatibility tests
|
|
38
38
|
run: python -m pytest tests/test_typer_adapter.py compatibility/consumers/beacon_typer/tests
|
|
39
39
|
|
|
40
|
+
typer-click-boundaries:
|
|
41
|
+
name: Typer ${{ matrix.typer-version }} / Click ${{ matrix.click-version }} / Python ${{ matrix.python-version }}
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
timeout-minutes: 20
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
matrix:
|
|
47
|
+
python-version: ["3.10", "3.14"]
|
|
48
|
+
typer-version: ["0.25.1", "0.27.2"]
|
|
49
|
+
click-version: ["8.2.1", "8.5.0"]
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
|
+
- name: Set up Python
|
|
53
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
54
|
+
with:
|
|
55
|
+
python-version: ${{ matrix.python-version }}
|
|
56
|
+
- name: Install framework and supported Typer adapter
|
|
57
|
+
run: |
|
|
58
|
+
python -m pip install "setuptools<77" "wheel"
|
|
59
|
+
python -m pip install ".[dev,typer]"
|
|
60
|
+
- name: Pin the tested Click/Typer boundary pair
|
|
61
|
+
run: python -m pip install --upgrade "click==${{ matrix.click-version }}" "typer==${{ matrix.typer-version }}"
|
|
62
|
+
- name: Install Beacon fixture without dependency resolution
|
|
63
|
+
run: python -m pip install --no-deps compatibility/consumers/beacon_typer
|
|
64
|
+
- name: Run adapter and Beacon compatibility tests
|
|
65
|
+
run: python -m pytest tests/test_typer_adapter.py compatibility/consumers/beacon_typer/tests
|
|
66
|
+
|
|
40
67
|
downstream:
|
|
41
68
|
name: Install and test independent consumers
|
|
42
69
|
runs-on: ubuntu-latest
|
|
@@ -56,11 +83,17 @@ jobs:
|
|
|
56
83
|
python -m pip install dist/*.whl
|
|
57
84
|
- name: Validate consumer manifest
|
|
58
85
|
run: python scripts/validate_consumers.py
|
|
59
|
-
- name: Install consumers
|
|
86
|
+
- name: Install consumers without dependency resolution
|
|
87
|
+
run: |
|
|
88
|
+
python -m pip install --no-deps compatibility/consumers/atlas_click
|
|
89
|
+
python -m pip install --no-deps compatibility/consumers/beacon_typer
|
|
90
|
+
python -m pip install --no-deps "compatibility/consumers/cinder_automation[observability]"
|
|
91
|
+
- name: Verify the installed framework matches the source release
|
|
60
92
|
run: |
|
|
61
|
-
|
|
62
|
-
python -
|
|
63
|
-
|
|
93
|
+
expected="$(tr -d '\r\n' < VERSION)"
|
|
94
|
+
actual="$(python -c 'from importlib.metadata import version; print(version("base-cli"))')"
|
|
95
|
+
test "$actual" = "$expected"
|
|
96
|
+
python -m pip check
|
|
64
97
|
- name: Run downstream compatibility suites
|
|
65
98
|
run: |
|
|
66
99
|
for tests in compatibility/consumers/*/tests; do
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
fail-fast: false
|
|
21
21
|
matrix:
|
|
22
22
|
python-version: ["3.10", "3.14"]
|
|
23
|
-
click-version: ["8.1.*", "8.2.*", "8.3.*", "8.4.*"]
|
|
23
|
+
click-version: ["8.1.*", "8.2.*", "8.3.*", "8.4.*", "8.5.*"]
|
|
24
24
|
pyyaml-version: ["6.0.*"]
|
|
25
25
|
steps:
|
|
26
26
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -22,6 +22,8 @@ on:
|
|
|
22
22
|
- ".github/workflows/dependency-matrix.yml"
|
|
23
23
|
- ".github/workflows/compatibility.yml"
|
|
24
24
|
- "tests/validate.sh"
|
|
25
|
+
- "tests/full_validate.sh"
|
|
26
|
+
- "docs/testing.md"
|
|
25
27
|
- ".github/workflows/docs.yml"
|
|
26
28
|
pull_request:
|
|
27
29
|
paths:
|
|
@@ -33,6 +35,8 @@ on:
|
|
|
33
35
|
- "scripts/validate_docs.py"
|
|
34
36
|
- "scripts/generate_api_reference.py"
|
|
35
37
|
- "tests/validate.sh"
|
|
38
|
+
- "tests/full_validate.sh"
|
|
39
|
+
- "docs/testing.md"
|
|
36
40
|
- ".github/workflows/docs.yml"
|
|
37
41
|
workflow_dispatch:
|
|
38
42
|
|
|
@@ -51,6 +55,8 @@ jobs:
|
|
|
51
55
|
steps:
|
|
52
56
|
- name: Check out source
|
|
53
57
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
58
|
+
with:
|
|
59
|
+
fetch-depth: 0
|
|
54
60
|
|
|
55
61
|
- name: Set up Python
|
|
56
62
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
@@ -14,12 +14,19 @@ on:
|
|
|
14
14
|
- "scripts/validate_examples.py"
|
|
15
15
|
- "scripts/generate_release_metadata.py"
|
|
16
16
|
- "scripts/validate_release_metadata.py"
|
|
17
|
+
- "scripts/verify_release_assets.py"
|
|
17
18
|
- "scripts/validate_changelog.py"
|
|
18
19
|
- "scripts/validate_release_ref.py"
|
|
20
|
+
- "scripts/validate_release_provenance.py"
|
|
19
21
|
- "CHANGELOG.md"
|
|
20
22
|
- "examples/**"
|
|
21
23
|
- "compatibility/**"
|
|
22
24
|
- "docs/releasing.md"
|
|
25
|
+
- "tests/test_package_workflow.py"
|
|
26
|
+
- "tests/test_verify_release_assets.py"
|
|
27
|
+
- "tests/test_validate_release_provenance.py"
|
|
28
|
+
- "requirements/release.in"
|
|
29
|
+
- "requirements/release.txt"
|
|
23
30
|
- ".github/workflows/package.yml"
|
|
24
31
|
push:
|
|
25
32
|
branches:
|
|
@@ -57,6 +64,8 @@ jobs:
|
|
|
57
64
|
steps:
|
|
58
65
|
- name: Check out source
|
|
59
66
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
67
|
+
with:
|
|
68
|
+
fetch-depth: 0
|
|
60
69
|
|
|
61
70
|
- name: Set up Python
|
|
62
71
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
@@ -80,9 +89,6 @@ jobs:
|
|
|
80
89
|
exit 1
|
|
81
90
|
fi
|
|
82
91
|
|
|
83
|
-
- name: Validate repository baseline
|
|
84
|
-
run: ./tests/validate.sh
|
|
85
|
-
|
|
86
92
|
- name: Validate changelog
|
|
87
93
|
run: python scripts/validate_changelog.py
|
|
88
94
|
|
|
@@ -98,10 +104,48 @@ jobs:
|
|
|
98
104
|
mkdir -p dist
|
|
99
105
|
|
|
100
106
|
- name: Install build and validation tools
|
|
101
|
-
run: python -m pip install --
|
|
107
|
+
run: python -m pip install --require-hashes --no-deps -r requirements/release.txt
|
|
102
108
|
|
|
103
109
|
- name: Build sdist and wheel
|
|
104
|
-
|
|
110
|
+
env:
|
|
111
|
+
# Keep the reproducibility input numeric; push-event timestamps are
|
|
112
|
+
# ISO-8601 strings and are not accepted by wheel/build backends.
|
|
113
|
+
SOURCE_DATE_EPOCH: '0'
|
|
114
|
+
run: |
|
|
115
|
+
rm -rf dist-a dist-b "$RUNNER_TEMP/base-cli-build-a" "$RUNNER_TEMP/base-cli-build-b"
|
|
116
|
+
mkdir -p dist-a dist-b
|
|
117
|
+
mkdir -p "$RUNNER_TEMP/base-cli-build-a" "$RUNNER_TEMP/base-cli-build-b"
|
|
118
|
+
git archive "$GITHUB_SHA" | tar -x -C "$RUNNER_TEMP/base-cli-build-a"
|
|
119
|
+
git archive "$GITHUB_SHA" | tar -x -C "$RUNNER_TEMP/base-cli-build-b"
|
|
120
|
+
(cd "$RUNNER_TEMP/base-cli-build-a" && python -m build --sdist --wheel --outdir "$GITHUB_WORKSPACE/dist-a")
|
|
121
|
+
(cd "$RUNNER_TEMP/base-cli-build-b" && python -m build --sdist --wheel --outdir "$GITHUB_WORKSPACE/dist-b")
|
|
122
|
+
python - <<'PY'
|
|
123
|
+
import gzip
|
|
124
|
+
import os
|
|
125
|
+
import tarfile
|
|
126
|
+
from pathlib import Path
|
|
127
|
+
|
|
128
|
+
epoch = int(os.environ["SOURCE_DATE_EPOCH"])
|
|
129
|
+
for directory in (Path("dist-a"), Path("dist-b")):
|
|
130
|
+
for path in directory.glob("*.tar.gz"):
|
|
131
|
+
with tarfile.open(path, "r:gz") as source:
|
|
132
|
+
members = [(member, source.extractfile(member).read() if member.isfile() else None) for member in source.getmembers()]
|
|
133
|
+
temporary = path.with_suffix(path.suffix + ".tmp")
|
|
134
|
+
with temporary.open("wb") as raw:
|
|
135
|
+
with gzip.GzipFile(filename=path.name, mode="wb", fileobj=raw, mtime=epoch) as compressed:
|
|
136
|
+
with tarfile.open(fileobj=compressed, mode="w|") as target:
|
|
137
|
+
for member, payload in sorted(members, key=lambda item: item[0].name):
|
|
138
|
+
member.mtime = epoch
|
|
139
|
+
member.uid = member.gid = 0
|
|
140
|
+
member.uname = member.gname = ""
|
|
141
|
+
member.pax_headers = {}
|
|
142
|
+
target.addfile(member, None if payload is None else __import__("io").BytesIO(payload))
|
|
143
|
+
temporary.replace(path)
|
|
144
|
+
PY
|
|
145
|
+
(cd dist-a && sha256sum * | sort) > dist-a.SHA256SUMS
|
|
146
|
+
(cd dist-b && sha256sum * | sort) > dist-b.SHA256SUMS
|
|
147
|
+
diff -u dist-a.SHA256SUMS dist-b.SHA256SUMS
|
|
148
|
+
cp dist-a/* dist/
|
|
105
149
|
|
|
106
150
|
- name: Validate artifact contents and metadata
|
|
107
151
|
run: python scripts/validate_package_artifact.py dist
|
|
@@ -112,7 +156,7 @@ jobs:
|
|
|
112
156
|
- name: Generate release checksums and SPDX SBOM
|
|
113
157
|
env:
|
|
114
158
|
SOURCE_REVISION: ${{ github.sha }}
|
|
115
|
-
SOURCE_DATE_EPOCH:
|
|
159
|
+
SOURCE_DATE_EPOCH: '0'
|
|
116
160
|
run: python scripts/generate_release_metadata.py dist
|
|
117
161
|
|
|
118
162
|
- name: Validate release checksums and SPDX SBOM
|
|
@@ -137,6 +181,7 @@ jobs:
|
|
|
137
181
|
path: |
|
|
138
182
|
dist/SBOM.spdx.json
|
|
139
183
|
dist/SHA256SUMS
|
|
184
|
+
dist/RELEASE-BOM-ROW.json
|
|
140
185
|
if-no-files-found: error
|
|
141
186
|
retention-days: 90
|
|
142
187
|
|
|
@@ -152,6 +197,8 @@ jobs:
|
|
|
152
197
|
steps:
|
|
153
198
|
- name: Check out source
|
|
154
199
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
200
|
+
with:
|
|
201
|
+
fetch-depth: 0
|
|
155
202
|
|
|
156
203
|
- name: Set up Python
|
|
157
204
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
@@ -190,9 +237,31 @@ jobs:
|
|
|
190
237
|
- name: Check installed dependency consistency
|
|
191
238
|
run: python -m pip check
|
|
192
239
|
|
|
240
|
+
provenance:
|
|
241
|
+
name: Verify reviewed release provenance
|
|
242
|
+
needs: [build, smoke]
|
|
243
|
+
if: ${{ (github.event_name == 'push' && github.ref_type == 'tag') || github.event_name == 'workflow_dispatch' }}
|
|
244
|
+
runs-on: ubuntu-latest
|
|
245
|
+
timeout-minutes: 10
|
|
246
|
+
permissions:
|
|
247
|
+
contents: read
|
|
248
|
+
steps:
|
|
249
|
+
- name: Check out source
|
|
250
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
251
|
+
with:
|
|
252
|
+
fetch-depth: 0
|
|
253
|
+
|
|
254
|
+
- name: Fetch trusted main history
|
|
255
|
+
run: git fetch --no-tags --prune origin "refs/heads/main:refs/remotes/origin/main"
|
|
256
|
+
|
|
257
|
+
- name: Verify release provenance
|
|
258
|
+
env:
|
|
259
|
+
PUBLISH_TARGET: ${{ inputs.publish_target || '' }}
|
|
260
|
+
run: python scripts/validate_release_provenance.py
|
|
261
|
+
|
|
193
262
|
publish:
|
|
194
263
|
name: Publish reviewed distribution
|
|
195
|
-
needs: [build, smoke]
|
|
264
|
+
needs: [build, smoke, provenance]
|
|
196
265
|
if: ${{ (github.event_name == 'push' && github.ref_type == 'tag') || github.event_name == 'workflow_dispatch' }}
|
|
197
266
|
runs-on: ubuntu-latest
|
|
198
267
|
timeout-minutes: 10
|
|
@@ -224,7 +293,7 @@ jobs:
|
|
|
224
293
|
|
|
225
294
|
attest:
|
|
226
295
|
name: Attest reviewed release
|
|
227
|
-
needs: [build, smoke]
|
|
296
|
+
needs: [build, smoke, provenance]
|
|
228
297
|
if: ${{ (github.event_name == 'push' && github.ref_type == 'tag') || github.event_name == 'workflow_dispatch' }}
|
|
229
298
|
runs-on: ubuntu-latest
|
|
230
299
|
timeout-minutes: 10
|
|
@@ -258,13 +327,22 @@ jobs:
|
|
|
258
327
|
|
|
259
328
|
release:
|
|
260
329
|
name: Create GitHub Release
|
|
261
|
-
needs: [build, smoke, publish, attest]
|
|
330
|
+
needs: [build, smoke, provenance, publish, attest]
|
|
262
331
|
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
|
|
263
332
|
runs-on: ubuntu-latest
|
|
264
333
|
timeout-minutes: 10
|
|
265
334
|
permissions:
|
|
266
335
|
contents: write
|
|
336
|
+
attestations: read
|
|
267
337
|
steps:
|
|
338
|
+
- name: Check out the tag source
|
|
339
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
340
|
+
|
|
341
|
+
- name: Set up Python
|
|
342
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
343
|
+
with:
|
|
344
|
+
python-version: "3.13"
|
|
345
|
+
|
|
268
346
|
- name: Download reviewed distributions
|
|
269
347
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
270
348
|
with:
|
|
@@ -277,18 +355,110 @@ jobs:
|
|
|
277
355
|
name: base-cli-release-metadata-${{ github.run_id }}
|
|
278
356
|
path: dist
|
|
279
357
|
|
|
280
|
-
- name:
|
|
358
|
+
- name: Verify and create immutable GitHub Release
|
|
281
359
|
env:
|
|
282
360
|
GH_TOKEN: ${{ github.token }}
|
|
283
361
|
run: |
|
|
362
|
+
set -euo pipefail
|
|
284
363
|
tag="$GITHUB_REF_NAME"
|
|
285
|
-
assets=(dist/*.whl dist/*.tar.gz dist/SHA256SUMS dist/SBOM.spdx.json)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
364
|
+
assets=(dist/*.whl dist/*.tar.gz dist/SHA256SUMS dist/SBOM.spdx.json dist/RELEASE-BOM-ROW.json)
|
|
365
|
+
tag_commit="$(gh api "repos/$GITHUB_REPOSITORY/commits/$tag" --jq .sha)"
|
|
366
|
+
if [[ "$tag_commit" != "$GITHUB_SHA" ]]; then
|
|
367
|
+
echo "Release tag $tag resolves to $tag_commit, not reviewed commit $GITHUB_SHA." >&2
|
|
368
|
+
exit 1
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
python scripts/validate_release_metadata.py dist
|
|
372
|
+
for asset in dist/*.whl dist/*.tar.gz; do
|
|
373
|
+
gh attestation verify "$asset" \
|
|
290
374
|
--repo "$GITHUB_REPOSITORY" \
|
|
291
|
-
--
|
|
292
|
-
--
|
|
293
|
-
--
|
|
375
|
+
--signer-workflow "github.com/$GITHUB_REPOSITORY/.github/workflows/package.yml" \
|
|
376
|
+
--source-digest "$GITHUB_SHA" \
|
|
377
|
+
--source-ref "$GITHUB_REF"
|
|
378
|
+
gh attestation verify "$asset" \
|
|
379
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
380
|
+
--signer-workflow "github.com/$GITHUB_REPOSITORY/.github/workflows/package.yml" \
|
|
381
|
+
--source-digest "$GITHUB_SHA" \
|
|
382
|
+
--source-ref "$GITHUB_REF" \
|
|
383
|
+
--predicate-type "https://spdx.dev/Document"
|
|
384
|
+
done
|
|
385
|
+
|
|
386
|
+
release_tmp="$(mktemp -d "$RUNNER_TEMP/base-cli-release.XXXXXX")"
|
|
387
|
+
trap 'rm -rf "$release_tmp"' EXIT
|
|
388
|
+
release_json="$release_tmp/release.json"
|
|
389
|
+
release_error="$release_tmp/release-error.txt"
|
|
390
|
+
release_response="$release_tmp/release-response.txt"
|
|
391
|
+
|
|
392
|
+
read_release_metadata() {
|
|
393
|
+
local retry_404="${1:-false}"
|
|
394
|
+
local attempt=1
|
|
395
|
+
local status_line
|
|
396
|
+
while :; do
|
|
397
|
+
if gh api --include "repos/$GITHUB_REPOSITORY/releases/tags/$tag" \
|
|
398
|
+
>"$release_response" 2>"$release_error"; then
|
|
399
|
+
awk 'BEGIN { body = 0 } { sub(/\r$/, ""); if (body) print; else if ($0 == "") body = 1 }' \
|
|
400
|
+
"$release_response" >"$release_json"
|
|
401
|
+
return 0
|
|
402
|
+
fi
|
|
403
|
+
status_line="$(sed -n '1s/\r$//p' "$release_response")"
|
|
404
|
+
if [[ "$status_line" =~ ^HTTP/[0-9.]+[[:space:]]404[[:space:]] ]]; then
|
|
405
|
+
if [[ "$retry_404" != true || "$attempt" -ge 5 ]]; then
|
|
406
|
+
return 1
|
|
407
|
+
fi
|
|
408
|
+
sleep $((attempt * 2))
|
|
409
|
+
attempt=$((attempt + 1))
|
|
410
|
+
continue
|
|
411
|
+
fi
|
|
412
|
+
cat "$release_error" >&2
|
|
413
|
+
return 2
|
|
414
|
+
done
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
verify_existing_release() {
|
|
418
|
+
local existing_assets="$release_tmp/existing-assets"
|
|
419
|
+
mkdir -p "$existing_assets"
|
|
420
|
+
gh release download "$tag" --repo "$GITHUB_REPOSITORY" --dir "$existing_assets"
|
|
421
|
+
python scripts/verify_release_assets.py \
|
|
422
|
+
--expected-dir dist \
|
|
423
|
+
--existing-dir "$existing_assets" \
|
|
424
|
+
--release-json "$release_json" \
|
|
425
|
+
--version-file VERSION \
|
|
426
|
+
--tag "$tag" \
|
|
427
|
+
--source-commit "$GITHUB_SHA" \
|
|
428
|
+
--resolved-tag-commit "$tag_commit"
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if read_release_metadata; then
|
|
432
|
+
verify_existing_release
|
|
433
|
+
echo "Existing release $tag is byte-for-byte identical; leaving the immutable release unchanged."
|
|
434
|
+
exit 0
|
|
435
|
+
else
|
|
436
|
+
release_status=$?
|
|
437
|
+
if [[ "$release_status" -ne 1 ]]; then
|
|
438
|
+
exit "$release_status"
|
|
439
|
+
fi
|
|
440
|
+
fi
|
|
441
|
+
|
|
442
|
+
create_status=0
|
|
443
|
+
gh release create "$tag" "${assets[@]}" \
|
|
444
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
445
|
+
--title "$tag" \
|
|
446
|
+
--generate-notes \
|
|
447
|
+
--notes "Published distributions and release metadata for $tag. See CHANGELOG.md for the reviewed release notes." \
|
|
448
|
+
|| create_status=$?
|
|
449
|
+
|
|
450
|
+
if read_release_metadata true; then
|
|
451
|
+
verify_existing_release
|
|
452
|
+
if [[ "$create_status" -ne 0 ]]; then
|
|
453
|
+
echo "gh release create failed with status $create_status; refusing to hide the publication error." >&2
|
|
454
|
+
exit "$create_status"
|
|
455
|
+
fi
|
|
456
|
+
echo "Verified immutable release $tag after publication."
|
|
457
|
+
else
|
|
458
|
+
release_status=$?
|
|
459
|
+
if [[ "$create_status" -ne 0 ]]; then
|
|
460
|
+
exit "$create_status"
|
|
461
|
+
fi
|
|
462
|
+
echo "Release $tag was created but could not be read back (HTTP 404)." >&2
|
|
463
|
+
exit "$release_status"
|
|
294
464
|
fi
|