devstuff 2.3.0__tar.gz → 2.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. devstuff-2.4.0/.github/workflows/bump.yml +180 -0
  2. devstuff-2.4.0/.github/workflows/publish.yml +96 -0
  3. {devstuff-2.3.0 → devstuff-2.4.0}/CHANGELOG.md +6 -0
  4. {devstuff-2.3.0 → devstuff-2.4.0}/PKG-INFO +1 -1
  5. {devstuff-2.3.0 → devstuff-2.4.0}/pyproject.toml +1 -1
  6. {devstuff-2.3.0 → devstuff-2.4.0}/uv.lock +1 -1
  7. devstuff-2.3.0/.github/workflows/bump.yml +0 -95
  8. devstuff-2.3.0/.github/workflows/publish.yml +0 -44
  9. {devstuff-2.3.0 → devstuff-2.4.0}/.claude/skills/run-stuff/SKILL.md +0 -0
  10. {devstuff-2.3.0 → devstuff-2.4.0}/.claude/skills/run-stuff/smoke.sh +0 -0
  11. {devstuff-2.3.0 → devstuff-2.4.0}/.github/workflows/ci.yml +0 -0
  12. {devstuff-2.3.0 → devstuff-2.4.0}/.github/workflows/test-installs.yml +0 -0
  13. {devstuff-2.3.0 → devstuff-2.4.0}/.gitignore +0 -0
  14. {devstuff-2.3.0 → devstuff-2.4.0}/CLAUDE.md +0 -0
  15. {devstuff-2.3.0 → devstuff-2.4.0}/README.md +0 -0
  16. {devstuff-2.3.0 → devstuff-2.4.0}/dev/Dockerfile +0 -0
  17. {devstuff-2.3.0 → devstuff-2.4.0}/dev/Dockerfile.ci +0 -0
  18. {devstuff-2.3.0 → devstuff-2.4.0}/dev/Makefile +0 -0
  19. {devstuff-2.3.0 → devstuff-2.4.0}/dev/docker-compose.yml +0 -0
  20. {devstuff-2.3.0 → devstuff-2.4.0}/devstuff +0 -0
  21. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/README.md +0 -0
  22. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/agent/development-plan.md +0 -0
  23. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/agent/specifications.md +0 -0
  24. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/agent/stack-decisions.md +0 -0
  25. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/ansible-config/development-plan.md +0 -0
  26. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/ansible-config/specifications.md +0 -0
  27. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/ansible-config/stack-decisions.md +0 -0
  28. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/bat-config/development-plan.md +0 -0
  29. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/bat-config/specifications.md +0 -0
  30. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/bat-config/stack-decisions.md +0 -0
  31. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/commitizen-config/development-plan.md +0 -0
  32. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/commitizen-config/specifications.md +0 -0
  33. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/commitizen-config/stack-decisions.md +0 -0
  34. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/docker-config/development-plan.md +0 -0
  35. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/docker-config/specifications.md +0 -0
  36. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/docker-config/stack-decisions.md +0 -0
  37. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/lazygit-config/development-plan.md +0 -0
  38. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/lazygit-config/specifications.md +0 -0
  39. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/lazygit-config/stack-decisions.md +0 -0
  40. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/precommit-config/development-plan.md +0 -0
  41. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/precommit-config/specifications.md +0 -0
  42. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/precommit-config/stack-decisions.md +0 -0
  43. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/starship-config/development-plan.md +0 -0
  44. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/starship-config/specifications.md +0 -0
  45. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/starship-config/stack-decisions.md +0 -0
  46. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/uv-backbone/development-plan.md +0 -0
  47. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/uv-backbone/specifications.md +0 -0
  48. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/uv-backbone/stack-decisions.md +0 -0
  49. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/verbose-mode/development-plan.md +0 -0
  50. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/verbose-mode/specifications.md +0 -0
  51. {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/verbose-mode/stack-decisions.md +0 -0
  52. {devstuff-2.3.0 → devstuff-2.4.0}/install.sh +0 -0
  53. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/__init__.py +0 -0
  54. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/__main__.py +0 -0
  55. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/__init__.py +0 -0
  56. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/approval.py +0 -0
  57. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/bridges.py +0 -0
  58. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/catalog.py +0 -0
  59. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/completion.py +0 -0
  60. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/config.py +0 -0
  61. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/loop.py +0 -0
  62. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/ollama.py +0 -0
  63. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/preflight.py +0 -0
  64. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/primitives.py +0 -0
  65. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/registry.py +0 -0
  66. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/sandbox.py +0 -0
  67. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/session.py +0 -0
  68. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/transcript.py +0 -0
  69. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/wizard.py +0 -0
  70. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent_tools.schema.json +0 -0
  71. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent_tools.yaml +0 -0
  72. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/base.py +0 -0
  73. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/catalog.py +0 -0
  74. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/cli.py +0 -0
  75. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/__init__.py +0 -0
  76. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/add_cmd.py +0 -0
  77. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/agent_cmd.py +0 -0
  78. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/catalog_cmd.py +0 -0
  79. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/configure_cmd.py +0 -0
  80. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/delete_cmd.py +0 -0
  81. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/docs_cmd.py +0 -0
  82. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/doctor_cmd.py +0 -0
  83. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/functions_cmd.py +0 -0
  84. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/help_cmd.py +0 -0
  85. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/install_cmd.py +0 -0
  86. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/list_cmd.py +0 -0
  87. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/remove_cmd.py +0 -0
  88. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/run_cmd.py +0 -0
  89. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/skills_cmd.py +0 -0
  90. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/update_cmd.py +0 -0
  91. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/__init__.py +0 -0
  92. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/__init__.py +0 -0
  93. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/detect.py +0 -0
  94. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/model.py +0 -0
  95. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/render.py +0 -0
  96. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/validate.py +0 -0
  97. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/wizard.py +0 -0
  98. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/__init__.py +0 -0
  99. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/detect.py +0 -0
  100. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/model.py +0 -0
  101. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/preview.py +0 -0
  102. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/render.py +0 -0
  103. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/wizard.py +0 -0
  104. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/__init__.py +0 -0
  105. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/detect.py +0 -0
  106. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/model.py +0 -0
  107. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/render.py +0 -0
  108. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/validate.py +0 -0
  109. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/wizard.py +0 -0
  110. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/__init__.py +0 -0
  111. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/detect.py +0 -0
  112. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/model.py +0 -0
  113. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/render.py +0 -0
  114. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/validate.py +0 -0
  115. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/wizard.py +0 -0
  116. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/__init__.py +0 -0
  117. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/detect.py +0 -0
  118. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/model.py +0 -0
  119. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/render.py +0 -0
  120. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/validate.py +0 -0
  121. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/wizard.py +0 -0
  122. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/__init__.py +0 -0
  123. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/detect.py +0 -0
  124. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/model.py +0 -0
  125. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/render.py +0 -0
  126. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/validate.py +0 -0
  127. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/wizard.py +0 -0
  128. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/__init__.py +0 -0
  129. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/fonts.py +0 -0
  130. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/model.py +0 -0
  131. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/preview.py +0 -0
  132. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/render.py +0 -0
  133. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/wizard.py +0 -0
  134. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/doctor.py +0 -0
  135. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/function_runner.py +0 -0
  136. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions.schema.json +0 -0
  137. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions.yaml +0 -0
  138. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions_catalog.py +0 -0
  139. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions_registry.py +0 -0
  140. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/generic.py +0 -0
  141. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/registry.py +0 -0
  142. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/tools.yaml +0 -0
  143. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/ui.py +0 -0
  144. {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/verbose.py +0 -0
  145. {devstuff-2.3.0 → devstuff-2.4.0}/tests/__init__.py +0 -0
  146. {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/__init__.py +0 -0
  147. {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/conftest.py +0 -0
  148. {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_agent_smoke.py +0 -0
  149. {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_agent_tui.py +0 -0
  150. {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_commitizen_config.py +0 -0
  151. {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_tools.py +0 -0
  152. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent.py +0 -0
  153. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_completion.py +0 -0
  154. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_loop.py +0 -0
  155. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_sandbox.py +0 -0
  156. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_tools.py +0 -0
  157. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_wizard.py +0 -0
  158. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_catalog.py +0 -0
  159. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_ansible.py +0 -0
  160. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_bat.py +0 -0
  161. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_commitizen.py +0 -0
  162. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_docker.py +0 -0
  163. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_lazygit.py +0 -0
  164. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_precommit.py +0 -0
  165. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_starship.py +0 -0
  166. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_doctor.py +0 -0
  167. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_functions.py +0 -0
  168. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_generic.py +0 -0
  169. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_registry.py +0 -0
  170. {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_verbose.py +0 -0
@@ -0,0 +1,180 @@
1
+ name: Bump version
2
+
3
+ on:
4
+ pull_request:
5
+ types: [closed]
6
+ branches:
7
+ - master
8
+
9
+ # Serialize bump runs against master regardless of which PR triggered them.
10
+ concurrency:
11
+ group: bump-master
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ bump:
16
+ name: cz bump
17
+ runs-on: ubuntu-latest
18
+ # Only run for PRs that were actually merged, not closed unmerged.
19
+ # A raw `git push` — which is how this job publishes its own commit —
20
+ # never fires pull_request events, so there's no infinite-loop risk
21
+ # from this job triggering itself (unlike the old push-to-master
22
+ # trigger, which needed an explicit "bump:" message guard for that).
23
+ if: github.event.pull_request.merged == true
24
+ permissions:
25
+ contents: write
26
+ env:
27
+ # secrets can't be referenced directly in step `if:` conditions, so
28
+ # surface presence via an env var instead.
29
+ HAS_GH_PAT: ${{ secrets.GH_PAT != '' }}
30
+
31
+ steps:
32
+ - name: Require GH_PAT
33
+ # GH_PAT (a Personal Access Token with repo write scope) is required
34
+ # so that the tag push re-triggers publish.yml — GitHub Actions does
35
+ # not re-process events from pushes made with GITHUB_TOKEN. Fail loudly
36
+ # instead of silently pushing a tag that will never publish.
37
+ if: env.HAS_GH_PAT != 'true'
38
+ run: |
39
+ echo "::error::GH_PAT secret is not set. It is required so the tag push triggers publish.yml (GITHUB_TOKEN pushes are not re-processed by Actions)."
40
+ exit 1
41
+
42
+ - uses: actions/checkout@v4
43
+ with:
44
+ ref: master # the merge already happened server-side by the time this runs
45
+ fetch-depth: 0 # commitizen needs full history to determine bump type
46
+ token: ${{ secrets.GH_PAT }}
47
+
48
+ - name: Install uv
49
+ uses: astral-sh/setup-uv@v5
50
+
51
+ - name: Configure git
52
+ run: |
53
+ git config user.name "github-actions[bot]"
54
+ git config user.email "github-actions[bot]@users.noreply.github.com"
55
+
56
+ - name: Run cz bump
57
+ id: bump
58
+ run: |
59
+ if uv run --group dev cz bump --yes; then
60
+ echo "bumped=true" >> "$GITHUB_OUTPUT"
61
+ else
62
+ EXIT=$?
63
+ if [ "$EXIT" -eq 21 ]; then
64
+ echo "No conventional commits found since last tag — nothing to bump."
65
+ echo "bumped=false" >> "$GITHUB_OUTPUT"
66
+ else
67
+ exit "$EXIT"
68
+ fi
69
+ fi
70
+
71
+ # Build-only PRs: `cz bump` exits 21 because `build` is deliberately not
72
+ # in bump_map (we don't want it consuming a PATCH slot from the main
73
+ # release line). Instead, ship a PEP 440 post-release — vX.Y.Z.postN —
74
+ # which is publishable to PyPI, sorts after vX.Y.Z but before the next
75
+ # vX.(Y+1).0, and is still recognised by `cz bump` as the current
76
+ # version tag (tag_format is `v$version` and `X.Y.Z.postN` is valid
77
+ # PEP 440), so the next regular bump stays coherent. A PR mixing `build`
78
+ # with feat/fix/etc. is handled by the `cz bump` path above — the build
79
+ # commits just fold into that release's changelog — so this step only
80
+ # fires when there was nothing else to release.
81
+ - name: Build-patch post-release
82
+ id: buildpatch
83
+ if: steps.bump.outputs.bumped == 'false'
84
+ run: |
85
+ set -euo pipefail
86
+
87
+ last_tag="$(git describe --tags --abbrev=0)"
88
+ commits="$(git log "${last_tag}..HEAD" --pretty=format:%s)"
89
+
90
+ # No build commits -> genuinely nothing to release.
91
+ if ! printf '%s\n' "$commits" | grep -qE '^build(\(.+\))?!?:'; then
92
+ echo "No build commits since ${last_tag} — nothing to release."
93
+ echo "buildpatch=false" >> "$GITHUB_OUTPUT"
94
+ exit 0
95
+ fi
96
+
97
+ # Safety net: if a higher-precedence release type slipped through,
98
+ # bail rather than ship a competing post-release. (cz bump exiting 21
99
+ # already implies this, but guard against future bump_map changes.)
100
+ if printf '%s\n' "$commits" | grep -qE '^(feat|fix|refactor|perf|deps)(\(.+\))?!?:'; then
101
+ echo "Higher-precedence release commits present alongside build — deferring to the main release track."
102
+ echo "buildpatch=false" >> "$GITHUB_OUTPUT"
103
+ exit 0
104
+ fi
105
+
106
+ # Current version -> release segment (strip any existing post/dev).
107
+ current_version="$(grep -E '^version = ' pyproject.toml | head -1 | sed -E 's/^version = "(.*)"$/\1/')"
108
+ base="$(printf '%s' "$current_version" | sed -E 's/(\.post[0-9]+|\.dev[0-9]+).*$//')"
109
+
110
+ # Next post number for this base (vX.Y.Z.post1, .post2, ...).
111
+ prev_n="$(git tag --list "v${base}.post*" | sed -E 's/^v.*\.post([0-9]+)$/\1/' | sort -n | tail -1 || true)"
112
+ next_n=$(( ${prev_n:-0} + 1 ))
113
+ new_version="${base}.post${next_n}"
114
+ new_tag="v${new_version}"
115
+
116
+ echo "Bumping build patch: ${current_version} -> ${new_version} (${new_tag})"
117
+
118
+ # Bump version in pyproject.toml.
119
+ sed -i -E "s/^version = \".*\"/version = \"${new_version}\"/" pyproject.toml
120
+
121
+ # Prepend a Build section to the changelog (matches cz's heading style).
122
+ date="$(date +%Y-%m-%d)"
123
+ build_entries="$(
124
+ printf '%s\n' "$commits" | grep -E '^build(\(.+\))?!?:' | while IFS= read -r subject; do
125
+ msg="$(printf '%s' "$subject" | sed -E 's/^build(\([^)]+\))?(!)?: //')"
126
+ scope="$(printf '%s' "$subject" | sed -nE 's/^build\(([^)]+)\).*/\1/p')"
127
+ if [ -n "$scope" ]; then
128
+ printf -- '- **%s**: %s\n' "$scope" "$msg"
129
+ else
130
+ printf -- '- %s\n' "$msg"
131
+ fi
132
+ done
133
+ )"
134
+ {
135
+ printf '## %s (%s)\n\n### Build\n\n%s\n' "$new_tag" "$date" "$build_entries"
136
+ cat CHANGELOG.md
137
+ } > CHANGELOG.md.new
138
+ mv CHANGELOG.md.new CHANGELOG.md
139
+
140
+ # Sync the lockfile against the new version before committing so the
141
+ # tag points at a single self-contained commit (no amend/retag needed).
142
+ uv lock
143
+ git add pyproject.toml CHANGELOG.md uv.lock
144
+ git commit -m "bump: version ${current_version} → ${new_version} (build patch)"
145
+ git tag -a "$new_tag" -m "$new_tag" HEAD
146
+
147
+ git push --follow-tags
148
+ if ! git ls-remote --exit-code --tags origin "refs/tags/${new_tag}" >/dev/null; then
149
+ echo "::error::${new_tag} was not found on origin after push --follow-tags (publish.yml build_patch job will not trigger)."
150
+ exit 1
151
+ fi
152
+
153
+ echo "buildpatch=true" >> "$GITHUB_OUTPUT"
154
+ echo "new_tag=${new_tag}" >> "$GITHUB_OUTPUT"
155
+
156
+ - name: Sync lockfile
157
+ if: steps.bump.outputs.bumped == 'true'
158
+ run: |
159
+ uv lock
160
+ git add uv.lock
161
+ if ! git diff --staged --quiet; then
162
+ # cz bump already tagged the pre-lock commit; amending moves HEAD
163
+ # to a new SHA, so the tag must be recreated to point at it.
164
+ # Must stay annotated (-a) — `git push --follow-tags` only
165
+ # pushes annotated tags, so a lightweight retag here would
166
+ # silently never reach the remote.
167
+ tag="$(git describe --tags --exact-match HEAD)"
168
+ git commit --amend --no-edit
169
+ git tag -f -a "$tag" -m "$tag" HEAD
170
+ fi
171
+
172
+ - name: Push commit and tag
173
+ if: steps.bump.outputs.bumped == 'true'
174
+ run: |
175
+ git push --follow-tags
176
+ tag="$(git describe --tags --exact-match HEAD)"
177
+ if ! git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null; then
178
+ echo "::error::${tag} was not found on origin after push --follow-tags (publish.yml will not trigger)."
179
+ exit 1
180
+ fi
@@ -0,0 +1,96 @@
1
+ name: Publish to PyPI
2
+
3
+ # A `v*` tag push triggers this workflow. Two release tracks share that
4
+ # prefix, so they're routed by job-level `if` conditions on the tag name
5
+ # rather than by disjoint trigger globs (GitHub Actions globs can't negate,
6
+ # so `v*` would match both and a glob-only split would double-publish):
7
+ #
8
+ # - Regular releases vX.Y.Z -> build / publish
9
+ # - Build-patch post vX.Y.Z.postN -> build_patch / publish_build_patch
10
+ #
11
+ # The post-release track exists so a PR that contains only `build`-type
12
+ # commits still ships a publishable, PEP 440-distinct version. bump.yml
13
+ # decides which tag to create; this file only reacts to whichever one lands.
14
+ on:
15
+ push:
16
+ tags:
17
+ - "v*"
18
+
19
+ jobs:
20
+ build:
21
+ name: Build distribution
22
+ if: ${{ !contains(github.ref, '.post') }}
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@v5
29
+
30
+ - name: Build wheel and source distribution
31
+ run: uv build
32
+
33
+ - name: Upload dist artifacts
34
+ uses: actions/upload-artifact@v4
35
+ with:
36
+ name: dist
37
+ path: dist/
38
+
39
+ publish:
40
+ name: Publish to PyPI
41
+ if: ${{ !contains(github.ref, '.post') }}
42
+ needs: build
43
+ runs-on: ubuntu-latest
44
+ environment:
45
+ name: pypi
46
+ url: https://pypi.org/p/devstuff
47
+ permissions:
48
+ id-token: write # required for OIDC trusted publishing
49
+ steps:
50
+ - name: Download dist artifacts
51
+ uses: actions/download-artifact@v4
52
+ with:
53
+ name: dist
54
+ path: dist/
55
+
56
+ - name: Publish to PyPI
57
+ uses: pypa/gh-action-pypi-publish@release/v1
58
+
59
+ build_patch:
60
+ name: Build build-patch distribution
61
+ if: ${{ contains(github.ref, '.post') }}
62
+ runs-on: ubuntu-latest
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+
66
+ - name: Install uv
67
+ uses: astral-sh/setup-uv@v5
68
+
69
+ - name: Build wheel and source distribution
70
+ run: uv build
71
+
72
+ - name: Upload dist artifacts
73
+ uses: actions/upload-artifact@v4
74
+ with:
75
+ name: dist-build-patch
76
+ path: dist/
77
+
78
+ publish_build_patch:
79
+ name: Publish build-patch to PyPI
80
+ if: ${{ contains(github.ref, '.post') }}
81
+ needs: build_patch
82
+ runs-on: ubuntu-latest
83
+ environment:
84
+ name: pypi
85
+ url: https://pypi.org/p/devstuff
86
+ permissions:
87
+ id-token: write # required for OIDC trusted publishing
88
+ steps:
89
+ - name: Download dist artifacts
90
+ uses: actions/download-artifact@v4
91
+ with:
92
+ name: dist-build-patch
93
+ path: dist/
94
+
95
+ - name: Publish to PyPI
96
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,3 +1,9 @@
1
+ ## v2.4.0 (2026-08-20)
2
+
3
+ ### Features
4
+
5
+ - **ci**: publish build-patch updates as PEP 440 post-releases
6
+
1
7
  ## v2.3.0 (2026-08-13)
2
8
 
3
9
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: devstuff
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: Development environment setup CLI for Linux
5
5
  Project-URL: Repository, https://github.com/thesawdawg/dev-setup-py
6
6
  Author-email: Sawyer <sawyerksu@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devstuff"
3
- version = "2.3.0"
3
+ version = "2.4.0"
4
4
  description = "Development environment setup CLI for Linux"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -167,7 +167,7 @@ wheels = [
167
167
 
168
168
  [[package]]
169
169
  name = "devstuff"
170
- version = "2.3.0"
170
+ version = "2.4.0"
171
171
  source = { editable = "." }
172
172
  dependencies = [
173
173
  { name = "click" },
@@ -1,95 +0,0 @@
1
- name: Bump version
2
-
3
- on:
4
- pull_request:
5
- types: [closed]
6
- branches:
7
- - master
8
-
9
- # Serialize bump runs against master regardless of which PR triggered them.
10
- concurrency:
11
- group: bump-master
12
- cancel-in-progress: false
13
-
14
- jobs:
15
- bump:
16
- name: cz bump
17
- runs-on: ubuntu-latest
18
- # Only run for PRs that were actually merged, not closed unmerged.
19
- # A raw `git push` — which is how this job publishes its own commit —
20
- # never fires pull_request events, so there's no infinite-loop risk
21
- # from this job triggering itself (unlike the old push-to-master
22
- # trigger, which needed an explicit "bump:" message guard for that).
23
- if: github.event.pull_request.merged == true
24
- permissions:
25
- contents: write
26
- env:
27
- # secrets can't be referenced directly in step `if:` conditions, so
28
- # surface presence via an env var instead.
29
- HAS_GH_PAT: ${{ secrets.GH_PAT != '' }}
30
-
31
- steps:
32
- - name: Require GH_PAT
33
- # GH_PAT (a Personal Access Token with repo write scope) is required
34
- # so that the tag push re-triggers publish.yml — GitHub Actions does
35
- # not re-process events from pushes made with GITHUB_TOKEN. Fail loudly
36
- # instead of silently pushing a tag that will never publish.
37
- if: env.HAS_GH_PAT != 'true'
38
- run: |
39
- echo "::error::GH_PAT secret is not set. It is required so the tag push triggers publish.yml (GITHUB_TOKEN pushes are not re-processed by Actions)."
40
- exit 1
41
-
42
- - uses: actions/checkout@v4
43
- with:
44
- ref: master # the merge already happened server-side by the time this runs
45
- fetch-depth: 0 # commitizen needs full history to determine bump type
46
- token: ${{ secrets.GH_PAT }}
47
-
48
- - name: Install uv
49
- uses: astral-sh/setup-uv@v5
50
-
51
- - name: Configure git
52
- run: |
53
- git config user.name "github-actions[bot]"
54
- git config user.email "github-actions[bot]@users.noreply.github.com"
55
-
56
- - name: Run cz bump
57
- id: bump
58
- run: |
59
- if uv run --group dev cz bump --yes; then
60
- echo "bumped=true" >> "$GITHUB_OUTPUT"
61
- else
62
- EXIT=$?
63
- if [ "$EXIT" -eq 21 ]; then
64
- echo "No conventional commits found since last tag — nothing to bump."
65
- echo "bumped=false" >> "$GITHUB_OUTPUT"
66
- else
67
- exit "$EXIT"
68
- fi
69
- fi
70
-
71
- - name: Sync lockfile
72
- if: steps.bump.outputs.bumped == 'true'
73
- run: |
74
- uv lock
75
- git add uv.lock
76
- if ! git diff --staged --quiet; then
77
- # cz bump already tagged the pre-lock commit; amending moves HEAD
78
- # to a new SHA, so the tag must be recreated to point at it.
79
- # Must stay annotated (-a) — `git push --follow-tags` only
80
- # pushes annotated tags, so a lightweight retag here would
81
- # silently never reach the remote.
82
- tag="$(git describe --tags --exact-match HEAD)"
83
- git commit --amend --no-edit
84
- git tag -f -a "$tag" -m "$tag" HEAD
85
- fi
86
-
87
- - name: Push commit and tag
88
- if: steps.bump.outputs.bumped == 'true'
89
- run: |
90
- git push --follow-tags
91
- tag="$(git describe --tags --exact-match HEAD)"
92
- if ! git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null; then
93
- echo "::error::${tag} was not found on origin after push --follow-tags (publish.yml will not trigger)."
94
- exit 1
95
- fi
@@ -1,44 +0,0 @@
1
- name: Publish to PyPI
2
-
3
- on:
4
- push:
5
- tags:
6
- - "v*"
7
-
8
- jobs:
9
- build:
10
- name: Build distribution
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/checkout@v4
14
-
15
- - name: Install uv
16
- uses: astral-sh/setup-uv@v5
17
-
18
- - name: Build wheel and source distribution
19
- run: uv build
20
-
21
- - name: Upload dist artifacts
22
- uses: actions/upload-artifact@v4
23
- with:
24
- name: dist
25
- path: dist/
26
-
27
- publish:
28
- name: Publish to PyPI
29
- needs: build
30
- runs-on: ubuntu-latest
31
- environment:
32
- name: pypi
33
- url: https://pypi.org/p/devstuff
34
- permissions:
35
- id-token: write # required for OIDC trusted publishing
36
- steps:
37
- - name: Download dist artifacts
38
- uses: actions/download-artifact@v4
39
- with:
40
- name: dist
41
- path: dist/
42
-
43
- - name: Publish to PyPI
44
- uses: pypa/gh-action-pypi-publish@release/v1
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes