From 75c8dbfe09185d1939626ff0ec96a980540e064d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 01:57:00 +0000 Subject: [PATCH 1/3] Trim template deps; align GitHub Actions and GitLab CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pyproject.toml: empty default `dependencies` (template should not pre-pick a stack). Move matplotlib/numpy/pandas/scikit-learn/scipy/seaborn into a new `science` extra with loose floors that match Python 3.13 wheel availability. Drop tqdm/watermark/missingno/wandb entirely — niche enough that users should add them explicitly. - .gitlab-ci.yml: rewrite to mirror python-app.yml — same 3.10–3.13 matrix, same install/pytest commands, plus a pre-commit job and `interruptible: true` to match GitHub's concurrency cancellation. Fix coverage regex so 0% parses. Add per-job timeouts. - python-app.yml + .gitlab-ci.yml: prominent KEEP IN SYNC header pointing at the sibling file. - README: document the new `science` extra and the dual-CI policy. --- .github/workflows/python-app.yml | 6 ++-- .gitlab-ci.yml | 61 ++++++++++++++++++++++---------- README.md | 25 ++++++++++--- pyproject.toml | 26 +++++++------- 4 files changed, 80 insertions(+), 38 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 1ed8739..0b1f7d2 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -1,5 +1,7 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions +# KEEP IN SYNC with .gitlab-ci.yml. This template ships CI for both GitHub and +# GitLab so the package can move between them without rewriting pipelines. +# When you change the test matrix, install command, or pytest invocation here, +# mirror the change in .gitlab-ci.yml. # # Third-party actions are pinned to commit SHAs (with version comments) for # supply-chain safety. Dependabot's `github-actions` ecosystem keeps both the diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 998106a..d3ab230 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,22 +1,45 @@ -# Optional GitLab CI mirror of `.github/workflows/python-app.yml`. -# https://medium.com/swlh/automate-testing-with-gitlab-pipelines-4d35c72c18a +# KEEP IN SYNC with .github/workflows/python-app.yml and pre-commit.yml. +# This template ships CI for both GitHub and GitLab. The test stage below is +# behaviorally equivalent to python-app.yml: same Python matrix (3.10–3.13), +# same install command (pip install -e ".[test]"), same pytest invocation. +# Coverage is exported as Cobertura XML (GitLab's native format) instead of +# uploaded to Codecov. +# +# CodeQL has no GitLab equivalent here; if you host on GitLab, consider +# GitLab's built-in SAST template instead: +# https://docs.gitlab.com/ee/user/application_security/sast/ + default: - image: python:3.12 - before_script: - - python -m pip install --upgrade pip - - pip install -e ".[test]" + interruptible: true # cancel superseded pipelines, mirroring GH `concurrency` + stages: - - test + - lint + - test + +pre-commit: + stage: lint + image: python:3.12 + timeout: 10m + script: + - python -m pip install --upgrade pip + - pip install pre-commit + - pre-commit run --all-files + test: - parallel: - matrix: - - PYTHON_VERSION: ["3.10", "3.11", "3.12", "3.13"] - image: python:$PYTHON_VERSION - script: - - pytest --cov --cov-report=term --cov-report=xml - coverage: '/TOTAL.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' - artifacts: - reports: - coverage_report: - coverage_format: cobertura - path: coverage.xml + stage: test + timeout: 15m + parallel: + matrix: + - PYTHON_VERSION: ["3.10", "3.11", "3.12", "3.13"] + image: python:$PYTHON_VERSION + before_script: + - python -m pip install --upgrade pip + - pip install -e ".[test]" + script: + - pytest --cov --cov-report=term --cov-report=xml + coverage: '/TOTAL.*? (\d+(?:\.\d+)?\%)$/' + artifacts: + reports: + coverage_report: + coverage_format: cobertura + path: coverage.xml diff --git a/README.md b/README.md index 8c9132e..2717520 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,12 @@ python -m ipykernel install --user --name=awesome_env # register Jupyter kerne jupyter notebook --port 4321 # launch on localhost (default auth enabled) ``` -The package declares the following extras in `pyproject.toml`: +The package declares the following extras in `pyproject.toml`. The default +`dependencies` list is **empty** on purpose — add only what your package +actually imports. The `science` extra is opt-in so users who don't need it +aren't forced to install hundreds of MB of wheels. +- `science` — `matplotlib`, `numpy`, `pandas`, `scikit-learn`, `scipy`, `seaborn` with loose floors - `test` — `pytest` + `pytest-cov` (what CI installs) - `docs` — Sphinx and the theme/extensions used by `docs/conf.py` - `notebook` — `IPython`, `ipywidgets`, `ipykernel` @@ -112,11 +116,22 @@ Code coverage is wired to [codecov.io](https://app.codecov.io/). Enable the repo CI/CD --- -Three additional workflows keep the repo healthy with no extra setup: +The template ships CI for **both GitHub and GitLab** so you can host on either +without rewriting pipelines. The two configs are kept behaviorally equivalent +for the test stage (same Python matrix, same install command, same coverage +report). When you change one, **mirror the change in the other**; both files +have a `KEEP IN SYNC` header pointing at the other. -- `.github/workflows/pre-commit.yml` runs the pre-commit hooks on every push and PR to `main`, so lint/format failures are caught in CI rather than only locally. -- `.github/workflows/codeql.yml` runs GitHub's [CodeQL](https://codeql.github.com/) static-analysis scanner on every push, every PR, and weekly on Mondays. Findings appear under the repo's *Security → Code scanning* tab. -- `.github/dependabot.yml` opens weekly PRs to update GitHub Actions versions and pip dependencies. Tune the `interval` or `open-pull-requests-limit` if the volume is too high. +GitHub-specific workflows (with no GitLab equivalent in this template): + +- `.github/workflows/python-app.yml` — test matrix on 3.10–3.13, coverage to Codecov. +- `.github/workflows/pre-commit.yml` — runs pre-commit hooks on push and PR to `main`. +- `.github/workflows/codeql.yml` — [CodeQL](https://codeql.github.com/) static analysis on push, PR, and weekly Mondays. Findings appear under *Security → Code scanning*. +- `.github/dependabot.yml` — weekly PRs to update GitHub Actions and pip dependencies. Tune `interval` or `open-pull-requests-limit` if volume is too high. + +GitLab equivalent: + +- `.gitlab-ci.yml` — mirrors the test matrix and adds a `pre-commit` job. CodeQL has no GitLab analogue here; if you host on GitLab, consider GitLab's built-in [SAST](https://docs.gitlab.com/ee/user/application_security/sast/) instead. Linting and Formatting --- diff --git a/pyproject.toml b/pyproject.toml index 1007fb9..083c63c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,20 +30,22 @@ readme = "README.md" # is populated. See PEP 639. license = {file = "LICENSE.md"} requires-python = ">=3.10" -dependencies = [ - "matplotlib>=3.9.0", - "numpy>=2.2.6", - "pandas>=2.1.4", - "scikit-learn>=1.7.2", - "scipy>=1.13.1", - "seaborn>=0.13.0", - "tqdm>=4.67.3", - "watermark>=2.6.0", - "missingno>=0.5.2", - "wandb>=0.26.1", -] +# Intentionally empty: this is a template. Add only what your package actually +# imports at runtime. Heavy science deps live in the `science` extra below so +# users opt in. +dependencies = [] [project.optional-dependencies] +science = [ + # Loose floors chosen to match Python 3.13 wheel availability on PyPI. + # Tighten only when your code actually depends on a newer API. + "matplotlib>=3.9", + "numpy>=2.1", + "pandas>=2.2", + "scikit-learn>=1.5", + "scipy>=1.14", + "seaborn>=0.13", +] test = [ "pytest>=7.4.0", "pytest-cov", From 3404ff22a59ba51eb5b508f570aeec13016377c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 02:18:59 +0000 Subject: [PATCH 2/3] Add personalize.sh and wire mypy into CI scripts/personalize.sh: one-shot rewrite of every place the original author's identity is baked in (package name, GitHub username, author, email, ORCID, codecov token) plus directory rename. Required flags --name, --username, --author, --email; optional --orcid (line is removed from CITATION.cff if omitted). README step 2 now references the script instead of an inline sed. Added explicit checklist items for logo and codecov-token replacement (previously easy to miss). mypy: add [tool.mypy] with permissive defaults (ignore_missing_imports, check_untyped_defs, no disallow_untyped_defs) so a fresh checkout passes. Ship a PEP 561 py.typed marker via package-data. Added a mypy step to python-app.yml (3.12 only, alongside test) and a parallel mypy job to the GitLab lint stage. Updated the README Typing section. --- .github/workflows/python-app.yml | 5 ++ .gitlab-ci.yml | 9 ++ README.md | 42 ++++----- my_package/py.typed | 0 pyproject.toml | 16 ++++ scripts/personalize.sh | 149 +++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 my_package/py.typed create mode 100755 scripts/personalize.sh diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 0b1f7d2..ade5ed0 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -42,6 +42,11 @@ jobs: run: | python -m pip install --upgrade pip pip install -e ".[test]" + - name: Type-check with mypy + if: matrix.python-version == '3.12' + run: | + pip install mypy + mypy my_package - name: Run tests with coverage run: pytest --cov --cov-report=xml - name: Upload coverage report diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d3ab230..5182dd7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,6 +25,15 @@ pre-commit: - pip install pre-commit - pre-commit run --all-files +mypy: + stage: lint + image: python:3.12 + timeout: 10m + script: + - python -m pip install --upgrade pip + - pip install -e ".[test]" mypy + - mypy my_package + test: stage: test timeout: 15m diff --git a/README.md b/README.md index 2717520..9f445b8 100644 --- a/README.md +++ b/README.md @@ -14,33 +14,30 @@ PyPI Package Template Instructions 1. Choose a name that does not exist in [pypi](https://pypi.org/). You can check by going to `https://pypi.org/simple/{my_awesome_new_package}`; a 404 means the name is available. -2. Replace all `my_package` names, strings, etc. in this repo with your desired package name (e.g., `my_awesome_new_package`). The command below makes this simple; run it after cloning the repo locally. It restricts the rewrite to plain-text files and skips `.git/` so binaries (logos, etc.) are left alone. The `sed` syntax below is for GNU `sed` (Linux); on macOS use `sed -i ''` instead of `sed -i`. +2. Run the personalization script. It rewrites every place the original author's identity is baked in (package name, GitHub username, author name, email, ORCID, codecov token) and renames the `my_package/` directory in one shot: ```bash -find . -type f \ - -not -path "./.git/*" \ - \( -name "*.py" -o -name "*.md" -o -name "*.rst" -o -name "*.toml" \ - -o -name "*.cfg" -o -name "*.yml" -o -name "*.yaml" -o -name "*.in" \ - -o -name "*.cff" -o -name "*.sh" -o -name "Makefile*" -o -name "CODEOWNERS" \) \ - -exec sed -i "s/my_package/my_awesome_new_package/g" {} + +scripts/personalize.sh \ + --name my_awesome_new_package \ + --username my-github-handle \ + --author "Jane Doe" \ + --email jane@example.com \ + --orcid 0000-0001-2345-6789 # optional; line is dropped from CITATION.cff if omitted ``` -Similarly, replace all instances of `mahynski` with your GitHub username, and rename the `my_package/` directory to match your chosen name. - -```bash -git mv my_package my_awesome_new_package -git commit -m "rename package to my_awesome_new_package" -``` +Review the diff (`git diff`), then commit. Once you're satisfied you can delete the script with `rm -r scripts/`. 3. Replace the placeholder `LICENSE.md` with the full text of your chosen license (see [choosealicense.com](https://choosealicense.com/)) and update the `license` field in `pyproject.toml` accordingly. Per [PEP 639](https://peps.python.org/pep-0639/), prefer an SPDX expression: `license = "MIT"`. -4. Get coding! Follow the best practices below. When you are ready to publish, proceed to the next step. -5. Before tagging a release, update **all** of: +4. Replace `docs/_static/logo.png` and `logo_transparent.png` with your own art (or remove `html_logo` from `docs/conf.py`). The committed art is AI-generated `my_package` placeholder. +5. Once codecov is enabled for the new repo, replace `REPLACE_WITH_CODECOV_TOKEN` in `README.md` and `docs/index.rst` with your project's token. +6. Get coding! Follow the best practices below. When you are ready to publish, proceed to the next step. +7. Before tagging a release, update **all** of: - `__version__` in `my_package/__init__.py` - `version:` and `date-released:` in `CITATION.cff` (and the `doi:` once Zenodo mints it) - the `Development Status` classifier in `pyproject.toml` -6. When finished, first enable preservation of this repo on [Zenodo](https://zenodo.org/), then create a release on GitHub. Zenodo will detect the new release and mint a DOI and badge. Update the `{github_id}` placeholder in this `README.md` and in `docs/index.rst` with the ID generated by Zenodo. -7. Follow [these instructions](https://packaging.python.org/en/latest/tutorials/packaging-projects/) to publish to PyPI. -8. The included GitHub Actions workflows trigger on pushes and PRs to `main`. If you adopt a `dev` → `main` pattern for releases, update the `on:` blocks in `.github/workflows/*.yml` so CI runs on `dev` too. +8. When finished, first enable preservation of this repo on [Zenodo](https://zenodo.org/), then create a release on GitHub. Zenodo will detect the new release and mint a DOI and badge. Update the `{github_id}` placeholder in this `README.md` and in `docs/index.rst` with the ID generated by Zenodo. +9. Follow [these instructions](https://packaging.python.org/en/latest/tutorials/packaging-projects/) to publish to PyPI. +10. The included GitHub Actions workflows trigger on pushes and PRs to `main`. If you adopt a `dev` → `main` pattern for releases, update the `on:` blocks in `.github/workflows/*.yml` so CI runs on `dev` too. Best Practices === @@ -153,12 +150,17 @@ pre-commit run --all-files Typing --- -Type hints are optional but recommended — see the [typing docs](https://docs.python.org/3/library/typing.html) and [mypy](https://mypy-lang.org/). Mypy is not run by pre-commit in this template; invoke it manually as needed: +Type hints are optional but recommended — see the [typing docs](https://docs.python.org/3/library/typing.html) and [mypy](https://mypy-lang.org/). [Mypy](https://mypy-lang.org/) is configured under `[tool.mypy]` in `pyproject.toml` and runs in CI on every push/PR (the GitHub `python-app.yml` test job runs it on Python 3.12; the GitLab `mypy` job runs in the `lint` stage). The package ships a [PEP 561](https://peps.python.org/pep-0561/) `py.typed` marker so downstream consumers see your annotations. + +Run mypy locally the same way CI does: ```bash -mypy --ignore-missing-imports my_new_file.py +pip install -e ".[dev]" +mypy my_package ``` +The default config is permissive (`ignore_missing_imports`, `check_untyped_defs`, no `disallow_untyped_defs`) so a fresh template checkout passes without effort. Tighten it as your code grows — e.g., flip on `disallow_untyped_defs = true` once you're ready to require annotations on public APIs. + Demo --- diff --git a/my_package/py.typed b/my_package/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 083c63c..60f7db4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,10 +99,26 @@ select = [ "UP", # pyupgrade ] +[tool.mypy] +# Permissive defaults so a fresh template checkout passes. Tighten as your +# code grows: turn on `disallow_untyped_defs = true` once you're ready to +# require type annotations on public APIs. +python_version = "3.10" +ignore_missing_imports = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_unused_configs = true +strict_equality = true +check_untyped_defs = true + [tool.setuptools.packages.find] where = ["."] include = ["my_package*"] +[tool.setuptools.package-data] +# Ship the PEP 561 marker so downstream consumers see the package's type hints. +my_package = ["py.typed"] + [tool.setuptools.dynamic] version = {attr = "my_package.__version__"} diff --git a/scripts/personalize.sh b/scripts/personalize.sh new file mode 100755 index 0000000..f5c81a6 --- /dev/null +++ b/scripts/personalize.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash +# Personalize this template for your project. +# +# Replaces every place the original author's identity is baked in +# (package name, GitHub username, author name, email, ORCID, codecov token) +# and renames the package directory. Run once, after cloning. +# +# Usage: +# scripts/personalize.sh \ +# --name my_awesome_package \ +# --username my-github-handle \ +# --author "Jane Doe" \ +# --email jane@example.com \ +# [--orcid 0000-0001-2345-6789] +# +# Flags --name, --username, --author, --email are required. --orcid is +# optional; if omitted, the orcid line is removed from CITATION.cff. +# +# After running, manually: +# 1. Replace LICENSE.md with your chosen license and update the `license` +# field in pyproject.toml to its SPDX expression. +# 2. Replace docs/_static/logo*.png with your own art (or remove +# `html_logo` from docs/conf.py). +# 3. Once codecov is enabled for the new repo, replace the placeholder +# token (the script leaves it as REPLACE_WITH_CODECOV_TOKEN). +# 4. Once Zenodo mints a DOI, fill in CITATION.cff and the badge in +# README.md / docs/index.rst. +# 5. Update pyproject.toml's `description` and the package docstring +# in /__init__.py. +# +# When you're satisfied, delete this script: `rm -r scripts/`. + +set -euo pipefail + +# ---- defaults (these are the strings to replace) ------------------------- +OLD_NAME="my_package" +OLD_USERNAME="mahynski" +OLD_AUTHOR="Nathan A. Mahynski" +OLD_EMAIL="nathan.mahynski@gmail.com" +OLD_ORCID="0000-0002-0008-8749" +OLD_CODECOV_TOKEN="YSLBQ33C7F" + +NEW_NAME="" +NEW_USERNAME="" +NEW_AUTHOR="" +NEW_EMAIL="" +NEW_ORCID="" + +# ---- arg parse ----------------------------------------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --name) NEW_NAME="$2"; shift 2 ;; + --username) NEW_USERNAME="$2"; shift 2 ;; + --author) NEW_AUTHOR="$2"; shift 2 ;; + --email) NEW_EMAIL="$2"; shift 2 ;; + --orcid) NEW_ORCID="$2"; shift 2 ;; + -h|--help) + sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//' + exit 0 + ;; + *) echo "Unknown arg: $1" >&2; exit 2 ;; + esac +done + +for var in NEW_NAME NEW_USERNAME NEW_AUTHOR NEW_EMAIL; do + if [[ -z "${!var}" ]]; then + echo "Missing required flag for ${var#NEW_} (lowercased). See --help." >&2 + exit 2 + fi +done + +# CITATION.cff stores given/family names as separate fields. Split the new +# author on the last space: "Jane Doe" -> given="Jane", family="Doe". +# "Jane A. Doe" -> given="Jane A.", family="Doe". +NEW_FAMILY="${NEW_AUTHOR##* }" +NEW_GIVEN="${NEW_AUTHOR% *}" +OLD_FAMILY="Mahynski" +OLD_GIVEN="Nathan" + +# ---- platform-aware sed -i ---------------------------------------------- +if [[ "$(uname)" == "Darwin" ]]; then + sedi=(sed -i '') +else + sedi=(sed -i) +fi + +# ---- collect target files (plain text, exclude .git, _build, this script) - +mapfile -t files < <(find . -type f \ + -not -path "./.git/*" \ + -not -path "./docs/_build/*" \ + -not -path "./scripts/personalize.sh" \ + \( -name "*.py" -o -name "*.md" -o -name "*.rst" -o -name "*.toml" \ + -o -name "*.cfg" -o -name "*.yml" -o -name "*.yaml" -o -name "*.in" \ + -o -name "*.cff" -o -name "*.sh" -o -name "Makefile*" \ + -o -name "CODEOWNERS" \)) + +# ---- rewrite ------------------------------------------------------------- +# Use | as sed delimiter for fields that may contain / (none here, but +# author/email are safer that way too). +for f in "${files[@]}"; do + "${sedi[@]}" \ + -e "s|${OLD_AUTHOR}|${NEW_AUTHOR}|g" \ + -e "s|${OLD_EMAIL}|${NEW_EMAIL}|g" \ + -e "s|${OLD_CODECOV_TOKEN}|REPLACE_WITH_CODECOV_TOKEN|g" \ + -e "s/${OLD_NAME}/${NEW_NAME}/g" \ + -e "s/${OLD_USERNAME}/${NEW_USERNAME}/g" \ + "$f" +done + +# CITATION.cff name fields (given/family stored separately). +"${sedi[@]}" \ + -e "s/family-names: ${OLD_FAMILY}/family-names: ${NEW_FAMILY}/" \ + -e "s/given-names: ${OLD_GIVEN}/given-names: ${NEW_GIVEN}/" \ + CITATION.cff + +# ORCID: rewrite if provided, otherwise drop the orcid line from CITATION.cff. +if [[ -n "$NEW_ORCID" ]]; then + "${sedi[@]}" -e "s|${OLD_ORCID}|${NEW_ORCID}|g" CITATION.cff +else + "${sedi[@]}" -e "/orcid:.*${OLD_ORCID}/d" CITATION.cff +fi + +# ---- rename package directory ------------------------------------------- +if [[ -d "$OLD_NAME" && "$OLD_NAME" != "$NEW_NAME" ]]; then + if git ls-files --error-unmatch "$OLD_NAME" >/dev/null 2>&1; then + git mv "$OLD_NAME" "$NEW_NAME" + else + mv "$OLD_NAME" "$NEW_NAME" + fi +fi + +cat < Date: Tue, 5 May 2026 02:26:18 +0000 Subject: [PATCH 3/3] Template polish: version, docs determinism, CI tightening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - __version__ and CITATION.cff version: 0.0.0 -> 0.0.0.dev0 (PEP 440 pre-release suffix makes the unreleased state explicit and prevents accidentally tagging "0.0.0" on PyPI). - docs/conf.py: hardcode copyright year (was datetime.date.today().year, non-deterministic build artifact); drop sys.path.insert hack and the now-unused os/sys imports — RTD installs the package via the docs extra. - docs/index.rst: remove the empty "Contents:" toctree so RTD's fail_on_warning build is clean. - .readthedocs.yml: fail_on_warning true so dangling refs don't rot. - codeql.yml: queries: security-and-quality (broader than default). - tests/test_example.py: convert from unittest.TestCase to pytest function style — what users should be writing in 2026. The trivial test now actually asserts something (version is a non-empty string) rather than being a literal no-op. - README: note that Dependabot is GH-only and point GitLab users at Renovate. --- .github/workflows/codeql.yml | 2 ++ .readthedocs.yml | 6 +++--- CITATION.cff | 2 +- README.md | 3 ++- docs/conf.py | 10 +++------- docs/index.rst | 5 ----- my_package/__init__.py | 2 +- tests/test_example.py | 27 ++++++++------------------- 8 files changed, 20 insertions(+), 37 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1e95c06..7e42dad 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,6 +31,8 @@ jobs: uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: languages: python + # Use the broader query suite (default + maintainability/style). + queries: security-and-quality - name: Perform CodeQL analysis uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: diff --git a/.readthedocs.yml b/.readthedocs.yml index 286c9b5..6a46b8d 100755 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -21,12 +21,12 @@ build: # Build documentation in the "docs/" directory with Sphinx sphinx: - fail_on_warning: false + # Fail on warnings so dangling cross-references don't silently rot. Flip to + # `false` if you intentionally ship docs with known warnings. + fail_on_warning: true configuration: docs/conf.py # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs # builder: "dirhtml" - # Fail on all warnings to avoid broken references - # fail_on_warning: true # Optionally build your docs in additional formats such as PDF and ePub # formats: diff --git a/CITATION.cff b/CITATION.cff index b462f01..3e71aea 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,7 +5,7 @@ authors: given-names: Nathan orcid: https://orcid.org/0000-0002-0008-8749 title: "my_package" -version: 0.0.0 +version: 0.0.0.dev0 # Populate `doi` and `date-released` once Zenodo mints a DOI for the first # release. Until then, leave them absent so cff-convert validation passes. # doi: 10.5281/zenodo.XXXXXXX diff --git a/README.md b/README.md index 9f445b8..45a3e4c 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,8 @@ GitHub-specific workflows (with no GitLab equivalent in this template): GitLab equivalent: -- `.gitlab-ci.yml` — mirrors the test matrix and adds a `pre-commit` job. CodeQL has no GitLab analogue here; if you host on GitLab, consider GitLab's built-in [SAST](https://docs.gitlab.com/ee/user/application_security/sast/) instead. +- `.gitlab-ci.yml` — mirrors the test matrix and adds `pre-commit` and `mypy` jobs. CodeQL has no GitLab analogue here; if you host on GitLab, consider GitLab's built-in [SAST](https://docs.gitlab.com/ee/user/application_security/sast/) instead. +- **No Dependabot on GitLab.** If you host on GitLab, consider [Renovate](https://docs.renovatebot.com/) (works on both platforms) or replace `.github/dependabot.yml` with a manual update cadence. Linting and Formatting --- diff --git a/docs/conf.py b/docs/conf.py index 3585953..34e9d1b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,17 +5,13 @@ https://www.sphinx-doc.org/en/master/usage/configuration.html """ -import datetime -import os -import sys - -sys.path.insert(0, os.path.abspath("..")) - # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "my_package" -copyright = f"{datetime.date.today().year}, Nathan A. Mahynski" +# Hardcoded for reproducible builds. Bump the year manually (or replace with +# a "{start}–{current}" range) when appropriate. +copyright = "2026, Nathan A. Mahynski" author = "Nathan A. Mahynski" # -- General configuration --------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index 73d3dbb..6ebb4a1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,11 +32,6 @@ License Information * Any mention of commercial products is for information only; it does not imply recommendation or endorsement by `NIST `_. -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - .. toctree:: :maxdepth: 3 :caption: API Reference diff --git a/my_package/__init__.py b/my_package/__init__.py index 922e87f..bcb27dc 100644 --- a/my_package/__init__.py +++ b/my_package/__init__.py @@ -4,6 +4,6 @@ Author: Nathan A. Mahynski """ -__version__ = "0.0.0" +__version__ = "0.0.0.dev0" __all__ = [] diff --git a/tests/test_example.py b/tests/test_example.py index e76ad8c..81daa06 100644 --- a/tests/test_example.py +++ b/tests/test_example.py @@ -1,26 +1,15 @@ """ -Unittests template example. +Pytest example template. -TODO: Replace this no-op test with real tests for your package. The dummy -test below exists only to keep the test suite green on a fresh template +TODO: Replace this with real tests for your package. The trivial test +below exists only to keep the test suite green on a fresh template checkout — coverage numbers it produces are not meaningful. - -Author: Nathan A. Mahynski """ -import unittest - -import my_package # noqa: F401 - - -class DummyTest(unittest.TestCase): - """Perform dummy tests.""" +import my_package - @classmethod - def setUpClass(cls): - """Set up things for all members of this test class.""" - return - def test_dummy(self): - """Perform a dummy test.""" - return +def test_version_is_set(): + """Sanity check: the package exposes a non-empty version string.""" + assert isinstance(my_package.__version__, str) + assert my_package.__version__