The full release flow is automated through GitHub Actions + PyPI Trusted Publishing — no API token ever has to live on disk. This page documents every step so the very first release and subsequent ones look the same.
- Create the PyPI project. If the name is still available, head to
https://pypi.org/manage/projects/ and create
loop-memory. - Register the GitHub Actions Trusted Publisher for the project.
On PyPI:
loop-memory→ Publishing → Add a new pending publisher:- Owner:
smartfind - Repository:
loop-memory - Workflow filename:
publish.yml - Environment name:
pypi
- Owner:
- Add a GitHub environment named
pypiin repository settings. No protection rules are required, but the name must match so the workflow can scope its token correctly.
After this is done once, every subsequent release needs no PyPI credentials.
- Bump
versioninpyproject.toml. - Move the
[Unreleased]block inCHANGELOG.mdinto a dated version section (## [0.4.0] - YYYY-MM-DD). - Land the change on
mainvia PR and wait fortests.yml+ the secret scanner to turn green. - Tag the merge commit:
git switch main git pull --ff-only git tag -s v0.4.0 -m "loop-memory 0.4.0" git push origin v0.4.0 - The
publish.ymlworkflow will:- build sdist + wheel,
- run
twine check, - publish to PyPI via OIDC,
- draft a GitHub release with auto-generated notes.
The first publish from a new GitHub environment needs a manual Approve click in the GitHub Actions UI; subsequent publishes from the same environment are automatic.
scripts/release.sh --tag v0.4.0 will:
- Verify the working tree is clean and on
main. - Confirm
pyproject.tomlversion matches the tag. - Run
ruff check .andpytest -q. - Build sdist + wheel into
dist/. - Run
twine check(must reportPASSEDfor every artefact). - Refuse to upload — that's what GitHub Actions is for.
To actually upload locally (only needed for the very first release, before the Trusted Publisher is registered), set:
export TWINE_USERNAME=__token__
export TWINE_PASSWORD=pypi-<your token>
./scripts/release.sh --uploadAfter the first successful upload the Trusted Publisher takes over and local uploads can be disabled again.
# Replace 0.4.4 with the version you intend to verify.
TAG=0.4.4
pip index versions loop-memory # should include $TAG
python -m pip install --upgrade "loop-memory==$TAG"
python -c "import loop_memory; print(loop_memory.__version__)" # -> $TAG
# Sanity: every CLI subcommand exits 0 after a clean install.
for cmd in "loop-memory --help" \
"loop-memory rules --help" \
"loop-memory recall --help" \
"loop-memory cognitive-sleep --help"; do
bash -c "$cmd" >/dev/null || { echo "FAIL: $cmd"; exit 1; }
doneThe smoke script in scripts/release.sh --tag v$TAG automates the
four bullets above (plus ruff check . and a full pytest -q) and
prints a final 0.4.x smoke OK line — the same one CI runs in
.github/workflows/tests.yml. To discover what is currently live on PyPI run:
pip index versions loop-memoryAnything in pyproject.toml that hasn't been tagged + pushed via
step 4 above stays on the next release. (Local code may already be
ahead — see git log --oneline origin/main.)