Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,66 @@ jobs:

- name: Publish package
run: uv publish --verbose --trusted-publishing always

reset:
name: Draft develop reset PR
# runs after a release is published, opens a PR merging master back into develop
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
defaults:
run:
shell: bash
steps:

- name: Checkout master branch
uses: actions/checkout@v7
with:
ref: master
fetch-depth: 0

- name: Setup Python
uses: astral-sh/setup-uv@v7
with:
enable-cache: false

- name: Install FloPy
run: uv sync --all-extras

- name: Update version numbers
id: version
run: |
ver=$(cat version.txt)
next=$(uv run scripts/update_version.py --post-release)
echo "version=$ver" >> $GITHUB_OUTPUT
echo "next=$next" >> $GITHUB_OUTPUT

- name: Format and lint Python files
run: |
uvx ruff format flopy/version.py
uvx ruff check --fix flopy/version.py

- name: Draft pull request
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
ver="${{ steps.version.outputs.version }}"
next="${{ steps.version.outputs.next }}"
branch="post-release-$ver-reset"

git config core.sharedRepository true
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git switch -c "$branch"
git add flopy/version.py version.txt CITATION.cff README.md docs/PyPI_release.md
git commit -m "ci(release): update version to $next"
git push origin "$branch"

body='
# Reset `develop` after release '$ver'

Merge (do not squash) this pull request to bring `master` back into `develop` and set the development version to `'$next'`.
'
gh pr create -B "develop" -H "$branch" --title "Reset develop after release $ver" --draft --body "$body"
19 changes: 2 additions & 17 deletions docs/make_release.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,9 @@ For the Conda distribution, there [is a bot](https://github.com/regro-cf-autotic

### Reset the develop branch

Make a new branch from `master`:
Publishing the release triggers a final job to draft a pull request resetting `develop` from `master`. The job sets the development version for the next cycle with `scripts/update_version.py --post-release`: the next anticipated minor version, with a `.devN` segment where `N` is the patch number just released (e.g. `3.10.0.dev2` after `3.9.2`, `3.12.0.dev0` after `3.11.0`). The version number complies with [PEP 440](https://peps.python.org/pep-0440/).

```shell
git checkout master
git switch -c post-x.y.z-release-reset
```

Update the version number for the next development cycle:

```shell
python scripts/update_version.py -v x.y.z.dev0
```

The version number must comply with [PEP 440](https://peps.python.org/pep-0440/).

Lint and format Python files: `ruff check .` and `ruff format .` from the project root.

Create and merge (don't squash) a pull request from this branch into `develop`.
Review the draft PR and merge it — don't squash. Squashing removes the commit history from `develop` and causes it to diverge from `master`.

## Conda

Expand Down
13 changes: 9 additions & 4 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,21 @@ The `update_version.py` script can be used to update FloPy version numbers. Runn

The script acquires a file lock before writing to files to make sure only one process edits the files at any given time and prevent desynchronization.

If the script is run with no arguments, the version number is not changed, but updated timestamps are written. To set the version number, use the `--version` (short `-v`) option, e.g.:
If the script is run with no arguments, the version number is not changed, but updated timestamps are written. In all cases the resulting version is printed to stdout. To set the version number, use the `--version` (short `-v`) option, e.g.:

```shell
python scripts/update_version.py -v 3.3.6
```

To get the current version number, use the `--get` flag (short `-g`):
Two flags derive the version instead of taking it explicitly:

- `--release` (short `-r`) strips the development segment (e.g. `.dev0`) from the current version, giving the version to release.
- `--post-release` (short `-p`) gives the development version for the next cycle: the next minor version, with a `.devN` segment where `N` is the patch number just released (e.g. `3.10.0.dev2` after `3.9.2`).

To print the version without writing any changes, add `--dry-run`:

```shell
python scripts/update_version.py -g
python scripts/update_version.py --dry-run
```

This simply returns the contents of `version.txt` and does not write any changes to the repository's files.
With no other flag this simply returns the contents of `version.txt`.
85 changes: 64 additions & 21 deletions scripts/update_version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
import re
import sys
import textwrap
from datetime import datetime
from pathlib import Path
Expand All @@ -9,12 +10,13 @@
from packaging.version import Version

_epilog = """\
Update version information stored in version.txt in the project root,
as well as several other files in the repository. If --version is not
provided, the version number will not be changed. A file lock is held
to synchronize file access. The version tag must comply with standard
'<major>.<minor>.<patch>' format conventions for semantic versioning.
To show the version without changing anything, use --get (short -g).
Update version information stored in version.txt in the project root, as
well as several other files in the repository, and print the new version.
If none of --version, --release or --post-release is provided, the version
number is not changed. A file lock is held to synchronize file access. The
version tag must comply with standard '<major>.<minor>.<patch>' format
conventions for semantic versioning. To print the version without changing
anything, add --dry-run.
"""
_project_name = "flopy"
_project_root_path = Path(__file__).parent.parent
Expand All @@ -39,10 +41,27 @@ def split_nonnumeric(s):
_current_version = Version(_version_txt_path.read_text().strip())


def release_version() -> Version:
"""Current version with any development segment (e.g. '.dev0') removed."""
return Version(_current_version.base_version)


def post_release_version() -> Version:
"""Development version for the next cycle, following a release.

Targets the next anticipated minor version, with the development segment
set to the micro (patch) number of the version just released: e.g. after
3.9.2 comes 3.10.0.dev2, and after 3.11.0 comes 3.12.0.dev0. The counter
marks how many releases into the series development has resumed.
"""
v = Version(_current_version.base_version)
return Version(f"{v.major}.{v.minor + 1}.0.dev{v.micro}")


def update_version_txt(version: Version):
with open(_version_txt_path, "w") as f:
f.write(str(version))
print(f"Updated {_version_txt_path} to version {version}")
print(f"Updated {_version_txt_path} to version {version}", file=sys.stderr)


def update_version_py(timestamp: datetime, version: Version):
Expand All @@ -53,7 +72,7 @@ def update_version_py(timestamp: datetime, version: Version):
)
f.write(f'__version__ = "{version}"\n')
f.close()
print(f"Updated {_version_py_path} to version {version}")
print(f"Updated {_version_py_path} to version {version}", file=sys.stderr)


def get_software_citation(timestamp: datetime, version: Version):
Expand Down Expand Up @@ -131,7 +150,7 @@ def update_readme_markdown(timestamp: datetime, version: Version):

f.write(f"{line}\n")

print(f"Updated {fpth} to version {version}")
print(f"Updated {fpth} to version {version}", file=sys.stderr)


def update_citation_cff(timestamp: datetime, version: Version):
Expand All @@ -149,7 +168,7 @@ def update_citation_cff(timestamp: datetime, version: Version):
citation, f, allow_unicode=True, default_flow_style=False, sort_keys=False
)

print(f"Updated {fpth} to version {version}")
print(f"Updated {fpth} to version {version}", file=sys.stderr)


def update_pypi_release(timestamp: datetime, version: Version):
Expand All @@ -166,7 +185,7 @@ def update_pypi_release(timestamp: datetime, version: Version):
f.write(f"{line}\n")

f.close()
print(f"Updated {fpth} to version {version}")
print(f"Updated {fpth} to version {version}", file=sys.stderr)


def update_version(
Expand All @@ -180,7 +199,7 @@ def update_version(
version = (
version
if version
else Version(previous.major, previous.minor, previous.micro)
else Version(f"{previous.major}.{previous.minor}.{previous.micro}")
)

with lock:
Expand Down Expand Up @@ -209,18 +228,42 @@ def update_version(
help="Specify the release version",
)
parser.add_argument(
"-g",
"--get",
"-r",
"--release",
required=False,
action="store_true",
help="Just get the current version number, no updates (defaults false)",
help=(
"Use the current development version with its development segment "
"(e.g. '.dev0') removed"
),
)
parser.add_argument(
"-p",
"--post-release",
required=False,
action="store_true",
help=(
"Use the development version for the next cycle: the next minor "
"version, with a development segment set to the released patch number"
),
)
parser.add_argument(
"--dry-run",
required=False,
action="store_true",
help="Print the version that would be written, and exit without writing",
)
args = parser.parse_args()

if args.get:
print(_current_version)
if args.post_release:
version = post_release_version()
elif args.release:
version = release_version()
elif args.version:
version = Version(args.version)
else:
update_version(
timestamp=datetime.now(),
version=(Version(args.version) if args.version else _current_version),
)
version = _current_version

if not args.dry_run:
update_version(timestamp=datetime.now(), version=version)
print(version)
Loading