name: Release Binaries # Builds the self-contained `db` CLI binary for each supported platform and # publishes them (with SHA256 checksums) to a GitHub Release. Triggered by # pushing a version tag (e.g. v0.2.1); also runnable manually for dry runs. on: push: tags: ["v*"] workflow_dispatch: permissions: contents: write # Re-pushing a tag (or re-dispatching) cancels an in-flight run for the same # ref so a stuck build can't pile up behind a newer attempt. concurrency: group: release-${{ github.ref }} cancel-in-progress: true jobs: build: name: Build ${{ matrix.target_os }}-${{ matrix.arch }} runs-on: ${{ matrix.runner }} # Cap the wait so a capacity-starved runner fails this leg in minutes # instead of hanging the release for GitHub's 24h queue limit. timeout-minutes: 30 strategy: fail-fast: false matrix: include: # Built on the oldest practical glibc for forward compatibility. - runner: ubuntu-22.04 target_os: linux arch: x86_64 # GitHub-hosted ARM Linux runner: free for public repos; requires a # Team/Enterprise plan for private repos. - runner: ubuntu-22.04-arm target_os: linux arch: aarch64 # macOS is Apple Silicon only: GitHub's Intel (macos-13) runners are # being deprecated and their queue is unreliable. Intel-Mac users can # still `pip install diffbot-python`. - runner: macos-14 # Apple Silicon target_os: darwin arch: aarch64 steps: - uses: actions/checkout@v4 # Fail fast if the tag doesn't match pyproject.toml's version, so the # GitHub Release name can never disagree with the version the binaries # report (db --version) or the one published to PyPI. - name: Check tag matches package version if: startsWith(github.ref, 'refs/tags/') run: | pkg=$(grep -m1 '^version' pyproject.toml | cut -d'"' -f2) tag=${GITHUB_REF_NAME#v} if [ "$pkg" != "$tag" ]; then echo "::error::Tag v${tag} does not match pyproject.toml version ${pkg}. Bump pyproject.toml (e.g. 'make bump-patch') and re-tag." exit 1 fi echo "Tag and package version agree: ${pkg}" - name: Install uv uses: astral-sh/setup-uv@v5 with: python-version: "3.12" - name: Build binary run: ./scripts/build_binary.sh --arch ${{ matrix.arch }} - name: Verify checksum working-directory: dist run: | if command -v sha256sum >/dev/null 2>&1; then sha256sum -c "db-${{ matrix.target_os }}-${{ matrix.arch }}.sha256" else shasum -a 256 -c "db-${{ matrix.target_os }}-${{ matrix.arch }}.sha256" fi - name: Upload artifact uses: actions/upload-artifact@v4 with: name: db-${{ matrix.target_os }}-${{ matrix.arch }} path: | dist/db-${{ matrix.target_os }}-${{ matrix.arch }} dist/db-${{ matrix.target_os }}-${{ matrix.arch }}.sha256 if-no-files-found: error release: name: Publish GitHub Release needs: build runs-on: ubuntu-latest # Only publish for real tag pushes; workflow_dispatch runs just build + verify. if: startsWith(github.ref, 'refs/tags/') steps: - name: Download all artifacts uses: actions/download-artifact@v4 with: path: dist merge-multiple: true - name: List artifacts run: ls -lR dist - name: Publish release env: GH_TOKEN: ${{ github.token }} # This job doesn't check out the repo, so gh can't infer it from a local # git remote — pass --repo explicitly on every gh call. run: | set -euo pipefail tag="${GITHUB_REF_NAME}" repo="${GITHUB_REPOSITORY}" if gh release view "$tag" --repo "$repo" >/dev/null 2>&1; then echo "Release $tag exists; uploading assets (clobbering)." gh release upload "$tag" dist/* --repo "$repo" --clobber else echo "Creating release $tag." gh release create "$tag" \ --repo "$repo" \ --title "$tag" \ --generate-notes \ dist/* fi