This document provides guidance to set up a development environment and discusses conventions used in this project.
Python3.11+. This project has historically aimed to support several recent versions of Python, loosely following NEP 29. In current and future development this window may narrow to follow SPEC 0 instead.
To get started, first fork and clone this repository. Then install the project in "editable" mode, along with all of the dependencies needed for running and development:
pip install -e . --group devDevelopers that use a uv environment can "sync" the project to install the project with all dependencies:
uv syncThis repository's tests use pytest and several plugins.
This repository's tests expect a few environment variables:
REPOS_PATH: the path to MODFLOW 6 example model repositoriesGITHUB_TOKEN: a GitHub authentication token
These may be set manually, but the recommended approach is to configure environment variables in a .env file in the project root, for instance:
REPOS_PATH=/path/to/repos
GITHUB_TOKEN=yourtoken...
The tests use pytest-dotenv to detect and load variables from this file.
Tests should be run from the autotest directory. To run the tests in parallel with verbose output:
pytest -v -n autoTests follow a few conventions for ease of use and maintenance.
Tests which must write to disk use pytest's built-in temp_dir fixture or one of this package's own scoped temporary directory fixtures.
Releases are automated by .github/workflows/release.yml.
Publishing to PyPI uses trusted publishing, so no
API token is needed, but the repository must have a release environment configured.
Important
PyPI matches a trusted publisher on the organisation 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. After any such rename,
update the publisher at https://pypi.org/manage/project/modflow-devtools/settings/publishing/ to match.
From the Actions tab, select Run workflow and fill in the form:
| Input | Description |
|---|---|
branch |
Branch to release from. Defaults to develop. |
version |
Explicit version number, e.g. 1.9.3. Defaults to the version in version.txt with its .dev suffix removed. |
run_tests |
Run the test suite before drafting the release. Defaults to true. |
This can also be done from the command line, for instance:
gh workflow run release.yml -f branch=developThe release version is normally the development version already set in version.txt (e.g.
1.10.0.dev0 releases as 1.10.0); pass version only to release something else. The workflow
creates a v<version> release branch, updates the version number, regenerates the changelog with
git-cliff and prepends it to HISTORY.md, runs the CI suite against the
branch, and opens a draft pull request into main.
A release can alternatively be started by pushing a release branch named v<major>.<minor>.<patch>.
Review the release pull request, in particular HISTORY.md. Mark it ready for review and merge it
into main. Merge rather than squash: squashing drops the commit history from main and makes
develop and main diverge, which causes later main updates to replay old release commits.
Merging into main drafts a GitHub release, with notes taken from the generated changelog.
Review the draft release and publish it. Publishing it triggers jobs that:
- build the package and upload it to PyPI
- open a follow-up pull request resetting
developfrommain, with the version number incremented to the next development version (minor bumped,.dev0suffix)
Merge (do not squash) the reset pull request to finish the release.
A few hours after the upload to PyPI, a bot opens a version pull request on the
feedstock. To start it immediately
instead, open an issue there titled @conda-forge-admin, please update version.
Important
The bot updates the version number and the checksum, and nothing else. Check the recipe's host
and run requirements against the dependencies the release actually declares, which are the
Requires-Dist lines of the sdist on PyPI. A maintainer can push corrections to the bot's branch.
Merging the feedstock pull request builds and uploads the package. It does not appear to a solver until the channel index is regenerated, which takes up to about an hour; the package is visible on anaconda.org before then.
Release notes are generated from commit messages with git-cliff, so commits reaching develop
should follow the conventional commits format (feat:,
fix:, refactor:, etc.). Commits that do not follow the convention are omitted from the
changelog without warning. See cliff.toml for the commit groups and which ones are
skipped.
Pull requests are squash merged, so the title becomes the commit message the notes are generated
from. .github/workflows/pull_request.yml rejects a title
that is not a conventional commit header, 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 from the notes.
Read the generated changelog on the release pull request before merging it, and make any necessary
edits to the section for the version being cut.