security + CI: mirror unsloth's hardening stack onto zoo (greenfield .github/) #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-License-Identifier: LGPL-3.0-or-later | |
| # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. | |
| # Builds the PyPI wheel + sdist from the PR branch, then verifies the | |
| # built wheel contains what we expect to ship and is importable in a | |
| # clean venv. Adapted from unsloth's wheel-smoke.yml: zoo has no | |
| # frontend / Tauri / Studio tree, so the content checks pivot to | |
| # "unsloth_zoo package present, no tests/ shipped, no stray .pyc, real | |
| # version string from dynamic metadata, import smoke succeeds". | |
| name: Wheel CI | |
| on: | |
| pull_request: | |
| paths: | |
| - 'pyproject.toml' | |
| - 'unsloth_zoo/**' | |
| - 'tests/**' | |
| - '.github/workflows/wheel-smoke.yml' | |
| push: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| wheel: | |
| name: Wheel build + content sanity + import smoke | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Harden runner (audit) | |
| uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Build wheel + sdist | |
| run: | | |
| python -m pip install --upgrade pip build | |
| rm -rf dist build ./*.egg-info | |
| python -m build | |
| - name: Wheel content sanity | |
| run: | | |
| python - <<'PY' | |
| import zipfile, glob, sys, re | |
| wheels = glob.glob("dist/unsloth_zoo-*.whl") | |
| if not wheels: | |
| print("FAIL: no wheel produced"); sys.exit(2) | |
| w = wheels[0] | |
| print(f"wheel: {w}") | |
| # Version sanity: dynamic metadata pulls from | |
| # unsloth_zoo.__init__.__version__; assert the filename | |
| # carries a non-zero SemVer-ish token, not "0.0.0". | |
| m = re.match(r"dist/unsloth_zoo-([^-]+)-py3-none-any\.whl", w) | |
| version = m.group(1) if m else None | |
| print(f"wheel version: {version}") | |
| with zipfile.ZipFile(w) as z: | |
| n = z.namelist() | |
| # Hard checks: must hold for any zoo release wheel. | |
| hard_checks = { | |
| "unsloth_zoo/__init__.py shipped": any(s == "unsloth_zoo/__init__.py" for s in n), | |
| "unsloth_zoo/rl_replacements.py shipped": any(s == "unsloth_zoo/rl_replacements.py" for s in n), | |
| "unsloth_zoo/temporary_patches/__init__.py shipped": any(s == "unsloth_zoo/temporary_patches/__init__.py" for s in n), | |
| "no .pyc files": not any(s.endswith(".pyc") for s in n), | |
| "no .git tree": not any(s.startswith(".git/") for s in n), | |
| "version is not 0.0.0": version is not None and version != "0.0.0", | |
| "METADATA present": any(s.endswith(".dist-info/METADATA") for s in n), | |
| } | |
| # Soft checks: warn only. Zoo's pyproject.toml currently | |
| # doesn't exclude tests/ or scripts/ from setuptools' | |
| # find_packages, so wheels DO ship them. Tightening | |
| # the packaging config is a separate follow-up; failing | |
| # the gate here would block this CI-bootstrap PR. | |
| soft_checks = { | |
| "no tests/ shipped": not any(s.startswith("tests/") for s in n), | |
| "no scripts/ shipped": not any(s.startswith("scripts/") for s in n), | |
| } | |
| print("Hard checks:") | |
| for k, v in hard_checks.items(): | |
| print(f" [{'PASS' if v else 'FAIL'}] {k}") | |
| print() | |
| print("Soft checks (warnings):") | |
| for k, v in soft_checks.items(): | |
| status = "PASS" if v else "WARN" | |
| print(f" [{status}] {k}") | |
| # Exit non-zero ONLY if a hard check failed. | |
| sys.exit(0 if all(hard_checks.values()) else 1) | |
| PY | |
| - name: Import smoke (clean venv) | |
| # unsloth_zoo/__init__.py:128 raises | |
| # `ImportError("Please install Unsloth via 'pip install unsloth'!")` | |
| # when the parent `unsloth` package is absent -- a deliberate | |
| # guardrail that prevents standalone zoo use. So a bare | |
| # `import unsloth_zoo` in a wheel-only venv WILL fail by | |
| # design; the smoke check pivots to reading the version | |
| # string from the installed dist-info METADATA instead. That | |
| # confirms the wheel installed AND the version string is | |
| # well-formed, without tripping the parent-import guard. | |
| run: | | |
| python -m venv /tmp/v | |
| /tmp/v/bin/pip install --upgrade pip | |
| /tmp/v/bin/pip install dist/unsloth_zoo-*.whl | |
| # Read version from dist-info METADATA via importlib.metadata. | |
| WHEEL_VERSION=$(/tmp/v/bin/python -c " | |
| from importlib.metadata import version | |
| print(version('unsloth_zoo')) | |
| ") | |
| echo "installed unsloth_zoo version: $WHEEL_VERSION" | |
| test -n "$WHEEL_VERSION" && test "$WHEEL_VERSION" != "0.0.0" | |
| - name: Upload wheel on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: unsloth-zoo-wheel | |
| path: dist/ | |
| retention-days: 7 |