diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 000000000..3bf558e17 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,43 @@ +name: pull request + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + branches: [develop] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + + title: + name: check title + # the release and reset pull requests are opened by a bot and are not + # squash merged, so their titles do not reach the changelog + if: github.event.pull_request.user.type != 'Bot' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + + - name: Check conventional commit format + env: + TITLE: ${{ github.event.pull_request.title }} + run: | + # pull requests are squash merged, so the title becomes the commit + # message in develop, and the changelog is generated from those + # messages. a title that is not a conventional commit is dropped + # from the release notes without warning. + types="build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test" + if [[ ! "$TITLE" =~ ^($types)(\([^\)]+\))?!?:\ .+ ]]; then + echo "::error::'$TITLE' is not a conventional commit header." + echo "" + echo "Use '(): ', for example:" + echo " feat(mf6): support adaptive time step retries" + echo "" + echo "Type is one of: ${types//|/, }." + echo "Only feat, fix, perf and refactor reach the release notes;" + echo "see cliff.toml. A user facing change needs one of those." + exit 1 + fi + echo "'$TITLE' is a conventional commit header" diff --git a/DEVELOPER.md b/DEVELOPER.md index 9399fcf0a..de96fbdcd 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -33,6 +33,7 @@ This document describes how to set up a FloPy development environment, run the e - [Snapshot testing](#snapshot-testing) - [Branching model](#branching-model) - [Deprecation policy](#deprecation-policy) + - [Releasing](#releasing) - [Miscellaneous](#miscellaneous) - [Locating the root](#locating-the-root) - [Dependency analysis](#dependency-analysis) @@ -459,6 +460,10 @@ This project loosely follows [NEP 23](https://numpy.org/neps/nep-0023-backwards- See the linked article for more detail. +## Releasing + +The release procedure is documented separately in [docs/make_release.md](docs/make_release.md). FloPy releases are automated from a pushed `v` branch, publish to PyPI with [trusted publishing](https://docs.pypi.org/trusted-publishers/) from a `release` environment, and reach conda-forge through the autotick bot. There are also a few manual steps (updating citations, reviewing deprecations, regenerating the MF6 module); see the release guide for the full sequence and the caveats. + ## Miscellaneous ### Locating the root diff --git a/docs/make_release.md b/docs/make_release.md index a17c8506d..23b9e5ed6 100644 --- a/docs/make_release.md +++ b/docs/make_release.md @@ -72,6 +72,8 @@ Pushing the release branch to the repository triggers the workflow. If the branch name ends with `rc`, it's a dry run and the workflow stops here. If the branch name does not end with `rc` the workflow creates a draft PR from the release branch into the `master` branch. +**Note:** the changelog is generated from commit messages with git-cliff. Development PRs are squash merged, so the PR title becomes that message, and a title that is not a [conventional commit](https://www.conventionalcommits.org/) header is dropped from the notes without warning. [`.github/workflows/pull_request.yml`](../.github/workflows/pull_request.yml) rejects such titles, but it cannot tell whether the type is the right one: a user facing change titled `chore:` still passes the check and is still dropped. Review the generated changelog on the release PR and add anything missing to the section for the version being cut. + ### Merge release branch to master Review the PR and merging if it passes inspection. @@ -86,8 +88,12 @@ Review the release and publish it. Publishing the release triggers a final job t The release workflow assumes [trusted publishing](https://docs.pypi.org/trusted-publishers/) has been configured in the PyPI admin interface. A GitHub environment called `release` is required (however it needs no secrets or environment variables). +**Note:** PyPI matches a trusted publisher on the organization name, the repository name, the workflow filename and the environment name. Renaming any of them silently invalidates the publisher, and nothing reports it until the next release fails with `invalid-publisher` — possibly long after the rename. After any such rename, update the publisher at to match. + For the Conda distribution, there [is a bot](https://github.com/regro-cf-autotick-bot) which will [automatically detect new package versions uploaded to PyPI and create a PR](https://github.com/conda-forge/flopy-feedstock/pull/50) to update the `conda-forge/flopy-feedstock` repository. This PR can be reviewed, updated if needed, and merged to update the package on the `conda-forge` channel. If it becomes necessary to manually publish an update to conda forge, see below. +**Note:** the bot updates only the version number and the checksum. If the release changed FloPy's dependencies or minimum versions, update the recipe's `host` and `run` requirements to match the `Requires-Dist` metadata of the sdist on PyPI before merging. A maintainer can push the correction to the bot's branch. + ### Reset the develop branch Make a new branch from `master`: