splent-cli 1.22.2__tar.gz → 1.23.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.
- {splent_cli-1.22.2/src/splent_cli.egg-info → splent_cli-1.23.0}/PKG-INFO +1 -1
- {splent_cli-1.22.2 → splent_cli-1.23.0}/pyproject.toml +1 -1
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_contracts.py +24 -6
- splent_cli-1.23.0/src/splent_cli/commands/check/check_github.py +126 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_fork.py +30 -14
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_release.py +9 -2
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_unlock.py +9 -3
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_build.py +19 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/tokens_setup.py +31 -11
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/index_refresh.py +54 -24
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/preflight.py +28 -15
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/registry.py +102 -48
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/release.py +66 -51
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/release_gate.py +20 -10
- splent_cli-1.23.0/src/splent_cli/utils/forge.py +224 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/git_url.py +47 -28
- {splent_cli-1.22.2 → splent_cli-1.23.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli.egg-info/SOURCES.txt +1 -0
- splent_cli-1.22.2/src/splent_cli/commands/check/check_github.py +0 -105
- {splent_cli-1.22.2 → splent_cli-1.23.0}/LICENSE +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/README.md +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/setup.cfg +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/check/check_releases.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_reset.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_configure.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_containers.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_create.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_deploy.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_env.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_release.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/product/product_validate.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/release/release_core.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/release/release_resume.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_migrate_catalog.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/marketplace.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/services/spl_store.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/io_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli.egg-info/requires.txt +0 -0
- {splent_cli-1.22.2 → splent_cli-1.23.0}/src/splent_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splent_cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.23.0
|
|
4
4
|
Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
|
|
5
5
|
Author-email: DiversoLab <diversolab@us.es>
|
|
6
6
|
Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
|
|
@@ -17,6 +17,7 @@ import click
|
|
|
17
17
|
import tomllib
|
|
18
18
|
|
|
19
19
|
from splent_cli.services import context, marketplace, spl_store
|
|
20
|
+
from splent_cli.utils import forge
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def _snapshot_version_key(pyproject_path: Path) -> tuple:
|
|
@@ -65,7 +66,7 @@ def _local_contract_requires(workspace: str, package: str) -> list[str] | None:
|
|
|
65
66
|
|
|
66
67
|
|
|
67
68
|
def _exists_remotely(org: str | None, package: str) -> bool:
|
|
68
|
-
"""Whether a feature not present locally at least exists on
|
|
69
|
+
"""Whether a feature not present locally at least exists on the forge.
|
|
69
70
|
|
|
70
71
|
Answers True whenever the question cannot be settled: no org to look
|
|
71
72
|
under, no network, a rate limit, anything unexpected. A diagnostic that
|
|
@@ -78,10 +79,27 @@ def _exists_remotely(org: str | None, package: str) -> bool:
|
|
|
78
79
|
import urllib.error
|
|
79
80
|
import urllib.request
|
|
80
81
|
|
|
81
|
-
# The UVL writes the org the way
|
|
82
|
-
# not the way the filesystem does ("splent_io"), so it is used verbatim.
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
# The UVL writes the org the way the forge spells it ("splent-io"), which
|
|
83
|
+
# is not the way the filesystem does ("splent_io"), so it is used verbatim.
|
|
84
|
+
#
|
|
85
|
+
# GitHub keeps the anonymous HEAD against the web URL it has always used.
|
|
86
|
+
# A self-hosted forge is far likelier to keep its repositories private, and
|
|
87
|
+
# a private repository answers 404 to a stranger, which would read here as
|
|
88
|
+
# a feature nobody ever wrote. When such a forge gives us a token we ask
|
|
89
|
+
# its API instead, with GET because Gitea-compatible routes are registered
|
|
90
|
+
# for GET alone and answer a HEAD with 405.
|
|
91
|
+
token = None if forge.is_github() else forge.token()
|
|
92
|
+
if token:
|
|
93
|
+
request = urllib.request.Request(
|
|
94
|
+
f"{forge.api_base()}/repos/{org}/{package}",
|
|
95
|
+
headers={
|
|
96
|
+
"Authorization": f"token {token}",
|
|
97
|
+
"Accept": "application/json",
|
|
98
|
+
"User-Agent": "splent-cli",
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
request = urllib.request.Request(forge.web_url(org, package), method="HEAD")
|
|
85
103
|
try:
|
|
86
104
|
with urllib.request.urlopen(request, timeout=10) as response:
|
|
87
105
|
return response.status < 400
|
|
@@ -230,7 +248,7 @@ def check_contracts(spl_name):
|
|
|
230
248
|
click.secho(
|
|
231
249
|
f" ✗ {short} is offered by the model but does not exist: "
|
|
232
250
|
f"{meta.get('org')}/{meta['package']} is not in this workspace, "
|
|
233
|
-
f"not in the cache and not on
|
|
251
|
+
f"not in the cache and not on {forge.host()}",
|
|
234
252
|
fg="red",
|
|
235
253
|
)
|
|
236
254
|
click.echo(
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import urllib.request
|
|
3
|
+
import urllib.error
|
|
4
|
+
import json
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from splent_cli.utils import forge
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command(
|
|
11
|
+
"check:github",
|
|
12
|
+
short_help="Verify forge credentials from .env (GITHUB_USER / forge token)",
|
|
13
|
+
)
|
|
14
|
+
def check_github():
|
|
15
|
+
# The command keeps its name, but the forge it checks is whatever the
|
|
16
|
+
# workspace is configured against: github.com by default, a self-hosted
|
|
17
|
+
# Forgejo when SPLENT_FORGE_HOST says so.
|
|
18
|
+
forge_host = forge.host()
|
|
19
|
+
token_var = forge.token_env_name()
|
|
20
|
+
|
|
21
|
+
click.echo(click.style(f"\n{forge_host} Credentials Check\n", fg="cyan", bold=True))
|
|
22
|
+
|
|
23
|
+
user = os.getenv("GITHUB_USER", "").strip()
|
|
24
|
+
# Resolved through forge so a self-hosted forge reads SPLENT_FORGE_TOKEN
|
|
25
|
+
# while GitHub keeps reading GITHUB_TOKEN, as it always has.
|
|
26
|
+
token = (forge.token() or "").strip()
|
|
27
|
+
|
|
28
|
+
# --- presence checks ---
|
|
29
|
+
if not user:
|
|
30
|
+
click.echo(click.style("[✖] ", fg="red") + "GITHUB_USER not set in .env")
|
|
31
|
+
click.secho(
|
|
32
|
+
" Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
|
|
33
|
+
)
|
|
34
|
+
raise SystemExit(1)
|
|
35
|
+
click.echo(click.style("[✔] ", fg="green") + f"GITHUB_USER = {user}")
|
|
36
|
+
|
|
37
|
+
if not token:
|
|
38
|
+
click.echo(click.style("[✖] ", fg="red") + f"{token_var} not set in .env")
|
|
39
|
+
click.secho(
|
|
40
|
+
" Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
|
|
41
|
+
)
|
|
42
|
+
raise SystemExit(1)
|
|
43
|
+
|
|
44
|
+
masked = token[:4] + "*" * (len(token) - 8) + token[-4:]
|
|
45
|
+
click.echo(click.style("[✔] ", fg="green") + f"{token_var} = {masked}")
|
|
46
|
+
|
|
47
|
+
# --- API call ---
|
|
48
|
+
# Vendor media types are GitHub's own; a Gitea-compatible forge does not
|
|
49
|
+
# know them and answers plain JSON, so they are only sent to GitHub.
|
|
50
|
+
headers = {
|
|
51
|
+
"Authorization": f"token {token}",
|
|
52
|
+
"Accept": "application/json",
|
|
53
|
+
"User-Agent": "splent-cli",
|
|
54
|
+
}
|
|
55
|
+
if forge.is_github():
|
|
56
|
+
headers["Accept"] = "application/vnd.github+json"
|
|
57
|
+
headers["X-GitHub-Api-Version"] = "2022-11-28"
|
|
58
|
+
|
|
59
|
+
click.echo(f"\nContacting the {forge_host} API...")
|
|
60
|
+
req = urllib.request.Request(f"{forge.api_base()}/user", headers=headers)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
64
|
+
data = json.loads(resp.read().decode())
|
|
65
|
+
rate_remaining = resp.headers.get("X-RateLimit-Remaining", "?")
|
|
66
|
+
rate_limit = resp.headers.get("X-RateLimit-Limit", "?")
|
|
67
|
+
except urllib.error.HTTPError as e:
|
|
68
|
+
if e.code == 401:
|
|
69
|
+
click.echo(
|
|
70
|
+
click.style("[✖] ", fg="red")
|
|
71
|
+
+ "Token invalid or expired (401 Unauthorized)"
|
|
72
|
+
)
|
|
73
|
+
click.secho(
|
|
74
|
+
" Run 'splent tokens:setup' for instructions on obtaining a valid token.",
|
|
75
|
+
fg="bright_black",
|
|
76
|
+
)
|
|
77
|
+
elif e.code == 403:
|
|
78
|
+
click.echo(
|
|
79
|
+
click.style("[✖] ", fg="red")
|
|
80
|
+
+ "Token lacks required permissions (403 Forbidden)"
|
|
81
|
+
)
|
|
82
|
+
scope = "repo" if forge.is_github() else "write:repository"
|
|
83
|
+
click.secho(
|
|
84
|
+
f" Run 'splent tokens:setup', the token needs the '{scope}' scope.",
|
|
85
|
+
fg="bright_black",
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
click.echo(
|
|
89
|
+
click.style("[✖] ", fg="red") + f"{forge_host} API error: HTTP {e.code}"
|
|
90
|
+
)
|
|
91
|
+
raise SystemExit(1)
|
|
92
|
+
except urllib.error.URLError as e:
|
|
93
|
+
click.echo(click.style("[✖] ", fg="red") + f"Network error: {e.reason}")
|
|
94
|
+
raise SystemExit(1)
|
|
95
|
+
|
|
96
|
+
# --- validate identity ---
|
|
97
|
+
api_login = data.get("login", "")
|
|
98
|
+
if api_login.lower() != user.lower():
|
|
99
|
+
click.echo(
|
|
100
|
+
click.style("[⚠] ", fg="yellow")
|
|
101
|
+
+ f"Token belongs to '{api_login}', but GITHUB_USER is '{user}'"
|
|
102
|
+
)
|
|
103
|
+
else:
|
|
104
|
+
click.echo(click.style("[✔] ", fg="green") + f"Authenticated as {api_login}")
|
|
105
|
+
|
|
106
|
+
# GitHub calls the display name "name"; a Gitea-compatible forge calls it
|
|
107
|
+
# "full_name", so without the fallback every Forgejo check reported "-".
|
|
108
|
+
name = data.get("name") or data.get("full_name") or "-"
|
|
109
|
+
click.echo(click.style("[✔] ", fg="green") + f"Name: {name}")
|
|
110
|
+
|
|
111
|
+
# The plan and the rate limit headers are GitHub's alone. A Gitea-compatible
|
|
112
|
+
# forge answers /user without either, and printing "-" and "?/? remaining"
|
|
113
|
+
# would read as a fault rather than as an absence.
|
|
114
|
+
if forge.is_github():
|
|
115
|
+
plan = (data.get("plan") or {}).get("name", "-")
|
|
116
|
+
click.echo(click.style("[✔] ", fg="green") + f"Plan: {plan}")
|
|
117
|
+
click.echo(
|
|
118
|
+
click.style("[✔] ", fg="green")
|
|
119
|
+
+ f"Rate limit: {rate_remaining}/{rate_limit} remaining"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
click.echo()
|
|
123
|
+
click.secho(f"Credentials for {forge_host} OK.", fg="green")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
cli_command = check_github
|
|
@@ -5,32 +5,46 @@ import requests
|
|
|
5
5
|
from splent_cli.commands.feature.feature_clone import feature_clone
|
|
6
6
|
from splent_cli.utils.cache_utils import make_feature_writable
|
|
7
7
|
from splent_cli.services import context
|
|
8
|
+
from splent_cli.utils import forge
|
|
8
9
|
from splent_cli.utils.feature_utils import normalize_namespace
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
def _forge_label() -> str:
|
|
13
|
+
"""How to name the forge in a message: the brand on GitHub, the host elsewhere."""
|
|
14
|
+
return "GitHub" if forge.is_github() else forge.host()
|
|
15
|
+
|
|
16
|
+
|
|
11
17
|
@click.command(
|
|
12
18
|
"feature:fork",
|
|
13
|
-
short_help="Fork a feature on
|
|
19
|
+
short_help="Fork a feature on the forge and clone it locally",
|
|
14
20
|
)
|
|
15
21
|
@click.argument("feature_name", required=True)
|
|
16
22
|
@click.option(
|
|
17
23
|
"--version", "-v", default="v1.0.0", help="Feature version (default: v1.0.0)"
|
|
18
24
|
)
|
|
19
25
|
def feature_fork(feature_name, version):
|
|
20
|
-
token =
|
|
26
|
+
token = forge.token()
|
|
21
27
|
github_user = os.getenv("GITHUB_USER")
|
|
22
28
|
|
|
23
29
|
if not token or not github_user:
|
|
24
|
-
click.secho(
|
|
30
|
+
click.secho(
|
|
31
|
+
f"✖ Error: {forge.token_env_name()} and GITHUB_USER must be set", fg="red"
|
|
32
|
+
)
|
|
25
33
|
raise SystemExit(1)
|
|
26
34
|
|
|
27
|
-
upstream_owner = "splent-io"
|
|
28
|
-
api_url = f"
|
|
35
|
+
upstream_owner = os.getenv("SPLENT_DEFAULT_ORG", "splent-io")
|
|
36
|
+
api_url = f"{forge.api_base()}/repos/{upstream_owner}/{feature_name}/forks"
|
|
29
37
|
headers = {
|
|
30
38
|
"Authorization": f"token {token}",
|
|
31
|
-
"Accept":
|
|
39
|
+
"Accept": (
|
|
40
|
+
"application/vnd.github+json" if forge.is_github() else "application/json"
|
|
41
|
+
),
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
# `default_branch_only` is a GitHub-only key. Gitea-compatible forges reject
|
|
45
|
+
# nothing but understand nothing either, so they get an empty body.
|
|
46
|
+
payload = {"default_branch_only": False} if forge.is_github() else {}
|
|
47
|
+
|
|
34
48
|
click.secho(
|
|
35
49
|
f"🔁 Forking {upstream_owner}/{feature_name} into {github_user}...", fg="cyan"
|
|
36
50
|
)
|
|
@@ -38,17 +52,17 @@ def feature_fork(feature_name, version):
|
|
|
38
52
|
resp = requests.post(
|
|
39
53
|
api_url,
|
|
40
54
|
headers=headers,
|
|
41
|
-
json=
|
|
55
|
+
json=payload,
|
|
42
56
|
timeout=30,
|
|
43
57
|
)
|
|
44
58
|
except requests.RequestException as e:
|
|
45
|
-
click.secho(f"❌ Could not reach
|
|
59
|
+
click.secho(f"❌ Could not reach {_forge_label()}: {e}", fg="red")
|
|
46
60
|
raise SystemExit(2)
|
|
47
61
|
|
|
48
62
|
if resp.status_code in (403, 429):
|
|
49
63
|
click.secho(
|
|
50
|
-
"⚠
|
|
51
|
-
"Set
|
|
64
|
+
f"⚠ {_forge_label()} API rate limit or access denied. "
|
|
65
|
+
f"Set {forge.token_env_name()} to a token with repo scope and try again.",
|
|
52
66
|
fg="yellow",
|
|
53
67
|
)
|
|
54
68
|
if os.getenv("SPLENT_DEBUG"):
|
|
@@ -73,22 +87,24 @@ def feature_fork(feature_name, version):
|
|
|
73
87
|
raise SystemExit(2)
|
|
74
88
|
click.secho(f"🌐 Fork created: {fork_html_url}", fg="green")
|
|
75
89
|
|
|
76
|
-
# Esperar a que
|
|
90
|
+
# Esperar a que la forja lo procese
|
|
77
91
|
for i in range(5):
|
|
78
92
|
try:
|
|
79
93
|
r = requests.get(
|
|
80
|
-
f"
|
|
94
|
+
f"{forge.api_base()}/repos/{github_user}/{feature_name}",
|
|
81
95
|
headers=headers,
|
|
82
96
|
timeout=30,
|
|
83
97
|
)
|
|
84
98
|
except requests.RequestException as e:
|
|
85
|
-
click.secho(
|
|
99
|
+
click.secho(
|
|
100
|
+
f"⌛ Waiting for {_forge_label()} to process fork... ({e})", fg="yellow"
|
|
101
|
+
)
|
|
86
102
|
time.sleep(3)
|
|
87
103
|
continue
|
|
88
104
|
if r.status_code == 200:
|
|
89
105
|
click.secho("✅ Fork is ready.", fg="green")
|
|
90
106
|
break
|
|
91
|
-
click.secho("⌛ Waiting for
|
|
107
|
+
click.secho(f"⌛ Waiting for {_forge_label()} to process fork...", fg="yellow")
|
|
92
108
|
time.sleep(3)
|
|
93
109
|
else:
|
|
94
110
|
click.secho("⚠ Fork may still be processing.", fg="yellow")
|
|
@@ -138,7 +138,12 @@ def _extract_templates(src_dir: Path) -> list[str]:
|
|
|
138
138
|
|
|
139
139
|
|
|
140
140
|
def _extract_template_hook_slots(src_dir: Path) -> list[str]:
|
|
141
|
-
"""Extract hook slot names declared in templates
|
|
141
|
+
"""Extract hook slot names declared in templates.
|
|
142
|
+
|
|
143
|
+
A template opens a slot either by iterating ``get_template_hooks(name)``
|
|
144
|
+
or by asking for the joined output with ``render_template_hooks(name)``
|
|
145
|
+
(the form used when the template keeps a fallback for an empty slot).
|
|
146
|
+
"""
|
|
142
147
|
templates_dir = src_dir / "templates"
|
|
143
148
|
if not templates_dir.exists():
|
|
144
149
|
return []
|
|
@@ -146,7 +151,9 @@ def _extract_template_hook_slots(src_dir: Path) -> list[str]:
|
|
|
146
151
|
for html_file in templates_dir.rglob("*.html"):
|
|
147
152
|
text = html_file.read_text()
|
|
148
153
|
slots.update(
|
|
149
|
-
re.findall(
|
|
154
|
+
re.findall(
|
|
155
|
+
r"""(?:get|render)_template_hooks\s*\(\s*['"]([^'"]+)['"]""", text
|
|
156
|
+
)
|
|
150
157
|
)
|
|
151
158
|
return sorted(slots)
|
|
152
159
|
|
|
@@ -4,6 +4,7 @@ import tomllib
|
|
|
4
4
|
import click
|
|
5
5
|
import requests
|
|
6
6
|
from splent_cli.services import context, compose
|
|
7
|
+
from splent_cli.utils import forge
|
|
7
8
|
from splent_cli.utils.cache_utils import make_feature_writable
|
|
8
9
|
from splent_cli.utils.proc import run
|
|
9
10
|
from splent_cli.utils.io_utils import load_toml, atomic_write
|
|
@@ -62,6 +63,9 @@ def _git_out(path: str, *args) -> subprocess.CompletedProcess:
|
|
|
62
63
|
def ensure_git_main(path: str, ns_git: str, name: str):
|
|
63
64
|
from splent_cli.utils.git_url import build_git_url
|
|
64
65
|
|
|
66
|
+
# build_git_url() resolves the host and transport through the forge module,
|
|
67
|
+
# so rewriting origin here is also what repoints a checkout cloned from a
|
|
68
|
+
# previous forge at the one the workspace is configured for now.
|
|
65
69
|
remote_url, _ = build_git_url(ns_git, name)
|
|
66
70
|
r = _git_out(path, "remote", "get-url", "origin")
|
|
67
71
|
if r.returncode == 0:
|
|
@@ -193,18 +197,20 @@ def _compile_assets(workspace: str, product_path: str, name: str):
|
|
|
193
197
|
# =====================================================================
|
|
194
198
|
def _warn_no_push_access(ns_git: str, name: str):
|
|
195
199
|
"""Print a warning if the user cannot push to the repo. Non-blocking."""
|
|
196
|
-
token =
|
|
200
|
+
token = forge.token()
|
|
197
201
|
if not token:
|
|
198
202
|
return # no token = can't check, don't nag
|
|
199
203
|
|
|
200
204
|
headers = {
|
|
201
|
-
"Accept":
|
|
205
|
+
"Accept": (
|
|
206
|
+
"application/vnd.github+json" if forge.is_github() else "application/json"
|
|
207
|
+
),
|
|
202
208
|
"Authorization": f"token {token}",
|
|
203
209
|
"User-Agent": "splent-cli",
|
|
204
210
|
}
|
|
205
211
|
try:
|
|
206
212
|
resp = requests.get(
|
|
207
|
-
f"
|
|
213
|
+
f"{forge.api_base()}/repos/{ns_git}/{name}",
|
|
208
214
|
headers=headers,
|
|
209
215
|
timeout=5,
|
|
210
216
|
)
|
|
@@ -644,6 +644,24 @@ def product_build(no_image, skip_preflight):
|
|
|
644
644
|
|
|
645
645
|
click.echo(f"\n🐳 Building Docker image: {product}:{version}...")
|
|
646
646
|
|
|
647
|
+
# A feature that is not published to PyPI is installed from git
|
|
648
|
+
# inside the builder stage, and the URL for that is built from the
|
|
649
|
+
# forge settings. The build sees none of the workspace environment,
|
|
650
|
+
# so forward whichever of them is configured. The token is
|
|
651
|
+
# deliberately not forwarded: a build arg is recorded in the image
|
|
652
|
+
# metadata, so the repositories reached this way must be readable
|
|
653
|
+
# anonymously.
|
|
654
|
+
forge_args = []
|
|
655
|
+
for var in (
|
|
656
|
+
"SPLENT_FORGE_HOST",
|
|
657
|
+
"SPLENT_FORGE_SSH_HOST",
|
|
658
|
+
"SPLENT_FORGE_SSH_PORT",
|
|
659
|
+
"SPLENT_FORGE_API",
|
|
660
|
+
):
|
|
661
|
+
value = os.getenv(var)
|
|
662
|
+
if value:
|
|
663
|
+
forge_args += ["--build-arg", f"{var}={value}"]
|
|
664
|
+
|
|
647
665
|
try:
|
|
648
666
|
subprocess.run(
|
|
649
667
|
[
|
|
@@ -653,6 +671,7 @@ def product_build(no_image, skip_preflight):
|
|
|
653
671
|
f"{product}:{version}",
|
|
654
672
|
"-t",
|
|
655
673
|
f"{product}:latest",
|
|
674
|
+
*forge_args,
|
|
656
675
|
"-f",
|
|
657
676
|
dockerfile,
|
|
658
677
|
workspace,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
|
+
from splent_cli.utils import forge
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
@click.command(
|
|
5
7
|
"tokens:setup",
|
|
6
|
-
short_help="Print setup instructions for
|
|
8
|
+
short_help="Print setup instructions for the git forge and PyPI tokens.",
|
|
7
9
|
)
|
|
8
10
|
def tokens():
|
|
9
11
|
"""
|
|
@@ -14,21 +16,39 @@ def tokens():
|
|
|
14
16
|
click.secho(" Required tokens for SPLENT", bold=True)
|
|
15
17
|
click.echo(click.style(f" {'─' * 56}", fg="bright_black"))
|
|
16
18
|
|
|
17
|
-
# ──
|
|
19
|
+
# ── Forge ─────────────────────────────────────────────────────────
|
|
20
|
+
# The variable to set, the settings path and the scopes all depend on the
|
|
21
|
+
# forge this workspace points at, so the section is written from forge
|
|
22
|
+
# instead of from a github.com literal.
|
|
18
23
|
click.echo()
|
|
19
|
-
click.secho("
|
|
24
|
+
click.secho(f" {forge.token_env_name()}", fg="cyan", bold=True)
|
|
20
25
|
click.echo(" Used by: feature:release, feature:versions, feature:upgrade,")
|
|
21
26
|
click.echo(" feature:search, check:github")
|
|
22
27
|
click.echo()
|
|
23
28
|
click.echo(" How to get it:")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if forge.is_github():
|
|
30
|
+
click.echo(" 1. Go to github.com → Settings → Developer settings")
|
|
31
|
+
click.echo(" → Personal access tokens → Tokens (classic)")
|
|
32
|
+
click.echo(" 2. Generate new token (classic)")
|
|
33
|
+
click.echo(" 3. Enable scope: repo (full control of private repos)")
|
|
34
|
+
click.echo(" 4. Copy the token (shown only once)")
|
|
35
|
+
click.echo()
|
|
36
|
+
click.secho(" Add to your .env:", fg="bright_black")
|
|
37
|
+
click.echo(f" {forge.token_env_name()}=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxx")
|
|
38
|
+
else:
|
|
39
|
+
# Forgejo and Gitea keep tokens under Applications, and their scopes are
|
|
40
|
+
# per resource instead of GitHub's single 'repo'. Releasing a feature
|
|
41
|
+
# writes to a repository and, for an org-owned one, to the org itself.
|
|
42
|
+
click.echo(f" 1. Go to {forge.host()} → Settings → Applications")
|
|
43
|
+
click.echo(" → Access Tokens")
|
|
44
|
+
click.echo(" 2. Generate new token")
|
|
45
|
+
click.echo(" 3. Enable scopes: write:repository and write:organization")
|
|
46
|
+
click.echo(" 4. Copy the token (shown only once)")
|
|
47
|
+
click.echo()
|
|
48
|
+
click.secho(" Add to your .env:", fg="bright_black")
|
|
49
|
+
click.echo(
|
|
50
|
+
f" {forge.token_env_name()}=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
51
|
+
)
|
|
32
52
|
|
|
33
53
|
# ── PyPI ──────────────────────────────────────────────────────────
|
|
34
54
|
click.echo()
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
"""Ask the marketplace index to rebuild itself.
|
|
2
2
|
|
|
3
3
|
The published index is what the marketplace serves, and it is built by a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
CI workflow that runs on a push to its own repository, on a schedule, or
|
|
5
|
+
on demand. A feature release or an SPL publication happens in a different
|
|
6
|
+
repository, so neither of them used to reach it: the index caught up on
|
|
7
|
+
the next cron run, hours later, and the only way to see a release in
|
|
8
|
+
production sooner was to press the button by hand.
|
|
9
9
|
|
|
10
10
|
Releasing now asks for the rebuild as its last step. It is deliberately
|
|
11
11
|
best effort: the release itself is already done and irreversible by then,
|
|
12
12
|
so a token without workflow scope, or a network that is down, is reported
|
|
13
13
|
and never turns a finished release into a failure.
|
|
14
|
+
|
|
15
|
+
Asking on demand is one POST to the workflow dispatch endpoint, which
|
|
16
|
+
GitHub and Gitea-compatible forges spell the same way and answer to the
|
|
17
|
+
same ``{"ref": ...}`` body. Only the headers differ, since GitHub's vendor
|
|
18
|
+
media type and API version pin mean nothing elsewhere and the credential
|
|
19
|
+
is read from the ``token`` scheme rather than ``Bearer``. A forge that
|
|
20
|
+
does not implement the endpoint answers 404, which is already handled the
|
|
21
|
+
same way as any other refusal.
|
|
14
22
|
"""
|
|
15
23
|
|
|
16
24
|
import json
|
|
@@ -20,6 +28,8 @@ import urllib.request
|
|
|
20
28
|
|
|
21
29
|
import click
|
|
22
30
|
|
|
31
|
+
from splent_cli.utils import forge
|
|
32
|
+
|
|
23
33
|
INDEX_REPO = os.getenv("SPLENT_INDEX_REPO", "splent-io/splent_index")
|
|
24
34
|
INDEX_WORKFLOW = os.getenv("SPLENT_INDEX_WORKFLOW", "build-index.yml")
|
|
25
35
|
INDEX_REF = os.getenv("SPLENT_INDEX_REF", "main")
|
|
@@ -28,22 +38,46 @@ TIMEOUT_SECONDS = 15
|
|
|
28
38
|
|
|
29
39
|
def _api_url() -> str:
|
|
30
40
|
return (
|
|
31
|
-
f"
|
|
41
|
+
f"{forge.api_base()}/repos/{INDEX_REPO}"
|
|
32
42
|
f"/actions/workflows/{INDEX_WORKFLOW}/dispatches"
|
|
33
43
|
)
|
|
34
44
|
|
|
35
45
|
|
|
46
|
+
def _headers(token: str) -> dict:
|
|
47
|
+
"""Request headers in the forge's own spelling.
|
|
48
|
+
|
|
49
|
+
GitHub wants its vendor media type, its API version pin and a bearer
|
|
50
|
+
credential. A Gitea-compatible forge understands none of the three and
|
|
51
|
+
reads the credential from the ``token`` scheme, so it gets plain JSON.
|
|
52
|
+
"""
|
|
53
|
+
if forge.is_github():
|
|
54
|
+
return {
|
|
55
|
+
"Accept": "application/vnd.github+json",
|
|
56
|
+
"Authorization": f"Bearer {token}",
|
|
57
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
"User-Agent": "splent-cli",
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
"Accept": "application/json",
|
|
63
|
+
"Authorization": f"token {token}",
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
"User-Agent": "splent-cli",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
36
69
|
def request_rebuild(*, quiet: bool = False) -> bool:
|
|
37
|
-
"""Trigger the index build workflow. True when
|
|
70
|
+
"""Trigger the index build workflow. True when the forge accepted it.
|
|
38
71
|
|
|
39
72
|
Never raises: the caller has already published something.
|
|
40
73
|
"""
|
|
41
|
-
token =
|
|
74
|
+
token = forge.token()
|
|
42
75
|
if not token:
|
|
43
76
|
if not quiet:
|
|
44
77
|
click.secho(
|
|
45
|
-
" index no
|
|
46
|
-
"asked to rebuild. It will catch up on its own
|
|
78
|
+
f" index no {forge.token_env_name()}, so the marketplace "
|
|
79
|
+
"index was not asked to rebuild. It will catch up on its own "
|
|
80
|
+
"schedule.",
|
|
47
81
|
fg="yellow",
|
|
48
82
|
)
|
|
49
83
|
return False
|
|
@@ -52,13 +86,7 @@ def request_rebuild(*, quiet: bool = False) -> bool:
|
|
|
52
86
|
_api_url(),
|
|
53
87
|
data=json.dumps({"ref": INDEX_REF}).encode(),
|
|
54
88
|
method="POST",
|
|
55
|
-
headers=
|
|
56
|
-
"Accept": "application/vnd.github+json",
|
|
57
|
-
"Authorization": f"Bearer {token}",
|
|
58
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
59
|
-
"Content-Type": "application/json",
|
|
60
|
-
"User-Agent": "splent-cli",
|
|
61
|
-
},
|
|
89
|
+
headers=_headers(token),
|
|
62
90
|
)
|
|
63
91
|
|
|
64
92
|
try:
|
|
@@ -66,11 +94,13 @@ def request_rebuild(*, quiet: bool = False) -> bool:
|
|
|
66
94
|
accepted = response.status in (200, 201, 204)
|
|
67
95
|
except urllib.error.HTTPError as error:
|
|
68
96
|
if not quiet:
|
|
69
|
-
hint =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
97
|
+
hint = ""
|
|
98
|
+
if error.code in (403, 404):
|
|
99
|
+
hint = (
|
|
100
|
+
" The token needs the workflow scope."
|
|
101
|
+
if forge.is_github()
|
|
102
|
+
else " The token needs write access to the index repository."
|
|
103
|
+
)
|
|
74
104
|
click.secho(
|
|
75
105
|
f" index the marketplace index refused the rebuild "
|
|
76
106
|
f"({error.code}).{hint} It will catch up on its own schedule.",
|
|
@@ -80,8 +110,8 @@ def request_rebuild(*, quiet: bool = False) -> bool:
|
|
|
80
110
|
except Exception:
|
|
81
111
|
if not quiet:
|
|
82
112
|
click.secho(
|
|
83
|
-
" index could not reach
|
|
84
|
-
"index. It will catch up on its own schedule.",
|
|
113
|
+
f" index could not reach {forge.host()} to rebuild the "
|
|
114
|
+
"marketplace index. It will catch up on its own schedule.",
|
|
85
115
|
fg="yellow",
|
|
86
116
|
)
|
|
87
117
|
return False
|