From ad814675d4b2bdaa3b350661acf000b8c14fa2cd Mon Sep 17 00:00:00 2001 From: Ihor Cherkashyn Date: Wed, 22 Apr 2026 15:39:44 +0200 Subject: [PATCH] Automate publishing to PyPi via GHA --- .github/workflows/publish-testpypi.yml | 154 +++++++++++++++++++++++++ .github/workflows/publish.yml | 135 ++++++++++++++++++++++ pyproject.toml | 4 +- 3 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/publish-testpypi.yml create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish-testpypi.yml b/.github/workflows/publish-testpypi.yml new file mode 100644 index 0000000..d094cca --- /dev/null +++ b/.github/workflows/publish-testpypi.yml @@ -0,0 +1,154 @@ +name: Publish to TestPyPI + +on: + workflow_dispatch: + inputs: + tag: + description: "Git tag to publish (e.g. v3.1.0rc1 for PEP 440 pre-release)" + required: true + +concurrency: + group: publish-testpypi-${{ inputs.tag }} + cancel-in-progress: false + +env: + PYTHON_VERSION: "3.12" + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ inputs.tag }} + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Verify tag matches package version + env: + INPUT_TAG: ${{ inputs.tag }} + run: | + if [[ ! "$INPUT_TAG" =~ ^v[0-9] ]]; then + echo "Invalid tag format: $INPUT_TAG (expected v[0-9]...)" + exit 1 + fi + TAG_VERSION="${INPUT_TAG#v}" + PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then + echo "Tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)" + exit 1 + fi + echo "Version check passed: $PKG_VERSION" + + - name: Install and build package + run: | + python -m pip install --upgrade pip build + python -m build + + - name: Upload distribution artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: dist + path: dist/ + if-no-files-found: error + retention-days: 5 + + test-package: + name: Verify built package (Python ${{ matrix.python-version }}) + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + strategy: + fail-fast: false + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + + steps: + - name: Checkout tests and metadata + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ inputs.tag }} + sparse-checkout: | + tests + pyproject.toml + sparse-checkout-cone-mode: false + + - name: Download distribution artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: dist + path: dist/ + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Verify sdist installs + run: | + python -m pip install --upgrade pip + python -m pip install dist/*.tar.gz + python -c "from signnow import __version__; print(f'sdist installed: {__version__}')" + python -m pip uninstall -y signnow-python-sdk + + - name: Install wheel and test dependencies + run: | + python -m pip install dist/*.whl + python -m pip install "pytest>=7.4.0" + + - name: Verify installed package is imported + run: | + python - <<'PY' + import os + import signnow + + path = os.path.realpath(signnow.__file__) + workspace = os.path.realpath(os.environ["GITHUB_WORKSPACE"]) + + print(f"Imported from: {path}") + print(f"Workspace: {workspace}") + + if path.startswith(workspace): + raise SystemExit(f"Package imported from repository workspace instead of installed wheel: {path}") + PY + + - name: Run tests against installed wheel + run: | + python -c "from signnow import __version__; print(f'wheel installed: {__version__}')" + pytest tests/ -v + + publish: + name: Publish to TestPyPI + needs: test-package + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: + name: testpypi + url: https://test.pypi.org/project/signnow-python-sdk/ + permissions: + contents: read + id-token: write + + steps: + - name: Download distribution artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: dist + path: dist/ + + - name: Publish to TestPyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + skip-existing: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..e65c434 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,135 @@ +name: Publish to PyPI + +on: + workflow_dispatch: + inputs: + tag: + description: "Git tag to republish (e.g. v3.1.0)" + required: true + +concurrency: + group: publish-pypi-${{ inputs.tag || github.ref_name }} + cancel-in-progress: false + +env: + PYTHON_VERSION: "3.12" + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ inputs.tag || github.ref_name }} + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Verify tag matches package version + env: + INPUT_TAG: ${{ inputs.tag || github.ref_name }} + run: | + if [[ ! "$INPUT_TAG" =~ ^v[0-9] ]]; then + echo "Invalid tag format: $INPUT_TAG (expected v[0-9]...)" + exit 1 + fi + TAG_VERSION="${INPUT_TAG#v}" + PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then + echo "Tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)" + exit 1 + fi + echo "Version check passed: $PKG_VERSION" + + - name: Install and build package + run: | + python -m pip install --upgrade pip build + python -m build + + - name: Upload distribution artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: dist + path: dist/ + if-no-files-found: error + retention-days: 5 + + test-package: + name: Verify built package (Python ${{ matrix.python-version }}) + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + strategy: + fail-fast: false + matrix: + python-version: ["3.8", "3.12"] + + steps: + - name: Checkout tests + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ inputs.tag || github.ref_name }} + sparse-checkout: | + tests + pyproject.toml + sparse-checkout-cone-mode: false + + - name: Download distribution artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: dist + path: dist/ + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Verify sdist installs + run: | + python -m pip install --upgrade pip + python -m pip install dist/*.tar.gz + python -c "from signnow import __version__; print(f'sdist installed: {__version__}')" + python -m pip uninstall -y signnow-python-sdk + + - name: Install wheel and test dependencies + run: | + python -m pip install dist/*.whl + python -m pip install "pytest>=7.4.0" "pytest-cov>=4.1.0" + + - name: Run tests against installed wheel + run: | + python -c "from signnow import __version__; print(f'wheel installed: {__version__}')" + pytest tests/ -v + + publish: + name: Publish to PyPI + needs: test-package + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: + name: pypi + url: https://pypi.org/project/signnow-python-sdk/ + permissions: + contents: read + id-token: write + + steps: + - name: Download distribution artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: dist + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 diff --git a/pyproject.toml b/pyproject.toml index 92b67ba..8e2d859 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,9 +39,9 @@ dev = [ ] [project.urls] -Homepage = "https://github.com/signnow/PythonSdk" +Homepage = "https://github.com/signnow/SNPythonSDK" Documentation = "https://docs.signnow.com" -Repository = "https://github.com/signnow/PythonSdk" +Repository = "https://github.com/signnow/SNPythonSDK" [tool.setuptools.packages.find] where = ["."]