Pypi test version file problem fixed 2 --pipTest #56 #57
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. | |
| # 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 |