From f2b07b94976feae4326fa1382b2eeb25681baaf9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Apr 2023 19:43:15 +0000 Subject: [PATCH 1/7] ci(release): update to development version 0.1.9 --- docs/conf.py | 2 +- modflow_devtools/__init__.py | 2 +- version.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index eb7b8a5..905fef5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,7 +8,7 @@ project = "modflow-devtools" author = "MODFLOW Team" -release = "0.1.8" +release = "0.1.9" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/modflow_devtools/__init__.py b/modflow_devtools/__init__.py index bce77e5..8a30d5e 100644 --- a/modflow_devtools/__init__.py +++ b/modflow_devtools/__init__.py @@ -1,6 +1,6 @@ __author__ = "Joseph D. Hughes" __date__ = "Apr 21, 2023" -__version__ = "0.1.8" +__version__ = "0.1.9" __maintainer__ = "Joseph D. Hughes" __email__ = "jdhughes@usgs.gov" __status__ = "Production" diff --git a/version.txt b/version.txt index 84aa3a7..82551ad 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.1.8 \ No newline at end of file +0.1.9 \ No newline at end of file From d199fc4e8d1314d2900658f61a1cfea103a30fea Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Fri, 21 Jul 2023 19:24:48 -0400 Subject: [PATCH 2/7] docs: add install and usage docs for node and doctoc (#86) --- docs/md/doctoc.md | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/md/doctoc.md b/docs/md/doctoc.md index 927368d..ec75263 100644 --- a/docs/md/doctoc.md +++ b/docs/md/doctoc.md @@ -1,11 +1,46 @@ # Generating TOCs -The [`doctoc`](https://www.npmjs.com/package/doctoc) tool can be used to automatically generate table of contents sections for markdown files. `doctoc` is distributed with the [Node Package Manager](https://docs.npmjs.com/cli/v7/configuring-npm/install). With Node installed use `npm install -g doctoc` to install `doctoc` globally. Then just run `doctoc `, e.g.: +The [`doctoc`](https://www.npmjs.com/package/doctoc) tool generates table of contents sections for markdown files. + +## Installing Node.js, `npm` and `doctoc`` + +`doctoc` is distributed with the [Node Package Manager](https://docs.npmjs.com/cli/v7/configuring-npm/install). [Node](https://nodejs.org/en) is a JavaScript runtime environment. + +On Ubuntu, Node can be installed with: + +```shell +sudo apt update +sudo apt install nodejs +``` + +On Windows, with [Chocolatey](https://community.chocolatey.org/packages/nodejs): + +```shell +choco install nodejs +``` + +Installers and binaries for Windows and macOS are [available for download](https://nodejs.org/en/download). + +Once Node is installed, install `doctoc` with: + +```shell +npm install -g doctoc +``` + +## Using `doctoc` + +Then TOCs can be generated with `doctoc `, e.g.: ```shell doctoc DEVELOPER.md ``` -This will insert HTML comments surrounding an automatically edited region, scanning for headers and creating an appropriately indented TOC tree. Subsequent runs are idempotent, updating if the file has changed or leaving it untouched if not. +This will insert HTML comments surrounding an automatically edited region, in which `doctoc` will create an appropriately indented TOC tree. Subsequent runs are idempotent, scanning for headers and only updating the TOC if the file header structure has changed. + +To run `doctoc` for all markdown files in a particular directory (recursive), use `doctoc some/path`. + +By default `doctoc` inserts a self-descriptive comment + +> **Table of Contents** *generated with DocToc* -To run `doctoc` for all markdown files in a particular directory (recursive), use `doctoc some/path`. \ No newline at end of file +This can be removed (and other content within the TOC region edited) — `doctoc` will not overwrite it, only the table. From 53b31cce34d221bade4c842efe3b5ed3034b2742 Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Wed, 26 Jul 2023 14:24:24 -0400 Subject: [PATCH 3/7] feat(set_env): add set_env contextmanager utility (#87) --- modflow_devtools/misc.py | 33 ++++++++++++++++++++++++++++++ modflow_devtools/test/test_misc.py | 17 +++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/modflow_devtools/misc.py b/modflow_devtools/misc.py index f42468d..c326c0b 100644 --- a/modflow_devtools/misc.py +++ b/modflow_devtools/misc.py @@ -29,6 +29,39 @@ def set_dir(path: PathLike): print(f"Returned to previous directory: {origin}") +@contextmanager +def set_env(*remove, **update): + """ + Temporarily updates the ``os.environ`` dictionary in-place. + + Referenced from https://stackoverflow.com/a/34333710/6514033. + + The ``os.environ`` dictionary is updated in-place so that the modification + is sure to work in all situations. + + :param remove: Environment variables to remove. + :param update: Dictionary of environment variables and values to add/update. + """ + env = environ + update = update or {} + remove = remove or [] + + # List of environment variables being updated or removed. + stomped = (set(update.keys()) | set(remove)) & set(env.keys()) + # Environment variables and values to restore on exit. + update_after = {k: env[k] for k in stomped} + # Environment variables and values to remove on exit. + remove_after = frozenset(k for k in update if k not in env) + + try: + env.update(update) + [env.pop(k, None) for k in remove] + yield + finally: + env.update(update_after) + [env.pop(k) for k in remove_after] + + class add_sys_path: """ Context manager for temporarily editing the system path diff --git a/modflow_devtools/test/test_misc.py b/modflow_devtools/test/test_misc.py index 9ff517e..5003940 100644 --- a/modflow_devtools/test/test_misc.py +++ b/modflow_devtools/test/test_misc.py @@ -11,6 +11,7 @@ get_packages, has_package, set_dir, + set_env, ) @@ -21,6 +22,22 @@ def test_set_dir(tmp_path): assert Path(os.getcwd()) != tmp_path +def test_set_env(tmp_path): + # test adding a variable + key = "TEST_ENV" + val = "test" + assert environ.get(key) is None + with set_env(**{key: val}): + assert environ.get(key) == val + with set_env(TEST_ENV=val): + assert environ.get(key) == val + + # test removing a variable + with set_env(**{key: val}): + with set_env(key): + assert environ.get(key) is None + + _repos_path = environ.get("REPOS_PATH") if _repos_path is None: _repos_path = Path(__file__).parent.parent.parent.parent From 32425e44ecd853176c921965120f494631f14a4a Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Wed, 26 Jul 2023 16:39:31 -0400 Subject: [PATCH 4/7] test: make get_namefile_path tests less brittle (#89) --- modflow_devtools/test/test_misc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modflow_devtools/test/test_misc.py b/modflow_devtools/test/test_misc.py index 5003940..fe6d2cb 100644 --- a/modflow_devtools/test/test_misc.py +++ b/modflow_devtools/test/test_misc.py @@ -184,7 +184,7 @@ def test_get_model_paths_largetestmodels(): def test_get_model_paths_exclude_patterns(models): path, expected_count = models paths = get_model_paths(path, excluded=["gwt"]) - assert len(paths) == expected_count + assert len(paths) >= expected_count @pytest.mark.skipif( @@ -227,7 +227,7 @@ def test_get_namefile_paths_largetestmodels(): def test_get_namefile_paths_exclude_patterns(models): path, expected_count = models paths = get_namefile_paths(path, excluded=["gwf"]) - assert len(paths) == expected_count + assert len(paths) >= expected_count @pytest.mark.skipif(not any(_example_paths), reason="examples not found") @@ -242,10 +242,10 @@ def test_get_namefile_paths_select_prefix(): @pytest.mark.skipif(not any(_example_paths), reason="examples not found") def test_get_namefile_paths_select_patterns(): paths = get_namefile_paths(_examples_path, selected=["gwf"]) - assert len(paths) == 70 + assert len(paths) >= 70 @pytest.mark.skipif(not any(_example_paths), reason="examples not found") def test_get_namefile_paths_select_packages(): paths = get_namefile_paths(_examples_path, packages=["wel"]) - assert len(paths) == 43 + assert len(paths) >= 43 From 284463886746b861d1eff565f70381d76af2ba32 Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Wed, 26 Jul 2023 17:06:01 -0400 Subject: [PATCH 5/7] ci: use packaging.version.Version in update_version.py (#91) * use packaging.version.Version in update_version.py * bump minor not patch in release.yml --- .github/workflows/release.yml | 5 +- scripts/update_version.py | 102 +++++----------------------------- 2 files changed, 15 insertions(+), 92 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 58819c5..a0375ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -237,11 +237,10 @@ jobs: reset_branch="post-release-${{ steps.latest_tag.outputs.tag }}-reset" git switch -c $reset_branch - # increment patch version + # increment minor version major_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f1) minor_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f2) - patch_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f3) - version="$major_version.$minor_version.$((patch_version + 1))" + version="$major_version.$((minor_version + 1)).0" python scripts/update_version.py -v "$version" python scripts/lint.py diff --git a/scripts/update_version.py b/scripts/update_version.py index 1543a58..1c078b8 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -7,6 +7,7 @@ from typing import NamedTuple from filelock import FileLock +from packaging.version import Version _project_name = "modflow-devtools" _project_root_path = Path(__file__).parent.parent @@ -14,69 +15,18 @@ _package_init_path = _project_root_path / "modflow_devtools" / "__init__.py" _readme_path = _project_root_path / "README.md" _docs_config_path = _project_root_path / "docs" / "conf.py" +_initial_version = Version("0.0.1") +_current_version = Version(_version_txt_path.read_text().strip()) -class Version(NamedTuple): - """Semantic version number""" - - major: int = 0 - minor: int = 0 - patch: int = 0 - - def __repr__(self): - return f"{self.major}.{self.minor}.{self.patch}" - - @classmethod - def from_string(cls, version: str) -> "Version": - t = version.split(".") - - vmajor = int(t[0]) - vminor = int(t[1]) - vpatch = int(t[2]) - - return cls(major=vmajor, minor=vminor, patch=vpatch) - - @classmethod - def from_file(cls, path: PathLike) -> "Version": - lines = [ - line.rstrip("\n") - for line in open(Path(path).expanduser().absolute(), "r") - ] - vmajor = vminor = vpatch = None - for line in lines: - line = line.strip() - if not any(line): - continue - t = line.split(".") - vmajor = int(t[0]) - vminor = int(t[1]) - vpatch = int(t[2]) - - assert ( - vmajor is not None and vminor is not None and vpatch is not None - ), "version string must follow semantic version format: major.minor.patch" - return cls(major=vmajor, minor=vminor, patch=vpatch) - - -class ReleaseType(Enum): - CANDIDATE = "Release Candidate" - RELEASE = "Release" - - -_initial_version = Version(0, 0, 1) -_current_version = Version.from_file(_version_txt_path) - - -def update_version_txt( - release_type: ReleaseType, timestamp: datetime, version: Version -): +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}") def update_init_py( - release_type: ReleaseType, timestamp: datetime, version: Version + timestamp: datetime, version: Version ): lines = _package_init_path.read_text().rstrip().split("\n") with open(_package_init_path, "w") as f: @@ -89,22 +39,8 @@ def update_init_py( print(f"Updated {_package_init_path} to version {version}") -def update_readme_markdown( - release_type: ReleaseType, timestamp: datetime, version: Version -): - lines = _readme_path.read_text().rstrip().split("\n") - with open(_readme_path, "w") as f: - for line in lines: - if "### Version " in line: - line = f"### Version {version}" - if release_type != ReleaseType.RELEASE: - line += f" — {release_type.value.lower()}" - f.write(f"{line}\n") - print(f"Updated {_readme_path} to version {version}") - - def update_docs_config( - release_type: ReleaseType, timestamp: datetime, version: Version + timestamp: datetime, version: Version ): lines = _docs_config_path.read_text().rstrip().split("\n") with open(_docs_config_path, "w") as f: @@ -116,25 +52,23 @@ def update_docs_config( def update_version( - release_type: ReleaseType, timestamp: datetime = datetime.now(), version: Version = None, ): lock_path = Path(_version_txt_path.name + ".lock") try: lock = FileLock(lock_path) - previous = Version.from_file(_version_txt_path) + previous = Version(_version_txt_path.read_text().strip()) version = ( version if version - else Version(previous.major, previous.minor, previous.patch) + else Version(previous.major, previous.minor, previous.micro) ) with lock: - update_version_txt(release_type, timestamp, version) - update_init_py(release_type, timestamp, version) - # update_readme_markdown(release_type, timestamp, version) - update_docs_config(release_type, timestamp, version) + update_version_txt(timestamp, version) + update_init_py(timestamp, version) + update_docs_config(timestamp, version) finally: try: lock_path.unlink() @@ -162,13 +96,6 @@ def update_version( required=False, help="Specify the release version", ) - parser.add_argument( - "-a", - "--approve", - required=False, - action="store_true", - help="Indicate release is approved (defaults to false for preliminary/development distributions)", - ) parser.add_argument( "-g", "--get", @@ -179,14 +106,11 @@ def update_version( args = parser.parse_args() if args.get: - print(_current_version) + print(Version(_version_txt_path.read_text().strip())) else: update_version( - release_type=ReleaseType.RELEASE - if args.approve - else ReleaseType.CANDIDATE, timestamp=datetime.now(), - version=Version.from_string(args.version) + version=Version(args.version) if args.version else _current_version, ) From c922293041ec3084e36c8ed7df493d3a3e35fb9a Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Wed, 26 Jul 2023 17:23:43 -0400 Subject: [PATCH 6/7] ci: fix release scripts and workflow (#92) --- .github/workflows/release.yml | 2 +- docs/conf.py | 2 +- modflow_devtools/__init__.py | 4 ++-- scripts/update_version.py | 2 +- version.txt | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0375ab..ad286b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -240,7 +240,7 @@ jobs: # increment minor version major_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f1) minor_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f2) - version="$major_version.$((minor_version + 1)).0" + version="$major_version.$((minor_version + 1)).0.dev0" python scripts/update_version.py -v "$version" python scripts/lint.py diff --git a/docs/conf.py b/docs/conf.py index 905fef5..60a42bc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,7 +8,7 @@ project = "modflow-devtools" author = "MODFLOW Team" -release = "0.1.9" +release = '0.2.0.dev0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/modflow_devtools/__init__.py b/modflow_devtools/__init__.py index 8a30d5e..4e9536a 100644 --- a/modflow_devtools/__init__.py +++ b/modflow_devtools/__init__.py @@ -1,6 +1,6 @@ __author__ = "Joseph D. Hughes" -__date__ = "Apr 21, 2023" -__version__ = "0.1.9" +__date__ = "Jul 26, 2023" +__version__ = "0.2.0.dev0" __maintainer__ = "Joseph D. Hughes" __email__ = "jdhughes@usgs.gov" __status__ = "Production" diff --git a/scripts/update_version.py b/scripts/update_version.py index 1c078b8..8a5abc7 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -66,7 +66,7 @@ def update_version( ) with lock: - update_version_txt(timestamp, version) + update_version_txt(version) update_init_py(timestamp, version) update_docs_config(timestamp, version) finally: diff --git a/version.txt b/version.txt index 82551ad..9a058eb 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.1.9 \ No newline at end of file +0.2.0.dev0 \ No newline at end of file From f86994a50c8498c54d69c2f4434fb0741be60785 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Jul 2023 21:25:51 +0000 Subject: [PATCH 7/7] ci(release): set version to 0.2.0, update changelog --- HISTORY.md | 6 ++++++ docs/conf.py | 2 +- modflow_devtools/__init__.py | 2 +- scripts/update_version.py | 8 ++------ version.txt | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 51bb447..6763a5d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,9 @@ +### Version 0.2.0 + +#### New features + +* [feat(set_env)](https://github.com/MODFLOW-USGS/modflow-devtools/commit/53b31cce34d221bade4c842efe3b5ed3034b2742): Add set_env contextmanager utility (#87). Committed by w-bonelli on 2023-07-26. + ### Version 0.1.8 #### New features diff --git a/docs/conf.py b/docs/conf.py index 60a42bc..663e602 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,7 +8,7 @@ project = "modflow-devtools" author = "MODFLOW Team" -release = '0.2.0.dev0' +release = "0.2.0" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/modflow_devtools/__init__.py b/modflow_devtools/__init__.py index 4e9536a..10e93c1 100644 --- a/modflow_devtools/__init__.py +++ b/modflow_devtools/__init__.py @@ -1,6 +1,6 @@ __author__ = "Joseph D. Hughes" __date__ = "Jul 26, 2023" -__version__ = "0.2.0.dev0" +__version__ = "0.2.0" __maintainer__ = "Joseph D. Hughes" __email__ = "jdhughes@usgs.gov" __status__ = "Production" diff --git a/scripts/update_version.py b/scripts/update_version.py index 8a5abc7..32878b9 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -25,9 +25,7 @@ def update_version_txt(version: Version): print(f"Updated {_version_txt_path} to version {version}") -def update_init_py( - timestamp: datetime, version: Version -): +def update_init_py(timestamp: datetime, version: Version): lines = _package_init_path.read_text().rstrip().split("\n") with open(_package_init_path, "w") as f: for line in lines: @@ -39,9 +37,7 @@ def update_init_py( print(f"Updated {_package_init_path} to version {version}") -def update_docs_config( - timestamp: datetime, version: Version -): +def update_docs_config(timestamp: datetime, version: Version): lines = _docs_config_path.read_text().rstrip().split("\n") with open(_docs_config_path, "w") as f: for line in lines: diff --git a/version.txt b/version.txt index 9a058eb..341cf11 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.2.0.dev0 \ No newline at end of file +0.2.0 \ No newline at end of file