Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: Streamline nox sessions #32

Merged
merged 2 commits into from
Dec 12, 2023
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
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