crackerjack 0.21.0__tar.gz → 0.21.2__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.
Files changed (89) hide show
  1. {crackerjack-0.21.0 → crackerjack-0.21.2}/PKG-INFO +1 -1
  2. crackerjack-0.21.2/crackerjack/.pre-commit-config-ai.yaml +135 -0
  3. crackerjack-0.21.2/crackerjack/.ruff_cache/0.12.1/5056746222905752453 +0 -0
  4. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/crackerjack.py +32 -19
  5. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/pyproject.toml +1 -1
  6. {crackerjack-0.21.0 → crackerjack-0.21.2}/pyproject.toml +1 -1
  7. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_crackerjack.py +167 -119
  8. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_crackerjack_runner.py +3 -2
  9. crackerjack-0.21.0/crackerjack/.ruff_cache/0.12.1/5056746222905752453 +0 -0
  10. {crackerjack-0.21.0 → crackerjack-0.21.2}/LICENSE +0 -0
  11. {crackerjack-0.21.0 → crackerjack-0.21.2}/README.md +0 -0
  12. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.gitignore +0 -0
  13. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.libcst.codemod.yaml +0 -0
  14. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pdm.toml +0 -0
  15. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pre-commit-config.yaml +0 -0
  16. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pytest_cache/.gitignore +0 -0
  17. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
  18. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pytest_cache/README.md +0 -0
  19. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
  20. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
  21. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/.gitignore +0 -0
  22. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  23. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  24. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  25. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  26. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  27. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  28. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
  29. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
  30. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
  31. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
  32. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
  33. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
  34. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.13/4240757255861806333 +0 -0
  35. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  36. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  37. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  38. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  39. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  40. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  41. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  42. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.12.0/5056746222905752453 +0 -0
  43. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  44. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  45. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  46. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  47. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  48. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  49. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  50. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  51. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  52. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  53. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  54. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  55. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  56. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  57. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  58. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  59. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  60. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  61. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  62. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  63. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  64. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  65. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  66. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  67. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  68. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  69. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
  70. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/__init__.py +0 -0
  71. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/__main__.py +0 -0
  72. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/errors.py +0 -0
  73. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/interactive.py +0 -0
  74. {crackerjack-0.21.0 → crackerjack-0.21.2}/crackerjack/py313.py +0 -0
  75. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/TESTING.md +0 -0
  76. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/__init__.py +0 -0
  77. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/conftest.py +0 -0
  78. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/data/comments_sample.txt +0 -0
  79. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/data/docstrings_sample.txt +0 -0
  80. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/data/expected_comments_sample.txt +0 -0
  81. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/data/init.py +0 -0
  82. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_errors.py +0 -0
  83. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_interactive.py +0 -0
  84. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_interactive_run.py +0 -0
  85. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_main.py +0 -0
  86. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_py313_advanced.py +0 -0
  87. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_py313_features.py +0 -0
  88. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_pytest_features.py +0 -0
  89. {crackerjack-0.21.0 → crackerjack-0.21.2}/tests/test_structured_errors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.21.0
3
+ Version: 0.21.2
4
4
  Summary: Crackerjack: code quality toolkit
5
5
  Keywords: bandit,black,creosote,mypy,pyright,pytest,refurb,ruff
6
6
  Author-Email: lesleslie <les@wedgwoodwebworks.com>
@@ -0,0 +1,135 @@
1
+ repos:
2
+ # File structure and format validators - check structure first
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v5.0.0
5
+ hooks:
6
+ - id: trailing-whitespace
7
+ name: trailing-whitespace
8
+ verbose: true
9
+ - id: end-of-file-fixer
10
+ name: end-of-file-fixer
11
+ verbose: true
12
+ - id: check-yaml
13
+ name: check-yaml
14
+ verbose: true
15
+ - id: check-toml
16
+ name: check-toml
17
+ verbose: true
18
+ - id: check-added-large-files
19
+ name: check-added-large-files
20
+ verbose: true
21
+
22
+ - repo: https://github.com/abravalheri/validate-pyproject
23
+ rev: v0.24.1
24
+ hooks:
25
+ - id: validate-pyproject
26
+ verbose: true
27
+
28
+ - repo: https://github.com/tox-dev/pyproject-fmt
29
+ rev: "v2.6.0"
30
+ hooks:
31
+ - id: pyproject-fmt
32
+ verbose: true
33
+
34
+ # Package management - once structure is valid
35
+ - repo: https://github.com/pdm-project/pdm
36
+ rev: 2.25.3
37
+ hooks:
38
+ - id: pdm-lock-check
39
+ verbose: true
40
+ - id: pdm-sync
41
+ verbose: true
42
+ additional_dependencies:
43
+ - keyring
44
+
45
+ - repo: https://github.com/astral-sh/uv-pre-commit
46
+ rev: 0.7.15
47
+ hooks:
48
+ - id: uv-lock
49
+ files: ^pyproject\.toml$
50
+ verbose: true
51
+
52
+ # Security check - early to prevent credential leaks
53
+ - repo: https://github.com/Yelp/detect-secrets
54
+ rev: v1.5.0
55
+ hooks:
56
+ - id: detect-secrets
57
+ exclude: 'pdm\.lock|uv\.lock|pyproject\.toml|tests/.*|docs/.*|.*\.md'
58
+ verbose: true
59
+
60
+ # Code quality tier 1 - quick fixes
61
+ - repo: https://github.com/codespell-project/codespell
62
+ rev: v2.4.1
63
+ hooks:
64
+ - id: codespell
65
+ verbose: true
66
+ additional_dependencies:
67
+ - tomli
68
+
69
+ - repo: https://github.com/astral-sh/ruff-pre-commit
70
+ rev: v0.12.1
71
+ hooks:
72
+ - id: ruff-check
73
+ verbose: true
74
+ args: [--output-format=json]
75
+ - id: ruff-format
76
+ verbose: true
77
+
78
+ # Code quality tier 2 - analysis
79
+ - repo: https://github.com/jendrikseipp/vulture
80
+ rev: 'v2.14'
81
+ hooks:
82
+ - id: vulture
83
+ verbose: true
84
+
85
+ - repo: https://github.com/fredrikaverpil/creosote
86
+ rev: v4.0.3
87
+ hooks:
88
+ - id: creosote
89
+ verbose: true
90
+
91
+ - repo: https://github.com/rohaquinlop/complexipy-pre-commit
92
+ rev: v3.0.0
93
+ hooks:
94
+ - id: complexipy
95
+ args: ["-d", "low", "-j"]
96
+ verbose: true
97
+
98
+ - repo: https://github.com/dosisod/refurb
99
+ rev: v2.1.0
100
+ hooks:
101
+ - id: refurb
102
+ verbose: true
103
+
104
+ # Code quality tier 3 - thorough checks
105
+ - repo: local
106
+ hooks:
107
+ - id: autotyping
108
+ name: autotyping
109
+ entry: python -m autotyping
110
+ args:
111
+ - --aggressive
112
+ - --only-without-imports
113
+ - --guess-common-names
114
+ - crackerjack
115
+ types_or: [ python, pyi ]
116
+ language: python
117
+ files: \.py$
118
+ verbose: true
119
+ additional_dependencies:
120
+ - autotyping>=24.3.0
121
+ - libcst>=1.1.0
122
+
123
+ - repo: https://github.com/PyCQA/bandit
124
+ rev: '1.8.5'
125
+ hooks:
126
+ - id: bandit
127
+ args: ["-c", "pyproject.toml", "--format", "json"]
128
+ verbose: true
129
+
130
+ - repo: https://github.com/RobertCraigie/pyright-python
131
+ rev: v1.1.402
132
+ hooks:
133
+ - id: pyright
134
+ verbose: true
135
+ args: ["--outputjson"]
@@ -12,7 +12,12 @@ from rich.console import Console
12
12
  from tomli_w import dumps
13
13
  from crackerjack.errors import ErrorCode, ExecutionError
14
14
 
15
- config_files = (".gitignore", ".pre-commit-config.yaml", ".libcst.codemod.yaml")
15
+ config_files = (
16
+ ".gitignore",
17
+ ".pre-commit-config.yaml",
18
+ ".pre-commit-config-ai.yaml",
19
+ ".libcst.codemod.yaml",
20
+ )
16
21
  default_python_version = "3.13"
17
22
 
18
23
 
@@ -534,20 +539,7 @@ class ProjectManager(BaseModel, arbitrary_types_allowed=True):
534
539
  code_cleaner: CodeCleaner
535
540
  config_manager: ConfigManager
536
541
  dry_run: bool = False
537
-
538
- def run_interactive(self, hook: str) -> None:
539
- success: bool = False
540
- while not success:
541
- fail = self.execute_command(
542
- ["pre-commit", "run", hook.lower(), "--all-files"]
543
- )
544
- if fail.returncode > 0:
545
- retry = input(f"\n\n{hook.title()} failed. Retry? (y/N): ")
546
- self.console.print()
547
- if retry.strip().lower() == "y":
548
- continue
549
- raise SystemExit(1)
550
- success = True
542
+ options: t.Any = None
551
543
 
552
544
  def update_pkg_configs(self) -> None:
553
545
  self.config_manager.copy_configs()
@@ -562,15 +554,21 @@ class ProjectManager(BaseModel, arbitrary_types_allowed=True):
562
554
  self.execute_command(["git", "branch", "-m", "main"])
563
555
  self.execute_command(["git", "add", "pyproject.toml"])
564
556
  self.execute_command(["git", "add", "pdm.lock"])
565
- self.execute_command(["pre-commit", "install"])
557
+ install_cmd = ["pre-commit", "install"]
558
+ if hasattr(self, "options") and getattr(self.options, "ai_agent", False):
559
+ install_cmd.extend(["-c", ".pre-commit-config-ai.yaml"])
560
+ self.execute_command(install_cmd)
566
561
  self.execute_command(["git", "config", "advice.addIgnoredFile", "false"])
567
562
  self.config_manager.update_pyproject_configs()
568
563
 
569
564
  def run_pre_commit(self) -> None:
570
565
  self.console.print("\nRunning pre-commit hooks...\n")
571
- check_all = self.execute_command(["pre-commit", "run", "--all-files"])
566
+ cmd = ["pre-commit", "run", "--all-files"]
567
+ if hasattr(self, "options") and getattr(self.options, "ai_agent", False):
568
+ cmd.extend(["-c", ".pre-commit-config-ai.yaml"])
569
+ check_all = self.execute_command(cmd)
572
570
  if check_all.returncode > 0:
573
- check_all = self.execute_command(["pre-commit", "run", "--all-files"])
571
+ check_all = self.execute_command(cmd)
574
572
  if check_all.returncode > 0:
575
573
  self.console.print("\n\nPre-commit failed. Please fix errors.\n")
576
574
  raise SystemExit(1)
@@ -644,7 +642,10 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
644
642
 
645
643
  def _update_precommit(self, options: t.Any) -> None:
646
644
  if self.pkg_path.stem == "crackerjack" and options.update_precommit:
647
- self.execute_command(["pre-commit", "autoupdate"])
645
+ update_cmd = ["pre-commit", "autoupdate"]
646
+ if options.ai_agent:
647
+ update_cmd.extend(["-c", ".pre-commit-config-ai.yaml"])
648
+ self.execute_command(update_cmd)
648
649
 
649
650
  def _clean_project(self, options: t.Any) -> None:
650
651
  if options.clean:
@@ -736,6 +737,17 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
736
737
  def _bump_version(self, options: OptionsProtocol) -> None:
737
738
  for option in (options.publish, options.bump):
738
739
  if option:
740
+ if str(option) in ("minor", "major"):
741
+ from rich.prompt import Confirm
742
+
743
+ if not Confirm.ask(
744
+ f"Are you sure you want to bump the {option} version?",
745
+ default=False,
746
+ ):
747
+ self.console.print(
748
+ f"[yellow]Skipping {option} version bump[/yellow]"
749
+ )
750
+ return
739
751
  self.execute_command(["pdm", "bump", option])
740
752
  break
741
753
 
@@ -777,6 +789,7 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
777
789
  self._update_project(options)
778
790
  self._update_precommit(options)
779
791
  self._clean_project(options)
792
+ self.project_manager.options = options
780
793
  if not options.skip_hooks:
781
794
  self.project_manager.run_pre_commit()
782
795
  else:
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.20.20"
7
+ version = "0.21.1"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
@@ -6,7 +6,7 @@ requires = [
6
6
 
7
7
  [project]
8
8
  name = "crackerjack"
9
- version = "0.21.0"
9
+ version = "0.21.2"
10
10
  description = "Crackerjack: code quality toolkit"
11
11
  readme = "README.md"
12
12
  keywords = [
@@ -287,20 +287,111 @@ class TestCrackerjackProcess:
287
287
  mock_project_manager_execute.return_value.returncode = 0
288
288
  mock_config_manager_execute.return_value.returncode = 0
289
289
  options = options_factory(bump="minor", no_config_updates=True)
290
- with patch.object(Crackerjack, "execute_command") as mock_cj_execute:
291
- mock_cj_execute.return_value = MagicMock(returncode=0, stdout="Success")
292
- with patch.object(Crackerjack, "_update_project") as mock_update_project:
293
- mock_update_project.side_effect = lambda opts: mock_console_print(
294
- "Skipping config updates."
295
- )
296
- cj = Crackerjack(dry_run=True)
297
- cj.process(options)
298
- mock_cj_execute.assert_any_call(["pdm", "bump", "minor"])
299
- console_print_calls = [str(call) for call in mock_console_print.call_args_list]
290
+ with patch("rich.prompt.Confirm.ask", return_value=True):
291
+ with patch.object(Crackerjack, "execute_command") as mock_cj_execute:
292
+ mock_cj_execute.return_value = MagicMock(returncode=0, stdout="Success")
293
+ with patch.object(
294
+ Crackerjack, "_update_project"
295
+ ) as mock_update_project:
296
+ mock_update_project.side_effect = lambda opts: mock_console_print(
297
+ "Skipping config updates."
298
+ )
299
+ cj = Crackerjack(dry_run=True)
300
+ cj.process(options)
301
+ mock_cj_execute.assert_any_call(["pdm", "bump", "minor"])
302
+ console_print_calls = [
303
+ str(call) for call in mock_console_print.call_args_list
304
+ ]
300
305
  assert any("Skipping config updates" in call for call in console_print_calls), (
301
306
  "Expected 'Skipping config updates' message was not printed"
302
307
  )
303
308
 
309
+ def test_bump_version_confirmation_minor_accepted(
310
+ self,
311
+ mock_execute: MagicMock,
312
+ mock_console_print: MagicMock,
313
+ tmp_path: Path,
314
+ tmp_path_package: Path,
315
+ create_package_dir: None,
316
+ options_factory: t.Callable[..., OptionsForTesting],
317
+ ) -> None:
318
+ options = options_factory(bump="minor", no_config_updates=True)
319
+ cj = Crackerjack(dry_run=True)
320
+
321
+ with patch("rich.prompt.Confirm.ask", return_value=True) as mock_confirm:
322
+ with patch.object(Crackerjack, "execute_command") as mock_exec:
323
+ mock_exec.return_value = MagicMock(returncode=0)
324
+ cj._bump_version(options)
325
+
326
+ mock_confirm.assert_called_once_with(
327
+ "Are you sure you want to bump the minor version?", default=False
328
+ )
329
+ mock_exec.assert_called_once_with(["pdm", "bump", "minor"])
330
+
331
+ def test_bump_version_confirmation_minor_declined(
332
+ self,
333
+ mock_execute: MagicMock,
334
+ mock_console_print: MagicMock,
335
+ tmp_path: Path,
336
+ tmp_path_package: Path,
337
+ create_package_dir: None,
338
+ options_factory: t.Callable[..., OptionsForTesting],
339
+ ) -> None:
340
+ options = options_factory(bump="minor", no_config_updates=True)
341
+ cj = Crackerjack(dry_run=True)
342
+
343
+ with patch("rich.prompt.Confirm.ask", return_value=False) as mock_confirm:
344
+ with patch.object(Crackerjack, "execute_command") as mock_exec:
345
+ mock_exec.return_value = MagicMock(returncode=0)
346
+ cj._bump_version(options)
347
+
348
+ mock_confirm.assert_called_once_with(
349
+ "Are you sure you want to bump the minor version?", default=False
350
+ )
351
+ mock_exec.assert_not_called()
352
+
353
+ def test_bump_version_confirmation_major(
354
+ self,
355
+ mock_execute: MagicMock,
356
+ mock_console_print: MagicMock,
357
+ tmp_path: Path,
358
+ tmp_path_package: Path,
359
+ create_package_dir: None,
360
+ options_factory: t.Callable[..., OptionsForTesting],
361
+ ) -> None:
362
+ options = options_factory(bump="major", no_config_updates=True)
363
+ cj = Crackerjack(dry_run=True)
364
+
365
+ with patch("rich.prompt.Confirm.ask", return_value=True) as mock_confirm:
366
+ with patch.object(Crackerjack, "execute_command") as mock_exec:
367
+ mock_exec.return_value = MagicMock(returncode=0)
368
+ cj._bump_version(options)
369
+
370
+ mock_confirm.assert_called_once_with(
371
+ "Are you sure you want to bump the major version?", default=False
372
+ )
373
+ mock_exec.assert_called_once_with(["pdm", "bump", "major"])
374
+
375
+ def test_bump_version_no_confirmation_micro(
376
+ self,
377
+ mock_execute: MagicMock,
378
+ mock_console_print: MagicMock,
379
+ tmp_path: Path,
380
+ tmp_path_package: Path,
381
+ create_package_dir: None,
382
+ options_factory: t.Callable[..., OptionsForTesting],
383
+ ) -> None:
384
+ options = options_factory(bump="micro", no_config_updates=True)
385
+ cj = Crackerjack(dry_run=True)
386
+
387
+ with patch("rich.prompt.Confirm.ask") as mock_confirm:
388
+ with patch.object(Crackerjack, "execute_command") as mock_exec:
389
+ mock_exec.return_value = MagicMock(returncode=0)
390
+ cj._bump_version(options)
391
+
392
+ mock_confirm.assert_not_called()
393
+ mock_exec.assert_called_once_with(["pdm", "bump", "micro"])
394
+
304
395
  def test_process_with_publish_option(
305
396
  self,
306
397
  mock_execute: MagicMock,
@@ -761,150 +852,107 @@ class TestCrackerjackProcess:
761
852
  cj.process(options)
762
853
  mock_cj_execute.assert_any_call(["pre-commit", "autoupdate"])
763
854
 
764
- def test_process_with_precommit_failure(
855
+ def test_update_precommit_ai_agent(
765
856
  self,
766
- mock_execute: MagicMock,
767
857
  mock_console_print: MagicMock,
768
858
  tmp_path: Path,
769
- tmp_path_package: Path,
770
- create_package_dir: None,
771
859
  options_factory: t.Callable[..., OptionsForTesting],
772
860
  ) -> None:
773
- options = options_factory(no_config_updates=True)
774
- with patch.object(ProjectManager, "run_pre_commit") as mock_run_precommit:
775
- mock_run_precommit.side_effect = SystemExit(1)
776
- with patch.object(Crackerjack, "_update_project"):
777
- with pytest.raises(SystemExit) as excinfo:
778
- cj = Crackerjack(dry_run=True)
861
+ crackerjack_path = tmp_path / "crackerjack"
862
+ crackerjack_path.mkdir(exist_ok=True)
863
+ options = options_factory(update_precommit=True, ai_agent=True)
864
+ with patch.object(Crackerjack, "execute_command") as mock_cj_execute:
865
+ mock_cj_execute.return_value = MagicMock(returncode=0, stdout="Success")
866
+ cj = Crackerjack(pkg_path=crackerjack_path, dry_run=True)
867
+ with patch.object(cj, "_setup_package"):
868
+ with patch.object(cj, "_update_project"):
779
869
  cj.process(options)
780
- assert excinfo.value.code == 1
781
- mock_run_precommit.assert_called_once()
870
+ mock_cj_execute.assert_any_call(
871
+ ["pre-commit", "autoupdate", "-c", ".pre-commit-config-ai.yaml"]
872
+ )
782
873
 
783
- def test_process_with_interactive_option(
874
+ def test_run_pre_commit_ai_agent(
784
875
  self,
785
- mock_execute: MagicMock,
786
876
  mock_console_print: MagicMock,
787
877
  tmp_path: Path,
788
- tmp_path_package: Path,
789
- create_package_dir: None,
790
878
  options_factory: t.Callable[..., OptionsForTesting],
791
879
  ) -> None:
792
- options = options_factory(interactive=True, no_config_updates=True)
793
- with patch.object(Crackerjack, "_update_project"):
794
- cj = Crackerjack(dry_run=True)
795
- cj.process(options)
880
+ mock_project = MagicMock()
881
+
882
+ options = options_factory(ai_agent=True)
883
+ mock_project.options = options
884
+
885
+ mock_project.console = MagicMock()
886
+
887
+ mock_project.execute_command.return_value = MagicMock(returncode=0)
796
888
 
797
- def test_project_manager_run_interactive_success(
889
+ ProjectManager.run_pre_commit(mock_project)
890
+
891
+ mock_project.execute_command.assert_called_with(
892
+ ["pre-commit", "run", "--all-files", "-c", ".pre-commit-config-ai.yaml"]
893
+ )
894
+
895
+ def test_pre_commit_install_ai_agent(
798
896
  self,
799
- mock_execute: MagicMock,
800
897
  mock_console_print: MagicMock,
801
898
  tmp_path: Path,
802
- tmp_path_package: Path,
803
- create_package_dir: None,
899
+ options_factory: t.Callable[..., OptionsForTesting],
804
900
  ) -> None:
805
- with patch.object(ProjectManager, "execute_command") as mock_pm_execute:
806
- mock_pm_execute.return_value = MagicMock(returncode=0, stdout="Success")
807
- code_cleaner = CodeCleaner(console=Console())
808
- config_manager = ConfigManager(
809
- our_path=Path(),
810
- pkg_path=Path(),
811
- pkg_name="test",
812
- console=Console(),
813
- python_version="3.9",
814
- dry_run=True,
815
- )
816
- pm = ProjectManager(
817
- our_path=Path(),
818
- pkg_path=Path(),
819
- pkg_dir=Path(),
820
- pkg_name="test",
821
- console=Console(),
822
- code_cleaner=code_cleaner,
823
- config_manager=config_manager,
824
- dry_run=True,
901
+ mock_project = MagicMock(spec=ProjectManager)
902
+
903
+ options = options_factory(ai_agent=True)
904
+ mock_project.options = options
905
+
906
+ mock_project.console = MagicMock()
907
+
908
+ mock_project.config_manager = MagicMock()
909
+
910
+ with patch.object(mock_project, "execute_command") as mock_execute:
911
+ mock_execute.return_value = MagicMock(
912
+ returncode=0,
913
+ stdout="package1\npackage2\n",
825
914
  )
826
- pm.run_interactive("black")
827
- mock_pm_execute.assert_called_once_with(
828
- ["pre-commit", "run", "black", "--all-files"]
915
+
916
+ original_method = ProjectManager.update_pkg_configs
917
+
918
+ original_method(mock_project)
919
+
920
+ mock_execute.assert_any_call(
921
+ ["pre-commit", "install", "-c", ".pre-commit-config-ai.yaml"]
829
922
  )
830
923
 
831
- def test_project_manager_run_interactive_failure_retry(
924
+ def test_process_with_precommit_failure(
832
925
  self,
833
926
  mock_execute: MagicMock,
834
927
  mock_console_print: MagicMock,
835
928
  tmp_path: Path,
836
929
  tmp_path_package: Path,
837
930
  create_package_dir: None,
931
+ options_factory: t.Callable[..., OptionsForTesting],
838
932
  ) -> None:
839
- with patch.object(ProjectManager, "execute_command") as mock_pm_execute:
840
- mock_pm_execute.side_effect = [
841
- MagicMock(returncode=1, stdout="", stderr="Failed"),
842
- MagicMock(returncode=0, stdout="Success"),
843
- ]
844
- with patch("builtins.input", return_value="y"):
845
- code_cleaner = CodeCleaner(console=Console())
846
- config_manager = ConfigManager(
847
- our_path=Path(),
848
- pkg_path=Path(),
849
- pkg_name="test",
850
- console=Console(),
851
- python_version="3.9",
852
- dry_run=True,
853
- )
854
- pm = ProjectManager(
855
- our_path=Path(),
856
- pkg_path=Path(),
857
- pkg_dir=Path(),
858
- pkg_name="test",
859
- console=Console(),
860
- code_cleaner=code_cleaner,
861
- config_manager=config_manager,
862
- dry_run=True,
863
- )
864
- pm.run_interactive("black")
865
- assert mock_pm_execute.call_count == 2
866
- mock_pm_execute.assert_called_with(
867
- ["pre-commit", "run", "black", "--all-files"]
868
- )
933
+ options = options_factory(no_config_updates=True)
934
+ with patch.object(ProjectManager, "run_pre_commit") as mock_run_precommit:
935
+ mock_run_precommit.side_effect = SystemExit(1)
936
+ with patch.object(Crackerjack, "_update_project"):
937
+ with pytest.raises(SystemExit) as excinfo:
938
+ cj = Crackerjack(dry_run=True)
939
+ cj.process(options)
940
+ assert excinfo.value.code == 1
941
+ mock_run_precommit.assert_called_once()
869
942
 
870
- def test_project_manager_run_interactive_failure_exit(
943
+ def test_process_with_interactive_option(
871
944
  self,
872
945
  mock_execute: MagicMock,
873
946
  mock_console_print: MagicMock,
874
947
  tmp_path: Path,
875
948
  tmp_path_package: Path,
876
949
  create_package_dir: None,
950
+ options_factory: t.Callable[..., OptionsForTesting],
877
951
  ) -> None:
878
- with patch.object(ProjectManager, "execute_command") as mock_pm_execute:
879
- mock_pm_execute.return_value = MagicMock(
880
- returncode=1, stdout="", stderr="Failed"
881
- )
882
- with patch("builtins.input", return_value="n"):
883
- code_cleaner = CodeCleaner(console=Console())
884
- config_manager = ConfigManager(
885
- our_path=Path(),
886
- pkg_path=Path(),
887
- pkg_name="test",
888
- console=Console(),
889
- python_version="3.9",
890
- dry_run=True,
891
- )
892
- pm = ProjectManager(
893
- our_path=Path(),
894
- pkg_path=Path(),
895
- pkg_dir=Path(),
896
- pkg_name="test",
897
- console=Console(),
898
- code_cleaner=code_cleaner,
899
- config_manager=config_manager,
900
- dry_run=True,
901
- )
902
- with pytest.raises(SystemExit) as excinfo:
903
- pm.run_interactive("black")
904
- assert excinfo.value.code == 1
905
- mock_pm_execute.assert_called_once_with(
906
- ["pre-commit", "run", "black", "--all-files"]
907
- )
952
+ options = options_factory(interactive=True, no_config_updates=True)
953
+ with patch.object(Crackerjack, "_update_project"):
954
+ cj = Crackerjack(dry_run=True)
955
+ cj.process(options)
908
956
 
909
957
  def test_config_manager_swap_package_name(
910
958
  self, mock_execute: MagicMock, mock_console_print: MagicMock, tmp_path: Path
@@ -88,8 +88,9 @@ def test_process_with_all_option(
88
88
  crackerjack, mocks = mock_crackerjack
89
89
  options = MockOptions(all="minor")
90
90
  with patch.object(Crackerjack, "_run_tests"):
91
- with patch("builtins.input", return_value="Test commit message"):
92
- crackerjack.process(options)
91
+ with patch("rich.prompt.Confirm.ask", return_value=True):
92
+ with patch("builtins.input", return_value="Test commit message"):
93
+ crackerjack.process(options)
93
94
  assert options.clean is True
94
95
  assert options.test is True
95
96
  assert options.publish == "minor"
File without changes
File without changes