Repository navigation
Support Python packages in the monorepo (DX-3182) - #75
Conversation
Add a uv workspace under python/* that releases through the existing Changesets flow. Each Python package carries a private package.json that holds its version. sync-version.mjs copies that version into pyproject.toml and _version.py, and publish-python.mjs uploads any version PyPI doesn't have yet. Scaffold upstash-hermes-memory at 0.0.0, which means unreleased. The CI and release workflow changes come in a separate commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTGdTkxgRFnNpNZow3diPo
|
The workflow part is in.
Both CI jobs are green. Before the first non-zero version, someone with PyPI access still needs to add a trusted publisher for |
There was a problem hiding this comment.
🟡 Changes recommended
Publication retries cannot recover unfinished tags or releases, and local Redis tests skip credentials stored in the documented .env file.
2 open issues
1 file not reviewed
pnpm-lock.yaml: Generated file
What changed in this PR
Adds Python package support alongside the existing pnpm monorepo, using Changesets as the single versioning system. The Hermes memory provider remains an unreleased scaffold.
Changes:
- Adds a uv workspace, Python lint/test commands, and CI integration.
- Extends version synchronization and adds trusted PyPI publishing with GitHub release creation.
- Introduces the Hermes package scaffold with version, wheel-content, and Redis smoke tests.
| File | Description |
|---|---|
uv.lock |
Locks Python workspace dependencies. |
scripts/sync-version.mjs |
Synchronizes Python version files from Changesets. |
scripts/publish-python.mjs |
Adds PyPI publishing, tags, and GitHub releases. |
README.md |
Lists the Python package and development commands. |
python/hermes-memory/tests/test_version.py |
Checks version and package-name consistency. |
python/hermes-memory/tests/test_redis.py |
Adds a Redis round-trip smoke test. |
python/hermes-memory/tests/test_package.py |
Checks public exports and wheel contents. |
python/hermes-memory/tests/conftest.py |
Provides a credential-gated Redis fixture. |
python/hermes-memory/src/upstash_hermes_memory/_version.py |
Stores the generated scaffold version. |
python/hermes-memory/src/upstash_hermes_memory/__init__.py |
Defines the scaffold’s public export. |
python/hermes-memory/README.md |
Documents the unreleased scaffold. |
python/hermes-memory/pyproject.toml |
Defines package metadata and build configuration. |
python/hermes-memory/package.json |
Carries the Changesets-managed version. |
pyproject.toml |
Configures the uv workspace, Ruff, and pytest. |
pnpm-workspace.yaml |
Includes Python version-carrier packages. |
pnpm-lock.yaml |
Registers the Python workspace member. |
package.json |
Adds Python commands and release integration. |
eslint.config.js |
Allows fetch in Node scripts. |
CLAUDE.md |
Documents Python development and release conventions. |
.gitignore |
Excludes Python environments and caches. |
.github/workflows/release.yml |
Installs uv and invokes Python publishing. |
.github/workflows/ci.yml |
Runs Python checks after Node checks. |
.changeset/config.json |
Enables private-package versioning without tagging. |
Review effort: Balanced
…tests (DX-3182) publish-python.mjs checked PyPI only, so a run that uploaded but failed to push the tag or create the GitHub release was skipped forever after. Upload, tag and release are now reconciled one by one; an existing tag is never moved. conftest.py loads the repo-root .env like the TS test-support does, without overriding variables already set (CI's secrets).
A version counts as published only when PyPI has both its wheel and its sdist. Otherwise the release is built and only the files PyPI does not list are uploaded, with --check-url as a second guard against a lagging JSON API. Before, a run that uploaded one file and then failed made every retry skip the upload.
|
Reviewed Validation performed:
I recommend checking those publisher cases into the repository as automated regression tests for A real PyPI upload and GitHub Actions OIDC/trusted-publishing authentication remain unverified. The PyPI trusted publisher for |


Resolves part of DX-3182.
This PR lets the monorepo hold Python packages that release through the existing Changesets flow, with no second versioning system. The first package is a scaffold for a Hermes Agent memory provider on Redis Search. It stays unreleased until the Python port lands.
How it works
pyproject.toml(memberspython/*) and a singleuv.lock.package.jsonthat only holds its version.python/*is added topnpm-workspace.yaml, sopnpm changesetlists the package andchangeset versionbumps it and writes its CHANGELOG. WithprivatePackages: { version: true, tag: false }, changesets never tags or publishes it.scripts/sync-version.mjsnow also finds everypython/*package and copies its version intopyproject.tomlandsrc/<module>/_version.py(theVERSIONconstant used for telemetry).--checkstill catches drift.ci:versionrunsuv lockafterwards, so the Version Packages PR carries a matching lockfile.scripts/publish-python.mjs(pnpm ci:publish:python) does for PyPI whatchangeset publishdoes for npm:uv buildanduv publishwith trusted publishing, then creates a<name>@<version>tag and a GitHub release.0.0.0means unreleased and is skipped, which is why this scaffold can merge without reaching PyPI.--dry-runprints the plan without changing anything.python/and registering a trusted publisher for it on PyPI. Nothing else changes.Also in this PR
python/hermes-memory:upstash-hermes-memoryat0.0.0, requires Python 3.11+, depends onupstash-redis>=1.8.0(which already supports Redis Search).py:lint(ruff) andpy:test(pytest);.gitignoreentries for the virtualenv and caches; afetchglobal forscripts/*.mjsin the eslint config.CLAUDE.mdand a README row.ci.ymlandrelease.yml(pushed from a local checkout, since the sandbox token could not edit workflow files): CI gets apython · lint · testjob (uv sync--locked, ruff, pytest) that runs after the Node job because both share the test Redis. The release job installs uv before changesets (ci:versionrunsuv lock) and runspnpm ci:publish:pythonin publish mode, using trusted publishing through the existingid-token: write.upstash-hermes-memory: the three version carriers agree, the built wheel holds the module with the right metadata, and a Redis round trip that skips without secrets.Not in this PR
upstash-hermes-memory, which must exist before its first non-zero version merges.Verified (in an Upstash Box)
pnpm lint,pnpm build,pnpm typecheckandnode scripts/sync-version.mjs --checkpass.uv sync --locked --all-packages,pnpm py:lintandpnpm py:testpass.upstash-hermes-memory: minorchangeset and ranpnpm ci:version. It bumpedpackage.json,pyproject.toml,_version.pyanduv.lockto 0.1.0 and wrote the CHANGELOG.publish-python.mjs --dry-runthen reportedwould build, publish and tag. I reverted all of it before committing.🤖 Generated with Claude Code
https://claude.ai/code/session_01JTGdTkxgRFnNpNZow3diPo