From 960399fb25acb5ce6db8963194dcc8d647d2c51d Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Apr 2026 11:47:45 -0400 Subject: [PATCH 1/4] Rewrite developing.rst, add make check-release Drop Travis/Pocoo/.pypirc/Homebrew refs; document trusted publishing flow. New Makefile target check-release verifies venv, clean tree, upstream remote, HISTORY.rst .devN entries, lint, lint-docs. Add UPSTREAM_REPO=$(UPSTREAM)/$(PROJECT_NAME) so fork maintainers can override. Add docs group to setup-venv sync so lint-docs works. Slim ready-release command to defer to developing.rst and call check-release. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/commands/ready-release.md | 18 ++--- Makefile | 25 ++++++- docs/developing.rst | 114 +++++++++++++++++++++--------- scripts/check_history_entries.py | 25 +++++++ 4 files changed, 132 insertions(+), 50 deletions(-) create mode 100644 scripts/check_history_entries.py diff --git a/.claude/commands/ready-release.md b/.claude/commands/ready-release.md index 20aee13c..45f1c4f7 100644 --- a/.claude/commands/ready-release.md +++ b/.claude/commands/ready-release.md @@ -1,21 +1,13 @@ -Walk me through readying a gxformat2 release. Run each step, check for problems, and pause if anything looks wrong. +Walk me through readying a gxformat2 release. The full process is documented in `docs/developing.rst` — read that first and defer to it for details. This command drives the interactive parts. ## Steps -1. **Check git status** - ensure working tree is clean, no missing files. +1. **Confirm target version** — read `__version__` in `gxformat2/__init__.py`, show me the current `.devN`, and ask me to confirm the target release version. -2. **Verify version** - read `gxformat2/__init__.py` and confirm `__version__` is a `.devN` variant of the intended release. Show me the current version and ask me to confirm the target release version. +2. **Populate changelog** — run `make add-history`, show me the new `HISTORY.rst` entries for review, pause so I can edit if needed, then commit the result (`make check-release` requires a clean tree). -3. **Setup venv** - check `.venv` exists. If not, run `make setup-venv`. Confirm dev-requirements are installed. +3. **Pre-release checks** — run `make check-release`. If it fails, stop and report. Do not try to work around failures; fix the underlying issue. -4. **Check UPSTREAM remote** - the Makefile defaults `UPSTREAM` to `galaxyproject`. Check if `$UPSTREAM` is set in the environment; if not, check if a git remote named `galaxyproject` exists. If it doesn't, check for `origin` or `upstream` remotes pointing to `galaxyproject/gxformat2` and offer to create a `galaxyproject` alias via `git remote add galaxyproject `. This must be resolved before `make release` can push. - -5. **Add history** - run `make add-history` to pull contributions into HISTORY.rst under the .dev0 entry. Show me the new HISTORY.rst additions for review. - -6. **Lint** - run `make clean && make lint`. Report any failures. - -7. **Review** - show me a summary of outstanding uncommitted changes (if any) and ask if I want to commit them before proceeding. - -8. **Release** - after I confirm, run `make release` which does: commit-version, new-version, release-artifacts, push-release. This tags, bumps to next dev version, and pushes upstream. +4. **Release** — after I confirm, run `make release`. Stop after each step and report status before moving to the next. diff --git a/Makefile b/Makefile index 56a9040a..8b765b9c 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,9 @@ BUILD_SCRIPTS_DIR=scripts DEV_RELEASE?=0 VERSION?=$(shell DEV_RELEASE=$(DEV_RELEASE) python $(BUILD_SCRIPTS_DIR)/print_version_for_release.py $(SOURCE_DIR) $(DEV_RELEASE)) DOC_URL?=https://gxformat2.readthedocs.org -PROJECT_URL?=https://github.com/jmchilton/gxformat2 PROJECT_NAME?=gxformat2 +UPSTREAM_REPO?=$(UPSTREAM)/$(PROJECT_NAME) +PROJECT_URL?=https://github.com/$(UPSTREAM_REPO) TEST_DIR?=tests DOCS_DIR?=docs @@ -45,7 +46,7 @@ clean-test: ## remove test and coverage artifacts setup-venv: ## setup a development virutalenv in current directory if command -v uv > /dev/null 2>&1; then \ - uv sync --group test --group lint --group mypy; \ + uv sync --group test --group lint --group mypy --group docs; \ else \ if [ ! -d $(VENV) ]; then \ python3 -m venv $(VENV); \ @@ -114,6 +115,26 @@ commit-version: ## Update version and history, commit. new-version: ## Mint a new version $(IN_VENV) DEV_RELEASE=$(DEV_RELEASE) python $(BUILD_SCRIPTS_DIR)/new_version.py $(SOURCE_DIR) $(VERSION) +check-release: ## pre-release checklist: venv, clean tree, history entries, lint, lint-docs + @echo "==> checking $(VENV) exists" + @test -d $(VENV) || \ + (echo "ERROR: $(VENV) does not exist; run 'make setup-venv'"; exit 1) + @echo "==> checking working tree is clean" + @test -z "$$(git status --porcelain)" || \ + (echo "ERROR: working tree has uncommitted changes or untracked files"; git status --short; exit 1) + @echo "==> checking UPSTREAM remote '$(UPSTREAM)' points to $(UPSTREAM_REPO)" + @git remote get-url $(UPSTREAM) 2>/dev/null | grep -q "$(UPSTREAM_REPO)" || \ + (echo "ERROR: remote '$(UPSTREAM)' missing or not pointing to $(UPSTREAM_REPO)."; \ + echo " Fix: git remote add $(UPSTREAM) git@github.com:$(UPSTREAM_REPO).git"; \ + echo " Or fork: make check-release UPSTREAM= UPSTREAM_REPO=/"; exit 1) + @echo "==> checking HISTORY.rst has entries under current .devN header" + @$(IN_VENV) python $(BUILD_SCRIPTS_DIR)/check_history_entries.py + @echo "==> make clean && make lint && make lint-docs" + @$(MAKE) clean + @$(MAKE) lint + @$(MAKE) lint-docs + @echo "==> check-release OK" + release-local: commit-version new-version push-release: ## Push a tagged release to github diff --git a/docs/developing.rst b/docs/developing.rst index 72649f29..9f8b2441 100644 --- a/docs/developing.rst +++ b/docs/developing.rst @@ -1,41 +1,85 @@ -================== -Release Checklist -================== +========== +Developing +========== -This page describes the process of releasing new versions of gxformat2. +This page describes how to set up a development environment for gxformat2 +and how to cut a release. -This release checklist is based on the `Pocoo Release Management Workflow -`_. +Development Setup +================= -This assumes ``~/.pypirc`` file exists with the following fields (variations) -are fine. +Create a virtualenv with dev dependencies installed:: + + make setup-venv + +This runs ``uv sync`` with the ``test``, ``lint``, ``mypy``, and ``docs`` +dependency groups. + +Optional pre-commit hooks:: + + make setup-git-hook-lint # lint on commit + make setup-git-hook-lint-and-test # lint + test on commit + +Running Tests and Linters +========================= :: - [pypi] - username: - password: - - [test] - repository:https://testpypi.python.org/pypi - username: - password: - - -* Review ``git status`` for missing files. -* Verify the latest Travis CI builds pass. -* ``make open-docs`` and review changelog. -* Ensure the target release is set correctly in ``galaxy/__init__.py`` ( - ``version`` will be a ``devN`` variant of target release). -* ``make clean && make lint && make test`` -* ``make release`` - - This process will push packages to test PyPI, allow review, publish - to production PyPI, tag the git repository, push the tag upstream, - and modify the Homebrew recipe. If changes are needed, such as manual - changes to the homebrew recipe, this can be broken down into steps - such as: - - * ``make release-local`` - * ``make push-release`` - * ``make release-brew`` + make test # pytest + make lint # ruff, flake8, black, mypy, schema build dry-run + make lint-docs # rebuild Sphinx HTML and check for warnings + +Set ``GXFORMAT2_TEST_IWC_DIRECTORY`` to a local clone of the IWC repository +to enable integration tests that exercise real-world workflows. + +Releases +======== + +gxformat2 publishes to PyPI via GitHub Actions trusted publishing — no +``~/.pypirc`` or PyPI credentials are needed on the release machine. Pushing +a version tag to ``galaxyproject/gxformat2`` triggers the publish workflow. + +Pre-release checklist +--------------------- + +Run:: + + make check-release + +This verifies: + +* ``.venv`` exists (or ``uv`` is available). +* Working tree is clean (no uncommitted or untracked files). +* The ``UPSTREAM`` git remote exists and points to ``UPSTREAM_REPO`` + (defaults: ``galaxyproject`` / ``galaxyproject/gxformat2``). +* ``HISTORY.rst`` has entries under the current ``.devN`` section. +* ``make clean``, ``make lint``, and ``make lint-docs`` all pass. + +Fork maintainers can override the defaults:: + + make check-release UPSTREAM=myfork UPSTREAM_REPO=me/gxformat2 + +Cutting the release +------------------- + +1. Populate the changelog from merged PRs:: + + make add-history + + Review the diff to ``HISTORY.rst``, adjust as needed, and commit it + (``make check-release`` in the next step requires a clean working tree). + +2. Confirm the target version in ``gxformat2/__init__.py`` (``__version__`` + is the ``.devN`` variant of the release you intend to cut). + +3. ``make check-release``. + +4. ``make release`` — this runs ``commit-version``, ``new-version``, and + ``push-release``, which commits the version bump, tags the release, + bumps ``__version__`` to the next ``.dev0``, and pushes ``main`` plus + the tag to ``UPSTREAM``. The pushed tag triggers the PyPI publish + workflow on GitHub. + +If you need finer control the release can be broken into its pieces: +``make release-local`` (commit, tag, next-dev) followed by +``make push-release`` (push main and tags). diff --git a/scripts/check_history_entries.py b/scripts/check_history_entries.py new file mode 100644 index 00000000..e871c4ac --- /dev/null +++ b/scripts/check_history_entries.py @@ -0,0 +1,25 @@ +"""Fail if HISTORY.rst's current .devN section has no bullet entries.""" +import re +import sys +from pathlib import Path + +HISTORY = Path(__file__).resolve().parent.parent / "HISTORY.rst" + + +def main() -> int: + text = HISTORY.read_text() + m = re.search(r"-{3,}\n(\d+\.\d+\.\d+\.dev\d+)\n-{3,}\n(.*?)(?=\n-{3,}\n|\Z)", text, re.DOTALL) + if not m: + print(f"ERROR: no .devN section found in {HISTORY}", file=sys.stderr) + return 1 + version, body = m.group(1), m.group(2) + bullets = [line for line in body.splitlines() if line.lstrip().startswith("*")] + if not bullets: + print(f"ERROR: {version} section in HISTORY.rst has no entries; run 'make add-history'", file=sys.stderr) + return 1 + print(f"OK: {version} has {len(bullets)} entr{'y' if len(bullets) == 1 else 'ies'}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 4060f3bc5d8a9e90ac625185bf8424f6aa777b34 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Apr 2026 11:51:19 -0400 Subject: [PATCH 2/4] Drop legacy git hook scripts; document pre-commit setup Remove scripts/pre-commit-lint{,-and-test} and the corresponding setup-git-hook-* Makefile targets. developing.rst now points at .pre-commit-config.yaml.sample + pre-commit install. Co-Authored-By: Claude Opus 4.7 (1M context) --- Makefile | 11 ++++++----- docs/developing.rst | 11 ++++++++--- scripts/pre-commit-lint | 3 --- scripts/pre-commit-lint-and-test | 4 ---- 4 files changed, 14 insertions(+), 15 deletions(-) delete mode 100755 scripts/pre-commit-lint delete mode 100644 scripts/pre-commit-lint-and-test diff --git a/Makefile b/Makefile index 8b765b9c..81d66fab 100644 --- a/Makefile +++ b/Makefile @@ -54,11 +54,12 @@ setup-venv: ## setup a development virutalenv in current directory $(IN_VENV) pip install -r requirements.txt && pip install -r dev-requirements.txt; \ fi -setup-git-hook-lint: ## setup precommit hook for linting project - cp $(BUILD_SCRIPTS_DIR)/pre-commit-lint .git/hooks/pre-commit - -setup-git-hook-lint-and-test: ## setup precommit hook for linting and testing project - cp $(BUILD_SCRIPTS_DIR)/pre-commit-lint-and-test .git/hooks/pre-commit +setup-pre-commit: ## install pre-commit hook (uses .pre-commit-config.yaml if present, else the .sample) + @if [ -f .pre-commit-config.yaml ]; then \ + $(IN_VENV) pre-commit install; \ + else \ + $(IN_VENV) pre-commit install --config .pre-commit-config.yaml.sample; \ + fi lint: ## check style with ruff, flake8, black, and mypy uv run --group lint isort --check --diff . diff --git a/docs/developing.rst b/docs/developing.rst index 9f8b2441..a8c82e41 100644 --- a/docs/developing.rst +++ b/docs/developing.rst @@ -15,10 +15,15 @@ Create a virtualenv with dev dependencies installed:: This runs ``uv sync`` with the ``test``, ``lint``, ``mypy``, and ``docs`` dependency groups. -Optional pre-commit hooks:: +Optional pre-commit hooks are configured via `pre-commit +`_. A sample configuration ships as +``.pre-commit-config.yaml.sample``. Install the hook with:: - make setup-git-hook-lint # lint on commit - make setup-git-hook-lint-and-test # lint + test on commit + make setup-pre-commit + +This uses ``.pre-commit-config.yaml`` if you have one (copy the sample and +customize), and otherwise falls back to the committed +``.pre-commit-config.yaml.sample``. Running Tests and Linters ========================= diff --git a/scripts/pre-commit-lint b/scripts/pre-commit-lint deleted file mode 100755 index 53769a25..00000000 --- a/scripts/pre-commit-lint +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -make lint diff --git a/scripts/pre-commit-lint-and-test b/scripts/pre-commit-lint-and-test deleted file mode 100644 index db1a6071..00000000 --- a/scripts/pre-commit-lint-and-test +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -make lint -make quick-test From 95a0fa0fef0788077287e56adfdcd6dd18aef4cb Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Apr 2026 11:59:50 -0400 Subject: [PATCH 3/4] Make dev dependency groups default for uv sync Set [tool.uv] default-groups in pyproject.toml so 'uv sync' picks up test/lint/mypy/docs automatically; simplify setup-venv accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) --- Makefile | 2 +- docs/developing.rst | 5 +++-- pyproject.toml | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 81d66fab..117ee6b3 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ clean-test: ## remove test and coverage artifacts setup-venv: ## setup a development virutalenv in current directory if command -v uv > /dev/null 2>&1; then \ - uv sync --group test --group lint --group mypy --group docs; \ + uv sync; \ else \ if [ ! -d $(VENV) ]; then \ python3 -m venv $(VENV); \ diff --git a/docs/developing.rst b/docs/developing.rst index a8c82e41..68bd47aa 100644 --- a/docs/developing.rst +++ b/docs/developing.rst @@ -12,8 +12,9 @@ Create a virtualenv with dev dependencies installed:: make setup-venv -This runs ``uv sync`` with the ``test``, ``lint``, ``mypy``, and ``docs`` -dependency groups. +This runs ``uv sync``. The ``test``, ``lint``, ``mypy``, and ``docs`` +dependency groups are marked as ``default-groups`` in ``pyproject.toml`` +so they install automatically. Optional pre-commit hooks are configured via `pre-commit `_. A sample configuration ships as diff --git a/pyproject.toml b/pyproject.toml index 3e53d302..9059c2a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,6 +79,9 @@ docs = [ "sphinxcontrib-mermaid", ] +[tool.uv] +default-groups = ["test", "lint", "mypy", "docs"] + [tool.ruff] exclude = [ "gxformat2/schema/v19_09.py", From 909c629f064fc116060e9e76a3d84fb8e2123e8a Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Apr 2026 12:09:08 -0400 Subject: [PATCH 4/4] Formatting... --- scripts/check_history_entries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_history_entries.py b/scripts/check_history_entries.py index e871c4ac..e95c43c2 100644 --- a/scripts/check_history_entries.py +++ b/scripts/check_history_entries.py @@ -1,4 +1,5 @@ """Fail if HISTORY.rst's current .devN section has no bullet entries.""" + import re import sys from pathlib import Path