From fc3dfde281225208e7aaa7554d5ebaeeee8d6c59 Mon Sep 17 00:00:00 2001 From: Jerome Choo Date: Mon, 8 Jun 2026 12:47:44 -0500 Subject: [PATCH 1/5] Self-contained binary --- .github/workflows/release.yml | 97 +++++++++++++++++++ .github/workflows/tests.yml | 23 +++++ README.md | 14 +++ install.sh | 171 ++++++++++++++++++++++++++++++++++ scripts/build_binary.sh | 140 ++++++++++++++++++++++++++++ scripts/db_entry.py | 11 +++ src/diffbot/cli/__init__.py | 3 +- 7 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100755 install.sh create mode 100755 scripts/build_binary.sh create mode 100644 scripts/db_entry.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fde168d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,97 @@ +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 + +jobs: + build: + name: Build ${{ matrix.target_os }}-${{ matrix.arch }} + runs-on: ${{ matrix.runner }} + 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 + - runner: macos-13 # Intel + target_os: darwin + arch: x86_64 + - runner: macos-14 # Apple Silicon + target_os: darwin + arch: aarch64 + steps: + - uses: actions/checkout@v4 + + - 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 }} + run: | + set -euo pipefail + tag="${GITHUB_REF_NAME}" + if gh release view "$tag" >/dev/null 2>&1; then + echo "Release $tag exists; uploading assets (clobbering)." + gh release upload "$tag" dist/* --clobber + else + echo "Creating release $tag." + gh release create "$tag" \ + --title "$tag" \ + --generate-notes \ + dist/* + fi diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ee9bd9a..67d1181 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,3 +45,26 @@ jobs: - name: Run tests run: uv run pytest + + # Smoke-test the standalone binary build so scripts/build_binary.sh can't + # silently break between releases. The release workflow builds the full + # cross-platform matrix; here we just prove the x86_64 Linux build works. + build-binary: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + python-version: "3.12" + + - name: Build standalone db binary + run: ./scripts/build_binary.sh + + - name: Verify checksum + working-directory: dist + run: sha256sum -c db-linux-x86_64.sha256 + + - name: Smoke-test binary + run: ./dist/db-linux-x86_64 --version diff --git a/README.md b/README.md index 3bdbef2..1854456 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,20 @@ uv tool install . This drops a `db` executable into `~/.local/bin` (ensure it is on your `PATH`). Use `--force` to reinstall or upgrade after changes, or `--editable` to have source edits take effect immediately. Alternatively, a plain `pip install .` (or `pip install -e .`) also installs the `db` entry point into the active environment. +### Standalone binary (no Python required) + +Every release also ships a self-contained `db` binary for Linux and macOS (x86_64 and aarch64/arm64) — no Python install needed. The installer detects your platform, verifies the SHA256 checksum, and installs (or upgrades) `db` into `~/.local/bin`: + +```bash +curl -fsSL https://raw.githubusercontent.com/diffbot/diffbot-python/main/install.sh | sh +``` + +Pin a specific release or install location with flags (or the `DB_VERSION` / `DB_INSTALL_DIR` environment variables); re-running the installer upgrades an existing install in place: + +```bash +curl -fsSL https://raw.githubusercontent.com/diffbot/diffbot-python/main/install.sh | sh -s -- --version v0.2.1 --bin-dir ~/bin +``` + ```bash export DIFFBOT_API_TOKEN=your-token-here diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..8763257 --- /dev/null +++ b/install.sh @@ -0,0 +1,171 @@ +#!/bin/sh +# +# install.sh — Install (or update) the standalone Diffbot `db` CLI binary. +# +# Detects your platform, downloads the matching binary from the latest GitHub +# release, verifies its SHA256 checksum, and installs it to a bin directory on +# your PATH. Re-running upgrades an existing install in place. +# +# Quick start: +# curl -fsSL https://raw.githubusercontent.com/diffbot/diffbot-python/main/install.sh | sh +# +# Options (also settable via env var): +# --version DB_VERSION Release tag to install (default: latest). +# --bin-dir DB_INSTALL_DIR Install location (default: ~/.local/bin). +# --repo DB_REPO Source repo (default: diffbot/diffbot-python). +# -h, --help +# +# Supported platforms: linux/darwin on x86_64 (x64/amd64) and aarch64 (arm64). +set -eu + +REPO="${DB_REPO:-diffbot/diffbot-python}" +VERSION="${DB_VERSION:-latest}" +BIN_DIR="${DB_INSTALL_DIR:-${HOME}/.local/bin}" +BIN_NAME="db" + +err() { printf 'error: %s\n' "$*" >&2; exit 1; } +info() { printf '%s\n' "$*" >&2; } + +# True if $1 is a Python console-script shim (pip / uv tool / pipx / venv), +# i.e. a text shebang wrapper pointing at python. Our standalone binary is an +# ELF/Mach-O file and never starts with "#!", so this never matches it. +is_python_console_script() { + [ -f "$1" ] || return 1 + [ "$(dd if="$1" bs=2 count=1 2>/dev/null)" = "#!" ] || return 1 + head -n1 "$1" 2>/dev/null | grep -q 'python' +} + +while [ $# -gt 0 ]; do + case "$1" in + --version) VERSION="$2"; shift 2 ;; + --version=*) VERSION="${1#*=}"; shift ;; + --bin-dir) BIN_DIR="$2"; shift 2 ;; + --bin-dir=*) BIN_DIR="${1#*=}"; shift ;; + --repo) REPO="$2"; shift 2 ;; + --repo=*) REPO="${1#*=}"; shift ;; + -h | --help) sed -n '2,/^[^#]/p' "$0" | sed 's/^# \{0,1\}//;$d'; exit 0 ;; + *) err "unknown argument: $1 (try --help)" ;; + esac +done + +# --- Detect platform ------------------------------------------------------- +os="$(uname -s)" +case "$os" in + Linux) os="linux" ;; + Darwin) os="darwin" ;; + *) err "unsupported OS: $os (this installer supports Linux and macOS)" ;; +esac + +arch="$(uname -m)" +case "$arch" in + x86_64 | x64 | amd64) arch="x86_64" ;; + aarch64 | arm64) arch="aarch64" ;; + *) err "unsupported architecture: $arch" ;; +esac + +asset="${BIN_NAME}-${os}-${arch}" + +# --- Pick a download tool -------------------------------------------------- +if command -v curl >/dev/null 2>&1; then + download() { curl -fsSL "$1" -o "$2"; } + fetch() { curl -fsSL "$1"; } +elif command -v wget >/dev/null 2>&1; then + download() { wget -qO "$2" "$1"; } + fetch() { wget -qO - "$1"; } +else + err "need curl or wget to download the binary" +fi + +# --- Resolve the release tag ---------------------------------------------- +if [ "$VERSION" = "latest" ]; then + info "Resolving latest release of ${REPO}..." + api="https://api.github.com/repos/${REPO}/releases/latest" + VERSION="$(fetch "$api" | grep -m1 '"tag_name"' \ + | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')" + [ -n "$VERSION" ] || err "could not determine the latest release tag from ${api}" +fi + +base="https://github.com/${REPO}/releases/download/${VERSION}" +info "Installing ${asset} from ${REPO} ${VERSION}" + +# --- Download binary + checksum into a temp dir ---------------------------- +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT INT TERM + +info "Downloading binary..." +download "${base}/${asset}" "${tmp}/${asset}" \ + || err "failed to download ${base}/${asset} (no build for ${os}/${arch} in ${VERSION}?)" + +info "Downloading checksum..." +download "${base}/${asset}.sha256" "${tmp}/${asset}.sha256" \ + || err "failed to download checksum ${base}/${asset}.sha256" + +# --- Verify checksum ------------------------------------------------------- +info "Verifying SHA256 checksum..." +( + cd "$tmp" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum -c "${asset}.sha256" + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 -c "${asset}.sha256" + else + err "need sha256sum or shasum to verify the download" + fi +) >/dev/null 2>&1 || err "checksum verification failed — refusing to install" +info "Checksum OK." + +# --- Install (atomically), updating any existing install ------------------- +mkdir -p "$BIN_DIR" +target="${BIN_DIR}/${BIN_NAME}" + +if [ -e "$target" ]; then + old="$("$target" --version 2>/dev/null || echo "unknown")" + if is_python_console_script "$target"; then + info "" + info "Warning: ${target} looks like a pip/uv-managed 'db' entry point (${old})," + info "not a binary installed by this script. Overwriting it replaces that launcher," + info "but your Python package manager still treats the file as its own — a later" + info "'pip install --upgrade' / 'uv tool upgrade' could clobber it again, and an" + info "uninstall would delete it. To avoid the conflict, remove the managed copy first:" + info " pip uninstall diffbot-python # or: uv tool uninstall diffbot-python" + info "" + info "Proceeding to overwrite ${target}..." + else + info "Updating existing install at ${target} (${old})" + fi +else + info "Installing to ${target}" +fi + +chmod +x "${tmp}/${asset}" +# mv within the same filesystem is atomic; fall back to cp for cross-device. +mv -f "${tmp}/${asset}" "$target" 2>/dev/null || cp -f "${tmp}/${asset}" "$target" + +new="$("$target" --version 2>/dev/null || echo "$VERSION")" +info "" +info "Installed ${new} -> ${target}" + +# --- PATH guidance --------------------------------------------------------- +case ":${PATH}:" in + *":${BIN_DIR}:"*) ;; + *) + info "" + info "Note: ${BIN_DIR} is not on your PATH. Add it, e.g.:" + info " export PATH=\"${BIN_DIR}:\$PATH\"" + ;; +esac + +# Warn if a different `db` shadows the one we just installed. +existing="$(command -v "$BIN_NAME" 2>/dev/null || true)" +if [ -n "$existing" ] && [ "$existing" != "$target" ]; then + info "" + info "Note: another '${BIN_NAME}' is first on your PATH and will take precedence:" + info " ${existing}" + if is_python_console_script "$existing"; then + info " (it looks pip/uv-managed; remove it with 'pip uninstall diffbot-python'" + info " or 'uv tool uninstall diffbot-python', or put ${BIN_DIR} earlier on PATH.)" + fi +fi + +info "" +info "Run '${BIN_NAME} --help' to get started." diff --git a/scripts/build_binary.sh b/scripts/build_binary.sh new file mode 100755 index 0000000..5ab2c3f --- /dev/null +++ b/scripts/build_binary.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +# +# build_binary.sh — Build a self-contained `db` CLI binary with PyInstaller and +# emit a matching SHA256 checksum. +# +# Usage: +# scripts/build_binary.sh [--arch ] [--name ] [--output-dir ] +# +# Accepted --arch aliases (normalized for the asset name): +# x86_64 | x64 | amd64 -> x86_64 +# aarch64 | arm64 -> aarch64 +# +# PyInstaller does not cross-compile, so --arch must match the host +# architecture; the flag exists only so callers can pass any accepted alias. +# Build on a matching machine (or CI runner) to target a different arch. +# +# Produces, in the output dir (default: ./dist): +# -- the executable +# --.sha256 `sha256sum -c`-compatible checksum +# +# Environment overrides: +# BUILD_PYTHON Python version for the isolated build venv (default 3.12). +set -euo pipefail + +BIN_NAME="db" +OUTPUT_DIR="" +REQUESTED_ARCH="" +BUILD_PYTHON="${BUILD_PYTHON:-3.12}" + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENTRY_SCRIPT="${REPO_ROOT}/scripts/db_entry.py" + +err() { printf 'error: %s\n' "$*" >&2; exit 1; } +log() { printf '==> %s\n' "$*" >&2; } + +normalize_arch() { + case "$1" in + x86_64 | x64 | amd64) echo "x86_64" ;; + aarch64 | arm64) echo "aarch64" ;; + *) err "unsupported architecture: $1 (expected x86_64|x64|amd64|aarch64|arm64)" ;; + esac +} + +detect_os() { + case "$(uname -s)" in + Linux) echo "linux" ;; + Darwin) echo "darwin" ;; + *) err "unsupported OS: $(uname -s) (expected Linux or Darwin)" ;; + esac +} + +while [ $# -gt 0 ]; do + case "$1" in + --arch) REQUESTED_ARCH="$2"; shift 2 ;; + --arch=*) REQUESTED_ARCH="${1#*=}"; shift ;; + --name) BIN_NAME="$2"; shift 2 ;; + --name=*) BIN_NAME="${1#*=}"; shift ;; + --output-dir) OUTPUT_DIR="$2"; shift 2 ;; + --output-dir=*) OUTPUT_DIR="${1#*=}"; shift ;; + -h | --help) sed -n '2,/^[^#]/p' "$0" | sed 's/^# \{0,1\}//;$d'; exit 0 ;; + *) err "unknown argument: $1 (try --help)" ;; + esac +done + +OS="$(detect_os)" +HOST_ARCH="$(normalize_arch "$(uname -m)")" +if [ -n "$REQUESTED_ARCH" ]; then + ARCH="$(normalize_arch "$REQUESTED_ARCH")" +else + ARCH="$HOST_ARCH" +fi + +if [ "$ARCH" != "$HOST_ARCH" ]; then + err "cannot build a '$ARCH' binary on a '$HOST_ARCH' host: PyInstaller does not + cross-compile. Run this on a '$ARCH' machine or a matching CI runner." +fi + +OUTPUT_DIR="${OUTPUT_DIR:-${REPO_ROOT}/dist}" +WORK_DIR="${REPO_ROOT}/build/pyinstaller" +VENV_DIR="${REPO_ROOT}/build/venv" +ASSET="${BIN_NAME}-${OS}-${ARCH}" + +mkdir -p "$OUTPUT_DIR" "$WORK_DIR" +log "Building ${ASSET} (os=${OS} arch=${ARCH}, python=${BUILD_PYTHON})" + +# --- Isolated build environment -------------------------------------------- +# Prefer uv (the project's standard); fall back to stdlib venv + pip. +if command -v uv >/dev/null 2>&1; then + log "Creating build venv with uv" + uv venv --python "$BUILD_PYTHON" "$VENV_DIR" >&2 + VENV_PY="${VENV_DIR}/bin/python" + uv pip install --python "$VENV_PY" pyinstaller "$REPO_ROOT" >&2 +else + log "uv not found; creating build venv with python -m venv" + python3 -m venv "$VENV_DIR" + VENV_PY="${VENV_DIR}/bin/python" + "$VENV_PY" -m pip install --upgrade pip >&2 + "$VENV_PY" -m pip install pyinstaller "$REPO_ROOT" >&2 +fi + +# --- Freeze the binary ----------------------------------------------------- +# --collect-submodules grabs the dynamically-imported cli subcommands; the +# library reads its own version via importlib.metadata, so bundle that too. +log "Running PyInstaller" +"$VENV_PY" -m PyInstaller \ + --onefile \ + --clean \ + --noconfirm \ + --name "$ASSET" \ + --distpath "$OUTPUT_DIR" \ + --workpath "$WORK_DIR" \ + --specpath "$WORK_DIR" \ + --collect-submodules diffbot \ + --copy-metadata diffbot-python \ + "$ENTRY_SCRIPT" >&2 + +BINARY="${OUTPUT_DIR}/${ASSET}" +[ -f "$BINARY" ] || err "expected binary not found at ${BINARY}" +chmod +x "$BINARY" + +# --- Smoke test ------------------------------------------------------------ +log "Smoke-testing binary (--version)" +"$BINARY" --version >&2 || err "binary failed to run" + +# --- Checksum -------------------------------------------------------------- +# Written with a bare filename (no path) so `sha256sum -c` works from the dir. +log "Generating SHA256 checksum" +( + cd "$OUTPUT_DIR" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$ASSET" > "${ASSET}.sha256" + else + shasum -a 256 "$ASSET" > "${ASSET}.sha256" + fi +) + +log "Done" +log " binary: ${BINARY}" +log " checksum: ${BINARY}.sha256" +cat "${BINARY}.sha256" >&2 diff --git a/scripts/db_entry.py b/scripts/db_entry.py new file mode 100644 index 0000000..ce1db0c --- /dev/null +++ b/scripts/db_entry.py @@ -0,0 +1,11 @@ +"""PyInstaller entry point for the standalone ``db`` CLI. + +PyInstaller freezes a *script*, not a ``console_scripts`` entry point, so this +thin wrapper mirrors :mod:`diffbot.cli.__main__` but with an absolute import +that resolves correctly inside the frozen bundle. +""" + +from diffbot.cli import main + +if __name__ == "__main__": + main() diff --git a/src/diffbot/cli/__init__.py b/src/diffbot/cli/__init__.py index 6cac621..2c547c8 100644 --- a/src/diffbot/cli/__init__.py +++ b/src/diffbot/cli/__init__.py @@ -11,7 +11,7 @@ from rich.progress import Progress, SpinnerColumn, TextColumn from datetime import datetime -from diffbot import CrawlEventType, AuthError, ExtractionError, APIError +from diffbot import __version__, CrawlEventType, AuthError, ExtractionError, APIError from ._common import get_client @@ -28,6 +28,7 @@ def print_markdown(text): @click.group() +@click.version_option(__version__, "-V", "--version", prog_name="db") def main(): """ Diffbot 🤖 Structure the world's knowledge. From b61e5a951e590922dc216bed1fa6f0150b006418 Mon Sep 17 00:00:00 2001 From: Jerome Choo Date: Mon, 8 Jun 2026 13:27:34 -0500 Subject: [PATCH 2/5] Bump to 0.2.2 --- .github/workflows/release.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fde168d..48670ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,20 @@ jobs: 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: From a0366be8d0540360f8007f0533c0a8ea9de64ae0 Mon Sep 17 00:00:00 2001 From: Jerome Choo Date: Mon, 8 Jun 2026 13:29:33 -0500 Subject: [PATCH 3/5] Actually bump pyproject.toml to 0.2.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 16894ca..a76e230 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "diffbot-python" -version = "0.2.1" +version = "0.2.2" description = "Python client library for Diffbot APIs" readme = "README.md" requires-python = ">=3.10" From 6b79d946b82017cb4aeaace3b9a8953dc3905a84 Mon Sep 17 00:00:00 2001 From: Jerome Choo Date: Mon, 8 Jun 2026 15:04:38 -0500 Subject: [PATCH 4/5] Drop Intel macOS from release matrix; add concurrency + build timeout GitHub's Intel (macos-13) runners are being deprecated and their queue is unreliable, which blocked the v0.2.2 release. Ship macOS as Apple Silicon only; Linux keeps x86_64 + aarch64. Also add a concurrency group so re-pushing a tag cancels an in-flight run, and a 30m build timeout so a capacity-starved runner fails fast instead of hanging for 24h. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/release.yml | 15 ++++++++++++--- README.md | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48670ad..9739d3c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,10 +11,19 @@ on: 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: @@ -28,9 +37,9 @@ jobs: - runner: ubuntu-22.04-arm target_os: linux arch: aarch64 - - runner: macos-13 # Intel - target_os: darwin - arch: x86_64 + # 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 diff --git a/README.md b/README.md index 1854456..20463e7 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ This drops a `db` executable into `~/.local/bin` (ensure it is on your `PATH`). ### Standalone binary (no Python required) -Every release also ships a self-contained `db` binary for Linux and macOS (x86_64 and aarch64/arm64) — no Python install needed. The installer detects your platform, verifies the SHA256 checksum, and installs (or upgrades) `db` into `~/.local/bin`: +Every release also ships a self-contained `db` binary for Linux (x86_64 and aarch64) and macOS (Apple Silicon) — no Python install needed. The installer detects your platform, verifies the SHA256 checksum, and installs (or upgrades) `db` into `~/.local/bin`: ```bash curl -fsSL https://raw.githubusercontent.com/diffbot/diffbot-python/main/install.sh | sh From 7f313c5976c2cca89103e891838d60059625eb4d Mon Sep 17 00:00:00 2001 From: Jerome Choo Date: Mon, 8 Jun 2026 15:10:27 -0500 Subject: [PATCH 5/5] Fix release publish: pass --repo to gh (job has no checkout) The release job downloads artifacts but never checks out the repo, so gh could not infer the repository from a local git remote and failed with "fatal: not a git repository". Pass --repo "$GITHUB_REPOSITORY" on every gh release call. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9739d3c..9af7645 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,15 +105,19 @@ jobs: - 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}" - if gh release view "$tag" >/dev/null 2>&1; then + 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/* --clobber + gh release upload "$tag" dist/* --repo "$repo" --clobber else echo "Creating release $tag." gh release create "$tag" \ + --repo "$repo" \ --title "$tag" \ --generate-notes \ dist/*