added all algo in primary --gen --lntt 1,2,3 #67
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
| name: Publish Python | |
| # Triggered on every push to main. | |
| # Workflow overview: | |
| # 1) parse : read commit message flags and calculate version (including | |
| # auto-dev if necessary). | |
| # 2) build-{linux,windows,mac} : three independent jobs that each compile the | |
| # C library for its platform, run the full pytest suite, and build a | |
| # platform-specific wheel. Separating them prevents one slow container | |
| # from doing all work and allows concurrent builds. | |
| # 3) publish-test : when --pipTest is requested and all builds succeed, the | |
| # three wheels are uploaded to TestPyPI. | |
| # 3.5) verify-testpkg : lightweight smoke checks install a wheel from | |
| # TestPyPI on each OS and exercise a couple of landing features. | |
| # 4) publish-live : when --pipLive with explicit version is given, push the | |
| # same wheels to PyPI and create a GitHub release. | |
| # | |
| # 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) | |
| # Enforce rules for live publishing: version must be set and a | |
| # corresponding changelog file should exist; abort early otherwise. | |
| if [ "$PIP_LIVE" = true ]; then | |
| if [ -z "$VERSION" ]; then | |
| echo "[ERROR] --pipLive specified but no --v/--version provided" >&2 | |
| exit 1 | |
| fi | |
| # look for note/changelog/0_0_1.md style file | |
| VER_UNDER=${VERSION//./_} | |
| CHG="note/changelog/${VER_UNDER}.md" | |
| if [ ! -f "$CHG" ]; then | |
| echo "[ERROR] changelog file not found for version $VERSION: $CHG" >&2 | |
| exit 1 | |
| fi | |
| fi | |
| 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" | |
| # -- 2a. Build + test on Linux ------------------------------------------------ | |
| build-linux: | |
| needs: parse | |
| if: needs.parse.outputs.should_run == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install GCC | |
| 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 linux \ | |
| --lib-ext .so \ | |
| --no-color | |
| - name: Bundle native binary into package | |
| shell: bash | |
| run: | | |
| mkdir -p libs/python/src/nextssl/lib | |
| BIN=$(find bin/linux/primary -name "main.so" 2>/dev/null | head -1) | |
| if [ -z "$BIN" ]; then echo "[ERROR] compiled binary not found"; exit 1; fi | |
| cp "$BIN" libs/python/src/nextssl/lib/nextssl.so | |
| - name: Run Python tests | |
| shell: bash | |
| run: python -m pytest test/ -v --tb=short | |
| - name: Build linux wheel | |
| shell: bash | |
| run: | | |
| cd libs/python | |
| python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --dist-dir dist | |
| ls dist/ | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-linux | |
| path: libs/python/dist/*.whl | |
| if-no-files-found: error | |
| # -- 2b. Build + test on Windows ---------------------------------------------- | |
| build-windows: | |
| needs: parse | |
| if: needs.parse.outputs.should_run == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - 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 windows \ | |
| --lib-ext .dll \ | |
| --no-color | |
| - name: Bundle native binary into package | |
| shell: bash | |
| run: | | |
| mkdir -p libs/python/src/nextssl/lib | |
| BIN=$(find bin/windows/primary -name "main.dll" 2>/dev/null | head -1) | |
| if [ -z "$BIN" ]; then echo "[ERROR] no DLL"; exit 1; fi | |
| cp "$BIN" libs/python/src/nextssl/lib/nextssl.dll | |
| - name: Run Python tests | |
| shell: bash | |
| run: python -m pytest test/ -v --tb=short | |
| - name: Build windows wheel | |
| shell: bash | |
| run: | | |
| cd libs/python | |
| python setup.py bdist_wheel --plat-name win_amd64 --dist-dir dist | |
| ls dist/ | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-windows | |
| path: libs/python/dist/*.whl | |
| if-no-files-found: error | |
| # -- 2c. Build + test on macOS ------------------------------------------------ | |
| build-mac: | |
| needs: parse | |
| if: needs.parse.outputs.should_run == 'true' | |
| runs-on: macos-latest # arm64 (Apple Silicon) — macos-13 Intel was deprecated Dec 2025 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - 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 mac \ | |
| --lib-ext .dylib \ | |
| --no-color | |
| - name: Bundle native binary into package | |
| shell: bash | |
| run: | | |
| mkdir -p libs/python/src/nextssl/lib | |
| BIN=$(find bin/mac/primary -name "main.dylib" 2>/dev/null | head -1) | |
| if [ -z "$BIN" ]; then echo "[ERROR] no dylib"; exit 1; fi | |
| cp "$BIN" libs/python/src/nextssl/lib/nextssl.dylib | |
| - name: Run Python tests | |
| shell: bash | |
| run: python -m pytest test/ -v --tb=short | |
| - name: Build mac wheel | |
| shell: bash | |
| run: | | |
| cd libs/python | |
| python setup.py bdist_wheel --plat-name macosx_14_0_arm64 --dist-dir dist | |
| ls dist/ | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-mac | |
| path: libs/python/dist/*.whl | |
| if-no-files-found: error | |
| # -- 3. Publish to TestPyPI -------------------------------------------------- | |
| publish-test: | |
| needs: [parse, build-linux, build-windows, build-mac] | |
| if: | | |
| needs.build-linux.result == 'success' && | |
| needs.build-windows.result == 'success' && | |
| needs.build-mac.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 | |
| # -- 3.5. Verify published package on each platform ------------------------ | |
| verify-testpkg: | |
| # Needs both publish-test (to wait for upload) and parse (for version output). | |
| needs: [parse, publish-test] | |
| if: needs.parse.outputs.pip_test == 'true' && needs.parse.outputs.no_release != 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| - os: windows-latest | |
| - os: macos-latest # arm64 — macos-13 Intel deprecated Dec 2025 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 # needed to access test/smoke.py | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install pytest | |
| run: python -m pip install --upgrade pip pytest | |
| - name: Install from TestPyPI | |
| shell: bash | |
| run: | | |
| VER="${{ needs.parse.outputs.version }}" | |
| PKG="${VER:+nextssl==$VER}" | |
| PKG="${PKG:-nextssl}" | |
| INDEX="--index-url https://test.pypi.org/simple" | |
| # TestPyPI may take a moment to index a freshly uploaded release. | |
| for attempt in 1 2 3 4 5; do | |
| pip install $INDEX --no-deps --pre "$PKG" && break | |
| echo "[WARN] attempt $attempt failed; retrying in 15s…" | |
| sleep 15 | |
| done | |
| - name: Print installed version | |
| shell: bash | |
| run: python -c "import nextssl; print('installed:', nextssl.__version__)" | |
| # Run smoke.py with --noconftest so conftest.py does NOT inject the local | |
| # source tree into sys.path. This guarantees we exercise the pip-installed | |
| # wheel, not repo source code. | |
| - name: Run smoke tests against installed wheel | |
| shell: bash | |
| run: pytest test/smoke.py --noconftest -v --tb=short | |
| # -- 4. Publish to PyPI + create GitHub release ------------------------------ | |
| publish-live: | |
| needs: [parse, build-linux, build-windows, build-mac] | |
| if: | | |
| needs.build-linux.result == 'success' && | |
| needs.build-windows.result == 'success' && | |
| needs.build-mac.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 | |
| - name: Sanity check version & changelog | |
| shell: bash | |
| run: | | |
| VER="${{ needs.parse.outputs.version }}" | |
| if [ -z "$VER" ]; then | |
| echo "[ERROR] publish-live invoked without version" >&2 | |
| exit 1 | |
| fi | |
| FILE="note/changelog/${VER//./_}.md" | |
| if [ ! -f "$FILE" ]; then | |
| echo "[ERROR] changelog file missing: $FILE" >&2 | |
| exit 1 | |
| fi | |
| - 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 |