lib-cli-exit-tools 1.0.1__tar.gz → 1.0.3__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.
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/CHANGELOG.md +10 -2
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/CONTRIBUTING.md +1 -1
- lib_cli_exit_tools-1.0.3/Makefile +98 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/PKG-INFO +68 -3
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/README.md +66 -2
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/codecov.yml +1 -1
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/packaging/README.md +2 -2
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/packaging/brew/Formula/lib-cli-exit-tools.rb +3 -3
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/packaging/conda/recipe/meta.yaml +2 -2
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/packaging/nix/flake.nix +2 -2
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/pyproject.toml +5 -1
- lib_cli_exit_tools-1.0.3/qlty.toml +2 -0
- lib_cli_exit_tools-1.0.3/scripts/__init__.py +3 -0
- lib_cli_exit_tools-1.0.3/scripts/_utils.py +125 -0
- lib_cli_exit_tools-1.0.3/scripts/build.py +37 -0
- lib_cli_exit_tools-1.0.3/scripts/bump.py +27 -0
- lib_cli_exit_tools-1.0.3/scripts/bump_major.py +17 -0
- lib_cli_exit_tools-1.0.3/scripts/bump_minor.py +17 -0
- lib_cli_exit_tools-1.0.3/scripts/bump_patch.py +17 -0
- {lib_cli_exit_tools-1.0.1/tools → lib_cli_exit_tools-1.0.3/scripts}/bump_version.py +1 -1
- lib_cli_exit_tools-1.0.3/scripts/clean.py +47 -0
- lib_cli_exit_tools-1.0.3/scripts/dev.py +18 -0
- lib_cli_exit_tools-1.0.3/scripts/install.py +19 -0
- lib_cli_exit_tools-1.0.3/scripts/menu.py +643 -0
- lib_cli_exit_tools-1.0.3/scripts/push.py +33 -0
- lib_cli_exit_tools-1.0.3/scripts/release.py +108 -0
- lib_cli_exit_tools-1.0.3/scripts/run_cli.py +17 -0
- lib_cli_exit_tools-1.0.3/scripts/test.py +96 -0
- lib_cli_exit_tools-1.0.3/scripts/version_current.py +22 -0
- lib_cli_exit_tools-1.0.1/Makefile +0 -253
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/.env.example +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/.github/workflows/ci.yml +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/.github/workflows/codeql.yml +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/.github/workflows/release.yml +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/.gitignore +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/AGENTS.md +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/LICENSE +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/notebooks/Quickstart.ipynb +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/src/lib_cli_exit_tools/__init__.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/src/lib_cli_exit_tools/__init__conf__.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/src/lib_cli_exit_tools/__main__.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/src/lib_cli_exit_tools/cli.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/src/lib_cli_exit_tools/lib_cli_exit_tools.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/src/lib_cli_exit_tools/py.typed +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/tests/test_cli.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/tests/test_cli_extra.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/tests/test_exit_tools.py +0 -0
- {lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/tests/test_lib_extra.py +0 -0
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.3] - 2025-09-16
|
|
4
|
+
|
|
5
|
+
- added make menu
|
|
6
|
+
|
|
7
|
+
## [1.0.2] - 2025-09-15
|
|
8
|
+
|
|
9
|
+
- minor internals
|
|
10
|
+
|
|
3
11
|
## [1.0.1] - 2025-09-15
|
|
4
12
|
|
|
5
|
-
-
|
|
13
|
+
- minor internals
|
|
6
14
|
|
|
7
15
|
## [1.0.0] - 2025-09-15
|
|
8
16
|
|
|
@@ -12,7 +20,7 @@
|
|
|
12
20
|
|
|
13
21
|
### Added
|
|
14
22
|
- CI job to execute Jupyter notebook on Python 3.10 (installs ipykernel, normalizes notebook IDs) and a tag‑time packaging‑consistency check.
|
|
15
|
-
- Packaging auto‑sync: `make test`, `make push`, and the bump script now align Conda/Brew/Nix files to `pyproject.toml` version, `requires-python`, and runtime deps. Homebrew resources are updated (URL + sha256) using PyPI metadata. A sync‑only mode is available via `python
|
|
23
|
+
- Packaging auto‑sync: `make test`, `make push`, and the bump script now align Conda/Brew/Nix files to `pyproject.toml` version, `requires-python`, and runtime deps. Homebrew resources are updated (URL + sha256) using PyPI metadata. A sync‑only mode is available via `python scripts/bump_version.py --sync-packaging`.
|
|
16
24
|
- Tests covering SystemExit variants, tolerant output printing, English CLI messages, and Windows mapping via monkeypatched `os.name`.
|
|
17
25
|
|
|
18
26
|
### Changed
|
|
@@ -28,7 +28,7 @@ Thanks for helping improve lib_cli_exit_tools! This guide keeps changes small, s
|
|
|
28
28
|
- Homebrew: ensures a `resource "<dep>"` for each dep; if pinned, uses that version; otherwise pins to the latest PyPI release, updating `url` + `sha256`.
|
|
29
29
|
- Nix: updates the package version, `pkgs.pythonXYZPackages`/`pkgs.pythonXYZ` to match the min Python, and `propagatedBuildInputs` to include all deps as `pypkgs.<name>`.
|
|
30
30
|
- Network note: Homebrew resource updates fetch PyPI metadata; if offline, those updates may be skipped.
|
|
31
|
-
- If packaging drifts, run: `python
|
|
31
|
+
- If packaging drifts, run: `python scripts/bump_version.py --sync-packaging`.
|
|
32
32
|
- CI enforces consistency on tags via a `packaging-consistency` job.
|
|
33
33
|
|
|
34
34
|
## 5) Tests & Style
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
SHELL := /bin/bash
|
|
2
|
+
.ONESHELL:
|
|
3
|
+
# Config
|
|
4
|
+
PY ?= python3
|
|
5
|
+
PIP ?= pip
|
|
6
|
+
PKG ?= lib_cli_exit_tools
|
|
7
|
+
GIT_REF ?= v0.1.0
|
|
8
|
+
REMOTE ?= origin
|
|
9
|
+
NIX_FLAKE ?= packaging/nix
|
|
10
|
+
HATCHLING_VERSION ?= 1.25.0
|
|
11
|
+
BREW_FORMULA ?= packaging/brew/Formula/lib-cli-exit-tools.rb
|
|
12
|
+
CONDA_RECIPE ?= packaging/conda/recipe
|
|
13
|
+
FAIL_UNDER ?= 80
|
|
14
|
+
# Coverage mode: on|auto|off (default: on locally)
|
|
15
|
+
# - on : always run coverage
|
|
16
|
+
# - auto : enable on CI or when CODECOV_TOKEN is set
|
|
17
|
+
# - off : never run coverage
|
|
18
|
+
COVERAGE ?= on
|
|
19
|
+
|
|
20
|
+
.PHONY: help install dev test run clean build push release version-current bump bump-patch bump-minor bump-major menu _bootstrap-dev
|
|
21
|
+
|
|
22
|
+
help: ## Show help
|
|
23
|
+
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
|
24
|
+
|
|
25
|
+
install: ## Install package editable
|
|
26
|
+
$(PY) scripts/install.py
|
|
27
|
+
|
|
28
|
+
dev: ## Install package with dev extras
|
|
29
|
+
$(PY) scripts/dev.py
|
|
30
|
+
|
|
31
|
+
_bootstrap-dev:
|
|
32
|
+
@if [ "$(SKIP_BOOTSTRAP)" = "1" ]; then \
|
|
33
|
+
echo "[bootstrap] Skipping dev dependency bootstrap (SKIP_BOOTSTRAP=1)"; \
|
|
34
|
+
else \
|
|
35
|
+
if ! command -v ruff >/dev/null 2>&1 || ! command -v pyright >/dev/null 2>&1 || ! python -c "import pytest" >/dev/null 2>&1; then \
|
|
36
|
+
echo "[bootstrap] Installing dev dependencies via '$(PIP) install -e .[dev]'"; \
|
|
37
|
+
$(PIP) install -e .[dev]; \
|
|
38
|
+
else \
|
|
39
|
+
echo "[bootstrap] Dev tools present"; \
|
|
40
|
+
fi; \
|
|
41
|
+
if ! python -c "import sqlite3" >/dev/null 2>&1; then \
|
|
42
|
+
echo "[bootstrap] sqlite3 stdlib module not available; installing pysqlite3-binary"; \
|
|
43
|
+
$(PIP) install pysqlite3-binary || true; \
|
|
44
|
+
fi; \
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
test: _bootstrap-dev ## Lint, type-check, run tests with coverage, upload to Codecov
|
|
48
|
+
$(PY) scripts/test.py --coverage=$(COVERAGE)
|
|
49
|
+
|
|
50
|
+
run: ## Run module CLI (requires dev install or src on PYTHONPATH)
|
|
51
|
+
$(PY) scripts/run_cli.py -- --help || true
|
|
52
|
+
|
|
53
|
+
version-current: ## Print current version from pyproject.toml
|
|
54
|
+
$(PY) scripts/version_current.py
|
|
55
|
+
|
|
56
|
+
bump: ## Bump version: VERSION=X.Y.Z or PART=major|minor|patch (default: patch); updates pyproject.toml and CHANGELOG.md
|
|
57
|
+
@set -e; \
|
|
58
|
+
if [ -n "$(VERSION)" ]; then \
|
|
59
|
+
$(PY) scripts/bump.py --version "$(VERSION)"; \
|
|
60
|
+
else \
|
|
61
|
+
$(PY) scripts/bump.py --part "$(PART)"; \
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
bump-patch: ## Bump patch version (X.Y.Z -> X.Y.(Z+1))
|
|
65
|
+
$(PY) scripts/bump_patch.py
|
|
66
|
+
|
|
67
|
+
bump-minor: ## Bump minor version (X.Y.Z -> X.(Y+1).0)
|
|
68
|
+
$(PY) scripts/bump_minor.py
|
|
69
|
+
|
|
70
|
+
bump-major: ## Bump major version ((X+1).0.0)
|
|
71
|
+
$(PY) scripts/bump_major.py
|
|
72
|
+
|
|
73
|
+
clean: ## Remove caches, build artifacts, and coverage
|
|
74
|
+
rm -rf \
|
|
75
|
+
.pytest_cache \
|
|
76
|
+
.ruff_cache \
|
|
77
|
+
.pyright \
|
|
78
|
+
.mypy_cache \
|
|
79
|
+
.tox .nox \
|
|
80
|
+
.eggs *.egg-info \
|
|
81
|
+
build dist \
|
|
82
|
+
htmlcov .coverage coverage.xml \
|
|
83
|
+
codecov.sh \
|
|
84
|
+
.cache \
|
|
85
|
+
result
|
|
86
|
+
|
|
87
|
+
push: ## Commit all changes once and push to GitHub (no CI monitoring)
|
|
88
|
+
$(PY) scripts/push.py --remote=$(REMOTE)
|
|
89
|
+
|
|
90
|
+
build: ## Build wheel/sdist and attempt conda, brew, and nix builds (auto-installs tools if missing)
|
|
91
|
+
$(PY) scripts/build.py
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
release: ## Create and push tag vX.Y.Z from pyproject, create GitHub release (if gh present), then sync packaging
|
|
95
|
+
$(PY) scripts/release.py --remote=$(REMOTE)
|
|
96
|
+
|
|
97
|
+
menu: ## Interactive TUI menu
|
|
98
|
+
$(PY) -u scripts/menu.py < /dev/tty > /dev/tty 2>&1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lib_cli_exit_tools
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: CLI exit handling helpers: clean signals, exit codes, and error printing
|
|
5
5
|
Project-URL: Homepage, https://github.com/bitranox/lib_cli_exit_tools
|
|
6
6
|
Project-URL: Repository, https://github.com/bitranox/lib_cli_exit_tools.git
|
|
@@ -24,6 +24,7 @@ Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
|
24
24
|
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
25
25
|
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
26
26
|
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
27
|
+
Requires-Dist: textual>=0.50; extra == 'dev'
|
|
27
28
|
Requires-Dist: twine>=5.1; extra == 'dev'
|
|
28
29
|
Description-Content-Type: text/markdown
|
|
29
30
|
|
|
@@ -135,13 +136,77 @@ pip install .
|
|
|
135
136
|
| `test` | Lint, type-check, run tests with coverage, upload to Codecov |
|
|
136
137
|
| `run` | Run module CLI (requires dev install or src on PYTHONPATH) |
|
|
137
138
|
| `version-current` | Print current version from pyproject.toml |
|
|
138
|
-
| `bump` | Bump version
|
|
139
|
+
| `bump` | Bump version (updates pyproject.toml and CHANGELOG.md) |
|
|
139
140
|
| `bump-patch` | Bump patch version (X.Y.Z -> X.Y.(Z+1)) |
|
|
140
141
|
| `bump-minor` | Bump minor version (X.Y.Z -> X.(Y+1).0) |
|
|
141
142
|
| `bump-major` | Bump major version ((X+1).0.0) |
|
|
142
143
|
| `clean` | Remove caches, build artifacts, and coverage |
|
|
143
144
|
| `push` | Commit all changes once and push to GitHub (no CI monitoring) |
|
|
144
145
|
| `build` | Build wheel/sdist and attempt conda, brew, and nix builds (auto-installs tools if missing) |
|
|
146
|
+
| `menu` | Interactive TUI to run targets and edit parameters (requires dev dep: textual) |
|
|
147
|
+
|
|
148
|
+
#### Target Parameters (env vars)
|
|
149
|
+
|
|
150
|
+
- Global
|
|
151
|
+
- `PY` (default: `python3`) — Python interpreter used to run scripts
|
|
152
|
+
- `PIP` (default: `pip`) — pip executable used by bootstrap/install
|
|
153
|
+
|
|
154
|
+
- `install`
|
|
155
|
+
- No specific parameters (respects `PY`, `PIP`).
|
|
156
|
+
|
|
157
|
+
- `dev`
|
|
158
|
+
- No specific parameters (respects `PY`, `PIP`).
|
|
159
|
+
|
|
160
|
+
- `test`
|
|
161
|
+
- `COVERAGE=on|auto|off` (default: `on`) — controls pytest coverage run and Codecov upload
|
|
162
|
+
- `SKIP_BOOTSTRAP=1` — skip auto-install of dev tools if missing
|
|
163
|
+
- `TEST_VERBOSE=1` — echo each command executed by the test harness
|
|
164
|
+
- Also respects `CODECOV_TOKEN` when needed for uploads
|
|
165
|
+
|
|
166
|
+
- `run`
|
|
167
|
+
- No parameters via `make` (always shows `--help`). For custom args: `python scripts/run_cli.py -- <args>`.
|
|
168
|
+
|
|
169
|
+
- `version-current`
|
|
170
|
+
- No parameters
|
|
171
|
+
|
|
172
|
+
- `bump`
|
|
173
|
+
- `VERSION=X.Y.Z` — explicit target version
|
|
174
|
+
- `PART=major|minor|patch` — semantic part to bump (default if `VERSION` not set: `patch`)
|
|
175
|
+
- Examples:
|
|
176
|
+
- `make bump VERSION=1.0.2`
|
|
177
|
+
- `make bump PART=minor`
|
|
178
|
+
|
|
179
|
+
- `bump-patch` / `bump-minor` / `bump-major`
|
|
180
|
+
- No parameters; shorthand for `make bump PART=...`
|
|
181
|
+
|
|
182
|
+
- `clean`
|
|
183
|
+
- No parameters
|
|
184
|
+
|
|
185
|
+
- `push`
|
|
186
|
+
- `REMOTE=<name>` (default: `origin`) — git remote to push to
|
|
187
|
+
|
|
188
|
+
- `build`
|
|
189
|
+
- No parameters via `make`. Advanced: use the script directly, e.g. `python scripts/build.py --no-conda --no-nix`.
|
|
190
|
+
|
|
191
|
+
- `release`
|
|
192
|
+
- `REMOTE=<name>` (default: `origin`) — git remote to push to
|
|
193
|
+
- Advanced (via script): `python scripts/release.py --retries 5 --retry-wait 3.0`
|
|
194
|
+
|
|
195
|
+
### Interactive Menu (Textual)
|
|
196
|
+
|
|
197
|
+
`make menu` launches a colorful terminal UI (powered by `textual`) to browse targets, edit parameters, and run them with live output.
|
|
198
|
+
|
|
199
|
+
Install dev extras if you haven’t:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
pip install -e .[dev]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Run the menu:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
make menu
|
|
209
|
+
```
|
|
145
210
|
|
|
146
211
|
#### Target Details
|
|
147
212
|
|
|
@@ -220,7 +285,7 @@ COVERAGE=on make test # force coverage and generate coverage.xml/codecov.
|
|
|
220
285
|
- Homebrew: updates the source URL tag to `vX.Y.Z` and sets `depends_on "python@X.Y"` to match `requires-python`.
|
|
221
286
|
- Nix: updates the package `version`, example `rev = "vX.Y.Z"`, and switches `pkgs.pythonXYZPackages` / `pkgs.pythonXYZ` to match the minimum Python version from `requires-python`.
|
|
222
287
|
|
|
223
|
-
- To run just the sync without bumping versions: `python
|
|
288
|
+
- To run just the sync without bumping versions: `python scripts/bump_version.py --sync-packaging`.
|
|
224
289
|
|
|
225
290
|
- On release tags (`v*.*.*`), CI validates that packaging files are consistent with `pyproject.toml` and will fail if they drift.
|
|
226
291
|
|
|
@@ -106,13 +106,77 @@ pip install .
|
|
|
106
106
|
| `test` | Lint, type-check, run tests with coverage, upload to Codecov |
|
|
107
107
|
| `run` | Run module CLI (requires dev install or src on PYTHONPATH) |
|
|
108
108
|
| `version-current` | Print current version from pyproject.toml |
|
|
109
|
-
| `bump` | Bump version
|
|
109
|
+
| `bump` | Bump version (updates pyproject.toml and CHANGELOG.md) |
|
|
110
110
|
| `bump-patch` | Bump patch version (X.Y.Z -> X.Y.(Z+1)) |
|
|
111
111
|
| `bump-minor` | Bump minor version (X.Y.Z -> X.(Y+1).0) |
|
|
112
112
|
| `bump-major` | Bump major version ((X+1).0.0) |
|
|
113
113
|
| `clean` | Remove caches, build artifacts, and coverage |
|
|
114
114
|
| `push` | Commit all changes once and push to GitHub (no CI monitoring) |
|
|
115
115
|
| `build` | Build wheel/sdist and attempt conda, brew, and nix builds (auto-installs tools if missing) |
|
|
116
|
+
| `menu` | Interactive TUI to run targets and edit parameters (requires dev dep: textual) |
|
|
117
|
+
|
|
118
|
+
#### Target Parameters (env vars)
|
|
119
|
+
|
|
120
|
+
- Global
|
|
121
|
+
- `PY` (default: `python3`) — Python interpreter used to run scripts
|
|
122
|
+
- `PIP` (default: `pip`) — pip executable used by bootstrap/install
|
|
123
|
+
|
|
124
|
+
- `install`
|
|
125
|
+
- No specific parameters (respects `PY`, `PIP`).
|
|
126
|
+
|
|
127
|
+
- `dev`
|
|
128
|
+
- No specific parameters (respects `PY`, `PIP`).
|
|
129
|
+
|
|
130
|
+
- `test`
|
|
131
|
+
- `COVERAGE=on|auto|off` (default: `on`) — controls pytest coverage run and Codecov upload
|
|
132
|
+
- `SKIP_BOOTSTRAP=1` — skip auto-install of dev tools if missing
|
|
133
|
+
- `TEST_VERBOSE=1` — echo each command executed by the test harness
|
|
134
|
+
- Also respects `CODECOV_TOKEN` when needed for uploads
|
|
135
|
+
|
|
136
|
+
- `run`
|
|
137
|
+
- No parameters via `make` (always shows `--help`). For custom args: `python scripts/run_cli.py -- <args>`.
|
|
138
|
+
|
|
139
|
+
- `version-current`
|
|
140
|
+
- No parameters
|
|
141
|
+
|
|
142
|
+
- `bump`
|
|
143
|
+
- `VERSION=X.Y.Z` — explicit target version
|
|
144
|
+
- `PART=major|minor|patch` — semantic part to bump (default if `VERSION` not set: `patch`)
|
|
145
|
+
- Examples:
|
|
146
|
+
- `make bump VERSION=1.0.2`
|
|
147
|
+
- `make bump PART=minor`
|
|
148
|
+
|
|
149
|
+
- `bump-patch` / `bump-minor` / `bump-major`
|
|
150
|
+
- No parameters; shorthand for `make bump PART=...`
|
|
151
|
+
|
|
152
|
+
- `clean`
|
|
153
|
+
- No parameters
|
|
154
|
+
|
|
155
|
+
- `push`
|
|
156
|
+
- `REMOTE=<name>` (default: `origin`) — git remote to push to
|
|
157
|
+
|
|
158
|
+
- `build`
|
|
159
|
+
- No parameters via `make`. Advanced: use the script directly, e.g. `python scripts/build.py --no-conda --no-nix`.
|
|
160
|
+
|
|
161
|
+
- `release`
|
|
162
|
+
- `REMOTE=<name>` (default: `origin`) — git remote to push to
|
|
163
|
+
- Advanced (via script): `python scripts/release.py --retries 5 --retry-wait 3.0`
|
|
164
|
+
|
|
165
|
+
### Interactive Menu (Textual)
|
|
166
|
+
|
|
167
|
+
`make menu` launches a colorful terminal UI (powered by `textual`) to browse targets, edit parameters, and run them with live output.
|
|
168
|
+
|
|
169
|
+
Install dev extras if you haven’t:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pip install -e .[dev]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Run the menu:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
make menu
|
|
179
|
+
```
|
|
116
180
|
|
|
117
181
|
#### Target Details
|
|
118
182
|
|
|
@@ -191,7 +255,7 @@ COVERAGE=on make test # force coverage and generate coverage.xml/codecov.
|
|
|
191
255
|
- Homebrew: updates the source URL tag to `vX.Y.Z` and sets `depends_on "python@X.Y"` to match `requires-python`.
|
|
192
256
|
- Nix: updates the package `version`, example `rev = "vX.Y.Z"`, and switches `pkgs.pythonXYZPackages` / `pkgs.pythonXYZ` to match the minimum Python version from `requires-python`.
|
|
193
257
|
|
|
194
|
-
- To run just the sync without bumping versions: `python
|
|
258
|
+
- To run just the sync without bumping versions: `python scripts/bump_version.py --sync-packaging`.
|
|
195
259
|
|
|
196
260
|
- On release tags (`v*.*.*`), CI validates that packaging files are consistent with `pyproject.toml` and will fail if they drift.
|
|
197
261
|
|
|
@@ -9,13 +9,13 @@ This directory contains packaging files and an automated sync flow so they stay
|
|
|
9
9
|
- Or semantic part: `make bump PART=patch|minor|major`
|
|
10
10
|
- To only synchronize packaging to the current `pyproject.toml` without changing versions:
|
|
11
11
|
```bash
|
|
12
|
-
python3
|
|
12
|
+
python3 scripts/bump_version.py --sync-packaging
|
|
13
13
|
```
|
|
14
14
|
- `make test` runs the same sync step up-front to keep packaging up to date.
|
|
15
15
|
|
|
16
16
|
## What auto-sync updates
|
|
17
17
|
|
|
18
|
-
The script `
|
|
18
|
+
The script `scripts/bump_version.py` updates the following when network access is available:
|
|
19
19
|
|
|
20
20
|
1) Conda recipe (`packaging/conda/recipe/meta.yaml`)
|
|
21
21
|
- Sets Jinja version to the current `pyproject.toml` version.
|
{lib_cli_exit_tools-1.0.1 → lib_cli_exit_tools-1.0.3}/packaging/brew/Formula/lib-cli-exit-tools.rb
RENAMED
|
@@ -3,8 +3,8 @@ class LibCliExitTools < Formula
|
|
|
3
3
|
|
|
4
4
|
desc "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
homepage "https://github.com/bitranox/lib_cli_exit_tools"
|
|
6
|
-
url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.0.
|
|
7
|
-
|
|
6
|
+
url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.0.3.tar.gz"
|
|
7
|
+
k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
8
8
|
license "MIT"
|
|
9
9
|
|
|
10
10
|
depends_on "python@3.10"
|
|
@@ -12,7 +12,7 @@ class LibCliExitTools < Formula
|
|
|
12
12
|
# Vendor Python deps (fill versions/sha256 for an actual formula)
|
|
13
13
|
resource "click" do
|
|
14
14
|
url "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz"
|
|
15
|
-
|
|
15
|
+
k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def install
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% set name = "lib_cli_exit_tools" %}
|
|
2
|
-
{% set version = "1.0.
|
|
2
|
+
{% set version = "1.0.3" %}
|
|
3
3
|
|
|
4
4
|
package:
|
|
5
5
|
name: {{ name|lower }}
|
|
@@ -13,7 +13,7 @@ source:
|
|
|
13
13
|
source:
|
|
14
14
|
# For released tags; replace sha256 before submitting to conda-forge
|
|
15
15
|
url: https://github.com/bitranox/{{ name }}/archive/refs/tags/v{{ version }}.tar.gz
|
|
16
|
-
|
|
16
|
+
k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
17
17
|
{% endif %}
|
|
18
18
|
|
|
19
19
|
build:
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
{
|
|
36
36
|
packages.default = pypkgs.buildPythonPackage {
|
|
37
37
|
pname = "lib_cli_exit_tools";
|
|
38
|
-
version = "1.0.
|
|
38
|
+
version = "1.0.3";
|
|
39
39
|
pyproject = true;
|
|
40
40
|
# Build from the repository root (two levels up from packaging/nix)
|
|
41
41
|
src = ../..;
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
# src = pkgs.fetchFromGitHub {
|
|
44
44
|
# owner = "bitranox";
|
|
45
45
|
# repo = "lib_cli_exit_tools";
|
|
46
|
-
# rev = "v1.0.
|
|
46
|
+
# rev = "v1.0.3";
|
|
47
47
|
# sha256 = "<fill-me>";
|
|
48
48
|
# };
|
|
49
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lib_cli_exit_tools"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.3"
|
|
4
4
|
description = "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -31,6 +31,7 @@ dev = [
|
|
|
31
31
|
"build>=1.2",
|
|
32
32
|
"twine>=5.1",
|
|
33
33
|
"codecov>=2.1.13",
|
|
34
|
+
"textual>=0.50",
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
[project.scripts]
|
|
@@ -59,6 +60,9 @@ target-version = "py313"
|
|
|
59
60
|
|
|
60
61
|
[tool.pyright]
|
|
61
62
|
typeCheckingMode = "strict"
|
|
63
|
+
exclude = [
|
|
64
|
+
"scripts/menu.py",
|
|
65
|
+
]
|
|
62
66
|
|
|
63
67
|
[tool.pytest.ini_options]
|
|
64
68
|
addopts = ["--doctest-modules"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import re
|
|
3
|
+
import shlex
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Mapping, Sequence
|
|
9
|
+
from subprocess import CompletedProcess
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(slots=True)
|
|
13
|
+
class RunResult:
|
|
14
|
+
code: int
|
|
15
|
+
out: str
|
|
16
|
+
err: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run(
|
|
20
|
+
cmd: Sequence[str] | str, *, check: bool = True, capture: bool = True, cwd: str | None = None, env: Mapping[str, str] | None = None, dry_run: bool = False
|
|
21
|
+
) -> RunResult:
|
|
22
|
+
if isinstance(cmd, str):
|
|
23
|
+
display = cmd
|
|
24
|
+
shell = True
|
|
25
|
+
args: Sequence[str] | str = cmd
|
|
26
|
+
else:
|
|
27
|
+
display = " ".join(shlex.quote(p) for p in cmd)
|
|
28
|
+
shell = False
|
|
29
|
+
args = list(cmd)
|
|
30
|
+
if dry_run:
|
|
31
|
+
print(f"[dry-run] {display}")
|
|
32
|
+
return RunResult(0, "", "")
|
|
33
|
+
proc: CompletedProcess[str] = subprocess.run(
|
|
34
|
+
args,
|
|
35
|
+
shell=shell,
|
|
36
|
+
cwd=cwd,
|
|
37
|
+
env=env,
|
|
38
|
+
text=True,
|
|
39
|
+
capture_output=capture,
|
|
40
|
+
)
|
|
41
|
+
if check and proc.returncode != 0:
|
|
42
|
+
raise SystemExit(proc.returncode)
|
|
43
|
+
return RunResult(int(proc.returncode or 0), proc.stdout or "", proc.stderr or "")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def cmd_exists(name: str) -> bool:
|
|
47
|
+
return subprocess.call(["bash", "-lc", f"command -v {shlex.quote(name)} >/dev/null 2>&1"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def read_version_from_pyproject(pyproject: Path = Path("pyproject.toml")) -> str:
|
|
51
|
+
try:
|
|
52
|
+
import tomllib # pyright: ignore[reportMissingImports, reportMissingTypeStubs]
|
|
53
|
+
|
|
54
|
+
data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
|
|
55
|
+
version = str(data.get("project", {}).get("version", ""))
|
|
56
|
+
if version:
|
|
57
|
+
return version
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
m = re.search(r'(?m)^version\s*=\s*"([0-9]+(?:\.[0-9]+){2})"', pyproject.read_text(encoding="utf-8"))
|
|
61
|
+
return m.group(1) if m else ""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ensure_clean_git_tree() -> None:
|
|
65
|
+
# unstaged or staged changes?
|
|
66
|
+
dirty = subprocess.call(["bash", "-lc", "! git diff --quiet || ! git diff --cached --quiet"], stdout=subprocess.DEVNULL)
|
|
67
|
+
if dirty == 0:
|
|
68
|
+
print("[release] Working tree not clean. Commit or stash changes first.", file=sys.stderr)
|
|
69
|
+
raise SystemExit(1)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def git_branch() -> str:
|
|
73
|
+
return run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture=True).out.strip()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def git_delete_tag(name: str, *, remote: str | None = None) -> None:
|
|
77
|
+
run(["git", "tag", "-d", name], check=False, capture=True)
|
|
78
|
+
if remote:
|
|
79
|
+
run(["git", "push", remote, f":refs/tags/{name}"], check=False)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def git_tag_exists(name: str) -> bool:
|
|
83
|
+
return subprocess.call(["bash", "-lc", f"git rev-parse -q --verify {shlex.quote('refs/tags/' + name)} >/dev/null"], stdout=subprocess.DEVNULL) == 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def git_create_annotated_tag(name: str, message: str) -> None:
|
|
87
|
+
run(["git", "tag", "-a", name, "-m", message])
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def git_push(remote: str, ref: str) -> None:
|
|
91
|
+
run(["git", "push", remote, ref])
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def gh_available() -> bool:
|
|
95
|
+
return cmd_exists("gh")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def gh_release_exists(tag: str) -> bool:
|
|
99
|
+
return subprocess.call(["bash", "-lc", f"gh release view {shlex.quote(tag)} >/dev/null 2>&1"], stdout=subprocess.DEVNULL) == 0
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def gh_release_create(tag: str, title: str, body: str) -> None:
|
|
103
|
+
run(["gh", "release", "create", tag, "-t", title, "-n", body], check=False)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def gh_release_edit(tag: str, title: str, body: str) -> None:
|
|
107
|
+
run(["gh", "release", "edit", tag, "-t", title, "-n", body], check=False)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def sync_packaging() -> None:
|
|
111
|
+
run([sys.executable, "scripts/bump_version.py", "--sync-packaging"], check=False)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def bootstrap_dev() -> None:
|
|
115
|
+
# Install dev deps if ruff/pyright/pytest are missing
|
|
116
|
+
if not (cmd_exists("ruff") and cmd_exists("pyright")):
|
|
117
|
+
print("[bootstrap] Installing dev dependencies via 'pip install -e .[dev]'")
|
|
118
|
+
run([sys.executable, "-m", "pip", "install", "-e", ".[dev]"])
|
|
119
|
+
# sqlite fallback (rare)
|
|
120
|
+
try:
|
|
121
|
+
from importlib import import_module
|
|
122
|
+
|
|
123
|
+
import_module("sqlite3")
|
|
124
|
+
except Exception:
|
|
125
|
+
run([sys.executable, "-m", "pip", "install", "pysqlite3-binary"], check=False)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from ._utils import cmd_exists, run
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command(help="Build wheel/sdist, optionally attempt conda/brew/nix builds if tools present")
|
|
10
|
+
@click.option("--conda/--no-conda", default=True, help="Attempt conda build if conda present")
|
|
11
|
+
@click.option("--brew/--no-brew", default=True, help="Attempt Homebrew build if brew present (macOS)")
|
|
12
|
+
@click.option("--nix/--no-nix", default=True, help="Attempt Nix build if nix present")
|
|
13
|
+
def main(conda: bool, brew: bool, nix: bool) -> None:
|
|
14
|
+
click.echo("[1/4] Building wheel/sdist via python -m build")
|
|
15
|
+
run([sys.executable, "-m", "build"]) # requires build in dev deps
|
|
16
|
+
|
|
17
|
+
click.echo("[2/4] Attempting conda-build")
|
|
18
|
+
if conda and cmd_exists("conda"):
|
|
19
|
+
run(["bash", "-lc", "CONDA_USE_LOCAL=1 conda build packaging/conda/recipe"], check=False)
|
|
20
|
+
else:
|
|
21
|
+
click.echo("[conda] skipping: conda not available or disabled")
|
|
22
|
+
|
|
23
|
+
click.echo("[3/4] Attempting Homebrew build/install from local formula")
|
|
24
|
+
if brew and cmd_exists("brew"):
|
|
25
|
+
run(["bash", "-lc", "brew install --build-from-source packaging/brew/Formula/lib-cli-exit-tools.rb"], check=False)
|
|
26
|
+
else:
|
|
27
|
+
click.echo("[brew] skipping: Homebrew not available or disabled")
|
|
28
|
+
|
|
29
|
+
click.echo("[4/4] Attempting Nix flake build")
|
|
30
|
+
if nix and cmd_exists("nix"):
|
|
31
|
+
run(["bash", "-lc", "nix build packaging/nix#default -L"], check=False)
|
|
32
|
+
else:
|
|
33
|
+
click.echo("[nix] skipping: nix not available or disabled")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
main()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
8
|
+
from scripts._utils import run # noqa: E402
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command(help="Bump version in pyproject.toml and CHANGELOG.md")
|
|
12
|
+
@click.option("--version", "version_", type=str, help="Explicit version X.Y.Z")
|
|
13
|
+
@click.option("--part", type=click.Choice(["major", "minor", "patch"]), default=None)
|
|
14
|
+
@click.option("--pyproject", type=click.Path(path_type=Path), default=Path("pyproject.toml"))
|
|
15
|
+
@click.option("--changelog", type=click.Path(path_type=Path), default=Path("CHANGELOG.md"))
|
|
16
|
+
def main(version_: str | None, part: str | None, pyproject: Path, changelog: Path) -> None:
|
|
17
|
+
args = [sys.executable, "scripts/bump_version.py"]
|
|
18
|
+
if version_:
|
|
19
|
+
args += ["--version", version_]
|
|
20
|
+
else:
|
|
21
|
+
args += ["--part", part or "patch"]
|
|
22
|
+
args += ["--pyproject", str(pyproject), "--changelog", str(changelog)]
|
|
23
|
+
run(args)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
main()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
8
|
+
from scripts.bump import main as bump_main # noqa: E402
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command(help="Bump major version")
|
|
12
|
+
def main() -> None: # pragma: no cover
|
|
13
|
+
bump_main.main(standalone_mode=False, args=["--part", "major"]) # type: ignore[attr-defined]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
main()
|