diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 26fe992bc..21d3d06ee 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,6 +15,20 @@ on: - 'v*-beta*' release: types: [published] + # Build and test, but never publish, when packaging changes, so a broken + # release pipeline is caught in review rather than at release time. + pull_request: + paths: + - '.github/workflows/publish.yml' + - 'accel.c' + - 'setup.py' + - 'setup.cfg' + - 'pyproject.toml' + - 'MANIFEST.in' + # Runner images and cibuildwheel's toolchains are retired out from under the + # workflow with no change to this repo, so build and test weekly as well. + schedule: + - cron: '0 6 * * 1' workflow_dispatch: inputs: publish_pypi: @@ -36,7 +50,14 @@ on: jobs: setup-database: - runs-on: ubuntu-latest + # Pull requests from forks get no secrets, so they cannot deploy the test + # cluster. + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + # Pinned, not ubuntu-latest: that label is moving to Ubuntu 26.04, and + # this job, publish and shutdown-database run on the image's own Python + # rather than one from setup-python, so the move would change it under + # them. The weekly scheduled run flags this image's retirement. + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 @@ -69,6 +90,10 @@ jobs: build-and-test: needs: setup-database + # setup-database is skipped only for a fork's pull request, which has no + # secrets. Those runs still build every wheel and check that its extension + # loads; only the cluster tests are left out. + if: ${{ !cancelled() && (needs.setup-database.result == 'success' || needs.setup-database.result == 'skipped') }} name: Build and test wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -77,7 +102,7 @@ jobs: matrix: os: - ubuntu-24.04 - - macos-14 + - macos-15 - windows-2022 steps: @@ -127,23 +152,24 @@ jobs: # only reruns the tests there, so the oldest and newest supported # interpreters both load the extension. CIBW_BUILD: "cp39-* cp315-*" - CIBW_SKIP: "pp* *-musllinux* *-manylinux_i686" + CIBW_SKIP: "*-musllinux* *-manylinux_i686" # cibuildwheel v3+ builds on manylinux_2_28 (glibc 2.28) by default. # Pin manylinux2014 (glibc 2.17) so CentOS/RHEL 7, Amazon Linux 2 and # Ubuntu 18.04 still get the C extension instead of falling back to # the pure-Python wheel without any warning. CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_MANYLINUX_AARCH64_IMAGE: manylinux2014 - CIBW_TEST_COMMAND: "pytest -v --pyargs singlestoredb.tests.test_basics" - # xdist because pyproject.toml's addopts sets -n/--dist, and PYTHONPATH - # points --pyargs at the workspace, so that pyproject is the inifile - # here. Without the plugin pytest exits on the unknown arguments. - CIBW_TEST_REQUIRES: "pytest pytest-xdist" + CIBW_TEST_COMMAND: ${{ needs.setup-database.result == 'success' && 'pytest -v --pyargs singlestoredb.tests.test_basics' || 'python -c "import _singlestoredb_accel, singlestoredb"' }} + CIBW_TEST_REQUIRES: "pytest" # CLUSTER_PASSWORD has to survive both the userinfo half of the URL # and the single-quoted shell word cibuildwheel evaluates, so keep the # secret alphanumeric: no ':', '@', '/', '%' or quote characters. CIBW_ENVIRONMENT: "SINGLESTOREDB_URL='mysql://admin:${{ secrets.CLUSTER_PASSWORD }}@${{ needs.setup-database.outputs.cluster-host }}:3306/${{ needs.setup-database.outputs.cluster-database }}?pure_python=0'" - PYTHONPATH: ${{ github.workspace }} + # No PYTHONPATH here: it would put the checkout ahead of the installed + # wheel, so the tests would import the checkout's singlestoredb and the + # extension that `pip install -e` built in place for the host Python, + # not the wheel under test. On Windows the 64-bit host's extension + # then fails to load in the 32-bit test interpreter. # - name: Build conda # if: ${{ matrix.os != 'windows-latest' }} @@ -186,8 +212,10 @@ jobs: publish: needs: build-and-test + if: ${{ github.event_name != 'pull_request' && github.event_name != 'schedule' }} - runs-on: ubuntu-latest + # Pinned for the same reason as setup-database. + runs-on: ubuntu-24.04 permissions: id-token: write # Required for OIDC trusted publishing and actions/attest @@ -258,8 +286,11 @@ jobs: shutdown-database: needs: [setup-database, build-and-test] - if: ${{ always() }} - runs-on: ubuntu-latest + # Not when setup-database was skipped (a fork's pull request): there is + # no cluster, and the empty ID would fail the shutdown step. + if: ${{ always() && needs.setup-database.result != 'skipped' }} + # Pinned for the same reason as setup-database. + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 diff --git a/pyproject.toml b/pyproject.toml index f7a140b75..e62d7daa8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["build", "setuptools>=61.0", "wheel"] +requires = ["build", "setuptools>=77.0"] build-backend = "setuptools.build_meta" [project] @@ -7,7 +7,8 @@ name = "singlestoredb" version = "1.18.2" description = "Interface to the SingleStoreDB database and workspace management APIs" readme = {file = "README.md", content-type = "text/markdown"} -license = {text = "Apache-2.0"} +license = "Apache-2.0" +license-files = ["LICENSE"] authors = [ {name = "SingleStore", email = "support@singlestore.com"} ] @@ -69,7 +70,7 @@ docs = [ ] build = [ "build", - "setuptools>=61.0", + "setuptools>=77.0", "wheel", ] dev = [ @@ -86,7 +87,9 @@ singlestoredb = "singlestoredb.pytest" include-package-data = true [tool.setuptools.packages.find] -exclude = ["docs*", "resources*", "examples*", "licenses*"] +# build* because discovery includes namespace packages, so an earlier build's +# build/lib/ tree would otherwise be packaged as a package of its own. +exclude = ["build*", "docs*", "resources*", "examples*", "licenses*"] [tool.setuptools.package-data] "*" = ["*.typed", "*.sql", "*.csv", "*.ipynb"] diff --git a/setup.py b/setup.py index 9ace49851..44458593a 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from setuptools import Extension from setuptools import setup -from wheel.bdist_wheel import bdist_wheel +from setuptools.command.bdist_wheel import bdist_wheel py_limited_api = '0x03080000' diff --git a/singlestoredb/tests/conftest.py b/singlestoredb/tests/conftest.py index 0de122150..0987d8615 100644 --- a/singlestoredb/tests/conftest.py +++ b/singlestoredb/tests/conftest.py @@ -330,6 +330,9 @@ def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: pass +# An xdist hook. Optional so the suite still starts where xdist is not +# installed, as when the release build tests the wheel's own copy of it. +@pytest.hookimpl(optionalhook=True) def pytest_testnodedown(node: Any, error: Any) -> None: """ Report, on the controller, what a worker could not terminate.