Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Release

# Publishing is a tag push:
#
# git tag v0.2.0 && git push origin v0.2.0
#
# No token anywhere. PyPI Trusted Publishing mints a short-lived credential from
# this workflow's OIDC identity, so there is no long-lived PYPI_API_KEY to leak,
# rotate, or paste into a shell. The previous scripts/release.sh read one out of
# ~/.pixeltable/config.toml.
#
# One-time setup on PyPI (Manage project -> Publishing -> Add a pending publisher):
# owner: pixeltable repository: pixelmemory
# workflow: release.yml environment: pypi

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
dry_run:
description: "Build and check only; do not publish"
type: boolean
default: true

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Version in pyproject.toml must match the tag
if: startsWith(github.ref, 'refs/tags/v')
run: |
tag="${GITHUB_REF_NAME#v}"
pyproject=$(grep -m1 '^version' pyproject.toml | cut -d'"' -f2)
echo "tag=$tag pyproject=$pyproject"
if [ "$tag" != "$pyproject" ]; then
echo "::error::Tag v$tag does not match pyproject.toml version $pyproject."
exit 1
fi

- run: uv build

- name: Check the artifacts
run: |
uv tool run twine check dist/*
ls -la dist/

# A release that cannot be installed is worse than no release. This is the
# same check that caught the 0.1.x breakage: install the actual artifact
# against the newest pixeltable our bound allows, then build an agent.
- name: Install the built wheel and smoke-test it
env:
PIXELTABLE_HOME: ${{ runner.temp }}/pxt-release
run: |
uv venv /tmp/verify --python 3.12
VIRTUAL_ENV=/tmp/verify uv pip install dist/*.whl
VIRTUAL_ENV=/tmp/verify uv pip list | grep -E "pixelmemory|pixeltable"
/tmp/verify/bin/python -c "
from pixelmemory import Memory
from pixelmemory.context import Text
m = Memory([Text(id='body', embed=False)], namespace='release_check')
assert m.table.columns() == ['body'], m.table.columns()
print('built wheel installs and builds a memory')
"

- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
if: startsWith(github.ref, 'refs/tags/v') && !inputs.dry_run
runs-on: ubuntu-latest
# The environment gives you a required-reviewer gate if you want one:
# Settings -> Environments -> pypi -> Required reviewers.
environment:
name: pypi
url: https://pypi.org/p/pixelmemory
permissions:
id-token: write # mints the OIDC token Trusted Publishing exchanges
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
45 changes: 45 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Releasing pixelmemory

```bash
# 1. bump the version
# pyproject.toml -> version = "X.Y.Z"
uv lock # keep uv.lock in step; CI runs `uv sync --locked` and will fail otherwise

# 2. land it on main through a PR, then
git tag vX.Y.Z && git push origin vX.Y.Z
```

The tag push runs `.github/workflows/release.yml`, which builds the sdist and
wheel, checks the tag matches `pyproject.toml`, installs the built wheel into a
clean environment and exercises it, then publishes to PyPI.

## There is no PyPI token

Publishing uses PyPI Trusted Publishing. PyPI mints a short-lived credential
from the workflow's OIDC identity, so there is no long-lived secret to leak,
rotate, or paste into a shell.

One-time setup, on PyPI under **Manage project -> Publishing -> Add a pending publisher**:

| field | value |
|---|---|
| owner | `pixeltable` |
| repository | `pixelmemory` |
| workflow | `release.yml` |
| environment | `pypi` |

To require a human approval before each publish, add a required reviewer to the
`pypi` environment under **Settings -> Environments**.

## Dry run

Run the workflow manually from the Actions tab with `dry_run` left checked. It
builds and verifies the artifact without publishing anything.

## Why the release script is gone

`scripts/release.sh` read a long-lived `PYPI_API_KEY` out of
`~/.pixeltable/config.toml`, required a git remote named `home` that a normal
clone does not have, and called `poetry build` after the project had moved to
uv. It also published without ever installing what it built, which is how
0.1.x shipped a package that could not import against a current Pixeltable.
64 changes: 0 additions & 64 deletions scripts/release.sh

This file was deleted.

Loading