Skip to content

Publish to PyPI

Publish to PyPI #10

Workflow file for this run

name: Publish to PyPI
on:
workflow_dispatch:
inputs:
version:
description: "Release version (e.g. 0.1.0)"
required: true
type: string
dry_run:
description: "Dry run (build + test only, no upload)"
type: boolean
default: false
permissions:
contents: write
id-token: write
jobs:
build:
name: Build wheel & sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build tools
run: pip install build tomli
- name: Patch version in pyproject.toml
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
python3 << 'PYEOF'
import os, re
ver = os.environ["RELEASE_VERSION"]
with open("pyproject.toml", "r") as f:
content = f.read()
content = re.sub(r'version\s*=\s*"[^"]+"', f'version = "{ver}"', content, count=1)
with open("pyproject.toml", "w") as f:
f.write(content)
print(f"Version set to: {ver}")
PYEOF
- name: Build wheel and sdist
run: python -m build
- name: Verify wheel contents
run: |
pip install dist/*.whl
python -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'Build OK: {len(r.all_detectors())} detectors')"
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
test-os:
name: "${{ matrix.os }} / Python ${{ matrix.python }}"
needs: build
strategy:
fail-fast: false
matrix:
include:
# Windows 10/11
- { os: windows-latest, python: "3.11" }
- { os: windows-latest, python: "3.12" }
- { os: windows-latest, python: "3.13" }
# macOS (Apple Silicon + Intel)
- { os: macos-latest, python: "3.11" }
- { os: macos-latest, python: "3.12" }
- { os: macos-latest, python: "3.13" }
# Ubuntu / Linux
- { os: ubuntu-latest, python: "3.11" }
- { os: ubuntu-latest, python: "3.12" }
- { os: ubuntu-latest, python: "3.13" }
- { os: ubuntu-22.04, python: "3.11" }
- { os: ubuntu-22.04, python: "3.12" }
runs-on: ${{ matrix.os }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install from wheel (no root, no system deps)
shell: bash
run: pip install dist/*.whl
- name: Verify CLI
run: osscodeiq --help
- name: Verify detectors
run: python -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'{len(r.all_detectors())} detectors')"
test-container:
name: "${{ matrix.name }}"
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# RHEL / UBI — pip install only, NO root, NO dnf
- { name: "UBI 8 / RHEL 8 (Python 3.11)", container: "registry.access.redhat.com/ubi8/python-311:latest" }
- { name: "UBI 9 / RHEL 9 (Python 3.11)", container: "registry.access.redhat.com/ubi9/python-311:latest" }
- { name: "UBI 9 / RHEL 9 (Python 3.12)", container: "registry.access.redhat.com/ubi9/python-312:latest" }
# Debian / Ubuntu slim
- { name: "Debian Bookworm (3.11)", container: "python:3.11-slim-bookworm" }
- { name: "Debian Bookworm (3.12)", container: "python:3.12-slim-bookworm" }
- { name: "Debian Bookworm (3.13)", container: "python:3.13-slim-bookworm" }
# Alpine (musl libc)
- { name: "Alpine (3.11)", container: "python:3.11-alpine" }
- { name: "Alpine (3.12)", container: "python:3.12-alpine" }
# Fedora
- { name: "Fedora 40", container: "fedora:40" }
container:
image: ${{ matrix.container }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Install Python (Amazon Linux / Fedora only)
if: contains(matrix.container, 'amazonlinux') || contains(matrix.container, 'fedora')
run: |
dnf install -y python3 python3-pip 2>/dev/null || yum install -y python3 python3-pip 2>/dev/null || true
- name: Install from wheel (no root system deps needed)
run: pip install dist/*.whl || pip3 install dist/*.whl
- name: Verify CLI
run: osscodeiq --help || python3 -m osscodeiq.cli --help
- name: Verify detectors
run: python3 -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'{len(r.all_detectors())} detectors')"
publish-pypi:
name: Publish to PyPI
needs: [test-os, test-container]
runs-on: ubuntu-latest
if: inputs.dry_run == false
environment:
name: pypi
url: https://pypi.org/p/osscodeiq
permissions:
id-token: write
attestations: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
with:
attestations: true
github-release:
name: Create GitHub Release
needs: publish-pypi
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ inputs.version }}
run: |
gh release create "v${VERSION}" dist/* \
--title "v${VERSION}" \
--generate-notes \
--latest