diff --git a/.gitignore b/.gitignore index 210d49a..3a1c54f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ dist/ secrets/ *.pem *.pem.gpg +.env +.env.* # Python __pycache__/ diff --git a/justfile b/justfile index df56b19..764c515 100644 --- a/justfile +++ b/justfile @@ -4,6 +4,11 @@ # Default browser profile for `install` / quick checks browser := "brave" +# Where `publish` builds, and where it looks up the PyPI token +dist_dir := "/tmp/dist-browser-cli" +pypi_token_entry := "api/packages/pypi" +pypi_check_url := "https://pypi.org/simple/" + # Show available recipes default: @just --list @@ -74,17 +79,27 @@ version-check: ext=$(grep -m1 '"version"' extension/manifest.json | cut -d'"' -f4); \ if [ "$py" = "$ext" ]; then echo "ok: $py"; else echo "MISMATCH pyproject=$py manifest=$ext"; exit 1; fi -# Build into /tmp/dist-browser-cli and publish using credentials from .env -publish: +# Build and upload to PyPI. Token: UV_PUBLISH_TOKEN, else .env, else password-store. +publish: version-check #!/usr/bin/env bash set -euo pipefail - set -a - [ ! -f .env ] || source .env - set +a - rm -rf /tmp/dist-browser-cli - mkdir -p /tmp/dist-browser-cli - uv build --out-dir /tmp/dist-browser-cli - uv publish /tmp/dist-browser-cli/* + source scripts/pypi_token.sh + load_pypi_token "{{pypi_token_entry}}" || exit 1 + rm -rf "{{dist_dir}}" + mkdir -p "{{dist_dir}}" + uv build --out-dir "{{dist_dir}}" + uv publish --check-url "{{pypi_check_url}}" "{{dist_dir}}"/* + +# Same build and token path as `publish`, but uploads nothing +publish-dry: version-check + #!/usr/bin/env bash + set -euo pipefail + source scripts/pypi_token.sh + load_pypi_token "{{pypi_token_entry}}" || exit 1 + rm -rf "{{dist_dir}}" + mkdir -p "{{dist_dir}}" + uv build --out-dir "{{dist_dir}}" + uv publish --dry-run --check-url "{{pypi_check_url}}" "{{dist_dir}}"/* # ── Demos ────────────────────────────────────────────────────────────── diff --git a/scripts/pypi_token.sh b/scripts/pypi_token.sh new file mode 100644 index 0000000..b3b9bb9 --- /dev/null +++ b/scripts/pypi_token.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Resolve the PyPI token for `uv publish` without ever printing it. +# +# Order: an already exported UV_PUBLISH_TOKEN, then .env in the repo root, then +# the password-store entry passed as $1. The repo intentionally ships no .env, +# so the password-store path is the normal one on a dev machine. + +load_pypi_token() { + local entry="${1:-}" + + if [ -z "${UV_PUBLISH_TOKEN:-}" ] && [ -f .env ]; then + set -a + # shellcheck disable=SC1091 + source .env + set +a + fi + + if [ -z "${UV_PUBLISH_TOKEN:-}" ] && [ -n "$entry" ] && command -v pass >/dev/null 2>&1; then + UV_PUBLISH_TOKEN="$(pass "$entry" 2>/dev/null | head -1 || true)" + fi + + if [ -z "${UV_PUBLISH_TOKEN:-}" ]; then + echo "error: no PyPI token found." >&2 + echo " export UV_PUBLISH_TOKEN=..., put it in .env, or store it in: pass ${entry:-}" >&2 + return 1 + fi + + export UV_PUBLISH_TOKEN + echo "Using PyPI token (${#UV_PUBLISH_TOKEN} chars) for upload." +}