# 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: # The runner's downloads from PyPI time out now and then (2026-10-22 evening: # ReadTimeoutError on files.pythonhosted.org in several jobs). Wait longer and retry. PIP_DEFAULT_TIMEOUT: "60" PIP_RETRIES: "10" UV_HTTP_TIMEOUT: "60" 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 # Two venvs: the tools, and the package alone. Auditing the tools' venv # reported the runner's own pip instead of anything the package brings. run: | python -m venv .tools .tools/bin/pip install -q --upgrade pip .tools/bin/pip install -q "$STANDARD" bandit pip-audit python -m venv .audit .audit/bin/pip install -q --upgrade pip if [ -n "${{ inputs.pre-install }}" ]; then .audit/bin/pip install -q ${{ inputs.pre-install }}; fi .audit/bin/pip install -q . - name: bandit run: .tools/bin/bandit -q -r "$(.tools/bin/napalm-standard package)" - name: pip-audit # What a user gets today from the package's ranges. Packages installed from # a URL (the package itself, a git pre-install) are not on PyPI's index yet. run: | .audit/bin/pip freeze --all | grep -vE ' @ |^-e ' > audit-requirements.txt cat audit-requirements.txt .tools/bin/pip-audit -r audit-requirements.txt --no-deps --disable-pip --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" # --verbose: on a refusal PyPI says why (429 on new projects, 2026-10-23/24). # --skip-existing: a re-run after a partly successful upload does not fail. .venv/bin/twine upload --non-interactive --verbose --skip-existing 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"