diff --git a/.dev/test-version b/.dev/test-version deleted file mode 100755 index f92e7f3..0000000 --- a/.dev/test-version +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -pkg_version=`python -c "import tomllib; d=tomllib.load(open('pyproject.toml','rb')); print('aio-fluid ' + d['project']['version'])"` -code=`uv run python -c 'import fluid; print(f"aio-fluid {fluid.__version__}")'` - -echo ${pkg_version} - -if [ "${pkg_version}" != "${code}" ]; then - echo "ERROR: pyproject version ${pkg_version} different from code version ${code}" - exit 1 -fi diff --git a/.github/instructions/release.instructions.md b/.github/instructions/release.instructions.md new file mode 100644 index 0000000..9cc387c --- /dev/null +++ b/.github/instructions/release.instructions.md @@ -0,0 +1,21 @@ +# Release Instructions + +Releases are driven by `v*` git tags. Pushing a tag triggers +`.github/workflows/release.yml`, which runs the test suite, publishes the +package to PyPI (`make publish`), then extracts the matching `## vX.Y.Z` +section from `docs/release-notes.md` and publishes it as the GitHub Release +body (re-runs update the existing release instead of failing). + +## Cutting a release + +1. Bump `version` in `pyproject.toml`. That is the single source of truth — + `fluid.__version__` is read from the installed package metadata, so there + is nothing else to edit. +2. Add a `## vX.Y.Z` section at the top of `docs/release-notes.md` describing + the changes. The release workflow fails if this section is missing. +3. Commit and push to `main`; let the `build` workflow pass. +4. Run `make release` — it reads the version from `pyproject.toml`, asks for + confirmation, then creates and pushes the `vX.Y.Z` tag. The `release` + workflow takes it from there. + +Do not publish to PyPI manually; let the tagged workflow do it. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 83792bc..cee590a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,6 @@ jobs: timeout-minutes: 5 env: PYTHON_ENV: ci - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} FLUID_FLAMEGRAPH_EXECUTABLE: "./flamegraph.pl" strategy: matrix: @@ -50,8 +49,6 @@ jobs: run: ./.dev/install-flamegraph - name: run lint run: make lint-test - - name: test version - run: make test-version - name: build docs run: make docs - name: tests @@ -62,9 +59,6 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} files: ./build/coverage.xml - - name: publish - if: ${{ github.ref == 'refs/heads/main' && matrix.python-version == '3.14' && github.event.head_commit.message == 'release' }} - run: make publish - name: publish-docs if: ${{ github.ref == 'refs/heads/main' && matrix.python-version == '3.14' }} run: make docs-publish diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d587654 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,85 @@ +name: release + +# Triggered by `v*` tags (push one with `make release`). Runs the test suite, +# publishes the package to PyPI, then extracts the matching section from +# docs/release-notes.md and publishes it as the GitHub Release body. Re-runs +# idempotently update an existing release rather than failing. + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + PYTHON_ENV: ci + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + FLUID_FLAMEGRAPH_EXECUTABLE: "./flamegraph.pl" + + services: + postgres: + image: postgres:15 + ports: + - 5432:5432 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test_db + + steps: + - name: checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + - name: Start Redis + uses: supercharge/redis-github-action@1.2.0 + with: + redis-version: 7 + - name: Install dependencies + run: make install + - name: Install flamegraph + env: + FG_PATH: "." + run: ./.dev/install-flamegraph + - name: run lint + run: make lint-test + - name: build docs + run: make docs + - name: tests + run: make test + - name: publish to PyPI + run: make publish + - name: Extract release notes + run: | + TAG="${GITHUB_REF_NAME}" + awk -v tag="## ${TAG}" ' + $0 == tag { in_section = 1; next } + /^## / && in_section { exit } + in_section { print } + ' docs/release-notes.md > /tmp/notes.md + if [ ! -s /tmp/notes.md ]; then + echo "No section '## ${TAG}' found in docs/release-notes.md" >&2 + exit 1 + fi + echo "--- extracted notes for ${TAG} ---" + cat /tmp/notes.md + - name: Publish GitHub Release + run: | + TAG="${GITHUB_REF_NAME}" + if gh release view "$TAG" >/dev/null 2>&1; then + gh release edit "$TAG" --notes-file /tmp/notes.md + else + gh release create "$TAG" --title "$TAG" --notes-file /tmp/notes.md + fi diff --git a/CLAUDE.md b/CLAUDE.md index f2b96f3..ea15d0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,3 +3,4 @@ @.github/instructions/makefile.instructions.md @.github/instructions/code.instructions.md @.github/instructions/tests.instructions.md +@.github/instructions/release.instructions.md diff --git a/Makefile b/Makefile index 2d4b439..263c210 100644 --- a/Makefile +++ b/Makefile @@ -47,13 +47,24 @@ outdated: ## Show outdated packages uv tree --outdated .PHONY: publish -publish: ## release to pypi and github tag +publish: ## release to pypi @uv build && uv publish --token $(PYPI_TOKEN) .PHONY: readme readme: ## generate readme.md cp docs/index.md readme.md +.PHONY: release +release: ## tag current version (from pyproject.toml) and push + $(eval VERSION := $(shell grep '^version' pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/')) + @read -p "Tagging with v$(VERSION), are you sure? [Y/n] " ans; \ + ans=$${ans:-Y}; \ + if [ "$$ans" = "Y" ] || [ "$$ans" = "y" ]; then \ + git tag -a v$(VERSION) -m "v$(VERSION)" && git push origin v$(VERSION); \ + else \ + echo "Aborted."; \ + fi + .PHONY: taplo-fmt taplo-fmt: ## format toml files with taplo taplo fmt @@ -65,10 +76,6 @@ test: ## test with coverage -m "not flaky" \ --cov --cov-report xml --cov-report html -.PHONY: test-version -test-version: ## check version compatibility - @./.dev/test-version - .PHONY: upgrade upgrade: ## upgrade all packages via uv uv lock --upgrade diff --git a/docs/index.md b/docs/index.md index b51253e..ab8a72d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,8 @@