jiggle-version 2.2.0__tar.gz → 2.2.1__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 (175) hide show
  1. jiggle_version-2.2.1/.gitattributes +27 -0
  2. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.github/workflows/build.yml +3 -3
  3. jiggle_version-2.2.1/.github/workflows/create_draft_release.yml +32 -0
  4. jiggle_version-2.2.1/.github/workflows/release.yml +155 -0
  5. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.github/workflows/zizmor.yml +2 -2
  6. jiggle_version-2.2.1/CHANGELOG.md +111 -0
  7. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/Makefile +24 -1
  8. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/PKG-INFO +9 -3
  9. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/README.md +7 -0
  10. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/build.py +2 -4
  11. jiggle_version-2.2.1/dead_code/setup.py +13 -0
  12. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/fetch_raw_data/pypi_versions.py +3 -2
  13. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/__about__.py +1 -1
  14. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_version/parse_dunder_version.py +0 -2
  15. jiggle_version-2.2.1/historical/test/sample_lib/__version__.py +5 -0
  16. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/__about__.py +1 -1
  17. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/discover.py +42 -4
  18. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/gitignore.py +47 -0
  19. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version_self.py +1 -0
  20. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/make_prompt.sh +0 -0
  21. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/pyproject.toml +260 -234
  22. jiggle_version-2.2.1/sample_projects/dupes_in_dunders/dupes/__version__.py +1 -0
  23. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/ver_in_weird_file/conf.py +6 -8
  24. jiggle_version-2.2.1/spec/bug-uv-cache-leak.md +120 -0
  25. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/stress_test.py +25 -17
  26. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_discover.py +50 -0
  27. jiggle_version-2.2.1/uv.lock +6796 -0
  28. jiggle_version-2.2.0/.github/workflows/publish_to_pypi.yml +0 -83
  29. jiggle_version-2.2.0/CHANGELOG.md +0 -71
  30. jiggle_version-2.2.0/codecov +0 -0
  31. jiggle_version-2.2.0/codecov.SHA256SUM +0 -1
  32. jiggle_version-2.2.0/codecov.SHA256SUM.sig +0 -16
  33. jiggle_version-2.2.0/dead_code/setup.py +0 -11
  34. jiggle_version-2.2.0/historical/test/sample_lib/__version__.py +0 -5
  35. jiggle_version-2.2.0/sample_projects/dupes_in_dunders/dupes/__version__.py +0 -1
  36. jiggle_version-2.2.0/uv.lock +0 -4886
  37. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.bandit +0 -0
  38. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.config/.isort.cfg +0 -0
  39. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.config/.pylintrc +0 -0
  40. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.config/.pylintrc_scripts +0 -0
  41. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.config/.pylintrc_test +0 -0
  42. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.config/.pynt +0 -0
  43. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.config/.yamllint +0 -0
  44. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.devcontainer/Dockerfile +0 -0
  45. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.devcontainer/devcontainer.json +0 -0
  46. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.env_example +0 -0
  47. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.gitignore +0 -0
  48. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.jiggle_version.config +0 -0
  49. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/.pre-commit-config.yaml +0 -0
  50. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/LICENSE +0 -0
  51. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/SECURITY.md +0 -0
  52. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/dead_code/Pipfile +0 -0
  53. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/dead_code/Pipfile.lock +0 -0
  54. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/dead_code/notes_for_mypyc.md +0 -0
  55. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/AUTHORS +0 -0
  56. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/CODE_OF_CONDUCT.md +0 -0
  57. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/ChangeLog +0 -0
  58. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/design_philosophy.md +0 -0
  59. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/prior_art.md +0 -0
  60. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/todo.md +0 -0
  61. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/usage_example/single_module_example/__init__.py +0 -0
  62. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/docs/usage_example/usage.py +0 -0
  63. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/fetch_raw_data/.gitignore +0 -0
  64. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/fetch_raw_data/bump_all.py +0 -0
  65. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/fetch_raw_data/bump_all_directly.py +0 -0
  66. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/fetch_raw_data/decomp.sh +0 -0
  67. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/fetch_raw_data/get_file.sh +0 -0
  68. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/__init__.py +0 -0
  69. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/__main__.py +0 -0
  70. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/central_module_finder.py +0 -0
  71. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/commands.py +0 -0
  72. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/file_inventory.py +0 -0
  73. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/file_makers.py +0 -0
  74. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/file_opener.py +0 -0
  75. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/find_modules_function.py +0 -0
  76. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/find_version_class.py +0 -0
  77. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/jiggle_class.py +0 -0
  78. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/module_finder.py +0 -0
  79. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_package/__init__.py +0 -0
  80. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_package/find_version_by_package.py +0 -0
  81. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_package/package_info_finder.py +0 -0
  82. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_python/__init__.py +0 -0
  83. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_python/is_this_okay.py +0 -0
  84. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_version/__init__.py +0 -0
  85. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_version/parse_kwarg_version.py +0 -0
  86. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/parse_version/schema_guesser.py +0 -0
  87. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/utils.py +0 -0
  88. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/jiggle_version/version_lint.py +0 -0
  89. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/__init__.py +0 -0
  90. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/go.sh +0 -0
  91. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/sample_lib/__init__.py +0 -0
  92. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_command_line.py +0 -0
  93. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_damanged_versions.py +0 -0
  94. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_find_by_ast.py +0 -0
  95. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_jiggle_class.py +0 -0
  96. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_main.py +0 -0
  97. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_parse_dunder_version.py +0 -0
  98. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_parse_kwarg_version.py +0 -0
  99. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_pep440.py +0 -0
  100. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_strings/__init__.py +0 -0
  101. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_taggint.py +0 -0
  102. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_version_schemes.py +0 -0
  103. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_x_package_finder.py +0 -0
  104. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/test_zcentral_module_finder.py +0 -0
  105. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/historical/test/tox_banner.py +0 -0
  106. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/__init__.py +0 -0
  107. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/__main__.py +0 -0
  108. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/auto.py +0 -0
  109. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/bump.py +0 -0
  110. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/config.py +0 -0
  111. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/git.py +0 -0
  112. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/parsers/__init__.py +0 -0
  113. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/parsers/ast_parser.py +0 -0
  114. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/parsers/config_parser.py +0 -0
  115. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/py.typed +0 -0
  116. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/pypi.py +0 -0
  117. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/schemes.py +0 -0
  118. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/update.py +0 -0
  119. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/utils/__init__.py +0 -0
  120. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/utils/cli_suggestions.py +0 -0
  121. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/utils/console.py +0 -0
  122. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/utils/files.py +0 -0
  123. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/jiggle_version/utils/logging_config.py +0 -0
  124. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/.gitignore +0 -0
  125. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/bar/.gitignore +0 -0
  126. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/bar/__init__.py +0 -0
  127. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/execise_foo.py +0 -0
  128. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/foo/.gitignore +0 -0
  129. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/foo/__init__.py +0 -0
  130. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/double_module/setup.py +0 -0
  131. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/dupes_in_dunders/dupes/__init__.py +0 -0
  132. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_src/.gitignore +0 -0
  133. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_src/exercise_me.py +0 -0
  134. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_src/file_module.py +0 -0
  135. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_src/setup.py +0 -0
  136. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_ver_in_file/.gitignore +0 -0
  137. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_ver_in_file/exercise_me.py +0 -0
  138. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_ver_in_file/file_module.py +0 -0
  139. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/file_module_ver_in_file/setup.py +0 -0
  140. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/init_project/some_thing/__init__.py +0 -0
  141. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/sample_src/.gitignore +0 -0
  142. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/sample_src/__init__.py +0 -0
  143. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/sample_src/a.py +0 -0
  144. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/sample_src/setup.py +0 -0
  145. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/setup_only/.gitignore +0 -0
  146. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/setup_only/setup.py +0 -0
  147. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/ver_in_weird_file/setup.cfg +0 -0
  148. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/ver_in_weird_file/setup.py +0 -0
  149. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/ver_in_weird_file/setup_helpers.py +0 -0
  150. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/ver_in_weird_file/ver_in_weird_file/__init__.py +0 -0
  151. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/ver_in_weird_file/ver_in_weird_file/smtp.py +0 -0
  152. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/with_unlikely_modules/setup.cfg +0 -0
  153. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/with_unlikely_modules/setup.py +0 -0
  154. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/with_unlikely_modules/test/__init__.py +0 -0
  155. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/sample_projects/with_unlikely_modules/thing/__init__.py +0 -0
  156. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/scripts/basic_checks.sh +0 -0
  157. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/scripts/integration_test.sh +0 -0
  158. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/scripts/make_prompt.sh +0 -0
  159. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/scripts/make_source.sh +0 -0
  160. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/spec/vnext.md +0 -0
  161. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/__init__.py +0 -0
  162. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_auto.py +0 -0
  163. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_config.py +0 -0
  164. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_discover_permission.py +0 -0
  165. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_git.py +0 -0
  166. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_gitignore.py +0 -0
  167. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_hypothesis_properties.py +0 -0
  168. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_main.py +0 -0
  169. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_parsers/__init__.py +0 -0
  170. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_parsers/test_ast_parser.py +0 -0
  171. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_parsers/test_ast_parser_encoding.py +0 -0
  172. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_parsers/test_config_parser.py +0 -0
  173. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_schemes.py +0 -0
  174. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/test/test_update.py +0 -0
  175. {jiggle_version-2.2.0 → jiggle_version-2.2.1}/tox.ini +0 -0
@@ -0,0 +1,27 @@
1
+ # Normalize line endings: store as LF in the repo, check out native on each OS.
2
+ * text=auto
3
+
4
+ # Files that break with CRLF must always be LF, even on Windows checkouts.
5
+ *.sh text eol=lf
6
+ *.bash text eol=lf
7
+ Dockerfile* text eol=lf
8
+ *.dockerfile text eol=lf
9
+
10
+ # Windows-only scripts stay CRLF.
11
+ *.bat text eol=crlf
12
+ *.cmd text eol=crlf
13
+ *.ps1 text eol=crlf
14
+
15
+ # Common binaries: never touch.
16
+ *.png binary
17
+ *.jpg binary
18
+ *.jpeg binary
19
+ *.gif binary
20
+ *.ico binary
21
+ *.pdf binary
22
+ *.zip binary
23
+ *.gz binary
24
+ *.woff binary
25
+ *.woff2 binary
26
+ *.ttf binary
27
+ *.eot binary
@@ -10,7 +10,7 @@ jobs:
10
10
  runs-on: ubuntu-latest
11
11
 
12
12
  steps:
13
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
13
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
14
14
  with:
15
15
  persist-credentials: false
16
16
 
@@ -26,7 +26,7 @@ jobs:
26
26
  run: |
27
27
  pipx install uv && pipx install "coderoller" && \
28
28
  pipx install isort && pipx install black && pipx install bandit && \
29
- pipx install pylint && pipx install pre-commit && pipx install poetry && pipx install hatch
29
+ pipx install pylint && pipx install poetry && pipx install hatch
30
30
 
31
31
  - name: Install Dependencies
32
32
  run: uv sync
@@ -35,7 +35,7 @@ jobs:
35
35
  run: chmod +x *.sh && uv run make check
36
36
 
37
37
  - name: Upload coverage reports to Codecov
38
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
38
+ uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
39
39
  with:
40
40
  token: ${{ secrets.CODECOV_TOKEN }}
41
41
 
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Create Draft Release
3
+ concurrency: changelog
4
+ on:
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+ permissions:
9
+ contents: read
10
+ jobs:
11
+ release:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: write
15
+ steps:
16
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
17
+ with:
18
+ fetch-depth: 0
19
+ persist-credentials: false
20
+ - name: Setup Python 3.14
21
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
22
+ with: {python-version: '3.14'}
23
+ - name: Set up uv
24
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
25
+ with:
26
+ enable-cache: true
27
+ - name: Sync dependencies
28
+ run: uv sync --frozen --group dev
29
+ - name: Creating Draft release
30
+ env:
31
+ GITHUB_TOKEN: ${{ github.token }}
32
+ run: uv run changelogmanager github-release --repository ${{ github.repository }}
@@ -0,0 +1,155 @@
1
+ ---
2
+ name: Release
3
+ concurrency: changelog
4
+ on:
5
+ release:
6
+ types: [released]
7
+ permissions:
8
+ contents: read
9
+ jobs:
10
+ bump:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: write
14
+ pull-requests: write
15
+ outputs:
16
+ branch: ${{ steps.push.outputs.branch }}
17
+ version: ${{ steps.release.outputs.version }}
18
+ steps:
19
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
20
+ with:
21
+ ref: ${{ github.event.release.target_commitish }}
22
+ fetch-depth: 0
23
+ persist-credentials: false
24
+ - name: Set up Python 3.14
25
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
26
+ with: {python-version: '3.14'}
27
+ - name: Set up uv
28
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
29
+ with:
30
+ enable-cache: false
31
+ - name: Sync dependencies
32
+ run: uv sync --frozen --group dev
33
+ - name: Bump changelog and version files
34
+ id: release
35
+ env:
36
+ RELEASE_VERSION: ${{ github.event.release.tag_name }}
37
+ run: |
38
+ VERSION="${RELEASE_VERSION#v}"
39
+ uv run changelogmanager release \
40
+ --override-version "$VERSION" \
41
+ --bump-versions \
42
+ --yes
43
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
44
+ - name: Commit and push bump branch
45
+ id: push
46
+ env:
47
+ GITHUB_TOKEN: ${{ github.token }}
48
+ RELEASE_ID: ${{ github.event.release.id }}
49
+ REPO: ${{ github.repository }}
50
+ RELEASE_VERSION: ${{ steps.release.outputs.version }}
51
+ run: |
52
+ BRANCH="release/bump-${RELEASE_ID}"
53
+ git config user.name "github-actions[bot]"
54
+ git config user.email "github-actions[bot]@users.noreply.github.com"
55
+ git checkout -b "${BRANCH}"
56
+ git add CHANGELOG.md pyproject.toml jiggle_version/__about__.py
57
+ git add -u
58
+ if git diff --cached --quiet; then
59
+ echo "::warning::No files changed after release --bump-versions; is the version already bumped?"
60
+ exit 1
61
+ fi
62
+ git commit -m "chore: release ${RELEASE_VERSION}"
63
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${REPO}.git"
64
+ REMOTE_SHA="$(git ls-remote --heads origin "${BRANCH}" | awk '{print $1}')"
65
+ if [ -n "${REMOTE_SHA}" ]; then
66
+ git push \
67
+ --force-with-lease="refs/heads/${BRANCH}:${REMOTE_SHA}" \
68
+ origin \
69
+ "HEAD:refs/heads/${BRANCH}"
70
+ else
71
+ git push origin "HEAD:refs/heads/${BRANCH}"
72
+ fi
73
+ echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
74
+ - name: Open pull request
75
+ env:
76
+ GITHUB_TOKEN: ${{ github.token }}
77
+ RELEASE_VERSION: ${{ steps.release.outputs.version }}
78
+ BASE_BRANCH: ${{ github.event.release.target_commitish }}
79
+ REPO: ${{ github.repository }}
80
+ BUMP_BRANCH: ${{ steps.push.outputs.branch }}
81
+ run: |
82
+ uv run changelogmanager github-pr \
83
+ --repository "${REPO}" \
84
+ --head "${BUMP_BRANCH}" \
85
+ --base "${BASE_BRANCH}" \
86
+ --title "chore: release ${RELEASE_VERSION}" \
87
+ --body "$(cat <<EOF
88
+ Automated release PR for **${RELEASE_VERSION}**.
89
+ Changes:
90
+ - \`CHANGELOG.md\`: promoted \`[Unreleased]\` to \`${RELEASE_VERSION}\`
91
+ - \`pyproject.toml\`: version bumped to \`${RELEASE_VERSION}\`
92
+ - Source version strings updated to \`${RELEASE_VERSION}\`
93
+ This PR was opened automatically by the Release workflow.
94
+ Merge it once the PyPI publish below succeeds.
95
+ EOF
96
+ )"
97
+ build:
98
+ runs-on: ubuntu-latest
99
+ needs: bump
100
+ steps:
101
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
102
+ with:
103
+ ref: ${{ needs.bump.outputs.branch }}
104
+ fetch-depth: 0
105
+ persist-credentials: false
106
+ - name: Set up Python 3.14
107
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
108
+ with: {python-version: '3.14'}
109
+ - name: Set up uv
110
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
111
+ with:
112
+ enable-cache: false
113
+ - name: Sync dependencies
114
+ run: uv sync --frozen
115
+ - name: Build
116
+ run: uv build --no-sources
117
+ - name: Verify dist version matches release
118
+ env:
119
+ EXPECTED: ${{ needs.bump.outputs.version }}
120
+ run: |
121
+ WHEEL=$(ls dist/*.whl 2>/dev/null | head -1)
122
+ if [ -z "$WHEEL" ]; then
123
+ echo "::error::No wheel found in dist/"
124
+ exit 1
125
+ fi
126
+ if ! echo "$WHEEL" | grep -q "${EXPECTED}"; then
127
+ echo "::error::Wheel ${WHEEL} does not contain expected version ${EXPECTED}"
128
+ exit 1
129
+ fi
130
+ echo "Verified: ${WHEEL} matches version ${EXPECTED}"
131
+ - name: Upload artifact
132
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
133
+ with:
134
+ name: packages
135
+ path: dist/
136
+ if-no-files-found: error
137
+ retention-days: 1
138
+ publish:
139
+ runs-on: ubuntu-latest
140
+ needs: build
141
+ permissions:
142
+ id-token: write
143
+ environment:
144
+ name: pypi
145
+ url: https://pypi.org/p/jiggle-version
146
+ steps:
147
+ - name: Download artifact
148
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
149
+ with:
150
+ name: packages
151
+ path: dist/
152
+ - name: Publish to PyPI
153
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
154
+ with:
155
+ verbose: 'true'
@@ -23,7 +23,7 @@ jobs:
23
23
  security-events: write
24
24
  steps:
25
25
  - name: Checkout repository
26
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
27
  with:
28
28
  persist-credentials: false
29
29
 
@@ -36,7 +36,7 @@ jobs:
36
36
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37
37
 
38
38
  - name: Upload SARIF file
39
- uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
39
+ uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
40
40
  with:
41
41
  # Path to SARIF file relative to the root of the repository
42
42
  sarif_file: results.sarif
@@ -0,0 +1,111 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [2.2.1] - 2026-07-04
8
+ ### Fixed
9
+ - Version discovery now honors nested `.gitignore` files and skips directories tagged with a `CACHEDIR.TAG` marker, so project-local caches (e.g. uv's `./.uv/`) no longer leak installed-package version files and cause false version-conflict errors.
10
+
11
+ ## [2.2.0] - 2026-05-24
12
+ ### Added
13
+ - Git tag checking via `check --git-tag` command: compares the agreed source version against the most recent git tag reachable from HEAD (`git describe --tags --abbrev=0`). A leading `v`/`V` is stripped before comparison. Exits `102` on mismatch; exits `0` with an informational note when no tags exist. Opt-in flag; existing pipelines are unaffected.
14
+
15
+ ### Fixed
16
+ - Version discovery no longer walks into virtual-environment roots. Any directory containing a `pyvenv.cfg` file (the standard venv marker written by pip, uv, and virtualenv) is skipped entirely, eliminating false version-conflict errors from installed-package `_version.py` / `__version__.py` files in ad-hoc venvs not covered by `.gitignore`. `site-packages` and `node_modules` are also added to the unconditional prune list.
17
+
18
+ ## [2.1.1] - 2026-03-27
19
+ ### Added
20
+ - Quiet option
21
+
22
+ ### Fixed
23
+ - UTF-8 error on printing output
24
+
25
+ ## [2.1.0] - 2025-10-20
26
+ ### Added
27
+ - Python 3.14 support
28
+ - Checks PyPI to see if current version is published. Skips bump if version is unpublished to avoid version number gaps when running on every Makefile or Justfile invocation.
29
+
30
+ ### Fixed
31
+ - Missing packaging library dependency
32
+
33
+ ## [2.0.1] - 2025-10-06
34
+ ### Fixed
35
+ - Missing dependency in pyproject.toml
36
+
37
+ ## [2.0.0] - 2025-10-06
38
+ ### Added
39
+ - 100% code rewrite with new commands
40
+
41
+ ### Changed
42
+ - Command signature changed
43
+
44
+ ### Removed
45
+ - Removed current command, replaced with others
46
+
47
+ ## [1.2.0] - 2025-10-05
48
+ ### Added
49
+ - Python 3.14 compatibility
50
+
51
+ ### Changed
52
+ - Replace die() utility with JiggleVersionException for cleaner error propagation
53
+ - Switch version metadata to __about__.py and use Path-based arguments internally
54
+ - Fix keyword typo in package metadata (verision-numbers → version-numbers)
55
+
56
+ ## [1.1.0] - 2023-05-28
57
+ ### Added
58
+ - Update to Python 3.11 and Actions
59
+ - MyPyC compilation support
60
+
61
+ ### Changed
62
+ - Build script improvements
63
+
64
+ ## [1.0.77] - 2021-12-11
65
+ ### Added
66
+ - Vendor versio library (comparable_mixin, version, version_scheme) to support version comparison logic
67
+
68
+ ## [1.0.76] - 2021-12-06
69
+ ### Added
70
+ - New build script
71
+
72
+ ### Changed
73
+ - Documentation improvements
74
+
75
+ ## [1.0.71] - 2020-05-23
76
+ ### Fixed
77
+ - Close file handles
78
+
79
+ ## [1.0.70] - 2020-02-09
80
+ ### Added
81
+ - Python 3 only support
82
+
83
+ ### Fixed
84
+ - Fix formatting issues
85
+
86
+ ## [1.0.68] - 2019-01-11
87
+ ### Added
88
+ - Force choice of module option
89
+ - Init feature for version file initialization
90
+
91
+ ### Fixed
92
+ - Fix bug causing incorrect behavior on certain path configurations
93
+ - Remove extraneous PyCharm IDE files accidentally included in the PyPI distribution
94
+
95
+ ## [0.1.2] - 2025-10-05
96
+ ### Fixed
97
+ - Python 3.13 support
98
+ - Last version before complete rewrite
99
+
100
+ [2.2.0]: https://github.com/matthewdeanmartin/jiggle_version/compare/v2.1.1...v2.2.0
101
+ [2.1.1]: https://github.com/matthewdeanmartin/jiggle_version/compare/v2.1.0...v2.1.1
102
+ [2.1.0]: https://github.com/matthewdeanmartin/jiggle_version/compare/v2.0.1...v2.1.0
103
+ [2.0.1]: https://github.com/matthewdeanmartin/jiggle_version/compare/v2.0.0...v2.0.1
104
+ [2.0.0]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.2.0...v2.0.0
105
+ [1.2.0]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.1.0...v1.2.0
106
+ [1.1.0]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.0.77...v1.1.0
107
+ [1.0.77]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.0.76...v1.0.77
108
+ [1.0.76]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.0.71...v1.0.76
109
+ [1.0.71]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.0.70...v1.0.71
110
+ [1.0.70]: https://github.com/matthewdeanmartin/jiggle_version/compare/v1.0.68...v1.0.70
111
+ [1.0.68]: https://github.com/matthewdeanmartin/jiggle_version/releases/tag/v1.0.68
@@ -83,7 +83,7 @@ bandit: .build_history/bandit
83
83
  # for when using -j (jobs, run in parallel)
84
84
  .NOTPARALLEL: .build_history/isort .build_history/black
85
85
 
86
- check: mypy test pylint bandit pre-commit
86
+ check: mypy test pylint bandit
87
87
 
88
88
  #.PHONY: publish_test
89
89
  #publish_test:
@@ -140,3 +140,26 @@ issues:
140
140
 
141
141
  pin_actions:
142
142
  pinact run --update .github/workflows/build.yml .github/workflows/publish_to_pypi.yml .github/workflows/zizmor.yml
143
+
144
+ # ── Dogfooding targets (independent, not wired into check) ───────────────────
145
+
146
+ .PHONY: version-check
147
+ version-check:
148
+ @uv run jiggle_version check
149
+
150
+ .PHONY: dev-status
151
+ dev-status:
152
+ @uv run troml-dev-status validate .
153
+
154
+ .PHONY: prerelease-check
155
+ prerelease-check: version-check dev-status
156
+ @echo "Pre-release checks passed."
157
+
158
+ .PHONY: dont-be-lazy
159
+ dont-be-lazy:
160
+ @uv run dont_be_lazy --root . --no-color summary
161
+ @uv run dont_be_lazy --root . --no-color scan jiggle_version --no-config-suppressions || true
162
+
163
+ .PHONY: pydoc-docs
164
+ pydoc-docs:
165
+ @uv run pydoc_fork jiggle_version -o ./pydoc/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jiggle_version
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Increment version number found in source code without regex
5
5
  Author-email: Matthew Martin <matthewdeanmartin@gmail.com>
6
6
  License-Expression: MIT
@@ -14,10 +14,9 @@ Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
- Classifier: Programming Language :: Python :: 3.13
18
17
  Classifier: Programming Language :: Python :: 3.14
19
18
  Classifier: Programming Language :: Python :: 3.15
20
- Requires-Python: >=3.8
19
+ Requires-Python: >=3.9
21
20
  Requires-Dist: packaging>=25.0
22
21
  Requires-Dist: pathspec>=0.12
23
22
  Requires-Dist: requests>=2.32.4
@@ -326,3 +325,10 @@ This is a CLI-first tool. Internal modules may change. If you import, prefer:
326
325
 
327
326
  * `jiggle_version.__about__.__version__`
328
327
  * Running via `python -m jiggle_version`
328
+
329
+ ## Project Links
330
+
331
+ - [GitHub](https://github.com/matthewdeanmartin/jiggle_version)
332
+ - [PyPI](https://pypi.org/project/jiggle-version/)
333
+ - [Bug Tracker](https://github.com/matthewdeanmartin/jiggle_version/issues)
334
+ - [Change Log](https://github.com/matthewdeanmartin/jiggle_version/blob/main/CHANGELOG.md)
@@ -298,3 +298,10 @@ This is a CLI-first tool. Internal modules may change. If you import, prefer:
298
298
 
299
299
  * `jiggle_version.__about__.__version__`
300
300
  * Running via `python -m jiggle_version`
301
+
302
+ ## Project Links
303
+
304
+ - [GitHub](https://github.com/matthewdeanmartin/jiggle_version)
305
+ - [PyPI](https://pypi.org/project/jiggle-version/)
306
+ - [Bug Tracker](https://github.com/matthewdeanmartin/jiggle_version/issues)
307
+ - [Change Log](https://github.com/matthewdeanmartin/jiggle_version/blob/main/CHANGELOG.md)
@@ -334,8 +334,6 @@ def bandit() -> None:
334
334
  do_bandit(IS_SHELL_SCRIPT_LIKE)
335
335
 
336
336
 
337
-
338
-
339
337
  @task()
340
338
  @skip_if_no_change("mccabe")
341
339
  @timed()
@@ -573,6 +571,7 @@ def package() -> None:
573
571
  """
574
572
  subprocess.run(["poetry", "build"])
575
573
 
574
+
576
575
  @task()
577
576
  @timed()
578
577
  @skip_if_no_change("parallel_checks")
@@ -587,7 +586,7 @@ def parallel_checks() -> None:
587
586
  vulture,
588
587
  do_lint,
589
588
  do_bandit,
590
- do_mccabe
589
+ do_mccabe,
591
590
  ]
592
591
  if IS_GITLAB:
593
592
  # other tasks assume there will be a LOC file by now.
@@ -684,7 +683,6 @@ def upload_package() -> None:
684
683
  do_upload_package()
685
684
 
686
685
 
687
-
688
686
  # FAST. FATAL ERRORS. DON'T CHANGE THINGS THAT CHECK IN
689
687
  @task(mypy, detect_secrets, git_secrets, check_package, vulture)
690
688
  @skip_if_no_change("pre_commit_hook")
@@ -0,0 +1,13 @@
1
+ from mypyc.build import mypycify
2
+ from setuptools import setup
3
+
4
+ setup(
5
+ name="jiggle_version",
6
+ packages=["jiggle_version"],
7
+ ext_modules=mypycify(
8
+ [
9
+ "--disallow-untyped-defs", # Pass a mypy flag
10
+ "jiggle_version",
11
+ ]
12
+ ),
13
+ )
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Get some sample version strings from the wild.
3
3
  """
4
+
4
5
  # https://python-forum.io/Thread-pip-list-available-packages
5
6
 
6
7
  # download file
@@ -48,7 +49,7 @@ def package_info(rows):
48
49
  if ".zip" in url or ".gz" in url:
49
50
  print(url)
50
51
  last = url
51
- except: # noqa
52
+ except: # noqa
52
53
  pass
53
54
  return last
54
55
 
@@ -74,7 +75,7 @@ def read_packages():
74
75
  print(package_name + " done")
75
76
  continue
76
77
  print(url)
77
- except: # noqa
78
+ except: # noqa
78
79
  continue
79
80
 
80
81
  response = requests.get(url)
@@ -11,7 +11,7 @@ __all__ = [
11
11
  ]
12
12
 
13
13
  __title__ = "jiggle_version"
14
- __version__ = "1.2.0"
14
+ __version__ = "2.2.1"
15
15
  __description__ = "Increment version number found in source code without regex"
16
16
  __readme__ = "README.md"
17
17
  __keywords__ = ["version", "version-numbers"]
@@ -29,8 +29,6 @@ version_tokens = [
29
29
  ]
30
30
 
31
31
 
32
-
33
-
34
32
  def find_by_ast(line: str, version_token: str = "__version__") -> Optional[str]:
35
33
  """
36
34
  Parses a line of Python code to find a version string using the `ast` module.
@@ -0,0 +1,5 @@
1
+ """
2
+ Init
3
+ """
4
+
5
+ __version__ = "2.2.1"
@@ -12,7 +12,7 @@ __all__ = [
12
12
  ]
13
13
 
14
14
  __title__ = "jiggle_version"
15
- __version__ = "2.2.0"
15
+ __version__ = "2.2.1"
16
16
  __description__ = "Increment version number found in source code without regex"
17
17
  __readme__ = "README.md"
18
18
  __keywords__ = ["version", "version-numbers"]
@@ -9,10 +9,14 @@ import logging
9
9
  from pathlib import Path
10
10
 
11
11
  # Use the new gitignore API
12
+ from pathspec import PathSpec
13
+ from pathspec.patterns import GitWildMatchPattern
14
+
12
15
  from .gitignore import (
13
- collect_default_spec,
16
+ build_gitignore_spec,
14
17
  is_path_explicitly_ignored,
15
18
  is_path_gitignored,
19
+ nested_gitignore_patterns,
16
20
  )
17
21
 
18
22
  # Files to search for recursively in the project root.
@@ -36,6 +40,12 @@ DEFAULT_IGNORE_DIRS = {
36
40
  # the project's own version declarations).
37
41
  VENV_MARKER_FILES = {"pyvenv.cfg"}
38
42
 
43
+ # Cache Directory Tagging Standard (https://bford.info/cachedir/). A directory
44
+ # containing a CACHEDIR.TAG with this signature is a cache (uv, pip, mypy, ruff,
45
+ # hatch, ...) and should be skipped entirely.
46
+ CACHEDIR_TAG_FILE = "CACHEDIR.TAG"
47
+ CACHEDIR_TAG_SIGNATURE = "Signature: 8a477f597d28d172789f06886806bc55"
48
+
39
49
  LOGGER = logging.getLogger(__name__)
40
50
 
41
51
 
@@ -56,8 +66,9 @@ def find_source_files(
56
66
  LOGGER.debug("project root %s, ignore_paths %s", project_root, ignore_paths)
57
67
  found_files: set[Path] = set()
58
68
 
59
- # Build a single PathSpec with repo/global ignores and any future extras
60
- spec = collect_default_spec(project_root)
69
+ # Build the base spec with repo/global ignores. Nested .gitignore files are
70
+ # layered on top as the walk descends into the directories that contain them.
71
+ base_spec = build_gitignore_spec(project_root)
61
72
 
62
73
  # Resolve user-provided ignore paths to absolute form for reliable comparison
63
74
  explicit_ignore_set = {(project_root / p).resolve() for p in (ignore_paths or [])}
@@ -66,13 +77,27 @@ def find_source_files(
66
77
  current_dir=project_root,
67
78
  project_root=project_root,
68
79
  found_files=found_files,
69
- spec=spec,
80
+ spec=base_spec,
70
81
  explicit_ignore_set=explicit_ignore_set,
71
82
  )
72
83
 
73
84
  return sorted(found_files)
74
85
 
75
86
 
87
+ def _is_cache_dir(directory: Path) -> bool:
88
+ """Return True if `directory` contains a valid CACHEDIR.TAG marker file."""
89
+ tag = directory / CACHEDIR_TAG_FILE
90
+ try:
91
+ if not tag.is_file():
92
+ return False
93
+ with tag.open("r", encoding="utf-8", errors="replace") as fh:
94
+ first_line = fh.readline().rstrip("\n").rstrip("\r")
95
+ return first_line == CACHEDIR_TAG_SIGNATURE
96
+ except OSError as exc:
97
+ LOGGER.warning("Skipping unreadable cache tag %s: %s", tag, exc)
98
+ return False
99
+
100
+
76
101
  def _walk_and_discover(
77
102
  *,
78
103
  current_dir: Path,
@@ -82,6 +107,14 @@ def _walk_and_discover(
82
107
  explicit_ignore_set: set[Path],
83
108
  ) -> None:
84
109
  """Recursively walk directories to find source files."""
110
+ # Layer any nested .gitignore in this directory onto the active spec, matching
111
+ # git's cascading semantics (e.g. uv drops `./.uv/.gitignore` containing `*`).
112
+ nested = nested_gitignore_patterns(current_dir, project_root)
113
+ if nested:
114
+ spec = PathSpec(
115
+ list(spec.patterns) + list(PathSpec.from_lines(GitWildMatchPattern, nested).patterns)
116
+ )
117
+
85
118
  try:
86
119
  items = list(current_dir.iterdir())
87
120
  except OSError as exc:
@@ -110,6 +143,11 @@ def _walk_and_discover(
110
143
  LOGGER.debug("Skipping venv root: %s", item)
111
144
  continue
112
145
 
146
+ # Skip cache directories tagged per the Cache Directory Tagging Standard.
147
+ if _is_cache_dir(item):
148
+ LOGGER.debug("Skipping tagged cache dir: %s", item)
149
+ continue
150
+
113
151
  # If top-level package dir has __init__.py, include it
114
152
  init_file = item / "__init__.py"
115
153
  try: