Add PyPI publishing workflow (Trusted Publishing / OIDC)#4480
Conversation
- .github/workflows/publish.yml: tag-triggered build then publish via PyPI Trusted Publishing (OIDC); TestPyPI first, then PyPI gated by GitHub Environment reviewer approval. No tokens stored. - RELEASE.md: one-time bootstrap (pending publishers, GitHub environments, tag protection) plus per-release SOP and known limitations. - MANIFEST.in: prune lisa/ai/data; deeply nested log paths trigger Windows 260-char limit during sdist build.
There was a problem hiding this comment.
Pull request overview
Adds a GitHub Actions-based release pipeline for publishing the mslisa package to TestPyPI and PyPI using PyPI Trusted Publishing (OIDC), along with release runbook documentation and an sdist pruning tweak to avoid Windows path-length issues during local builds.
Changes:
- Introduce a tag-triggered
publish.ymlworkflow that builds artifacts, validates withtwine check, publishes to TestPyPI, then publishes to PyPI behind a GitHub Environment approval gate. - Add
RELEASE.mdwith bootstrap steps (pending publishers/environments/tag protection) and a per-release SOP. - Prune
lisa/ai/datafrom the sdist viaMANIFEST.into reduce deep-path issues on Windows.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
RELEASE.md |
Documents bootstrap + release SOP for Trusted Publishing and local dry-run guidance. |
MANIFEST.in |
Excludes lisa/ai/data from sdists to mitigate Windows path-length failures. |
.github/workflows/publish.yml |
Implements the build + TestPyPI + gated PyPI publish workflow using OIDC. |
| # Try installing into a fresh venv | ||
| py -3.12 -m venv C:\tmp\mslisa-local | ||
| $wheel = (Get-Item dist\mslisa-*.whl).FullName | ||
| & C:\tmp\mslisa-local\Scripts\python.exe -m pip install "$wheel[azure]" |
| - **sdist build fails on Windows** because `setuptools_scm` includes every git- | ||
| tracked file (including deeply nested logs under `lisa/ai/data/...`) and the | ||
| resulting paths exceed Windows' 260-character limit. CI builds on Linux are | ||
| unaffected. The wheel is what users actually install. | ||
| - **`MANIFEST.in` `prune` rules don't apply** to files already tracked by git | ||
| when `setuptools_scm` is the file finder. To shrink the sdist, move | ||
| `lisa/ai/data/` out of git (git-lfs or a sibling repo). |
| prune .github | ||
| exclude .git* | ||
|
|
||
| # AI training data is large and not needed at runtime; exclude from sdist/wheel. |
| on: | ||
| push: | ||
| tags: | ||
| # CalVer: e.g. 20260420.1, 20260420.2 | ||
| - "2[0-9][0-9][0-9][0-9][0-9][0-9][0-9].*" |
| --- | ||
|
|
||
| ## Cutting a release | ||
|
|
There was a problem hiding this comment.
This section shows PowerShell commands for the TestPyPI smoke test. Consider adding a bash equivalent too.
|
|
||
| jobs: | ||
| build: | ||
| name: Build sdist + wheel |
There was a problem hiding this comment.
The build job validates metadata with twine check but doesn't verify the wheel is actually installable. Adding a quick dry-run install step would catch packaging issues early.
- name: Smoke-test wheel
run: |
pip install dist/mslisa-*.whl --no-deps
python -c "import lisa; print('OK')"
mslisaPyPI Publishing — Design and Pre-release Testing Guide1. Why
pip install?1.1 Current state (before this change)
The only documented way to install LISA has been:
git clone https://github.com/microsoft/lisa.git cd lisa python3 -m pip install --editable .[azure,libvirt] --config-settings editable_mode=compatThis approach has several long-standing problems:
git clone--editablemodepip install lisa==X.Y.Z, no pinning, no rollback[azure,libvirt])1.2 Goal
Make this work for any Python user:
Properties:
YYYYMMDD.N)--editabledevelopment workflow — contributors are not affected2. Design principles
2.1 Package name
mslisalisais already taken on PyPI, so the project ships asmslisa(Microsoft LISA).pip install mslisa[project.scripts] lisa = "lisa.main:cli", so users still typelisa.import lisa.2.2 Versioning: CalVer +
setuptools_scmYYYYMMDD.N(e.g.20260420.1,20260420.2)__version__;setuptools_scmderives the version from the git tag..1→.2→.3.2.3 Trusted Publishing (OIDC, no secrets)
No long-lived API tokens anywhere. GitHub Actions proves its identity to PyPI / TestPyPI over OIDC.
microsoft/lisarepo +publish.ymlworkflow + GitHub Environment name (pypiortestpypi).2.4 Two-stage publish: TestPyPI → PyPI (with manual gate)
Every pushed tag automatically:
pypiThis gives a "back out" window so a broken artifact never reaches the production index unattended.
2.5 Optional extras for opt-in features
pip install mslisainstalls only core dependencies (paramiko, PyYAML, etc.).Platform-specific and developer dependencies are opt-in via extras (lisa/pyproject.toml):
azurelibvirtawsbaremetalaidocs,mypy,pylint,black,flake8,isort,typing,test3. How the package is built
3.1 Key files
prunedoesn't apply to files already tracked by git whensetuptools_scmis the file finder)3.2 Build backend
setuptools+setuptools_scmmslisa-<version>-py3-none-any.whl(pure-Python wheel, cross-platform)mslisa-<version>.tar.gz(sdist)3.3 Known limitations
setuptools_scmincludes every git-tracked file (including deeply nested paths underlisa/ai/data/...), and the resulting paths exceed Windows' 260-character limit. The CI build on Linux is unaffected. End users install the wheel.MANIFEST.inprunerules don't apply to files already tracked by git, because the file finder issetuptools_scm. To shrink the sdist, movelisa/ai/data/out of git (git-lfs or a sibling repo).4. How the publish workflow runs
4.1 Trigger
lisa/.github/workflows/publish.yml:
4.2 The three jobs
buildactions/checkout@v4withfetch-depth: 0(setuptools_scmneeds the full tag history)python -m buildproduces wheel + sdistpython -m twine check dist/*validates metadatadist/as an artifactpublish-testpypitestpypi(no reviewers required)pypa/gh-action-pypi-publish@release/v1with OIDChttps://test.pypi.org/legacy/publish-pypipublish-testpypisucceedingpypi(required reviewers configured)4.3 One-time bootstrap (already done)
See lisa/RELEASE.md. Summary:
microsoft/lisa+publish.yml+ environment name).testpypiandpypi, and add 1–2 LSG maintainers as required reviewers onpypi.2[0-9][0-9][0-9][0-9][0-9][0-9][0-9].*so only release managers can push release tags.4.4 Cutting a release
5. Pre-release testing (before approving the final PyPI push)
Before the
pypienvironment is approved, the artifact already exists on TestPyPI. Anyone can install it from there and run end-to-end validation.5.1 On WSL / Ubuntu 24.04 (recommended — isolated environment)
5.2 On Windows
5.3 Local dry run (no upload anywhere)
Whenever you change
pyproject.tomlorMANIFEST.in, build locally first:5.4 Verification checklist (run for every rc)
pip installcompletes with no dependency conflictslisa --helpprints normallylisa --versionmatches the tagpython -c "import lisa"succeedsexamples/runbook/hello_world.yml) runs to completion6. Contributor workflow is unchanged
Publishing to PyPI does not affect contributors. They keep using:
git clone https://github.com/microsoft/lisa.git cd lisa python3 -m pip install --editable .[azure,libvirt] --config-settings editable_mode=compatSource edits are live, selftests work as before, PRs work as before.
One-line summary:
pip install mslisais for users;pip install -e .is for contributors.7. References