diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 01c9840..314662a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -227,3 +227,83 @@ jobs: - name: Publish working-directory: npm run: npm publish --provenance --access public + + build-wheels: + name: Build wheel (${{ matrix.target }}) + needs: release + runs-on: ${{ matrix.os }} + if: startsWith(github.ref, 'refs/tags/v') + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + manylinux: '2_28' + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + manylinux: '2_28' + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + manylinux: musllinux_1_2 + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + manylinux: musllinux_1_2 + - target: x86_64-apple-darwin + os: macos-latest + manylinux: 'off' + - target: aarch64-apple-darwin + os: macos-latest + manylinux: 'off' + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Align Cargo version with tag + run: | + VERSION="${GITHUB_REF_NAME#v}" + # Force the wheel version to match the release tag in case the in-repo + # Cargo.toml was not bumped before tagging. Mirrors the npm step above. + sed -i.bak -E "0,/^version = \"[^\"]*\"$/s//version = \"${VERSION}\"/" \ + crates/clickhousectl/Cargo.toml + rm crates/clickhousectl/Cargo.toml.bak + + - name: Build wheel + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 + with: + target: ${{ matrix.target }} + manylinux: ${{ matrix.manylinux }} + working-directory: pypi + args: --release --out dist + + - name: Upload wheel artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: wheel-${{ matrix.target }} + path: pypi/dist/*.whl + + publish-pypi: + name: Publish to PyPI + needs: build-wheels + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + # `id-token: write` is required for PyPI OIDC trusted publishing. The + # publisher action picks up OIDC automatically when no token input is + # provided. + permissions: + id-token: write + contents: read + steps: + - name: Download wheel artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: wheels + pattern: wheel-* + merge-multiple: true + + - name: Publish + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + with: + packages-dir: wheels diff --git a/.gitignore b/.gitignore index 9ccd964..7e46b74 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ /npm/package-lock.json /npm/*.tgz /npm/README.md +/pypi/target/ +/pypi/dist/ .env /.env /.env.local diff --git a/CLAUDE.md b/CLAUDE.md index 176685c..b5c0c82 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -126,7 +126,7 @@ cargo add -p clickhouse-cloud-api url - If the user references a GitHub issue (e.g. "work on issue 3"), use `gh issue view 3` to get the details, then create a branch like `issue-3-short-description`. - Update `README.md` and any relevant documentation as part of the change — PRs should include doc updates for new or changed functionality. - Commit to the branch, push, and create a PR with `gh pr create`. -- Releases are done by tagging `main` (e.g. `git tag v0.1.4 && git push origin v0.1.4`), which triggers the GitHub Actions release workflow. Bump all of these to the same version in lockstep: `crates/clickhousectl/Cargo.toml` (`version` and the `clickhouse-cloud-api` dep version), `crates/clickhouse-cloud-api/Cargo.toml`, and `npm/package.json`. The workflow also re-aligns `npm/package.json` to the tag at publish time, but bump it in the repo too so the source-of-truth matches. Release-time secrets: `CARGO_REGISTRY_TOKEN` for crates.io; npm uses OIDC trusted publishing (configured one-time on npmjs.com against this repo, `release.yml`, and the `publish-npm` job). +- Releases are done by tagging `main` (e.g. `git tag v0.1.4 && git push origin v0.1.4`), which triggers the GitHub Actions release workflow. Bump all of these to the same version in lockstep: `crates/clickhousectl/Cargo.toml` (`version` and the `clickhouse-cloud-api` dep version), `crates/clickhouse-cloud-api/Cargo.toml`, and `npm/package.json`. The workflow also re-aligns `npm/package.json` to the tag at publish time, but bump it in the repo too so the source-of-truth matches. `pypi/pyproject.toml` does *not* need a manual bump — maturin pulls the wheel version from `crates/clickhousectl/Cargo.toml` (via `dynamic = ["version"]`), and the `build-wheels` job also re-aligns the Cargo version to the tag at publish time. Release-time secrets: `CARGO_REGISTRY_TOKEN` for crates.io; npm uses OIDC trusted publishing (configured one-time on npmjs.com against this repo, `release.yml`, and the `publish-npm` job); PyPI uses OIDC trusted publishing (configured one-time on pypi.org against this repo, `release.yml`, and the `publish-pypi` job). ## Testing locally diff --git a/README.md b/README.md index 8e614c7..bb92c06 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,18 @@ npm install -g clickhousectl This installs an npm wrapper package that downloads the matching prebuilt binary from `builds.clickhouse.com` at install time. Both `clickhousectl` and `chctl` are exposed as commands. If you use `npm install --ignore-scripts`, the download is skipped — fall back to one of the other install paths. +### pip + +```bash +pip install clickhousectl +# or +pipx install clickhousectl +# or +uv tool install clickhousectl +``` + +This installs a prebuilt wheel containing the matching `clickhousectl` binary. Linux (glibc and musl, x86_64 and aarch64) and macOS (Intel and Apple Silicon) wheels are published to PyPI. + ### From crates.io Builds from source: diff --git a/pypi/pyproject.toml b/pypi/pyproject.toml new file mode 100644 index 0000000..6a88cd0 --- /dev/null +++ b/pypi/pyproject.toml @@ -0,0 +1,19 @@ +[build-system] +requires = ["maturin>=1.7,<2.0"] +build-backend = "maturin" + +[project] +name = "clickhousectl" +dynamic = ["version", "description", "license", "readme", "authors", "urls"] +requires-python = ">=3.9" +keywords = ["clickhouse", "cli", "database", "cloud"] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Topic :: Database", +] + +[tool.maturin] +bindings = "bin" +manifest-path = "../crates/clickhousectl/Cargo.toml" +strip = true