From d5e6db1f525a343850ead9e887dba5b689e6aa4d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 24 Jun 2023 14:32:16 +0200 Subject: [PATCH 1/4] ci: rename github workflow publish.yml to release.yaml: --- .github/workflows/{publish.yml => release.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{publish.yml => release.yaml} (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/release.yaml similarity index 100% rename from .github/workflows/publish.yml rename to .github/workflows/release.yaml From 357b3b6d47a49ebc996f7a74619a8e0881020ccc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 24 Jun 2023 14:56:51 +0200 Subject: [PATCH 2/4] maint: update RELEASE.md and release.yaml, use tbump --- .github/workflows/release.yaml | 24 +++++---- RELEASE.md | 90 ++++++++++++++-------------------- oauthenticator/_version.py | 19 +++---- pyproject.toml | 32 ++++++++++++ setup.py | 12 +---- 5 files changed, 90 insertions(+), 87 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index eb41ad2c..a3a7848d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,21 +1,20 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions # name: Release -# always build releases (to make sure wheel-building works) -# but only publish to PyPI on tags +# Always tests wheel building, but only publish to PyPI on pushed tags. on: pull_request: paths-ignore: - "docs/**" - ".github/workflows/*.yaml" - - "!.github/workflows/publish.yaml" + - "!.github/workflows/release.yaml" push: paths-ignore: - "docs/**" - ".github/workflows/*.yaml" - - "!.github/workflows/publish.yaml" + - "!.github/workflows/release.yaml" branches-ignore: - "dependabot/**" - "pre-commit-ci-update-config" @@ -25,11 +24,19 @@ on: jobs: build-release: runs-on: ubuntu-22.04 + permissions: + # id-token=write is required for pypa/gh-action-pypi-publish, and the PyPI + # project needs to be configured to trust this workflow. + # + # ref: https://github.com/jupyterhub/team-compass/issues/648 + # + id-token: write + steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" - name: install build package run: | @@ -43,8 +50,5 @@ jobs: ls -l dist - name: publish to pypi - uses: pypa/gh-action-pypi-publish@v1.8.6 + uses: pypa/gh-action-pypi-publish@release/v1 if: startsWith(github.ref, 'refs/tags/') - with: - user: __token__ - password: ${{ secrets.pypi_password }} diff --git a/RELEASE.md b/RELEASE.md index a3fec1f4..8b24367e 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,76 +1,60 @@ # How to make a release -`oauthenticator` is a package [available on -PyPI](https://pypi.org/project/oauthenticator/) and -[conda-forge](https://conda-forge.org/). These are instructions on how to make a -release on PyPI. The PyPI release is done automatically by TravisCI when a tag -is pushed. +`oauthenticator` is a package available on [PyPI] and on [conda-forge]. -For you to follow along according to these instructions, you need: +These are the instructions on how to make a release. -- To have push rights to the [oauthenticator GitHub - repository](https://github.com/jupyterhub/oauthenticator). +## Pre-requisites + +- Push rights to this GitHub repository ## Steps to make a release -1. Checkout main and make sure it is up to date. +1. Create a PR updating `CHANGELOG.md` with [github-activity] and continue when + its merged. - ```shell - ORIGIN=${ORIGIN:-origin} # set to the canonical remote, e.g. 'upstream' if 'origin' is not the official repo - git checkout main - git fetch $ORIGIN main - git reset --hard $ORIGIN/main - # WARNING! This next command deletes any untracked files in the repo - git clean -xfd - ``` + Advice on this procedure can be found in [this team compass + issue](https://github.com/jupyterhub/team-compass/issues/563). -1. Update [CHANGELOG.md](CHANGELOG.md). Doing this can be made easier with the - help of the - [choldgraf/github-activity](https://github.com/choldgraf/github-activity) - utility. +2. Checkout main and make sure it is up to date. -1. Set the `version_info` variable in [\_version.py](oauthenticator/_version.py) - appropriately and make a commit. - - ``` - git add oauthenticator/_version.py - VERSION=... # e.g. 1.2.3 - git commit -m "release $VERSION" + ```shell + git checkout main + git fetch origin main + git reset --hard origin/main ``` -1. Reset the `version_info` variable in - [\_version.py](oauthenticator/_version.py) appropriately with an incremented - patch version and a `dev` element, then make a commit. +3. Update the version, make commits, and push a git tag with `tbump`. - ``` - git add oauthenticator/_version.py - git commit -m "back to dev" + ```shell + pip install tbump ``` -1. Push your two commits to main. + `tbump` will ask for confirmation before doing anything. ```shell - # first push commits without a tags to ensure the - # commits comes through, because a tag can otherwise - # be pushed all alone without company of rejected - # commits, and we want have our tagged release coupled - # with a specific commit in main - git push $ORIGIN main + # Example versions to set: 1.0.0, 1.0.0b1 + VERSION= + tbump ${VERSION} ``` -1. Create a git tag for the pushed release commit and push it. + Following this, the [CI system] will build and publish a release. - ```shell - git tag -a $VERSION -m $VERSION HEAD~1 +4. Reset the version back to dev, e.g. `1.0.1.dev` after releasing `1.0.0`. - # then verify you tagged the right commit - git log - - # then push it - git push $ORIGIN refs/tags/$VERSION + ```shell + # Example version to set: 1.0.1.dev + NEXT_VERSION= + tbump --no-tag ${NEXT_VERSION}.dev ``` -1. Following the release to PyPI, an automated PR should arrive to - [conda-forge/oauthenticator-feedstock](https://github.com/conda-forge/oauthenticator-feedstock), - check for the tests to succeed on this PR and then merge it to successfully - update the package for `conda` on the conda-forge channel. +5. Following the release to PyPI, an automated PR should arrive within 24 hours + to [conda-forge/oauthenticator-feedstock] with instructions on releasing to + conda-forge. You are welcome to volunteer doing this, but aren't required as + part of making this release to PyPI. + +[github-activity]: https://github.com/executablebooks/github-activity +[pypi]: https://pypi.org/project/oauthenticator/ +[ci system]: https://github.com/jupyterhub/oauthenticator/actions/workflows/release.yaml +[conda-forge]: https://anaconda.org/conda-forge/oauthenticator +[conda-forge/oauthenticator-feedstock]: https://github.com/conda-forge/oauthenticator-feedstock diff --git a/oauthenticator/_version.py b/oauthenticator/_version.py index e8c37fce..4ceaf95c 100644 --- a/oauthenticator/_version.py +++ b/oauthenticator/_version.py @@ -1,14 +1,7 @@ -"""oauthenticator version info""" -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. +# __version__ should be updated using tbump, based on configuration in +# pyproject.toml, according to instructions in RELEASE.md. +# +__version__ = "15.1.1.dev" -version_info = ( - 15, - 1, - 1, - 'dev', # comment-out this line for a release -) -__version__ = '.'.join(map(str, version_info[:3])) - -if len(version_info) > 3: - __version__ = f'{__version__}{version_info[3]}' +# version_info looks like (1, 2, 3, "dev") if __version__ is 1.2.3.dev +version_info = tuple(int(p) if p.isdigit() else p for p in __version__.split(".")) diff --git a/pyproject.toml b/pyproject.toml index f3627e3c..a0ed1937 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,3 +39,35 @@ asyncio_mode = "auto" testpaths = [ "oauthenticator/tests" ] + + +# tbump is used to simplify and standardize the release process when updating +# the version, making a git commit and tag, and pushing changes. +# +# ref: https://github.com/your-tools/tbump#readme +# +[tool.tbump] +github_url = "https://github.com/jupyterhub/oauthenticator" + +[tool.tbump.version] +current = "15.1.1.dev" +regex = ''' + (?P\d+) + \. + (?P\d+) + \. + (?P\d+) + (?P
((a|b|rc)\d+)|)
+    \.?
+    (?P(?<=\.)dev\d*|)
+'''
+
+[tool.tbump.git]
+message_template = "Bump to {new_version}"
+tag_template = "{new_version}"
+
+[[tool.tbump.file]]
+src = "setup.py"
+
+[[tool.tbump.file]]
+src = "oauthenticator/_version.py"
diff --git a/setup.py b/setup.py
index 5a9f6fc7..47848583 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,6 @@
 # Minimal Python version sanity check (from IPython/Jupyterhub)
 # -----------------------------------------------------------------------------
 
-import os
 import sys
 
 from setuptools import find_packages, setup
@@ -25,19 +24,10 @@ def run(self):
         )
 
 
-pjoin = os.path.join
-here = os.path.abspath(os.path.dirname(__file__))
-
-# Get the current package version.
-version_ns = {}
-with open(pjoin(here, 'oauthenticator', '_version.py')) as f:
-    exec(f.read(), {}, version_ns)
-
-
 setup_args = dict(
     name='oauthenticator',
     packages=find_packages(),
-    version=version_ns['__version__'],
+    version="15.1.1.dev",
     description="OAuthenticator: Authenticate JupyterHub users with common OAuth providers",
     long_description=open("README.md").read(),
     long_description_content_type="text/markdown",

From 6f2bfca51a4dc1234177f04bb6b9aca376da4676 Mon Sep 17 00:00:00 2001
From: Erik Sundell 
Date: Sat, 24 Jun 2023 14:58:23 +0200
Subject: [PATCH 3/4] maint: misc details to flake8/pytest/codecov config

---
 .coveragerc                |  2 --
 .flake8                    | 12 ++++++++----
 .github/workflows/test.yml |  3 ++-
 .readthedocs.yaml          |  2 --
 pyproject.toml             | 17 ++++++++++++++++-
 5 files changed, 26 insertions(+), 10 deletions(-)
 delete mode 100644 .coveragerc

diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index 5a20d721..00000000
--- a/.coveragerc
+++ /dev/null
@@ -1,2 +0,0 @@
-[report]
-omit = *test*
diff --git a/.flake8 b/.flake8
index 5a429973..f6fcb84e 100644
--- a/.flake8
+++ b/.flake8
@@ -1,12 +1,16 @@
+# flake8 is used for linting Python code setup to automatically run with
+# pre-commit.
+#
+# ref: https://flake8.pycqa.org/en/latest/user/configuration.html
+#
+
 [flake8]
 # Ignore style and complexity
 # E: style errors
 # W: style warnings
 # C: complexity
-# F841: local variable assigned but never used
-ignore = E, C, W, F841
+# D: docstring warnings (unused pydocstyle extension)
+ignore = E, C, W, D
 builtins =
     c
     get_config
-exclude =
-    __init__.py
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index e463c553..a2b62a76 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -66,6 +66,7 @@ jobs:
 
       - name: Run tests
         run: |
-          pytest -v --color=yes --cov=oauthenticator oauthenticator
+          pytest
 
+      # GitHub action reference: https://github.com/codecov/codecov-action
       - uses: codecov/codecov-action@v3
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 4302b184..bbe94147 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -12,8 +12,6 @@ build:
 sphinx:
   configuration: docs/source/conf.py
 
-formats: []
-
 python:
   install:
     - requirements: docs/requirements.txt
diff --git a/pyproject.toml b/pyproject.toml
index a0ed1937..9d667f83 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -33,14 +33,29 @@ target_version = [
     "py311",
 ]
 
+
+# pytest is used for running Python based tests
+#
+# ref: https://docs.pytest.org/en/stable/
+#
 [tool.pytest.ini_options]
-addopts = "--verbose --color=yes --durations=10"
+addopts = "--verbose --color=yes --durations=10 --cov=oauthenticator"
 asyncio_mode = "auto"
 testpaths = [
     "oauthenticator/tests"
 ]
 
 
+# pytest-cov / coverage is used to measure code coverage of tests
+#
+# ref: https://coverage.readthedocs.io/en/stable/config.html
+#
+[tool.coverage.run]
+omit = [
+  "oauthenticator/tests/**",
+]
+
+
 # tbump is used to simplify and standardize the release process when updating
 # the version, making a git commit and tag, and pushing changes.
 #

From 7d701830c1c20cad1ae8a06a45f78d321a88caa9 Mon Sep 17 00:00:00 2001
From: Erik Sundell 
Date: Sat, 24 Jun 2023 14:58:39 +0200
Subject: [PATCH 4/4] maint: please new flake8 config

---
 oauthenticator/__init__.py           | 12 ++++++------
 oauthenticator/tests/mocks.py        |  2 +-
 oauthenticator/tests/test_cilogon.py |  6 +++---
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/oauthenticator/__init__.py b/oauthenticator/__init__.py
index 72b62466..5bfaabe9 100644
--- a/oauthenticator/__init__.py
+++ b/oauthenticator/__init__.py
@@ -1,8 +1,8 @@
 # include github, bitbucket, google here for backward-compatibility
 # don't add new oauthenticators here.
-from ._version import __version__, version_info
-from .bitbucket import *
-from .cilogon import *
-from .github import *
-from .google import *
-from .oauth2 import *
+from ._version import __version__, version_info  # noqa
+from .bitbucket import *  # noqa
+from .cilogon import *  # noqa
+from .github import *  # noqa
+from .google import *  # noqa
+from .oauth2 import *  # noqa
diff --git a/oauthenticator/tests/mocks.py b/oauthenticator/tests/mocks.py
index b896c4db..b20af39e 100644
--- a/oauthenticator/tests/mocks.py
+++ b/oauthenticator/tests/mocks.py
@@ -253,5 +253,5 @@ async def no_code_test(authenticator):
     handler = Mock(spec=web.RequestHandler)
     handler.get_argument = Mock(return_value=None)
     with pytest.raises(web.HTTPError) as exc:
-        auth_model = await authenticator.get_authenticated_user(handler, None)
+        await authenticator.get_authenticated_user(handler, None)
     assert exc.value.status_code == 400
diff --git a/oauthenticator/tests/test_cilogon.py b/oauthenticator/tests/test_cilogon.py
index 5e0c44a6..bb7b2f8a 100644
--- a/oauthenticator/tests/test_cilogon.py
+++ b/oauthenticator/tests/test_cilogon.py
@@ -81,7 +81,7 @@ async def test_cilogon_missing_alternate_claim(cilogon_client):
         alternative_user_model('jtkirk@ufp.gov', 'uid')
     )
     with raises(HTTPError):
-        auth_model = await authenticator.get_authenticated_user(handler, None)
+        await authenticator.get_authenticated_user(handler, None)
 
 
 async def test_deprecated_config(caplog):
@@ -188,7 +188,7 @@ async def test_allowed_idps_invalid_config_username_derivation_options(caplog):
         }
     }
 
-    with raises(ValidationError, match='Additional properties are not allowed') as e:
+    with raises(ValidationError, match='Additional properties are not allowed'):
         CILogonOAuthenticator(config=cfg)
 
 
@@ -331,7 +331,7 @@ async def test_not_allowed_domains_and_stripping(cilogon_client):
 
     # The domain to be stripped isn't allowed, so it should fail
     with raises(HTTPError):
-        auth_model = await authenticator.get_authenticated_user(handler, None)
+        await authenticator.get_authenticated_user(handler, None)
 
 
 async def test_allowed_domains_and_stripping(cilogon_client):