Releasing¶
Releases are triggered by pushing a git tag. The tag name determines the channel.
Tag Conventions¶
| Tag format | Channel | GitHub Release type |
|---|---|---|
v1.2.3-alpha.1 |
alpha | Pre-release |
v1.2.3-beta.1 |
beta | Pre-release |
v1.2.3-rc.1 |
rc | Pre-release |
v1.2.3 |
stable | Latest release |
Creating a Release¶
# Alpha
git tag v1.2.3-alpha.1 && git push origin v1.2.3-alpha.1
# Beta
git tag v1.2.3-beta.1 && git push origin v1.2.3-beta.1
# Release candidate
git tag v1.2.3-rc.1 && git push origin v1.2.3-rc.1
# Stable
git tag v1.2.3 && git push origin v1.2.3
The CI workflow (build-dmg.yml, named "Build Provisa Packages") triggers on any v* tag and runs these jobs, most in parallel:
- Resolve release metadata. Detects channel from tag suffix, derives PEP 440 version and asset names.
- Download / package Trino plugins. Pulls Calcite Trino connectors and packages a tarball.
- Pull core / obs / demo Docker images. Saves service image tarballs (arm64, plus amd64 core for the Windows container tier)
- Build macOS Core / Obs / Demo DMGs. Run on
macos-14(Apple Silicon), airgapped. - Build Linux AppImage. Core, airgapped.
- Build Windows Core installer. Native, embedded Python, no Docker.
- Build Windows Container-tier installer. WSL2 + Trino, fetches images on demand (no VirtualBox/OVA)
- Build JDBC driver. Maven shaded JAR.
- Build and test Python client, then Publish to PyPI
- Publish GitHub Release. Uploads all assets, sets pre-release flag for alpha/beta/rc.
Release Assets¶
Each release publishes the following assets, all attached to the GitHub Release (the wheel also goes to PyPI):
| Asset | Platform / Use |
|---|---|
Provisa-<tag>-macOS.dmg |
macOS Core (Apple Silicon, airgapped) |
Provisa-Runtime-<tag>-macOS.dmg |
macOS native Python runtime (mount alongside Core) |
Provisa-Obs-<tag>-macOS.dmg |
macOS Observability extension |
Provisa-Demo-<tag>-macOS.dmg |
macOS Demo extension (requires Obs) |
Provisa-<tag>-linux-x86_64.AppImage |
Linux x86_64 core (airgapped) |
Provisa-<tag>-windows-x64.exe |
Windows x64 native installer (embedded Python, no Docker) |
Provisa-Container-<tag>-windows-x64.exe |
Windows x64 container-tier upgrade (WSL2 + Trino) |
provisa-jdbc-<tag>.jar |
JDBC driver for Tableau, PowerBI and DBeaver |
provisa_client-<pep440>-py3-none-any.whl |
Python client (also PyPI) |
provisa-core-images-<tag>.tar.gz |
Core Services image tarballs (arm64, airgapped) |
provisa-core-images-amd64-<tag>.zip |
Core Services images (amd64, Windows container tier / airgap) |
provisa-obs-images-<tag>.tar.gz |
Observability Stack images (optional) |
provisa-demo-images-<tag>.tar.gz |
Demo Data Pack images (optional) |
provisa-trino-plugins-<tag>.tar.gz |
Coordination Engine connectors (SharePoint, Splunk, File) |
The Python client version is automatically converted to PEP 440 format:
v0.1.0-alpha.1 → 0.1.0a1, v0.1.0-beta.1 → 0.1.0b1, v0.1.0-rc.1 → 0.1.0rc1.
PyPI Publishing Setup (one-time)¶
The publish-pypi (provisa-client) and publish-provisa-pypi (provisa) jobs in .github/workflows/build-dmg.yml publish with PyPI trusted publishing. The job's GitHub OIDC token (id-token: write) authenticates it, and no API token is read. Register this repository and build-dmg.yml as a trusted publisher for each project on pypi.org (Your projects → Manage → Publishing). The publishes then run automatically on every tag.
Signing and Supply Chain¶
What each release artifact carries, as the release workflows build it:
| Artifact | Signature |
|---|---|
provisa and provisa-client on PyPI |
Sigstore-signed PEP 740 attestation per file (attestations: true), verifiable on pypi.org |
provisa-pg-ext, provisa-duckdb-ext on PyPI |
Sigstore-signed PEP 740 attestation per file |
JDBC driver .jar |
Sigstore build-provenance attestation (actions/attest-build-provenance) |
macOS .dmg installers |
Developer ID code signature and Apple notarization |
Windows .exe installers |
Not signed |
Linux .AppImage |
Not signed |
The signed install on any OS is pip install provisa, the Sigstore-attested PyPI package. A reviewer who needs a signed artifact on Linux or Windows installs with pip. The Windows build signs with Authenticode when a code-signing certificate is configured (WINDOWS_CERT_PFX_BASE64, WINDOWS_CERT_PFX_PASSWORD, WINDOWS_CERT_TIMESTAMP_URL, read by packaging/windows/build-sfx.ps1); none is configured, so it ships unsigned.
Dependencies and CVE gates:
- Python dependencies are locked with hashes in
uv.lock; CI installs them withuv sync --frozen. - The
CVE Auditworkflow (.github/workflows/cve-audit.yml) runspip-auditover the locked set on every push tomain, on every pull request and weekly. - At release, the
provisaandprovisa-clientbuilds each produce a CycloneDX SBOM and runpip-auditover what ships. The JDBC driver is scanned withgrype --fail-on medium. A finding fails the job, and the release publishes nothing, because the PyPI uploads and the GitHub release both wait on these jobs. The SBOMs and CVE reports are attached to each release. - The desktop installers build the
provisawheel from source on the release runner and resolve its dependencies withpipat build time, not fromuv.lock; the release CVE gate scans the locked set, not the installers' bundled dependencies. packaging/pypi/requirements-embedded.lockis a hash-pinned resolution ofprovisa[embedded]for airgap mirrors (seepackaging/pypi/MIRROR-CHECKLIST.md). It is regenerated by hand, not per release.
Required Repository Secrets¶
Configure these under Settings → Secrets → Actions:
| Secret | Required for | Description |
|---|---|---|
APPLE_CERT_P12_BASE64 |
Signed builds | Base64-encoded .p12 certificate file (see below) |
APPLE_CERT_P12_PASSWORD |
Signed builds | Password set when exporting the .p12 from Keychain Access |
APPLE_DEVELOPER_ID |
Signed builds | Full cert name: Developer ID Application: Your Name (TEAMID) |
APPLE_NOTARYTOOL_APPLE_ID |
Notarized builds | Apple ID email |
APPLE_NOTARYTOOL_PASSWORD |
Notarized builds | App-specific password from appleid.apple.com (not your login password) |
APPLE_NOTARYTOOL_TEAM_ID |
Notarized builds | 10-character Apple Team ID |
Builds without these secrets succeed but produce an unsigned/unnotarized DMG (users will see a Gatekeeper warning).
Exporting the .p12 Certificate¶
- Open Keychain Access → login keychain → My Certificates
- Find Developer ID Application: Your Name (TEAMID) and expand it to confirm the private key is nested underneath
- Select both the certificate and its private key → right-click → Export 2 Items → save as
.p12→ set a strong password - Base64-encode and copy to clipboard:
- Paste as the value of
APPLE_CERT_P12_BASE64; setAPPLE_CERT_P12_PASSWORDto the password from step 3
Finding Your Certificate Name¶
Copy the full string in quotes. That string is the value for APPLE_DEVELOPER_ID.
Deleting a Bad Tag¶
Then delete the corresponding GitHub Release in the UI before re-tagging.