Skip to content

Commit

Permalink
Merge pull request #32 from ESKYoung/streamline-nox
Browse files Browse the repository at this point in the history
ci: Streamline `nox` sessions
  • Loading branch information
ESKYoung authored Dec 12, 2023
2 parents d9309e3 + 6da2b58 commit 3012b7a
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 215 deletions.
2 changes: 1 addition & 1 deletion .github/actions/install-poetry/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ runs:
using: "composite"
steps:
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}

Expand Down
23 changes: 6 additions & 17 deletions .github/workflows/nox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest]
python: ["3.9", "3.10"]
nox_session: ["_example", "docs", "pre-commit", "testing"]
nox_session: ["docs", "example", "pre-commit", "testing"]
exclude:
- os: macos-latest
nox_session: "docs"
Expand All @@ -27,29 +27,18 @@ jobs:

steps:
- name: Checkout the revision
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Poetry
id: install-poetry
uses: ./.github/actions/install-poetry
with:
python-version: ${{ matrix.python }}

- name: Install dependencies for `_example` `nox` session
if: ${{ matrix.nox_session == '_example' }}
run: poetry install --with=ci-cd --sync --no-ansi

- name: Install dependencies for `docs` `nox` session
if: ${{ matrix.nox_session == 'docs' }}
run: poetry install --with=ci-cd,docs --sync --no-ansi

- name: Install dependencies for `pre-commit` `nox` session
if: ${{ matrix.nox_session == 'pre-commit' }}
run: poetry install --with=ci-cd,pre-commit,testing --sync --no-ansi

- name: Install dependencies for `testing` `nox` session
if: ${{ matrix.nox_session == 'testing' }}
run: poetry install --with=ci-cd,testing --sync --no-ansi
- name: Install dependencies
run: poetry install --with=ci-cd --sync --no-ansi --no-root
shell: bash

- name: Run `nox` session `${{ matrix.nox_session }}`
run: poetry run nox --force-python python --session ${{ matrix.nox_session }}
shell: bash
4 changes: 3 additions & 1 deletion .github/workflows/sphinx-build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

steps:
- name: Checkout the revision
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Poetry
uses: ./.github/actions/install-poetry
Expand All @@ -19,9 +19,11 @@ jobs:

- name: Install dependencies
run: poetry install --with=docs --sync --no-ansi
shell: bash

- name: Build documentation
run: poetry run sphinx-build -b html docs/ docs/_build
shell: bash

- name: Deploy documentation to GitHub Pages
if: ${{ startsWith(github.ref, 'refs/tags') && !env.ACT }}
Expand Down
148 changes: 45 additions & 103 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""Define ``nox`` sessions."""

import os
import sys
from importlib.util import module_from_spec, spec_from_file_location
from tempfile import TemporaryDirectory
from typing import Iterable, Optional

import nox
from nox_poetry import Session, session

# Define the Python versions under test
PYTHON_VERSIONS = ["3.9", "3.10"]
Expand All @@ -16,123 +16,87 @@
# Set `nox` to stop the session on the first failure
nox.options.stop_on_first_error = True


def install_example_group_dependencies(
nox_session: Session,
groups: Optional[Iterable[str]] = None,
) -> None:
"""Install Poetry group dependencies with the root package.
Args:
nox_session (Session): a ``nox_poetry.Session`` object.
groups (Optional[Iterable[str]], optional): an iterable of group names from
``pyproject.toml``, which contain packages required for this session. If
``None``, only the ``ci-cd`` dependency group will be installed.
Returns:
None.
"""
# Create a temporary folder to store a requirements file of the root package, its
# main dependencies, and specified group dependencies
temporary_directory_path = nox_session.create_tmp()
temporary_requirements = os.path.join(
temporary_directory_path,
f"requirements-{nox_session.name}.txt",
)

# Export specific groups to a `requirements.txt` file, and then install them
nox_session.run_always(
"poetry",
"export",
"--format=requirements.txt",
f"--output={temporary_requirements}",
"--with={}".format(",".join({"ci-cd", *groups}) if groups else "ci-cd"),
external=True,
)
nox_session.run_always(
"pip",
"install",
"-r",
temporary_requirements,
"--quiet",
external=True,
)
nox_session.poetry.installroot()
# Load the `install_poetry_venv` function from the template to prevent duplication of
# code
template_noxfile_specification = spec_from_file_location(
"template_noxfile", "{{ cookiecutter.repository_name }}/noxfile.py"
)
if template_noxfile_specification and template_noxfile_specification.loader:
template_noxfile_module = module_from_spec(template_noxfile_specification)
sys.modules["template_noxfile"] = template_noxfile_module
template_noxfile_specification.loader.exec_module(template_noxfile_module)
install_poetry_venv = template_noxfile_module.install_poetry_venv
else:
raise ImportError("Could not load `install_poetry_venv` from template repository!")


@session(
name="pre-commit", # session name must match Poetry group dependency name
@nox.session(
name="pre-commit",
python=PYTHON_VERSIONS,
)
def run_pre_commit_hooks_on_all_files(nox_session: Session) -> None:
def run_pre_commit_hooks_on_all_files(nox_session: nox.Session) -> None:
"""Run pre-commit hooks on all files, and check the hooks pass.
Args:
nox_session (Session): a ``nox_poetry.Session`` object.
Returns:
None.
nox_session (Session): a ``nox.Session`` object.
"""
nox_session.install(".")
install_poetry_venv(
nox_session=nox_session,
dependency_groups=["pre-commit", "testing"],
root=False,
)
args = nox_session.posargs or ["run", "--all-files", "--show-diff-on-failure"]
nox_session.run("pre-commit", *args, external=True)


@session(
name="testing", # session name must match Poetry group dependency name
@nox.session(
name="testing",
python=PYTHON_VERSIONS,
)
def run_pytest_suite(nox_session: Session) -> None:
def run_pytest_suite(nox_session: nox.Session) -> None:
"""Check all tests pass, and check for code coverage.
Args:
nox_session (Session): a ``nox_poetry.Session`` object.
Returns:
None.
nox_session (Session): a ``nox.Session`` object.
"""
nox_session.install(".")
install_poetry_venv(nox_session=nox_session, dependency_groups=["testing"])
args = nox_session.posargs or ["--cov"]
nox_session.run("pytest", *args, success_codes=[0, 5], external=True)


@session(
name="docs", # session name must match Poetry group dependency name
@nox.session(
name="docs",
python=PYTHON_VERSIONS,
)
@nox.parametrize("builder", ["html", "linkcheck"])
def build_and_test_sphinx_documentation(nox_session: Session, builder: str) -> None:
def build_and_test_sphinx_documentation(nox_session: nox.Session, builder: str) -> None:
"""Build the Sphinx documentation, and check it works correctly.
Args:
nox_session (Session): a ``nox.Session`` object.
builder (str): a valid ``Sphinx`` builder name.
Returns:
None.
"""
nox_session.install(".")
install_poetry_venv(nox_session=nox_session, dependency_groups=["docs"])
docs_build_directory = os.path.join(nox_session.create_tmp(), "docs/_build")
args = nox_session.posargs or ["docs", docs_build_directory]
nox_session.run("sphinx-build", "-b", builder, *args, external=True)


@session(name="_example", python=PYTHON_VERSIONS)
def build_example_project(nox_session: Session) -> None:
@nox.session(name="example", python=PYTHON_VERSIONS)
def build_example_project(nox_session: nox.Session) -> None:
"""Build an example project to test it has been developed created.
Args:
nox_session (Session): a ``nox_poetry.Session`` object.
Note, during development, you must commit any changes before running this Nox
session, as ``cruft`` requires Git commits to propagate any development changes.
Returns:
None.
Args:
nox_session (Session): a ``nox.Session`` object.
"""
nox_session.install(".")
install_poetry_venv(nox_session=nox_session, root=False)

# Create a temporary directory, where an example project will be built for testing
# purposes. This is required, as the example project needs Git initialised, and it
Expand All @@ -141,44 +105,22 @@ def build_example_project(nox_session: Session) -> None:
example_project_repository = os.path.join(
temporary_directory, "example-project"
)
example_project_requirements = os.path.join(
example_project_repository,
f"requirements-{nox_session.name}.txt",
)

# Run `cookiecutter` using this project to create an example project called
# "Example Project"; use the default values for all other inputs
# Use `cruft` to create an example project called `Example Project` from this
# cookiecutter template; use the default values for all other inputs
nox_session.run(
"cookiecutter",
"cruft",
"create",
f"--output-dir={temporary_directory}",
'--extra-context={"project_name": "Example Project"}',
"--no-input",
os.getcwd(),
"project_name=Example Project",
external=True,
)

# Change the current working directory for the `nox` session
nox_session.chdir(example_project_repository)

# Remove any Python packages in the current `nox` session
with open(example_project_requirements, "w") as f:
f.write(str(nox_session.run("pip", "freeze", silent=True, external=True)))
nox_session.run(
"pip",
"uninstall",
"--yes",
"--quiet",
f"--requirement={example_project_requirements}",
external=True,
)

# Install all the example project dependencies; this is necessary, otherwise
# dependencies will not be installed correctly, and nested `nox` sessions will
# fail
install_example_group_dependencies(
nox_session=nox_session,
groups=["cookiecutter", "docs", "notebook", "pre-commit", "testing"],
)

# Initialise Git in the example project, and stage all changes
nox_session.run("git", "init", "--quiet", external=True)
nox_session.run("git", "add", ".", external=True)
Expand Down
Loading

0 comments on commit 3012b7a

Please sign in to comment.