From 81ed956bfa60764fd36b324fe7b83f7a51da7271 Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Wed, 8 Jan 2025 12:32:26 -0700 Subject: [PATCH 01/11] Fix: update release workflow to follow pypa and pypi guidelines --- CHANGELOG.md | 5 + .../workflows/release.yml | 109 +++++++++++------- 2 files changed, 70 insertions(+), 44 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..61a93e3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# pyOpenSci packaging template Changelog + +## [Unreleased] + +* Update release workflow to follow PyPA / PyPI recommended practices (@lwasser, #48) diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index 34fb5af..d948ab6 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -1,53 +1,74 @@ -name: CD +name: Publish to PyPI on: - push: - tags: - - 'v?[0-9]+.[0-9]+.[0-9]+' - - 'v?[0-9]+.[0-9]+.[0-9]+(a|b|rc|post|dev)[0-9]+' - -jobs: - prerequisites: - uses: ./.github/workflows/test.yml - release: - needs: [prerequisites] - strategy: - matrix: - os: [ubuntu-latest] - python-version: ["3.12"] - runs-on: ${{ matrix.os }} - permissions: - # Write permissions are needed to create OIDC tokens. - id-token: write - # Write permissions are needed to make GitHub releases. - contents: write + types: [published] +jobs: + # Setup build separate from publish for added security + # See https://github.com/pypa/gh-action-pypi-publish/issues/217#issuecomment-1965727093 + build: + runs-on: ubuntu-latest + # This ensures that the publish action only runs in the main repository + # rather than forks + # Environment is encouraged for increased security + environment: build steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} + - name: Checkout + uses: actions/checkout@v4 + with: + # This fetch element is only important if you are use SCM based + # versioning (that looks at git tags to gather the version) + fetch-depth: 100 + # Need the tags so that setuptools-scm can form a valid version number + - name: Fetch git tags + run: git fetch origin 'refs/tags/*:refs/tags/*' - - name: Install hatch - uses: pypa/hatch@install + - name: Setup Python + uses: actions/setup-python@v5 + with: + # You can modify what version of Python you want to use for your release + python-version: "3.11" - - name: Build package - run: hatch build + # Security recommends we should pin deps. Should we pin the workflow version? + - name: Install hatch + uses: pypa/hatch@install - # We rely on a trusted publisher configuration being present on PyPI, - # see https://docs.pypi.org/trusted-publishers/. - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Build package using Hatch + run: | + hatch build + echo "" + echo "Generated files:" + ls -lh dist/ - - name: GH release - uses: softprops/action-gh-release@v2 - with: - body: > - Please see - https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md - for the full release notes. - draft: false - prerelease: false + # Store an artifact of the build to use in the publish step below + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + publish: + name: >- + Publish Python 🐍 distribution 📦 to PyPI + # Modify the repo name below to be your project's repo name. + if: github.repository_owner == 'pyopensci' + needs: + - build + runs-on: ubuntu-latest + # Environment required here for trusted publisher + environment: + name: pypi + # Modify the url to be the name of your package + url: https://pypi.org/p/yourPackage + permissions: + id-token: write # this permission is mandatory for PyPI publishing + steps: + - name: Download dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish package to PyPI + # Only publish to real PyPI on release + if: github.event_name == 'release' + uses: pypa/gh-action-pypi-publish@release/v1 From f7ae35d21a78dd9f3693b81fb5bcb864162a8612 Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Wed, 8 Jan 2025 17:23:17 -0700 Subject: [PATCH 02/11] Fix: indentation error and readme instructions --- README.md | 22 +++++++++---------- .../workflows/release.yml | 2 +- tests/conftest.py | 9 +++++++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 24873f1..a2b4a70 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ To use this template: 1. Install copier using [pipx](https://pipx.pypa.io/stable/) or pip preferably with a [virtual environment](https://www.pyopensci.org/python-package-guide/CONTRIBUTING.html#create-a-virtual-environment). Global Installation: - + ```console pipx install copier ``` - + or Environment specific installation: ```console @@ -37,8 +37,9 @@ To use this template: ```console copier copy gh:pyopensci/pyos-package-template path/here ``` - - The command below will create the package directory in your current working directory. + + The command below will create the package directory in your current working directory. + ```console copier copy gh:pyopensci/pyos-package-template . ``` @@ -48,14 +49,13 @@ To use this template: as your source. You can read more about generating your project in the [copier documentation](https://copier.readthedocs.io/en/stable/generating/). - ## Run the template workflow -Once you have installed copier, you are ready to create your Python package template. -First, run the command below from your favorite shell. Note that this is copying our template from GitHub so it +Once you have installed copier, you are ready to create your Python package template. +First, run the command below from your favorite shell. Note that this is copying our template from GitHub so it will require internet access to run properly. -The command below will create the package directory in your current working directory. +The command below will create the package directory in your current working directory. `copier copy gh:pyopensci/pyos-package-template .` @@ -64,13 +64,13 @@ If you wish to create the package directory in another directory you can specify `copier copy gh:pyopensci/pyos-package-template dirname-here` ## Template overview -The copier template will ask you a series of questions which you can respond to. The questions will -help you customize the template. + +The copier template will ask you a series of questions which you can respond to. The questions will +help you customize the template. Below is what the template workflow will look like when you run it. In the example below, you "fully customize" the template. - ```console ➜ copier copy gh:pyopensci/pyos-package-template . 🎤 Who is the copyright holder, for example, yourself or your organization? Used in the license diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index d948ab6..48beba0 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -62,7 +62,7 @@ jobs: url: https://pypi.org/p/yourPackage permissions: id-token: write # this permission is mandatory for PyPI publishing - steps: + steps: - name: Download dists uses: actions/download-artifact@v4 with: diff --git a/tests/conftest.py b/tests/conftest.py index 888f6dd..3b70fa8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ -"""Ruff is forcing me to write a docstring for conftest.py.""" +"""Fixtures used in our test suite for the pyOpenSci Python package +template.""" import shutil from pathlib import Path @@ -16,6 +17,7 @@ COPIER_CONFIG_PATH = Path(__file__).parents[1] / "copier.yml" INCLUDES_PATH = Path(__file__).parents[1] / "includes" + def _load_copier_config() -> dict: yaml = YAML(typ="safe") with COPIER_CONFIG_PATH.open("r") as yfile: @@ -29,6 +31,7 @@ def _load_copier_config() -> dict: # pytest hooks # -------------------------------------------------- + def pytest_addoption(parser: "Parser") -> None: """Add options to pytest.""" parser.addoption( @@ -40,10 +43,12 @@ def pytest_addoption(parser: "Parser") -> None: "otherwise, use a temporary directoy and remove it afterwards.", ) + # -------------------------------------------------- # Fixtures - autouse # -------------------------------------------------- + @pytest.fixture(scope="session", autouse=True) def cleanup_hatch_envs( pytestconfig: pytest.Config, @@ -67,10 +72,12 @@ def cleanup_hatch_envs( finally: shutil.rmtree(hatch_dir, ignore_errors=True) + # --------------------------------------------- # Fixtures - exports # --------------------------------------------- + @pytest.fixture(scope="session") def monkeypatch_session() -> Generator[MonkeyPatch, None, None]: """Monkeypatch you can use with a session scoped fixture.""" From 878094c3c389cfec4ce3d86991bea0a6dedc9893 Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 14:22:31 -0700 Subject: [PATCH 03/11] Apply suggestions from code review --- .../workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index 48beba0..7850f5e 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -51,7 +51,7 @@ jobs: name: >- Publish Python 🐍 distribution 📦 to PyPI # Modify the repo name below to be your project's repo name. - if: github.repository_owner == 'pyopensci' + if: github.repository_owner == "{{ username }}" needs: - build runs-on: ubuntu-latest @@ -59,7 +59,7 @@ jobs: environment: name: pypi # Modify the url to be the name of your package - url: https://pypi.org/p/yourPackage + url: https://pypi.org/p/${{ package_name }} permissions: id-token: write # this permission is mandatory for PyPI publishing steps: From cf61545c47df40bc51f7cbeb8a6c072d82437d28 Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 14:38:18 -0700 Subject: [PATCH 04/11] Update template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml --- .../workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index 7850f5e..5c118cf 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -9,8 +9,6 @@ jobs: # See https://github.com/pypa/gh-action-pypi-publish/issues/217#issuecomment-1965727093 build: runs-on: ubuntu-latest - # This ensures that the publish action only runs in the main repository - # rather than forks # Environment is encouraged for increased security environment: build steps: From aa4c2597f7a7241779514dee5cc5de06ec899056 Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 14:38:29 -0700 Subject: [PATCH 05/11] Update template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml --- .../workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index 5c118cf..b6f8ff3 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -30,7 +30,7 @@ jobs: # Security recommends we should pin deps. Should we pin the workflow version? - name: Install hatch - uses: pypa/hatch@install + uses: pypa/hatch@a3c83ab3d481fbc2dc91dd0088628817488dd1d5 - name: Build package using Hatch run: | From ddc5597771cbf4400005d9fff8b59739db1cf1be Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 15:25:14 -0700 Subject: [PATCH 06/11] fix: add contributing instructinos --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0980df1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing to the pyOpenSci Python package template + +To work on the template locally, you can call the copier template using: + +`copier copy /path/to/your/template destination-folder` From 48f3cc22cbaa05668db0a6a4f533077bc9df4c5f Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 15:26:52 -0700 Subject: [PATCH 07/11] pydata sphinx theme as the default --- copier.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copier.yml b/copier.yml index fb1b199..dc07813 100644 --- a/copier.yml +++ b/copier.yml @@ -77,7 +77,7 @@ documentation: type: str help: "Do you want to include documentation for your project and which framework do you want to use?" choices: - "Sphinx (https://www.pyopensci.org/pyos-sphinx-theme)": sphinx + "Sphinx (https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html)": sphinx "mkdocs-material (https://squidfunk.github.io/mkdocs-material)": mkdocs No: "" default: "{% if use_default != 'minimal' %}sphinx{% else %}{% endif %}" @@ -111,7 +111,7 @@ license: type: str help: | Which license do you want to use? Includes a LICENSE file in the repository root. - For more information, see: + For more information, see: - https://www.pyopensci.org/python-package-guide/documentation/repository-files/license-files.html - https://opensource.org/licenses choices: From 375b29ebe38ce23030ed1ac3db419167fe9c8e7a Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 15:30:02 -0700 Subject: [PATCH 08/11] fix: add tests back to run before build --- .../workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index b6f8ff3..21bde1d 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -5,9 +5,12 @@ on: types: [published] jobs: + prerequisites: + uses: ./.github/workflows/test.yml # Setup build separate from publish for added security # See https://github.com/pypa/gh-action-pypi-publish/issues/217#issuecomment-1965727093 build: + needs: [prerequisites] runs-on: ubuntu-latest # Environment is encouraged for increased security environment: build @@ -50,7 +53,7 @@ jobs: Publish Python 🐍 distribution 📦 to PyPI # Modify the repo name below to be your project's repo name. if: github.repository_owner == "{{ username }}" - needs: + needs: - build runs-on: ubuntu-latest # Environment required here for trusted publisher From 471236bf6427682c0857f275c9deb83669bbdef8 Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 16:02:39 -0700 Subject: [PATCH 09/11] fix: begin to create development instruction fix: update contributing --- CONTRIBUTING.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0980df1..857a206 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,15 @@ # Contributing to the pyOpenSci Python package template -To work on the template locally, you can call the copier template using: +To work on the template locally, you can call the copier template directly. Note that by default, `copier` uses the latest tag in your commit history. To ensure it uses the latest commit on your current active branch use: -`copier copy /path/to/your/template destination-folder` +`copier copy -r HEAD /path/to/your/template destination-dir` + +If you want to test it against the latest tag in your local commit history, you can use: + +`copier copy /path/to/your/template destination-dir` + +## Run the tests + +You can use Hatch to run all of the tests for the template: + +`hatch run test:run` From 00884d7b2fd98d8bba0471a6a26a93c3dcb7e52e Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 27 Feb 2025 17:01:18 -0700 Subject: [PATCH 10/11] fix: tests - run pydoclint on src and texts add: run pydoclint on tests too --- template/pyproject.toml.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 42aea9b..c3f90c4 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -208,7 +208,7 @@ dependencies = [ detached = true [tool.hatch.envs.style.scripts] -docstrings = "pydoclint" +docstrings = "pydoclint src/ tests/" code = "ruff check {args}" format = "ruff format {args}" check = ["docstrings", "code"] From ad839c511f06ae9b5adef40ece656a49c7b655aa Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Fri, 28 Feb 2025 13:09:42 -0700 Subject: [PATCH 11/11] edits from agriyakhetarpal Co-authored-by: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> --- .../workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml index 21bde1d..0c2c107 100644 --- a/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml +++ b/template/{% if use_git and dev_platform == 'GitHub' %}.github{% endif %}/workflows/release.yml @@ -48,6 +48,7 @@ jobs: with: name: python-package-distributions path: dist/ + if-no-files-found: error publish: name: >- Publish Python 🐍 distribution 📦 to PyPI @@ -69,6 +70,7 @@ jobs: with: name: python-package-distributions path: dist/ + merge-multiple: true - name: Publish package to PyPI # Only publish to real PyPI on release if: github.event_name == 'release'