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.
- devstuff-2.4.0/.github/workflows/bump.yml +180 -0
- devstuff-2.4.0/.github/workflows/publish.yml +96 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/CHANGELOG.md +6 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/PKG-INFO +1 -1
- {devstuff-2.3.0 → devstuff-2.4.0}/pyproject.toml +1 -1
- {devstuff-2.3.0 → devstuff-2.4.0}/uv.lock +1 -1
- devstuff-2.3.0/.github/workflows/bump.yml +0 -95
- devstuff-2.3.0/.github/workflows/publish.yml +0 -44
- {devstuff-2.3.0 → devstuff-2.4.0}/.claude/skills/run-stuff/SKILL.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/.claude/skills/run-stuff/smoke.sh +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/.github/workflows/ci.yml +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/.github/workflows/test-installs.yml +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/.gitignore +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/CLAUDE.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/README.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/dev/Dockerfile +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/dev/Dockerfile.ci +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/dev/Makefile +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/dev/docker-compose.yml +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/devstuff +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/README.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/agent/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/agent/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/agent/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/ansible-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/ansible-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/ansible-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/bat-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/bat-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/bat-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/commitizen-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/commitizen-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/commitizen-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/docker-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/docker-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/docker-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/lazygit-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/lazygit-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/lazygit-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/precommit-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/precommit-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/precommit-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/starship-config/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/starship-config/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/starship-config/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/uv-backbone/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/uv-backbone/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/uv-backbone/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/verbose-mode/development-plan.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/verbose-mode/specifications.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/docs/specs/verbose-mode/stack-decisions.md +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/install.sh +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/__main__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/approval.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/bridges.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/catalog.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/completion.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/config.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/loop.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/ollama.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/preflight.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/primitives.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/registry.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/sandbox.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/session.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/transcript.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent_tools.schema.json +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/agent_tools.yaml +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/base.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/catalog.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/cli.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/add_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/agent_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/catalog_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/configure_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/delete_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/docs_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/doctor_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/functions_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/help_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/install_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/list_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/remove_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/run_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/skills_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/commands/update_cmd.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/detect.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/validate.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/ansible/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/detect.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/preview.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/bat/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/detect.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/validate.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/commitizen/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/detect.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/validate.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/docker/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/detect.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/validate.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/lazygit/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/detect.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/validate.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/precommit/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/fonts.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/model.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/preview.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/render.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/configure/starship/wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/doctor.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/function_runner.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions.schema.json +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions.yaml +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions_catalog.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/functions_registry.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/generic.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/registry.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/tools.yaml +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/ui.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/src/dev_setup/verbose.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/__init__.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/conftest.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_agent_smoke.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_agent_tui.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_commitizen_config.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/integration/test_tools.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_completion.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_loop.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_sandbox.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_tools.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_agent_wizard.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_catalog.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_ansible.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_bat.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_commitizen.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_docker.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_lazygit.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_precommit.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_configure_starship.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_doctor.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_functions.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_generic.py +0 -0
- {devstuff-2.3.0 → devstuff-2.4.0}/tests/test_registry.py +0 -0
- {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,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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|