From 0ce37a464d73439fd46dea321974705a340a5327 Mon Sep 17 00:00:00 2001 From: Andrii Liekariev Date: Sat, 6 Jun 2026 21:59:17 +0300 Subject: [PATCH] Add make tag release-cut target with branch and tag-exists guards Wraps the CHANGELOG rewrite, commit, tag, and atomic push into a single `make tag VERSION=X.Y.Z`. Refuses to run off main or when the tag already exists, so a typo or retry can't leave an orphan changelog commit. --- DEVELOPMENT.md | 10 ++++------ Makefile | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 6298c1e..a7d82d0 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -58,14 +58,12 @@ These instructions are specific to PyCharm. ## Releasing a New Plugin Version -1. Move the entries under `## [Unreleased]` in `CHANGELOG.md` to a new `## [X.Y.Z] - YYYY-MM-DD` section. `qgis-plugin-ci` requires a 3-part `MAJOR.MINOR.PATCH` version — `## [1.2]` will be silently ignored and the published changelog will be empty. -2. Commit and push to `main`. -3. Tag and push (3-part versions only): +1. Make sure `CHANGELOG.md`'s `## [Unreleased]` section has the entries you want shipped, then on `main` with a clean working tree run: ```shell - git tag X.Y.Z - git push origin X.Y.Z + make tag VERSION=X.Y.Z ``` -4. The `Release` workflow (`.github/workflows/release.yaml`) runs the tests, then publishes to plugins.qgis.org and creates a GitHub Release with the `.zip` attached. + This rewrites `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`, inserts a fresh empty `## [Unreleased]` above it, commits, tags, and pushes both to `origin` atomically. `qgis-plugin-ci` requires a 3-part `MAJOR.MINOR.PATCH` version — `## [1.2]` would be silently ignored and the published changelog would be empty. +2. The `Release` workflow (`.github/workflows/release.yaml`) runs the tests, then publishes to plugins.qgis.org and creates a GitHub Release with the `.zip` attached. Required GitHub Secrets (one-time setup, repo Settings → Secrets and variables → Actions): - `OSGEO_USERNAME` — your plugins.qgis.org account username diff --git a/Makefile b/Makefile index bb50e68..717b2a6 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,25 @@ QGIS_VERSION ?= 4.0.0 IMAGE := qgis-for-pptl:$(QGIS_VERSION) CONTAINER := qgis_pptl -.PHONY: build run install install-dev test test-coverage test-perf test-all stop clean +.PHONY: build run install install-dev test test-coverage test-perf test-all stop clean tag + +define FINALIZE_CHANGELOG +import os, pathlib, datetime, sys +v = os.environ['VERSION'] +p = pathlib.Path('CHANGELOG.md') +lines = p.read_text().splitlines() +header = '## [Unreleased]' +try: + i = lines.index(header) +except ValueError: + sys.exit('No [Unreleased] section in CHANGELOG.md') +j = next((k for k in range(i + 1, len(lines)) if lines[k].startswith('## [')), len(lines)) +if not [l for l in lines[i + 1:j] if l.strip()]: + sys.exit('[Unreleased] has no entries; add some before tagging') +lines[i:i + 1] = [header, '', f'## [{v}] - {datetime.date.today().isoformat()}'] +p.write_text('\n'.join(lines) + '\n') +endef +export FINALIZE_CHANGELOG build: DOCKER_SCAN_SUGGEST=false docker build -t $(IMAGE) -f Dockerfile . @@ -45,3 +63,19 @@ stop: clean: stop -docker rm $(CONTAINER) + +tag: + @test -n "$(VERSION)" || { echo "Usage: make tag VERSION=X.Y.Z" >&2; exit 2; } + @echo "$(VERSION)" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-.+)?$$' \ + || { echo "VERSION must be N.N.N or N.N.N-suffix" >&2; exit 2; } + @branch=$$(git rev-parse --abbrev-ref HEAD); test "$$branch" = "main" \ + || { echo "Must be on main; currently on $$branch" >&2; exit 2; } + @git diff --quiet && git diff --cached --quiet \ + || { echo "Working tree not clean; commit or stash first" >&2; exit 2; } + @! git rev-parse --verify --quiet "refs/tags/$(VERSION)" >/dev/null \ + || { echo "Tag $(VERSION) already exists" >&2; exit 2; } + @VERSION="$(VERSION)" python3 -c "$$FINALIZE_CHANGELOG" + git add CHANGELOG.md + git commit -m "Update CHANGELOG for version $(VERSION) release" + git tag "$(VERSION)" + git push --atomic origin HEAD "$(VERSION)"