py-wlcli 0.8.0__py3-none-any.whl

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 (320) hide show
  1. py_wlcli-0.8.0.dist-info/METADATA +139 -0
  2. py_wlcli-0.8.0.dist-info/RECORD +320 -0
  3. py_wlcli-0.8.0.dist-info/WHEEL +4 -0
  4. py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
  5. py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
  6. wl_cli/__init__.py +27 -0
  7. wl_cli/__main__.py +130 -0
  8. wl_cli/capabilities.py +79 -0
  9. wl_cli/cc/__init__.py +5 -0
  10. wl_cli/cc/checks.py +1019 -0
  11. wl_cli/cc/cli.py +370 -0
  12. wl_cli/cc/mutate.py +1350 -0
  13. wl_cli/cli_click.py +156 -0
  14. wl_cli/commands/__init__.py +84 -0
  15. wl_cli/commands/base.py +80 -0
  16. wl_cli/commands/buildcommands/__init__.py +1 -0
  17. wl_cli/commands/buildcommands/build.py +71 -0
  18. wl_cli/commands/buildcommands/build_utils.py +47 -0
  19. wl_cli/commands/buildcommands/builders/__init__.py +18 -0
  20. wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
  21. wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
  22. wl_cli/commands/buildcommands/builders/environment.py +81 -0
  23. wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
  24. wl_cli/commands/buildcommands/builders/stubs.py +58 -0
  25. wl_cli/commands/buildcommands/dist.py +326 -0
  26. wl_cli/commands/buildcommands/test.py +89 -0
  27. wl_cli/commands/clean/__init__.py +1 -0
  28. wl_cli/commands/clean/build.py +151 -0
  29. wl_cli/commands/clean/clean.py +294 -0
  30. wl_cli/commands/clean/clean_utils.py +77 -0
  31. wl_cli/commands/clean/core.py +236 -0
  32. wl_cli/commands/clean/dead_code/__init__.py +0 -0
  33. wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
  34. wl_cli/commands/clean/dead_code/detector.py +161 -0
  35. wl_cli/commands/clean/dead_code/fixer.py +176 -0
  36. wl_cli/commands/clean/dead_code/reporter.py +295 -0
  37. wl_cli/commands/clean/dead_code/utils.py +0 -0
  38. wl_cli/commands/clean/lfs.py +215 -0
  39. wl_cli/commands/clean/python.py +81 -0
  40. wl_cli/commands/clean/rust.py +91 -0
  41. wl_cli/commands/clean/utils/__init__.py +34 -0
  42. wl_cli/commands/clean/venv.py +91 -0
  43. wl_cli/commands/cleanup.py +73 -0
  44. wl_cli/commands/click_base.py +79 -0
  45. wl_cli/commands/config/__init__.py +1 -0
  46. wl_cli/commands/config/config.py +291 -0
  47. wl_cli/commands/debug/__init__.py +6 -0
  48. wl_cli/commands/debug/debug.py +309 -0
  49. wl_cli/commands/debug/debug_cache.py +86 -0
  50. wl_cli/commands/debug/debug_subagent.py +156 -0
  51. wl_cli/commands/debug/output_formatter.py +165 -0
  52. wl_cli/commands/debug/source_hasher.py +92 -0
  53. wl_cli/commands/format/__init__.py +13 -0
  54. wl_cli/commands/format/format_cache_adapter.py +125 -0
  55. wl_cli/commands/format/format_command.py +197 -0
  56. wl_cli/commands/format/format_coordinator.py +191 -0
  57. wl_cli/commands/format/format_executor.py +470 -0
  58. wl_cli/commands/format/format_outcome.py +44 -0
  59. wl_cli/commands/format/format_path_handler.py +1016 -0
  60. wl_cli/commands/format/format_progress.py +62 -0
  61. wl_cli/commands/format/format_reporter.py +31 -0
  62. wl_cli/commands/format/python_formatter.py +506 -0
  63. wl_cli/commands/format/rust_formatter.py +106 -0
  64. wl_cli/commands/gate/__init__.py +1 -0
  65. wl_cli/commands/gate/gate_command.py +862 -0
  66. wl_cli/commands/gate/preflight.py +445 -0
  67. wl_cli/commands/git/__init__.py +1 -0
  68. wl_cli/commands/git/config.py +169 -0
  69. wl_cli/commands/git/git_facts.py +60 -0
  70. wl_cli/commands/git/git_flow.py +86 -0
  71. wl_cli/commands/git/hook.py +112 -0
  72. wl_cli/commands/git/policy.py +216 -0
  73. wl_cli/commands/git/push_update.py +82 -0
  74. wl_cli/commands/initenv/__init__.py +1 -0
  75. wl_cli/commands/initenv/common/__init__.py +23 -0
  76. wl_cli/commands/initenv/common/check_git_bash.py +78 -0
  77. wl_cli/commands/initenv/common/error_handler.py +68 -0
  78. wl_cli/commands/initenv/common/exceptions.py +52 -0
  79. wl_cli/commands/initenv/components/__init__.py +4 -0
  80. wl_cli/commands/initenv/components/cargo_sync.py +240 -0
  81. wl_cli/commands/initenv/components/config_manager.py +106 -0
  82. wl_cli/commands/initenv/components/directory_utils.py +71 -0
  83. wl_cli/commands/initenv/components/git/__init__.py +9 -0
  84. wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
  85. wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
  86. wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
  87. wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
  88. wl_cli/commands/initenv/components/git/git_utility.py +254 -0
  89. wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
  90. wl_cli/commands/initenv/components/git_initializer.py +212 -0
  91. wl_cli/commands/initenv/components/i18n_manager.py +104 -0
  92. wl_cli/commands/initenv/components/log_manager.py +241 -0
  93. wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
  94. wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
  95. wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
  96. wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
  97. wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
  98. wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
  99. wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
  100. wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
  101. wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
  102. wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
  103. wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
  104. wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
  105. wl_cli/commands/initenv/components/rust_initializer.py +145 -0
  106. wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
  107. wl_cli/commands/initenv/components/symlink_manager.py +52 -0
  108. wl_cli/commands/initenv/components/venv_manager.py +118 -0
  109. wl_cli/commands/initenv/initenv.py +169 -0
  110. wl_cli/commands/initenv/services/__init__.py +11 -0
  111. wl_cli/commands/initenv/services/environment_initializer.py +288 -0
  112. wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
  113. wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
  114. wl_cli/commands/lfs/README.md +221 -0
  115. wl_cli/commands/lfs/__init__.py +5 -0
  116. wl_cli/commands/lfs/cli.py +106 -0
  117. wl_cli/commands/lfs/config.py +164 -0
  118. wl_cli/commands/lfs/hook.py +61 -0
  119. wl_cli/commands/lfs/initializer.py +531 -0
  120. wl_cli/commands/lfs/manager.py +95 -0
  121. wl_cli/commands/lfs/policy.py +165 -0
  122. wl_cli/commands/lfs/watcher.py +294 -0
  123. wl_cli/commands/lint/__init__.py +1 -0
  124. wl_cli/commands/lint/lint_command.py +297 -0
  125. wl_cli/commands/lint/lint_executor.py +409 -0
  126. wl_cli/commands/lint/lint_reporter.py +579 -0
  127. wl_cli/commands/lint/lint_run.py +258 -0
  128. wl_cli/commands/lint/mypy_config.py +90 -0
  129. wl_cli/commands/registry.py +103 -0
  130. wl_cli/commands/self/__init__.py +7 -0
  131. wl_cli/commands/self/common/__init__.py +8 -0
  132. wl_cli/commands/self/common/capabilities.py +30 -0
  133. wl_cli/commands/self/common/config.py +27 -0
  134. wl_cli/commands/self/common/version_cache.py +85 -0
  135. wl_cli/commands/self/handlers/__init__.py +19 -0
  136. wl_cli/commands/self/handlers/base_handler.py +28 -0
  137. wl_cli/commands/self/handlers/install_handler.py +85 -0
  138. wl_cli/commands/self/handlers/status_handler.py +66 -0
  139. wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
  140. wl_cli/commands/self/handlers/update_handler.py +102 -0
  141. wl_cli/commands/self/handlers/version_handler.py +16 -0
  142. wl_cli/commands/self/self_command.py +237 -0
  143. wl_cli/commands/self/services/__init__.py +15 -0
  144. wl_cli/commands/self/services/installation_manager.py +187 -0
  145. wl_cli/commands/self/services/local_installer.py +165 -0
  146. wl_cli/commands/self/services/pypi_updater.py +278 -0
  147. wl_cli/commands/self/services/windows_update.py +174 -0
  148. wl_cli/commands/testcommands/__init__.py +8 -0
  149. wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
  150. wl_cli/commands/testcommands/batch_processor.py +278 -0
  151. wl_cli/commands/testcommands/cache_manager.py +1156 -0
  152. wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
  153. wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
  154. wl_cli/commands/testcommands/progress_manager.py +79 -0
  155. wl_cli/commands/testcommands/python_env_detector.py +207 -0
  156. wl_cli/commands/testcommands/report_generator.py +101 -0
  157. wl_cli/commands/testcommands/single_test_executor.py +250 -0
  158. wl_cli/commands/testcommands/speed_checker.py +118 -0
  159. wl_cli/commands/testcommands/test_command_builder.py +165 -0
  160. wl_cli/commands/testcommands/test_commands.py +786 -0
  161. wl_cli/commands/testcommands/test_reporter.py +181 -0
  162. wl_cli/commands/testcommands/test_result_handler.py +96 -0
  163. wl_cli/commands/testcommands/test_runner.py +1343 -0
  164. wl_cli/commands/testcommands/test_utils.py +197 -0
  165. wl_cli/commands/testcommands/utils/__init__.py +17 -0
  166. wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
  167. wl_cli/commands/testcommands/utils/env_info.py +36 -0
  168. wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
  169. wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
  170. wl_cli/commands/testcommands/utils/project_root.py +61 -0
  171. wl_cli/commands/update/__init__.py +9 -0
  172. wl_cli/commands/update/services/__init__.py +21 -0
  173. wl_cli/commands/update/services/backup_manager.py +207 -0
  174. wl_cli/commands/update/services/component_manager.py +83 -0
  175. wl_cli/commands/update/services/conflict_resolver.py +189 -0
  176. wl_cli/commands/update/services/vendors_updater.py +85 -0
  177. wl_cli/commands/update/services/version_tracker.py +127 -0
  178. wl_cli/commands/update/update_command.py +270 -0
  179. wl_cli/commands/update/utils/__init__.py +3 -0
  180. wl_cli/commands/version/__init__.py +9 -0
  181. wl_cli/commands/version/version_command.py +155 -0
  182. wl_cli/commands/workflow.py +477 -0
  183. wl_cli/env/__init__.py +5 -0
  184. wl_cli/env/cli.py +167 -0
  185. wl_cli/env_status.py +157 -0
  186. wl_cli/envelope.py +91 -0
  187. wl_cli/exceptions.py +85 -0
  188. wl_cli/lib/__init__.py +3 -0
  189. wl_cli/lib/rust_utils.py +35 -0
  190. wl_cli/mcp/__init__.py +6 -0
  191. wl_cli/mcp/capabilities.py +19 -0
  192. wl_cli/mcp/cli.py +22 -0
  193. wl_cli/mcp/gitlab.py +553 -0
  194. wl_cli/mcp/k8s.py +344 -0
  195. wl_cli/mcp/protocol.py +159 -0
  196. wl_cli/platform.py +139 -0
  197. wl_cli/quality/__init__.py +18 -0
  198. wl_cli/utils/__init__.py +7 -0
  199. wl_cli/utils/cache.py +491 -0
  200. wl_cli/utils/cache_ledger.py +355 -0
  201. wl_cli/utils/cache_store.py +546 -0
  202. wl_cli/utils/config/__init__.py +27 -0
  203. wl_cli/utils/config/config_loader.py +163 -0
  204. wl_cli/utils/config/core.py +862 -0
  205. wl_cli/utils/config/defaults.py +142 -0
  206. wl_cli/utils/config/env_exporter.py +86 -0
  207. wl_cli/utils/config/env_parser.py +108 -0
  208. wl_cli/utils/config/validation.py +280 -0
  209. wl_cli/utils/error_handler.py +165 -0
  210. wl_cli/utils/file_copier.py +56 -0
  211. wl_cli/utils/file_operations.py +506 -0
  212. wl_cli/utils/language_utils.py +122 -0
  213. wl_cli/utils/lazy_import.py +240 -0
  214. wl_cli/utils/log_levels.py +35 -0
  215. wl_cli/utils/log_rotators.py +74 -0
  216. wl_cli/utils/logging.py +458 -0
  217. wl_cli/utils/output_context.py +142 -0
  218. wl_cli/utils/performance/__init__.py +1 -0
  219. wl_cli/utils/performance/decorators.py +111 -0
  220. wl_cli/utils/performance/exporter.py +38 -0
  221. wl_cli/utils/performance/models.py +16 -0
  222. wl_cli/utils/performance/monitor.py +161 -0
  223. wl_cli/utils/performance_monitor.py +32 -0
  224. wl_cli/utils/platform_adapter.py +131 -0
  225. wl_cli/utils/pre_commit.py +266 -0
  226. wl_cli/utils/pre_commit_config.py +72 -0
  227. wl_cli/utils/root.py +521 -0
  228. wl_cli/utils/root_fallback.py +256 -0
  229. wl_cli/utils/run_evidence.py +236 -0
  230. wl_cli/utils/structured_logger.py +18 -0
  231. wl_cli/utils/structured_logging/__init__.py +19 -0
  232. wl_cli/utils/structured_logging/config.py +337 -0
  233. wl_cli/utils/structured_logging/core.py +177 -0
  234. wl_cli/utils/structured_logging/env.py +56 -0
  235. wl_cli/utils/structured_logging/filtering.py +75 -0
  236. wl_cli/utils/structured_logging/filters.py +55 -0
  237. wl_cli/utils/structured_logging/formatters.py +251 -0
  238. wl_cli/utils/structured_logging/handlers.py +308 -0
  239. wl_cli/utils/structured_logging/logger.py +354 -0
  240. wl_cli/utils/subprocess.py +71 -0
  241. wl_cli/utils/subprocess_utils.py +260 -0
  242. wl_cli/utils/test_utils/__init__.py +5 -0
  243. wl_cli/utils/test_utils/test_filter.py +36 -0
  244. wl_cli/utils/uv_run.py +79 -0
  245. wl_cli/utils/uv_tool.py +117 -0
  246. wl_cli/utils/uv_tree.py +71 -0
  247. wl_cli/utils/uv_workspace.py +174 -0
  248. wl_cli/utils/version/__init__.py +15 -0
  249. wl_cli/utils/version/pypi_version_checker.py +95 -0
  250. wl_cli/utils/version/version_comparator.py +133 -0
  251. wl_cli/utils/version/version_detectors.py +312 -0
  252. wl_cli/utils/version/version_service.py +131 -0
  253. wl_cli/utils/version/version_updater.py +198 -0
  254. wl_cli/utils/wl_dir_updater.py +1440 -0
  255. wl_cli/utils/workspace_detector/__init__.py +28 -0
  256. wl_cli/utils/workspace_detector/core.py +161 -0
  257. wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
  258. wl_cli/utils/workspace_detector/exceptions.py +5 -0
  259. wl_cli/utils/workspace_detector/types.py +17 -0
  260. wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
  261. wl_cli/vendors/config/.codespellrc +2 -0
  262. wl_cli/vendors/config/dependencies.toml +10 -0
  263. wl_cli/vendors/config/mypy.ini +62 -0
  264. wl_cli/vendors/config/pyproject_defaults.toml +28 -0
  265. wl_cli/vendors/config/template_config.toml +24 -0
  266. wl_cli/vendors/config/tool_configs.toml +76 -0
  267. wl_cli/vendors/git/.gitignore +122 -0
  268. wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
  269. wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
  270. wl_cli/vendors/hooks/commit-msg.sample +24 -0
  271. wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
  272. wl_cli/vendors/hooks/post-update.sample +8 -0
  273. wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
  274. wl_cli/vendors/hooks/pre-commit +45 -0
  275. wl_cli/vendors/hooks/pre-commit.legacy +31 -0
  276. wl_cli/vendors/hooks/pre-commit.sample +49 -0
  277. wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
  278. wl_cli/vendors/hooks/pre-push.sample +60 -0
  279. wl_cli/vendors/hooks/pre-rebase.sample +169 -0
  280. wl_cli/vendors/hooks/pre-receive.sample +24 -0
  281. wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
  282. wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
  283. wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
  284. wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
  285. wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
  286. wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
  287. wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
  288. wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
  289. wl_cli/vendors/hooks/unix/post-update.sample +8 -0
  290. wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
  291. wl_cli/vendors/hooks/unix/pre-commit +190 -0
  292. wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
  293. wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
  294. wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
  295. wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
  296. wl_cli/vendors/hooks/unix/pre-push +417 -0
  297. wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
  298. wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
  299. wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
  300. wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
  301. wl_cli/vendors/hooks/update.sample +128 -0
  302. wl_cli/vendors/hooks/win/work.bat +63 -0
  303. wl_cli/vendors/project/.gitignore +90 -0
  304. wl_cli/vendors/project/.python-version +1 -0
  305. wl_cli/vendors/project/LICENSE +21 -0
  306. wl_cli/vendors/project/README.md +0 -0
  307. wl_cli/vendors/project/pyproject.toml +27 -0
  308. wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
  309. wl_cli/vendors/project/pyproject_workspace.toml +14 -0
  310. wl_cli/vendors/readme/README.md +120 -0
  311. wl_cli/vendors/rust/Cargo.toml +14 -0
  312. wl_cli/vendors/rust/lib/__init__.py +3 -0
  313. wl_cli/vendors/rust/lib/rust_utils.py +31 -0
  314. wl_cli/vendors/rust/src/lib.rs +13 -0
  315. wl_cli/vendors/rust/src/main.rs +3 -0
  316. wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
  317. wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
  318. wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
  319. wl_cli/vendors/sop_doc/python-sop.md +342 -0
  320. wl_cli/vendors/sop_doc/rust-readme.md +219 -0
@@ -0,0 +1,52 @@
1
+ """Custom exceptions for initenv module."""
2
+
3
+ from ....exceptions import CommandError
4
+
5
+
6
+ class InitEnvError(CommandError):
7
+ """Base exception for initenv errors."""
8
+
9
+ def __init__(self, message: str) -> None:
10
+ super().__init__(message)
11
+
12
+
13
+ class ProjectStructureError(InitEnvError):
14
+ """Failed to create project structure."""
15
+
16
+ def __init__(self, message: str) -> None:
17
+ super().__init__(message)
18
+
19
+
20
+ class VenvCreationError(InitEnvError):
21
+ """Failed to create virtual environment."""
22
+
23
+ def __init__(self, message: str) -> None:
24
+ super().__init__(message)
25
+
26
+
27
+ class GitInitError(InitEnvError):
28
+ """Failed to initialize Git repository."""
29
+
30
+ def __init__(self, message: str) -> None:
31
+ super().__init__(message)
32
+
33
+
34
+ class GitLfsInitError(InitEnvError):
35
+ """Failed to initialize Git LFS."""
36
+
37
+ def __init__(self, message: str) -> None:
38
+ super().__init__(message)
39
+
40
+
41
+ class CargoSyncError(InitEnvError):
42
+ """Failed to sync Cargo.toml with pyproject.toml."""
43
+
44
+ def __init__(self, message: str) -> None:
45
+ super().__init__(message)
46
+
47
+
48
+ class LfsLockError(InitEnvError):
49
+ """Failed to initialize LFS lock workflow configuration."""
50
+
51
+ def __init__(self, message: str) -> None:
52
+ super().__init__(message)
@@ -0,0 +1,4 @@
1
+ """Reusable components for initenv module."""
2
+
3
+ # No exports needed - imports are direct by path
4
+ __all__ = []
@@ -0,0 +1,240 @@
1
+ """
2
+ Cargo.toml synchronization functionality for init command.
3
+ """
4
+
5
+ import re
6
+ from pathlib import Path
7
+ from typing import Any, cast
8
+
9
+ # Try to import TOML libraries
10
+ tomllib: Any
11
+ try:
12
+ import tomllib
13
+ except ImportError:
14
+ # If not available, we'll handle it when needed
15
+ tomllib = None
16
+
17
+ # --- Constants for Cargo sync ---
18
+ PYO3_VERSION = "0.21"
19
+ PYO3_FEATURES = ["extension-module"]
20
+
21
+
22
+ def load_toml(path: Path) -> dict[str, Any]:
23
+ """Loads a TOML file, handling errors."""
24
+ if tomllib is None:
25
+ raise RuntimeError("错误: 需要 Python 3.11+ 或安装 tomli/toml 包")
26
+
27
+ try:
28
+ with open(path, "rb") as f:
29
+ return cast("dict[str, Any]", tomllib.load(f))
30
+ except FileNotFoundError:
31
+ raise FileNotFoundError(f"错误: {path} 未找到。")
32
+ except Exception as e:
33
+ raise RuntimeError(f"错误: 解析 {path} 失败: {e}")
34
+
35
+
36
+ def _format_complex_value(value: Any) -> str:
37
+ """Format complex values like dicts and mixed lists."""
38
+ if isinstance(value, dict):
39
+ parts = []
40
+ for k, v in value.items():
41
+ if isinstance(v, str):
42
+ parts.append(f'{k} = "{v}"')
43
+ elif isinstance(v, bool):
44
+ parts.append(f"{k} = {str(v).lower()}")
45
+ elif isinstance(v, list):
46
+ formatted_list = "[" + ", ".join(f'"{item}"' for item in v) + "]"
47
+ parts.append(f"{k} = {formatted_list}")
48
+ else:
49
+ parts.append(f"{k} = {v}")
50
+ return "{ " + ", ".join(parts) + " }"
51
+ elif isinstance(value, bool):
52
+ return str(value).lower()
53
+ else:
54
+ return str(value)
55
+
56
+
57
+ def _write_toml_value(f, key: str, value: Any) -> None:
58
+ """Write a single TOML key-value pair."""
59
+ if isinstance(value, str):
60
+ f.write(f'{key} = "{value}"\n')
61
+ elif isinstance(value, bool):
62
+ f.write(f"{key} = {str(value).lower()}\n")
63
+ elif isinstance(value, list):
64
+ if all(isinstance(item, str) for item in value):
65
+ formatted_list = "[" + ", ".join(f'"{item}"' for item in value) + "]"
66
+ f.write(f"{key} = {formatted_list}\n")
67
+ else:
68
+ # For complex lists like pyo3 dependency
69
+ f.write(f"{key} = {_format_complex_value(value)}\n")
70
+ elif isinstance(value, dict):
71
+ f.write(f"{key} = {_format_complex_value(value)}\n")
72
+ else:
73
+ f.write(f"{key} = {value}\n")
74
+
75
+
76
+ def _write_toml_dict(data: dict[str, Any], f) -> None:
77
+ """Simple TOML writer for basic data structures."""
78
+ for key, value in data.items():
79
+ if isinstance(value, dict):
80
+ f.write(f"\n[{key}]\n")
81
+ for subkey, subvalue in value.items():
82
+ _write_toml_value(f, subkey, subvalue)
83
+ else:
84
+ _write_toml_value(f, key, value)
85
+
86
+
87
+ def write_toml(data: dict[str, Any], path: Path) -> None:
88
+ """Writes data to a TOML file, handling errors."""
89
+ try:
90
+ with open(path, "w", encoding="utf-8") as f:
91
+ _write_toml_dict(data, f)
92
+ except OSError as e:
93
+ raise RuntimeError(f"错误: 写入 {path} 失败: {e}")
94
+
95
+
96
+ def clean_cargo_name(name: str) -> str:
97
+ """Cleans a string to be a valid Cargo package name.
98
+ - Replaces invalid characters (anything not letter, number, _, -) with underscore.
99
+ - Ensures it doesn't start/end with invalid chars like _ or -.
100
+ """
101
+ cleaned = re.sub(r"[^a-zA-Z0-9_]+", "_", name.replace("-", "_"))
102
+ cleaned = cleaned.strip("_")
103
+ if not cleaned:
104
+ return "rust_package"
105
+ if cleaned[0].isdigit():
106
+ cleaned = "_" + cleaned
107
+ return cleaned
108
+
109
+
110
+ def extract_rust_crate_name(pyproject_data: dict[str, Any]) -> str | None:
111
+ """Extracts and cleans the Rust crate name from pyproject data."""
112
+ try:
113
+ maturin_config = pyproject_data["tool"]["maturin"]
114
+ module_name = maturin_config.get("module-name")
115
+ if module_name and "." in module_name:
116
+ raw_name = module_name.split(".")[-1]
117
+ return clean_cargo_name(raw_name)
118
+ else:
119
+ return None
120
+ except KeyError:
121
+ return None
122
+
123
+
124
+ def format_authors(authors_list: list[dict[str, str]]) -> list[str]:
125
+ """Formats pyproject authors for Cargo.toml."""
126
+ formatted = []
127
+ for author in authors_list:
128
+ name = author.get("name")
129
+ email = author.get("email")
130
+ if name and email:
131
+ formatted.append(f"{name} <{email}>")
132
+ elif name:
133
+ formatted.append(name)
134
+ return formatted
135
+
136
+
137
+ def extract_pyproject_metadata(pyproject_data: dict[str, Any]) -> dict[str, Any]:
138
+ """Extracts relevant metadata from pyproject data."""
139
+ project_info = pyproject_data.get("project", {})
140
+ return {
141
+ "version": project_info.get("version"),
142
+ "description": project_info.get("description"),
143
+ "authors": project_info.get("authors"),
144
+ "license": project_info.get("license"),
145
+ }
146
+
147
+
148
+ def update_cargo_package_section(
149
+ cargo_data: dict[str, Any],
150
+ metadata: dict[str, Any],
151
+ rust_crate_name: str | None,
152
+ cargo_path: Path,
153
+ ) -> None:
154
+ """Updates the [package] section of Cargo data."""
155
+ package_info = cargo_data.setdefault("package", {})
156
+
157
+ # Update name
158
+ if rust_crate_name:
159
+ package_info["name"] = rust_crate_name
160
+ elif "name" not in package_info:
161
+ raise RuntimeError(
162
+ "错误: 无法确定 Cargo 包名,且 Cargo.toml 中未设置 [package].name"
163
+ )
164
+
165
+ # Update other metadata
166
+ if metadata["version"]:
167
+ package_info["version"] = metadata["version"]
168
+ if metadata["description"]:
169
+ package_info["description"] = metadata["description"]
170
+ if metadata["authors"]:
171
+ package_info["authors"] = format_authors(metadata["authors"])
172
+ if metadata["license"]:
173
+ # Handle license format from pyproject.toml
174
+ if isinstance(metadata["license"], dict) and "file" in metadata["license"]:
175
+ # If license is in format {"file": "LICENSE"}, use license-file field
176
+ # Calculate the relative path from Cargo.toml to the license file
177
+ license_file = metadata["license"]["file"]
178
+ # Calculate the relative path from Cargo.toml's directory to the license file
179
+ # The license file is usually in the project root, so we need to go up one level
180
+ # if Cargo.toml is in a subdirectory like rust/
181
+ relative_path = Path(license_file)
182
+ if not relative_path.is_absolute():
183
+ # If the license file path is relative in pyproject.toml, it's relative to the project root
184
+ # So we need to calculate the relative path from Cargo.toml's directory
185
+ # Assuming Cargo.toml is in rust/ directory, go up one level
186
+ relative_path = Path("../") / license_file
187
+ # Always use forward slashes in Cargo.toml paths, regardless of platform
188
+ package_info["license-file"] = str(relative_path).replace("\\", "/")
189
+ else:
190
+ # Otherwise use regular license field
191
+ package_info["license"] = metadata["license"]
192
+
193
+
194
+ def update_cargo_dependencies(cargo_data: dict[str, Any]) -> None:
195
+ """Updates the [dependencies] section, adding pyo3 if missing."""
196
+ dependencies = cargo_data.setdefault("dependencies", {})
197
+ if "pyo3" not in dependencies:
198
+ dependencies["pyo3"] = {"version": PYO3_VERSION, "features": PYO3_FEATURES}
199
+
200
+
201
+ def update_cargo_lib_section(cargo_data: dict[str, Any]) -> None:
202
+ """Updates the [lib] section, ensuring crate-type is set."""
203
+ if "lib" not in cargo_data:
204
+ cargo_data["lib"] = {"crate-type": ["cdylib"]}
205
+ elif "crate-type" not in cargo_data.get("lib", {}):
206
+ cargo_data.setdefault("lib", {})["crate-type"] = ["cdylib"]
207
+
208
+
209
+ def sync_toml_files(pyproject_path: Path, cargo_path: Path) -> None:
210
+ """Loads pyproject.toml and Cargo.toml, syncs metadata, and saves Cargo.toml."""
211
+ # Load pyproject.toml
212
+ pyproject_data = load_toml(pyproject_path)
213
+
214
+ # Check if Cargo.toml exists
215
+ if not cargo_path.exists():
216
+ # If Cargo.toml doesn't exist, skip syncing
217
+ from wl_cli.utils.logging import log_info_pair
218
+
219
+ log_info_pair(
220
+ f"Cargo.toml not found at {cargo_path}, skipping sync",
221
+ f"未找到 {cargo_path},跳过同步",
222
+ )
223
+ return
224
+
225
+ # Load Cargo.toml
226
+ cargo_data = load_toml(cargo_path)
227
+
228
+ # Extract info
229
+ rust_crate_name = extract_rust_crate_name(pyproject_data)
230
+ pyproject_metadata = extract_pyproject_metadata(pyproject_data)
231
+
232
+ # Update Cargo data
233
+ update_cargo_package_section(
234
+ cargo_data, pyproject_metadata, rust_crate_name, cargo_path
235
+ )
236
+ update_cargo_dependencies(cargo_data)
237
+ update_cargo_lib_section(cargo_data)
238
+
239
+ # Write back Cargo.toml
240
+ write_toml(cargo_data, cargo_path)
@@ -0,0 +1,106 @@
1
+ """Configuration manager utility."""
2
+
3
+ import json
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Any, cast
7
+
8
+ import tomllib as toml # stdlib (py>=3.11): typed, so mypy is env-independent
9
+
10
+ from ....utils.platform_adapter import PlatformAdapter
11
+
12
+
13
+ class ConfigManager:
14
+ """Manager for loading and managing configuration."""
15
+
16
+ def __init__(self, config_file: str = "config.json") -> None:
17
+ self.config_file = config_file
18
+ self.config: dict[str, Any] = {}
19
+ self.load_config()
20
+ self._vendor_path = self._get_vendor_path()
21
+
22
+ def _get_vendor_path(self) -> Path:
23
+ """Get the path to the vendors directory."""
24
+ current_file = Path(__file__)
25
+ vendor_path = current_file.parent.parent.parent.parent / "vendors"
26
+ return vendor_path.resolve()
27
+
28
+ def load_config(self) -> None:
29
+ """Load configuration from file or environment variables."""
30
+ # Load from config file if exists
31
+ if Path(self.config_file).exists():
32
+ try:
33
+ with open(self.config_file, encoding="utf-8") as f:
34
+ self.config = json.load(f)
35
+ except (json.JSONDecodeError, OSError):
36
+ # If file loading fails, continue with empty config
37
+ self.config = {}
38
+
39
+ # Override with environment variables
40
+ self._load_from_env()
41
+
42
+ def _load_from_env(self) -> None:
43
+ """Load configuration from environment variables."""
44
+ # Example environment variable loading
45
+ python_version = os.environ.get("PYTHON_VERSION")
46
+ if python_version:
47
+ self.config["python_version"] = python_version
48
+
49
+ venv_path = os.environ.get("VENV_PATH")
50
+ if venv_path:
51
+ self.config["venv_path"] = venv_path
52
+
53
+ def get(self, key: str, default: Any = None) -> Any:
54
+ """Get configuration value by key."""
55
+ return self.config.get(key, default)
56
+
57
+ def set(self, key: str, value: Any) -> None:
58
+ """Set configuration value by key."""
59
+ self.config[key] = value
60
+
61
+ def get_vendor_config_path(self, config_file: str) -> Path:
62
+ """Get the path to a configuration file in the vendors directory.
63
+
64
+ For hooks, automatically selects the appropriate directory based on the platform,
65
+ unless an explicit platform directory (win/unix) is already specified.
66
+ """
67
+
68
+ # Check if we're dealing with hooks and no explicit platform is specified
69
+ if config_file.startswith("hooks") and not any(
70
+ platform in config_file for platform in ["/win", "/unix", "win/", "unix/"]
71
+ ):
72
+ # Determine the appropriate hook directory based on platform
73
+ if PlatformAdapter.is_windows(): # Windows
74
+ # Replace hooks/ with hooks/win/
75
+ if config_file == "hooks":
76
+ config_file = "hooks/win"
77
+ elif config_file.startswith("hooks/"):
78
+ config_file = config_file.replace("hooks/", "hooks/win/")
79
+ else: # Unix-like (Linux, macOS)
80
+ # Replace hooks/ with hooks/unix/
81
+ if config_file == "hooks":
82
+ config_file = "hooks/unix"
83
+ elif config_file.startswith("hooks/"):
84
+ config_file = config_file.replace("hooks/", "hooks/unix/")
85
+
86
+ return self._vendor_path / config_file
87
+
88
+ def load_vendor_config(self, config_file: str) -> dict[str, Any]:
89
+ """Load configuration from a file in the vendors directory."""
90
+ config_path = self._vendor_path / config_file
91
+ if not config_path.exists():
92
+ return {}
93
+
94
+ try:
95
+ if config_path.suffix == ".toml":
96
+ with open(config_path, "rb") as f:
97
+ return toml.load(f) # tomllib typed -> returns dict[str, Any]
98
+ elif config_path.suffix == ".json":
99
+ with open(config_path, encoding="utf-8") as f:
100
+ return cast("dict[str, Any]", json.load(f))
101
+ else:
102
+ # For other file types, return the raw content
103
+ with open(config_path, encoding="utf-8") as f:
104
+ return {"content": f.read()}
105
+ except (OSError, json.JSONDecodeError, toml.TOMLDecodeError):
106
+ return {}
@@ -0,0 +1,71 @@
1
+ """
2
+ Directory utilities for deployment functionality.
3
+ """
4
+
5
+ import os
6
+
7
+ # Whitelist: only these files or directories will be deployed
8
+ DEPLOY_WHITELIST = [
9
+ ".uv",
10
+ ".lingma",
11
+ "tools",
12
+ "Makefile",
13
+ ".python-version",
14
+ "project_vendors",
15
+ ]
16
+
17
+
18
+ def find_py_directories() -> list[str]:
19
+ """
20
+ 查找当前目录下所有以 py_ 开头的文件夹
21
+ """
22
+ current_dir = os.path.dirname(os.path.abspath(__file__))
23
+ py_dirs = []
24
+
25
+ try:
26
+ for item in os.listdir(current_dir):
27
+ item_path = os.path.join(current_dir, item)
28
+ if item.startswith("py_") and os.path.isdir(item_path):
29
+ py_dirs.append(item_path)
30
+ except OSError:
31
+ pass
32
+
33
+ return py_dirs
34
+
35
+
36
+ def is_ignored_directory(dir_name: str) -> bool:
37
+ """
38
+ 检查目录是否应被忽略
39
+ """
40
+ ignored_dirs = [".git", "__pycache__"] # 移除了 .cursor 和 .uv
41
+ for ignored in ignored_dirs:
42
+ if ignored in dir_name.split(os.sep):
43
+ return True
44
+ return False
45
+
46
+
47
+ def is_excluded_file(file_name: str) -> bool:
48
+ """
49
+ 检查文件是否应被排除不创建链接
50
+ """
51
+ excluded_files = ["README.md", ".gitignore"] # 添加.gitignore到排除列表
52
+ return file_name in excluded_files
53
+
54
+
55
+ def is_in_whitelist(path: str, source_dir: str) -> bool:
56
+ """
57
+ 检查文件或目录是否在白名单中
58
+ """
59
+ try:
60
+ rel_path = os.path.relpath(path, source_dir)
61
+ if rel_path == ".":
62
+ return False
63
+
64
+ # 检查路径或其父目录是否在白名单中
65
+ path_parts = rel_path.split(os.sep)
66
+ for i in range(len(path_parts)):
67
+ if os.sep.join(path_parts[: i + 1]) in DEPLOY_WHITELIST:
68
+ return True
69
+ return False
70
+ except (OSError, ValueError):
71
+ return False
@@ -0,0 +1,9 @@
1
+ # Git utilities module
2
+
3
+ from .git_flow_manager import GitFlowManager
4
+ from .gitignore_manager import GitignoreManager
5
+
6
+ __all__ = [
7
+ "GitignoreManager",
8
+ "GitFlowManager",
9
+ ]
@@ -0,0 +1,143 @@
1
+ """分支创建器,处理 Git Flow 分支创建逻辑"""
2
+
3
+ import re
4
+ import subprocess
5
+ import sys
6
+
7
+ from wl_cli.utils.logging import log_info, log_info_pair
8
+
9
+
10
+ class BranchCreator:
11
+ """处理 Git Flow 分支创建逻辑"""
12
+
13
+ def __init__(self, git_utility):
14
+ """
15
+ 初始化 BranchCreator 类
16
+
17
+ Args:
18
+ git_utility: GitUtility 实例,用于执行基础 Git 操作
19
+ """
20
+ self.git_utility = git_utility
21
+
22
+ def setup_initial_branches(self) -> None:
23
+ """设置初始的 Git Flow 分支结构(main 和 develop)"""
24
+ log_info_pair("Starting setup_initial_branches...", "开始设置初始分支...")
25
+
26
+ current_branch = self.git_utility.get_current_branch()
27
+ log_info_pair(
28
+ f"Current branch: {current_branch}", f"当前分支: {current_branch}"
29
+ )
30
+
31
+ if not self.git_utility.branch_exists("main"):
32
+ log_info_pair("Creating main branch...", "创建 main 分支...")
33
+
34
+ if current_branch != "main":
35
+ log_info("Creating main branch directly...")
36
+ self.git_utility.create_and_checkout_branch("main")
37
+ else:
38
+ log_info("Already on main branch")
39
+
40
+ log_info_pair("✓ Main branch created", "✓ Main 分支创建成功")
41
+ else:
42
+ log_info_pair("Main branch already exists", "Main 分支已存在")
43
+ self.git_utility.checkout_branch("main")
44
+
45
+ if not self.git_utility.branch_exists("develop"):
46
+ log_info_pair("Creating develop branch...", "创建 develop 分支...")
47
+
48
+ log_info("Creating develop branch directly...")
49
+ self.git_utility.create_and_checkout_branch("develop")
50
+
51
+ log_info_pair("✓ Develop branch created", "✓ Develop 分支创建成功")
52
+ else:
53
+ log_info_pair("Develop branch already exists", "Develop 分支已存在")
54
+ self.git_utility.checkout_branch("develop")
55
+
56
+ def check_and_pull_branch(self, branch_name: str) -> None:
57
+ """
58
+ 检查分支是否存在并拉取最新更改
59
+
60
+ Args:
61
+ branch_name: 要检查和拉取的分支名称
62
+ """
63
+ log_info_pair(
64
+ f"Checking and pulling {branch_name} branch...",
65
+ f"正在检查并拉取 {branch_name} 分支...",
66
+ )
67
+
68
+ try:
69
+ if self.git_utility.local_branch_exists(branch_name):
70
+ self.git_utility.checkout_branch(branch_name)
71
+
72
+ self.git_utility.pull_branch(branch_name)
73
+ log_info_pair(
74
+ f"✓ {branch_name} branch pulled successfully",
75
+ f"✓ {branch_name} 分支拉取成功",
76
+ )
77
+ else:
78
+ self.git_utility.create_branch_from_remote(branch_name)
79
+ log_info_pair(
80
+ f"✓ {branch_name} branch created from remote",
81
+ f"✓ 从远程创建 {branch_name} 分支成功",
82
+ )
83
+ except subprocess.CalledProcessError as e:
84
+ log_info_pair(
85
+ f"Warning: Failed to check and pull {branch_name} branch",
86
+ f"警告: 检查并拉取 {branch_name} 分支失败",
87
+ )
88
+ log_info_pair(f"Error details: {e}", f"错误详情: {e}")
89
+
90
+ def create_git_flow_branch(self, work_type: str, source_branch: str) -> None:
91
+ """
92
+ 根据工作类型创建 Git Flow 分支
93
+
94
+ Args:
95
+ work_type: 工作类型 (feature, fix, hotfix, release)
96
+ source_branch: 源分支名称
97
+ """
98
+ log_info_pair(
99
+ f"Creating {work_type} branch from {source_branch}...",
100
+ f"正在从 {source_branch} 创建 {work_type} 分支...",
101
+ )
102
+
103
+ try:
104
+ if not sys.stdin.isatty():
105
+ log_info_pair(
106
+ "Non-interactive environment detected, skipping branch creation",
107
+ "检测到非交互环境,跳过分支创建",
108
+ )
109
+ return
110
+
111
+ current_branch = self.git_utility.get_current_branch()
112
+
113
+ if current_branch != source_branch:
114
+ self.git_utility.checkout_branch(source_branch)
115
+
116
+ branch_name = input(
117
+ f"Enter {work_type} branch name (e.g., {work_type}/my-feature): "
118
+ )
119
+
120
+ if not re.match(r"^[a-zA-Z0-9._/-]+$", branch_name):
121
+ log_info_pair(
122
+ f"Invalid branch name: {branch_name}",
123
+ f"无效的分支名称: {branch_name}",
124
+ )
125
+ return
126
+
127
+ if not branch_name.startswith(f"{work_type}/"):
128
+ branch_name = f"{work_type}/{branch_name}"
129
+
130
+ self.git_utility.create_and_checkout_branch(branch_name)
131
+
132
+ log_info_pair(
133
+ f"✓ Created and checked out {branch_name} branch",
134
+ f"✓ 创建并切换到 {branch_name} 分支成功",
135
+ )
136
+ except subprocess.CalledProcessError as e:
137
+ log_info_pair(
138
+ f"Warning: Failed to create {work_type} branch",
139
+ f"警告: 创建 {work_type} 分支失败",
140
+ )
141
+ log_info_pair(f"Error details: {e}", f"错误详情: {e}")
142
+ except KeyboardInterrupt:
143
+ log_info_pair("\nOperation cancelled by user", "\n用户取消了操作")
@@ -0,0 +1,24 @@
1
+ """Git Flow 接口定义"""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class GitFlowInterface(ABC):
7
+ """Git Flow 接口,定义 Git Flow 操作的基本方法"""
8
+
9
+ @abstractmethod
10
+ def setup_git_flow_branches(self) -> None:
11
+ """
12
+ 设置 Git Flow 分支结构
13
+ """
14
+ pass
15
+
16
+ @abstractmethod
17
+ def setup_git_flow_branches_by_work_type(self, work_type: str) -> None:
18
+ """
19
+ 根据工作类型设置 Git Flow 分支
20
+
21
+ Args:
22
+ work_type: 工作类型 (feature, fix, hotfix, release)
23
+ """
24
+ pass