Skip to content

Pypi test version file problem fixed 2 --pipTest #56 #57

Pypi test version file problem fixed 2 --pipTest #56

Pypi test version file problem fixed 2 --pipTest #56 #57

name: Publish Python
# Triggered on every push to main.
# Flags in the commit message control what happens (all case-insensitive):
#
# --v X.Y.Z or --version X.Y.Z -> set the package version before building
# --PipTest (or --piptest etc.) -> publish to TestPyPI after tests pass
# --pipLive (or --piplive etc.) -> publish to PyPI + create GitHub release
# --noRelease (or --norelease etc.) -> build and test only -- no publishing,
# no tags, no releases (even if --v set)
#
# If --PipTest is given without --v, the version is auto-set to
# <current>.devN where N is the git commit count (unique per push).
#
# Changelog: place note/changelog/<X_Y_Z>.md before pushing --pipLive.
# e.g. note/changelog/0_1_0.md for version 0.1.0
on:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
id-token: write
# -----------------------------------------------------------------------------
jobs:
# -- 1. Parse commit message flags --------------------------------------------
parse:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.p.outputs.version }}
has_version: ${{ steps.p.outputs.has_version }}
pip_test: ${{ steps.p.outputs.pip_test }}
pip_live: ${{ steps.p.outputs.pip_live }}
no_release: ${{ steps.p.outputs.no_release }}
should_run: ${{ steps.p.outputs.should_run }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for git rev-list --count
- name: Parse commit message
id: p
shell: bash
run: |
MSG="${{ github.event.head_commit.message }}"
VERSION=$(python3 -c "
import re, sys
m = re.search(r'--(?:v|version)\s+([\d]+(?:\.[\d]+)*)', sys.argv[1])
print(m.group(1) if m else '')
" "$MSG")
# All flags are matched case-insensitively so --pipTest / --PipTest
# / --PIPTEST all work the same way.
PIP_TEST=$(echo "$MSG" | grep -qi -- '--piptest' && echo true || echo false)
PIP_LIVE=$(echo "$MSG" | grep -qi -- '--piplive' && echo true || echo false)
NO_RELEASE=$(echo "$MSG" | grep -qi -- '--norelease' && echo true || echo false)
# When --PipTest is used without an explicit version, auto-build a
# unique dev version: take the base version from pyproject.toml and
# append .devN where N is the current git commit count.
# This ensures every push produces a distinct wheel on TestPyPI and
# avoids silent skip-existing collisions.
if [ "$PIP_TEST" = true ] && [ -z "$VERSION" ]; then
BASE_VER=$(python3 -c "
import re
with open('libs/python/pyproject.toml') as f:
m = re.search(r'^version\s*=\s*\"([^\"]+)\"', f.read(), re.MULTILINE)
print(m.group(1).split('-')[0] if m else '0.0.1')
")
COMMIT_N=$(git rev-list --count HEAD)
VERSION="${BASE_VER}.dev${COMMIT_N}"
echo "[INFO] No --v flag — using auto dev version: $VERSION"
fi
HAS_VERSION=$([ -n "$VERSION" ] && echo true || echo false)
if [ "$HAS_VERSION" = true ] || [ "$PIP_TEST" = true ] || [ "$PIP_LIVE" = true ]; then
SHOULD_RUN=true
else
SHOULD_RUN=false
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "has_version=$HAS_VERSION" >> $GITHUB_OUTPUT
echo "pip_test=$PIP_TEST" >> $GITHUB_OUTPUT
echo "pip_live=$PIP_LIVE" >> $GITHUB_OUTPUT
echo "no_release=$NO_RELEASE" >> $GITHUB_OUTPUT
echo "should_run=$SHOULD_RUN" >> $GITHUB_OUTPUT
echo "=== Parsed flags ==="
echo " version : $VERSION"
echo " has_version : $HAS_VERSION"
echo " pip_test : $PIP_TEST"
echo " pip_live : $PIP_LIVE"
echo " no_release : $NO_RELEASE"
echo " should_run : $SHOULD_RUN"
# -- 2. Build C library, run tests, produce platform wheels ------------------
build:
needs: parse
if: needs.parse.outputs.should_run == 'true'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
platform: linux
lib_ext: .so
plat_tag: linux_x86_64
- os: windows-latest
platform: windows
lib_ext: .dll
plat_tag: win_amd64
- os: macos-latest
platform: mac
lib_ext: .dylib
plat_tag: macosx_10_9_x86_64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install GCC (Linux)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install -y gcc
- name: Install Python build tools
run: python -m pip install --upgrade pip setuptools wheel pytest
- name: Set version in pyproject.toml
if: needs.parse.outputs.has_version == 'true'
shell: bash
run: python libs/python/tools/version.py --set "${{ needs.parse.outputs.version }}"
- name: Build C library
shell: bash
run: |
python runner.py \
--build system:main \
--platform ${{ matrix.platform }} \
--lib-ext ${{ matrix.lib_ext }} \
--no-color
- name: Bundle native binary into package
shell: bash
run: |
mkdir -p libs/python/src/nextssl/lib
BIN=$(find bin/${{ matrix.platform }}/primary \
-name "main${{ matrix.lib_ext }}" \
2>/dev/null | head -1)
if [ -z "$BIN" ]; then
echo "[ERROR] Compiled binary not found under bin/${{ matrix.platform }}/primary/"
echo "Expected: main${{ matrix.lib_ext }}"
ls -R bin/${{ matrix.platform }}/ || true
exit 1
fi
cp "$BIN" "libs/python/src/nextssl/lib/nextssl${{ matrix.lib_ext }}"
echo "[OK] Bundled: $BIN -> libs/python/src/nextssl/lib/nextssl${{ matrix.lib_ext }}"
- name: Run Python tests
shell: bash
run: python -m pytest test/ -v --tb=short
- name: Build platform wheel
shell: bash
run: |
cd libs/python
# setup.py overrides get_tag() -> py3-none-<plat>, so no extra
# --python-tag / --abi-tag flags needed here.
python setup.py bdist_wheel \
--plat-name ${{ matrix.plat_tag }} \
--dist-dir dist
echo "[OK] Wheel built:"
ls dist/
- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: wheel-${{ matrix.platform }}
path: libs/python/dist/*.whl
if-no-files-found: error
# -- 3. Publish to TestPyPI --------------------------------------------------
publish-test:
needs: [parse, build]
if: |
needs.build.result == 'success' &&
needs.parse.outputs.pip_test == 'true' &&
needs.parse.outputs.no_release != 'true'
runs-on: ubuntu-latest
steps:
- name: Download all platform wheels
uses: actions/download-artifact@v4
with:
pattern: wheel-*
path: dist
merge-multiple: true
- name: List wheels to publish
run: ls -lh dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
repository-url: https://test.pypi.org/legacy/
skip-existing: true
verbose: true
# -- 4. Publish to PyPI + create GitHub release ------------------------------
publish-live:
needs: [parse, build]
if: |
needs.build.result == 'success' &&
needs.parse.outputs.pip_live == 'true' &&
needs.parse.outputs.no_release != 'true' &&
needs.parse.outputs.has_version == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Download all platform wheels
uses: actions/download-artifact@v4
with:
pattern: wheel-*
path: dist
merge-multiple: true
- name: List wheels to publish
run: ls -lh dist/
- name: Find changelog file
id: notes
shell: bash
run: |
VER="${{ needs.parse.outputs.version }}"
SLUG=$(echo "$VER" | tr '.' '_')
NOTE_PATH="note/changelog/${SLUG}.md"
if [ -f "$NOTE_PATH" ]; then
echo "path=$NOTE_PATH" >> $GITHUB_OUTPUT
echo "[OK] Using changelog: $NOTE_PATH"
else
printf "Release %s\n" "$VER" > /tmp/release_notes.md
echo "path=/tmp/release_notes.md" >> $GITHUB_OUTPUT
echo "[WARN] No changelog at $NOTE_PATH -- using placeholder"
fi
- name: Create git tag
shell: bash
run: |
VER="${{ needs.parse.outputs.version }}"
TAG="py-v${VER}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "[SKIP] Tag $TAG already exists"
else
git tag "$TAG"
git push origin "$TAG" \
&& echo "[OK] Tag $TAG pushed" \
|| echo "[WARN] Failed to push tag -- continuing anyway"
fi
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
VER="${{ needs.parse.outputs.version }}"
TAG="py-v${VER}"
gh release create "$TAG" dist/*.whl \
--title "NextSSL Python $VER" \
--notes-file "${{ steps.notes.outputs.path }}" \
&& echo "[OK] GitHub release created: $TAG" \
|| echo "[WARN] Release creation failed (may already exist)"
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
skip-existing: true
verbose: true