feat: standards, shared CI and release tooling for NAPALM drivers
STANDARDS.md binds every repository in the NAPALM org: one default branch, Conventional Commits, the device-types contract, test-driven development, clean-code limits, a quality ratchet for existing code, packaging rules and releases by tag to PyPI. napalm-standard holds what a tool can check: the conformance check (with release rules on a tag), the quality ratchet (ported from netOrk's quality_ratchet.py), and the Gitea release from the CHANGELOG section. .gitea/workflows/driver.yml is the CI every driver calls; template/ is a new driver's starting point and passes the check as it is.
This commit is contained in:
commit
db6b2e4ea7
37 files changed
+2894
No files matched your search
@@ -0,0 +1,200 @@
|
||||
# The CI every NAPALM driver runs (STANDARDS.md §7). A driver repo calls it from
|
||||
# its own .gitea/workflows/ci.yml — see template/.gitea/workflows/ci.yml.
|
||||
#
|
||||
# Every job that installs builds its own venv in the workspace: jobs on one
|
||||
# runner share /opt/hostedtoolcache, and two `pip install -e .` into the same
|
||||
# site-packages break each other (netOrk REPOSITORY_GUIDELINES §10).
|
||||
#
|
||||
# upload-artifact/download-artifact stay on v3: v4 refuses to run on Gitea
|
||||
# ("not currently supported on GHES").
|
||||
|
||||
name: driver
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
standard-ref:
|
||||
description: "Version of napalm-standard to check with; keep it equal to the @ref of this workflow"
|
||||
type: string
|
||||
default: "main"
|
||||
pre-install:
|
||||
description: "pip specs installed before the package (until its dependencies are on PyPI)"
|
||||
type: string
|
||||
default: ""
|
||||
python-versions:
|
||||
description: "JSON list of the Python versions the tests run on"
|
||||
type: string
|
||||
default: '["3.10", "3.11", "3.12", "3.13"]'
|
||||
pypi-url:
|
||||
description: "Upload target; https://test.pypi.org/legacy/ for a dry run"
|
||||
type: string
|
||||
default: "https://upload.pypi.org/legacy/"
|
||||
secrets:
|
||||
PYPI_TOKEN:
|
||||
required: false
|
||||
|
||||
env:
|
||||
STANDARD: "napalm-standard @ git+https://git.netork.io/NAPALM/driver-standard.git@${{ inputs.standard-ref }}"
|
||||
|
||||
jobs:
|
||||
standard:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install napalm-standard
|
||||
run: |
|
||||
python -m venv .venv
|
||||
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
|
||||
.venv/bin/pip install -q "$STANDARD"
|
||||
- name: Conformance
|
||||
run: |
|
||||
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
||||
napalm-standard check --tag "$GITHUB_REF_NAME"
|
||||
else
|
||||
napalm-standard check
|
||||
fi
|
||||
- name: Quality ratchet
|
||||
run: napalm-standard ratchet
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: ruff
|
||||
run: |
|
||||
python -m venv .venv
|
||||
.venv/bin/pip install -q ruff
|
||||
.venv/bin/ruff format --check .
|
||||
.venv/bin/ruff check .
|
||||
|
||||
types:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install
|
||||
run: |
|
||||
python -m venv .venv
|
||||
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
|
||||
.venv/bin/pip install -q "$STANDARD" mypy
|
||||
if [ -n "${{ inputs.pre-install }}" ]; then .venv/bin/pip install -q ${{ inputs.pre-install }}; fi
|
||||
.venv/bin/pip install -q -e ".[dev]"
|
||||
- name: mypy
|
||||
run: mypy "$(napalm-standard package)"
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ${{ fromJSON(inputs.python-versions) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install
|
||||
run: |
|
||||
python -m venv .venv
|
||||
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
|
||||
if [ -n "${{ inputs.pre-install }}" ]; then .venv/bin/pip install -q ${{ inputs.pre-install }}; fi
|
||||
.venv/bin/pip install -q -e ".[dev]" pytest pytest-cov
|
||||
- name: pytest
|
||||
# --cov takes its source from [tool.coverage.run], the floor from fail_under.
|
||||
run: python -m pytest -q --tb=short --cov --cov-report=term-missing
|
||||
|
||||
lowest:
|
||||
# The floors in pyproject.toml are a promise: install exactly them and run the tests.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install the lowest versions the package allows
|
||||
run: |
|
||||
python -m venv .venv
|
||||
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
|
||||
.venv/bin/pip install -q uv
|
||||
if [ -n "${{ inputs.pre-install }}" ]; then .venv/bin/uv pip install --python .venv/bin/python -q ${{ inputs.pre-install }}; fi
|
||||
.venv/bin/uv pip install --python .venv/bin/python -q --resolution lowest-direct -e ".[dev]" pytest
|
||||
- name: pytest
|
||||
run: python -m pytest -q --tb=short -p no:cacheprovider
|
||||
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install
|
||||
run: |
|
||||
python -m venv .venv
|
||||
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
|
||||
.venv/bin/pip install -q "$STANDARD" bandit pip-audit
|
||||
if [ -n "${{ inputs.pre-install }}" ]; then .venv/bin/pip install -q ${{ inputs.pre-install }}; fi
|
||||
.venv/bin/pip install -q .
|
||||
- name: bandit
|
||||
run: bandit -q -r "$(napalm-standard package)"
|
||||
- name: pip-audit
|
||||
# The installed set: what a user gets today from the package's ranges.
|
||||
# A package not on PyPI yet (the driver itself, a git pre-install) is skipped with a note.
|
||||
run: pip-audit --skip-editable --progress-spinner off
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Build wheel and sdist
|
||||
run: |
|
||||
python -m venv .venv
|
||||
.venv/bin/pip install -q build twine
|
||||
.venv/bin/python -m build
|
||||
.venv/bin/twine check --strict dist/*
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: dist/*
|
||||
|
||||
publish:
|
||||
# Only a release tag publishes, and only when everything above passed.
|
||||
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v')
|
||||
needs: [standard, lint, types, test, lowest, security, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
- name: Upload to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
TWINE_REPOSITORY_URL: ${{ inputs.pypi-url }}
|
||||
run: |
|
||||
if [ -z "$TWINE_PASSWORD" ]; then echo "no PYPI_TOKEN secret in this repo"; exit 1; fi
|
||||
python -m venv .venv
|
||||
.venv/bin/pip install -q twine "$STANDARD"
|
||||
.venv/bin/twine upload --non-interactive dist/*
|
||||
- name: Gitea release
|
||||
env:
|
||||
GITEA_URL: ${{ github.server_url }}
|
||||
GITEA_REPOSITORY: ${{ github.repository }}
|
||||
GITEA_TOKEN: ${{ github.token }}
|
||||
run: .venv/bin/napalm-standard release "$GITHUB_REF_NAME"
|
||||
Reference in new issue
Block a user