gitpr-cli 1.4.2__tar.gz → 1.4.3__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.
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/PKG-INFO +1 -1
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/gitpr_cli.egg-info/PKG-INFO +1 -1
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/publish_pull_request.py +18 -16
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/main.py +19 -0
- gitpr_cli-1.4.3/src/updater.py +274 -0
- gitpr_cli-1.4.3/tests/test_updater.py +519 -0
- gitpr_cli-1.4.2/src/updater.py +0 -145
- gitpr_cli-1.4.2/tests/test_updater.py +0 -231
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/LICENSE +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/README.md +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/gitpr_cli.egg-info/SOURCES.txt +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/gitpr_cli.egg-info/dependency_links.txt +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/gitpr_cli.egg-info/entry_points.txt +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/gitpr_cli.egg-info/requires.txt +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/gitpr_cli.egg-info/top_level.txt +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/pyproject.toml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/setup.cfg +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ai_providers.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/activate_policy_pack.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/baseline_gate.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/baseline_summary.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/calculate_risk.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/compare_against_baseline.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/create_baseline.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/generate_mentor_explanation.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/generate_pr_explanation.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/generate_test_file.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/install_policy_pack.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/resolve_effective_policy.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/run_check.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/suppress_finding.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/application/use_cases/validate_policy_pack.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/blame_engine.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/branding/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/branding/badge_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/branding/badge_data.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/cache.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/changelog_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/chat_memory.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/commit_classifier.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/config.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/config_schema.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/core.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/demo/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/demo/demo_runner.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/demo/fake_ai_provider.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/demo/scenarios/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/demo/scenarios/laravel_bug_fix.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/demo/scenarios/security_issue.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/diff_parser.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/doc_links.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/baseline/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/baseline/baseline_comparator.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/baseline/baseline_fingerprint.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/baseline/baseline_manifest.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/baseline/baseline_types.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/baseline/suppression_policy.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/check/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/check/check_config.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/check/check_output.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/check/check_types.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/finding/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/finding/finding_types.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/linter/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/linter/sast_finding_mapper.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/mentor/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/mentor/mentor_explanation_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/policy/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/policy/policy_checksum.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/policy/policy_compatibility.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/policy/policy_manifest.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/policy/policy_resolver.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/policy/policy_types.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/pr/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/pr/explain_section_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/risk/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/risk/risk_calculator.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/risk/risk_explanation.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/risk/risk_rules.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/risk/risk_types.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/tests_generation/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/tests_generation/framework_detector.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/tests_generation/test_content_types.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/domain/tests_generation/test_scaffold_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/apply_fix.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/fix_history.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/patch_applier.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/patch_extractor.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/patch_provenance.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/patch_safety_classifier.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/fix/rollback_fix.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/github_api.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/i18n.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/baseline/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/baseline/local_baseline_repository.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/ci/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/ci/junit_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/ci/masking.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/ci/sarif_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/git/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/git/identity.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/git/patch_applier.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/git/risk_history_reader.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/git/selective_stager.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/git/test_matcher.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/linter/external/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/linter/external/bandit_bridge.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/linter/external/base_bridge.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/linter/external/gitleaks_bridge.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/linter/external/semgrep_bridge.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/policy/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/policy/local_policy_repository.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/policy/policy_pack_loader.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/azure_devops_provider.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/base.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/bitbucket_provider.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/factory.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/github_provider.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/gitlab_provider.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/infrastructure/scm/web_links.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/issue_engine.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ledger.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/linter_engine.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/linter_wizard.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/mcp_server.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/metrics.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/net.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/laravel-quality/CHANGELOG.md +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/laravel-quality/README.md +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/laravel-quality/linter.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/laravel-quality/policy.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/node-quality/README.md +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/node-quality/linter.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/node-quality/policy.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/php-security/README.md +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/php-security/linter.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/php-security/policy.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/vue-quality/README.md +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/vue-quality/linter.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/policy_packs/vue-quality/policy.yml +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/release_engine.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/review/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/review/diff_normalizer.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/review/diff_source.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/review/remote_pr.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/review/render.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/reviewer_resolution.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/reviewer_suggestion.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/security.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/security_ruleset.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/spinner.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/split/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/split/apply_split_plan.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/split/generate_split_plan.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/split/hunk_grouper.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/split/hunk_parser.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/split/split_plan.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/suggest_reviewers.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/tui_issue.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/chat_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/config_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/demo/__init__.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/demo/demo_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/demo/demo_help_screen.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/demo/demo_screens.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/help_screen.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/issue_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/linter_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/metrics_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/metrics_migration_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/pr_publish_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/ui/pr_publish_help.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/usage_log.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/src/version_bump.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_baseline_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_blame_engine_ranges.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_blame_metrics.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_changelog_builder.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_chat_backend.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_check_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_commit_classifier.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_config_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_config_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_config_schema.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_config_store.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_config_suggest_reviewers.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_config_validation.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_core.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_diff_failure_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_diff_parser.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_explain_command.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_external_linters.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_full_diff_encoding.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_i18n.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_install_wizard.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_issue_engine.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_linter_metrics.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_linter_presets.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_main_suggest_reviewers.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_mcp_prompts.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_mcp_server.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_mcp_server_e2e.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_mentor_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_metrics.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_net_timeouts.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_plugins.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_policy_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_pr_publish_app.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_pr_publish_linter_modal.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_pre_save.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_release_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_release_engine.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_reviewer_resolution.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_reviewer_suggestion.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_risk_cli.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_security_ruleset.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_skill_command.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_skill_context.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_skill_templates.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_smart_excludes.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_suggest_reviewers.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_tests_command.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_thinking_words.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_usage_log.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_version_bump.py +0 -0
- {gitpr_cli-1.4.2 → gitpr_cli-1.4.3}/tests/test_web_links.py +0 -0
|
@@ -129,20 +129,20 @@ def publish_pull_request_use_case(
|
|
|
129
129
|
message=__("PR description must not be empty."),
|
|
130
130
|
)
|
|
131
131
|
|
|
132
|
-
if not getattr(provider, "token", ""):
|
|
133
|
-
return PublishPrOutcome(
|
|
134
|
-
status="auth_required",
|
|
135
|
-
provider=provider.name,
|
|
136
|
-
source_branch=source_branch,
|
|
137
|
-
target_branch=target_branch,
|
|
138
|
-
http_status=401,
|
|
139
|
-
message=__(
|
|
140
|
-
"{provider} token is not configured. Run 'gitpr' in a terminal to authenticate.",
|
|
141
|
-
provider=label,
|
|
142
|
-
),
|
|
143
|
-
)
|
|
144
|
-
|
|
145
132
|
if check_remote_state:
|
|
133
|
+
if not getattr(provider, "token", ""):
|
|
134
|
+
return PublishPrOutcome(
|
|
135
|
+
status="auth_required",
|
|
136
|
+
provider=provider.name,
|
|
137
|
+
source_branch=source_branch,
|
|
138
|
+
target_branch=target_branch,
|
|
139
|
+
http_status=401,
|
|
140
|
+
message=__(
|
|
141
|
+
"{provider} token is not configured. Run 'gitpr' in a terminal to authenticate.",
|
|
142
|
+
provider=label,
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
146
|
try:
|
|
147
147
|
existing: Optional[PullRequestResult] = provider.check_existing_pull_request(
|
|
148
148
|
repo_ref, source_branch
|
|
@@ -254,10 +254,12 @@ def publish_pull_request_use_case(
|
|
|
254
254
|
message=mask_sensitive_text(str(exc)),
|
|
255
255
|
)
|
|
256
256
|
|
|
257
|
+
pr_url = getattr(result, "url", "")
|
|
258
|
+
pr_number = getattr(result, "number", 0)
|
|
257
259
|
return PublishPrOutcome(
|
|
258
260
|
status="success",
|
|
259
|
-
url=
|
|
260
|
-
number=
|
|
261
|
+
url=pr_url,
|
|
262
|
+
number=pr_number,
|
|
261
263
|
provider=provider.name,
|
|
262
264
|
source_branch=source_branch,
|
|
263
265
|
target_branch=target_branch,
|
|
@@ -265,6 +267,6 @@ def publish_pull_request_use_case(
|
|
|
265
267
|
message=__(
|
|
266
268
|
"PR successfully created on {provider}: {pr_url}",
|
|
267
269
|
provider=label,
|
|
268
|
-
pr_url=
|
|
270
|
+
pr_url=pr_url,
|
|
269
271
|
),
|
|
270
272
|
)
|
|
@@ -75,6 +75,18 @@ def _may_ask_the_user(quiet=False, hook=False, mcp=False):
|
|
|
75
75
|
return False
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
def _subcommand_help_in_argv(argv=None):
|
|
79
|
+
"""Whether the raw command line asks for help.
|
|
80
|
+
|
|
81
|
+
Click clears the group context arguments before this callback runs, so by
|
|
82
|
+
the time the update gate below executes, ``gitpr release -h`` is
|
|
83
|
+
indistinguishable from ``gitpr release``. The raw tokens are the only
|
|
84
|
+
surviving record. The scan fails open, which costs at most one skipped gate.
|
|
85
|
+
"""
|
|
86
|
+
tokens = sys.argv[1:] if argv is None else argv
|
|
87
|
+
return any(token in ("-h", "--help") for token in tokens)
|
|
88
|
+
|
|
89
|
+
|
|
78
90
|
def print_banner():
|
|
79
91
|
"""Displays the project ASCII Art signature"""
|
|
80
92
|
banner = r"""
|
|
@@ -677,6 +689,13 @@ def cli(
|
|
|
677
689
|
# first, so everything below runs only when NO subcommand was requested —
|
|
678
690
|
# the legacy flag surface stays byte-for-byte intact (ADR-002).
|
|
679
691
|
if ctx.invoked_subcommand is not None:
|
|
692
|
+
# The mandatory update gate covers subcommands too, but only when the
|
|
693
|
+
# run may stop and ask the user: a release in CI, a pipe or a hook must
|
|
694
|
+
# never be blocked. Help tokens are read from the raw argv because
|
|
695
|
+
# Click clears the context arguments before this callback runs.
|
|
696
|
+
if may_ask and not update and not help_flag and not _subcommand_help_in_argv():
|
|
697
|
+
if enforce_update_required():
|
|
698
|
+
sys.exit(1)
|
|
680
699
|
return
|
|
681
700
|
|
|
682
701
|
# ============================================================
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from packaging.version import InvalidVersion, Version
|
|
8
|
+
|
|
9
|
+
# Current version of your local executable (Update this on every new build!)
|
|
10
|
+
# ── Must stay ABOVE the i18n import: i18n.py lazily imports __lang_version__
|
|
11
|
+
# from here, and pyproject.toml reads __version__ via setuptools attr:.
|
|
12
|
+
__version__ = "1.4.3" # GitPR current version
|
|
13
|
+
__lang_version__ = "v0.0.35" # Language dictionary version control
|
|
14
|
+
__scripts_version__ = (
|
|
15
|
+
"v0.0.4" # Git Hook scripts version control (independent from __lang_version__)
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from src.i18n import __
|
|
19
|
+
from src.net import bounded_urlopen
|
|
20
|
+
|
|
21
|
+
PYPI_API_URL = "https://pypi.org/pypi/gitpr-cli/json"
|
|
22
|
+
|
|
23
|
+
# How long a PyPI answer is trusted before the next check goes back online.
|
|
24
|
+
# A rolling window, not the local calendar day: a release published after the
|
|
25
|
+
# first run of the day used to stay invisible until midnight.
|
|
26
|
+
UPDATE_CACHE_TTL_SECONDS = 3600
|
|
27
|
+
|
|
28
|
+
# Wall-clock ceiling for the PyPI fetch, DNS resolution included.
|
|
29
|
+
UPDATE_FETCH_HARD_TIMEOUT = 3.0
|
|
30
|
+
|
|
31
|
+
# Environment switch used to mute the mandatory update check (test harness and
|
|
32
|
+
# offline automation). Parsed as a strict boolean: only _SKIP_TRUTHY_VALUES
|
|
33
|
+
# disable it, so `GITPR_SKIP_UPDATE_CHECK=false` still checks.
|
|
34
|
+
SKIP_UPDATE_CHECK_ENV = "GITPR_SKIP_UPDATE_CHECK"
|
|
35
|
+
_SKIP_TRUTHY_VALUES = ("1", "true", "yes", "on", "y")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_gitpr_dir():
|
|
39
|
+
"""Returns the ~/.gitpr/ directory path."""
|
|
40
|
+
return os.path.join(os.path.expanduser("~"), ".gitpr")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_update_cache_file():
|
|
44
|
+
"""Returns the path to the update cache file."""
|
|
45
|
+
return os.path.join(get_gitpr_dir(), "update_cache.json")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _coerce_version(value):
|
|
49
|
+
"""Parses *value* as a PEP 440 version, or returns None when it is not one."""
|
|
50
|
+
if not isinstance(value, str):
|
|
51
|
+
return None
|
|
52
|
+
text = value.strip()
|
|
53
|
+
# packaging rejects a leading "v"; the old helper stripped every "v" in the
|
|
54
|
+
# string, which corrupted suffixes such as 1.2.3-dev.
|
|
55
|
+
if text[:1] in ("v", "V"):
|
|
56
|
+
text = text[1:]
|
|
57
|
+
if not text:
|
|
58
|
+
return None
|
|
59
|
+
try:
|
|
60
|
+
return Version(text)
|
|
61
|
+
except InvalidVersion:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def parse_version(version_str):
|
|
66
|
+
"""Converts 'v0.1.0' or '0.1.0' into a tuple (0, 1, 0) for version math.
|
|
67
|
+
|
|
68
|
+
Kept for compatibility with outside callers. The tuple is lossy for
|
|
69
|
+
pre/post/dev markers, so the update gate itself compares Version objects
|
|
70
|
+
through _is_newer() instead.
|
|
71
|
+
"""
|
|
72
|
+
parsed = _coerce_version(version_str)
|
|
73
|
+
return parsed.release if parsed is not None else (0, 0, 0)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_update_check_disabled():
|
|
77
|
+
"""Returns True when the update check must be muted for this execution."""
|
|
78
|
+
value = os.environ.get(SKIP_UPDATE_CHECK_ENV, "")
|
|
79
|
+
return value.strip().lower() in _SKIP_TRUTHY_VALUES
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _cache_is_fresh(cache_data, now=None):
|
|
83
|
+
"""Whether *cache_data* carries a timestamp inside the freshness window.
|
|
84
|
+
|
|
85
|
+
A timestamp in the future (the clock moved backwards) counts as stale, so
|
|
86
|
+
a clock jump can never freeze the cache on an old answer indefinitely.
|
|
87
|
+
"""
|
|
88
|
+
checked_at = cache_data.get("checked_at")
|
|
89
|
+
if not isinstance(checked_at, str):
|
|
90
|
+
return False
|
|
91
|
+
try:
|
|
92
|
+
written_at = datetime.fromisoformat(checked_at)
|
|
93
|
+
current = datetime.now() if now is None else now
|
|
94
|
+
age = (current - written_at).total_seconds()
|
|
95
|
+
except (TypeError, ValueError):
|
|
96
|
+
# Not a valid timestamp, or a timezone-aware stamp this cache never writes.
|
|
97
|
+
return False
|
|
98
|
+
return 0 <= age < UPDATE_CACHE_TTL_SECONDS
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _read_cached_version(cache_file):
|
|
102
|
+
"""Returns the cached PyPI version while it is still fresh, else "".
|
|
103
|
+
|
|
104
|
+
Only the current schema is read: a document from the former calendar-day
|
|
105
|
+
cache has no ``checked_at`` and therefore counts as expired.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
with open(cache_file, "r", encoding="utf-8", errors="replace") as f:
|
|
109
|
+
cache_data = json.load(f)
|
|
110
|
+
except Exception:
|
|
111
|
+
return ""
|
|
112
|
+
|
|
113
|
+
if not isinstance(cache_data, dict) or not _cache_is_fresh(cache_data):
|
|
114
|
+
return ""
|
|
115
|
+
|
|
116
|
+
version = cache_data.get("version")
|
|
117
|
+
if isinstance(version, str) and version.strip():
|
|
118
|
+
return version.strip()
|
|
119
|
+
return ""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _write_cached_version(cache_file, version):
|
|
123
|
+
"""Writes the cache atomically; the caller treats a failure as non-fatal."""
|
|
124
|
+
folder = os.path.dirname(cache_file)
|
|
125
|
+
if folder:
|
|
126
|
+
os.makedirs(folder, exist_ok=True)
|
|
127
|
+
|
|
128
|
+
payload = {"checked_at": datetime.now().isoformat(), "version": version}
|
|
129
|
+
handle = tempfile.NamedTemporaryFile(
|
|
130
|
+
"w",
|
|
131
|
+
encoding="utf-8",
|
|
132
|
+
dir=folder or ".",
|
|
133
|
+
prefix=".update-cache-",
|
|
134
|
+
suffix=".tmp",
|
|
135
|
+
delete=False,
|
|
136
|
+
)
|
|
137
|
+
try:
|
|
138
|
+
with handle:
|
|
139
|
+
json.dump(payload, handle)
|
|
140
|
+
os.replace(handle.name, cache_file)
|
|
141
|
+
except Exception:
|
|
142
|
+
try:
|
|
143
|
+
os.unlink(handle.name)
|
|
144
|
+
except OSError:
|
|
145
|
+
pass
|
|
146
|
+
raise
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _fetch_pypi_version():
|
|
150
|
+
"""Returns the version published on PyPI, or "" when it cannot be read."""
|
|
151
|
+
raw = bounded_urlopen(
|
|
152
|
+
PYPI_API_URL,
|
|
153
|
+
timeout=3,
|
|
154
|
+
hard_timeout=UPDATE_FETCH_HARD_TIMEOUT,
|
|
155
|
+
headers={"User-Agent": "GitPR-Updater"},
|
|
156
|
+
)
|
|
157
|
+
if not raw:
|
|
158
|
+
return ""
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
data = json.loads(raw.decode("utf-8", errors="replace"))
|
|
162
|
+
version = data["info"]["version"]
|
|
163
|
+
except Exception:
|
|
164
|
+
return ""
|
|
165
|
+
|
|
166
|
+
# Only a parseable version is trusted: a captive portal or a malformed
|
|
167
|
+
# mirror must not poison the cache for a whole hour.
|
|
168
|
+
if _coerce_version(version) is None:
|
|
169
|
+
return ""
|
|
170
|
+
return version.strip()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def get_latest_remote_version(force=False):
|
|
174
|
+
"""Fetches the latest version published on PyPI, with a one-hour cache.
|
|
175
|
+
|
|
176
|
+
``force=True`` (the ``--update`` flag) always asks PyPI: that command
|
|
177
|
+
promises an immediate verification, so it must not answer from the cache.
|
|
178
|
+
Returns "" when the remote version is unknown — offline, stalled DNS, or a
|
|
179
|
+
body that is not a version — and the caller then lets the run proceed.
|
|
180
|
+
"""
|
|
181
|
+
cache_file = get_update_cache_file()
|
|
182
|
+
|
|
183
|
+
if not force:
|
|
184
|
+
cached = _read_cached_version(cache_file)
|
|
185
|
+
if cached:
|
|
186
|
+
return cached
|
|
187
|
+
|
|
188
|
+
latest_version = _fetch_pypi_version()
|
|
189
|
+
if latest_version:
|
|
190
|
+
try:
|
|
191
|
+
_write_cached_version(cache_file, latest_version)
|
|
192
|
+
except Exception:
|
|
193
|
+
pass # A read-only home or a locked file must not hide the answer
|
|
194
|
+
return latest_version
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _is_newer(remote_version, local_version=None):
|
|
198
|
+
"""Whether *remote_version* is a version this install should upgrade to.
|
|
199
|
+
|
|
200
|
+
PEP 440 ordering instead of a tuple comparison, so 1.4.10 > 1.4.9 and
|
|
201
|
+
pre/post/dev markers follow the standard. A remote pre-release is ignored
|
|
202
|
+
while the local install is stable: pip will not install it without --pre,
|
|
203
|
+
so blocking would tell the user to run a command that does nothing. An
|
|
204
|
+
unparseable version on either side is "unknown", never "newer".
|
|
205
|
+
"""
|
|
206
|
+
remote = _coerce_version(remote_version)
|
|
207
|
+
local = _coerce_version(__version__ if local_version is None else local_version)
|
|
208
|
+
if remote is None or local is None:
|
|
209
|
+
return False
|
|
210
|
+
if remote.is_prerelease and not local.is_prerelease:
|
|
211
|
+
return False
|
|
212
|
+
return remote > local
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def enforce_update_required():
|
|
216
|
+
"""Blocks execution while a newer GitPR version is published on PyPI.
|
|
217
|
+
|
|
218
|
+
Prints the upgrade instructions and returns True when the caller must stop.
|
|
219
|
+
Returns False when the local version is current, when the remote version is
|
|
220
|
+
unknown (offline, so the user could not upgrade anyway), when it is a
|
|
221
|
+
pre-release a stable install cannot move to, or when the check is disabled
|
|
222
|
+
via GITPR_SKIP_UPDATE_CHECK.
|
|
223
|
+
"""
|
|
224
|
+
if is_update_check_disabled():
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
latest_version = get_latest_remote_version()
|
|
228
|
+
|
|
229
|
+
if not _is_newer(latest_version):
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
click.echo("")
|
|
233
|
+
click.secho(
|
|
234
|
+
__(
|
|
235
|
+
"⚠️ A new version of GitPR is available: {current_version} -> {latest_version}",
|
|
236
|
+
current_version=__version__,
|
|
237
|
+
latest_version=latest_version,
|
|
238
|
+
),
|
|
239
|
+
fg="yellow",
|
|
240
|
+
bold=True,
|
|
241
|
+
)
|
|
242
|
+
click.secho(
|
|
243
|
+
__("GitPR must be updated before it can run: pip install --upgrade gitpr-cli"),
|
|
244
|
+
fg="cyan",
|
|
245
|
+
bold=True,
|
|
246
|
+
)
|
|
247
|
+
click.echo("")
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def check_and_update():
|
|
252
|
+
"""Function triggered only when the user forces the --update flag."""
|
|
253
|
+
latest_version = get_latest_remote_version(force=True)
|
|
254
|
+
|
|
255
|
+
if not latest_version:
|
|
256
|
+
click.secho(__("❌ Could not check for updates at this moment."), fg="red")
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
if _is_newer(latest_version):
|
|
260
|
+
click.secho(
|
|
261
|
+
__(
|
|
262
|
+
"\n🚀 New GitPR version found (v{latest_version})!",
|
|
263
|
+
latest_version=latest_version,
|
|
264
|
+
),
|
|
265
|
+
fg="green",
|
|
266
|
+
bold=True,
|
|
267
|
+
)
|
|
268
|
+
click.secho(
|
|
269
|
+
__("Run to update: pip install --upgrade gitpr-cli"), fg="cyan", bold=True
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
click.secho(
|
|
273
|
+
__("✅ You are already using the latest version of GitPR."), fg="green"
|
|
274
|
+
)
|