Skip to content

fix(deploy): upgrade every keel distribution, and verify all of them - #243

Merged
eaitbrahim merged 1 commit into
mainfrom
fix/deploy-upgrades-siblings
Aug 11, 2026
Merged

fix(deploy): upgrade every keel distribution, and verify all of them#243
eaitbrahim merged 1 commit into
mainfrom
fix/deploy-upgrades-siblings

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

Found while deploying v0.6.0. The ~/keel venv, immediately before the install:

keel-broker-api      0.5.5
keel-broker-coinbase 0.5.5
keel-broker-fake     0.5.5
keel-core            0.5.5
keel-trader          0.5.7      <- only this had been upgraded, across TWO releases

The documented step installs keel_trader only. Its siblings are required with no version, so
the keel-core 0.5.5 already on disk satisfied keel-core and was never touched. The engine ran
new code against old libraries for two releases.

The verification step could not see it. The README calls keel --version "the check that
matters"; it reports the keel-trader distribution's version and nothing else, so it printed
0.6.0 while keel-core sat at 0.5.5. A check blind to the failure mode is worse than none,
because it is trusted.

Reproduced end to end from the real wheels (0.5.5 set built from 2a58ffb with the versions
rewritten, 0.6.0 set built from 2a58ffb unchanged), running exactly what the README said:

$ uv pip install --python .venv --find-links Release Release/keel_trader-0.6.0-py3-none-any.whl
 - keel-trader==0.5.5
 + keel-trader==0.6.0
$ uv pip list --python .venv | grep keel
keel-broker-api      0.5.5
keel-broker-coinbase 0.5.5
keel-broker-fake     0.5.5
keel-core            0.5.5
keel-trader          0.6.0
$ .venv/bin/keel --version
keel 0.6.0+bbbbbbbbbbbb [release]

1. The install command

V=0.6.0
gh release download "v$V" --repo CodeGateSoftware/keel --pattern '*.whl' --dir Release/
uv pip install --python .venv --find-links Release \
  Release/keel_core-$V-py3-none-any.whl \
  Release/keel_broker_api-$V-py3-none-any.whl \
  Release/keel_broker_coinbase-$V-py3-none-any.whl \
  Release/keel_trader-$V-py3-none-any.whl
.venv/bin/keel versions
.venv/bin/keel status

A wheel path is a direct requirement — that exact file is installed regardless of what is
already there — so naming all four is what actually moves them. --find-links Release stays, now
resolving the pinned siblings locally rather than from PyPI where they do not exist. Installing
by path rather than by bare name is unchanged and still explained: keel on PyPI is an
unrelated project.

Deliberately not Release/*.whl, the obvious route. uv build --all-packages builds every
workspace member and gh release create dist/* publishes all of them, so the glob installs two
distributions production must not have. Measured:

$ uv pip install --python .venv --find-links Release Release/*.whl
keel-broker-fake       0.6.0
keel-broker-robinhood  0.6.0
pynacl                 1.6.2
cffi                   2.1.1

The four named wheels are keel-trader's whole dependency closure — nothing more, nothing less.

2. Pins — decided yes, with wheel-metadata evidence

Every intra-workspace dependency is now == the workspace version, in the root and in all four
package pyproject.tomls.

[tool.uv.sources] workspace = true governs resolution during development and says nothing about
published metadata, so I built and read the wheel rather than assuming. Before:

$ unzip -p keel_trader-0.6.0-py3-none-any.whl '*/METADATA' | grep Requires-Dist
Requires-Dist: click>=8.4.2
Requires-Dist: keel-core
Requires-Dist: keel-broker-api
Requires-Dist: keel-broker-coinbase

After:

Requires-Dist: click>=8.4.2
Requires-Dist: keel-core==0.6.0
Requires-Dist: keel-broker-api==0.6.0
Requires-Dist: keel-broker-coinbase==0.6.0

The constraint lands, and the pin alone fixes the bug for anyone who ignores the README — the
old command against the new metadata, from the same 0.5.5 starting state:

$ uv pip install --python .venv --find-links Release Release/keel_trader-0.6.0-py3-none-any.whl
 - keel-broker-api==0.5.5      + keel-broker-api==0.6.0
 - keel-broker-coinbase==0.5.5 + keel-broker-coinbase==0.6.0
 - keel-core==0.5.5            + keel-core==0.6.0
 - keel-trader==0.5.5          + keel-trader==0.6.0

Why pin, given the README fix already works. The README binds only the person reading it. The
pin binds pip. These are not third-party dependencies with independent release cycles — they are
cut from this repo, at one version, in one uv build --all-packages, so == states a fact rather
than guessing at compatibility, and there is no version skew it would wrongly forbid. The cost is
that a bump must move the pins; the bump commit already edits version in all five files, so this
is incremental work on a commit that exists rather than a new obligation, and
tests/test_packaging.py fails the build if a pin is left behind — the failure mode of a forgotten
pin is otherwise silent (the wheel still builds and still installs, it just stops forcing the
upgrade). >= was rejected: it forbids an older sibling but not a mixed install, and mixed is
the thing that happened. uv.lock is unchanged — workspace sources already resolved to the members.

3. A verification step that can fail

New keel versions, registered in keel/cli.py. It prints the same build-identity line, then
every keel-* distribution in the running interpreter's environment, and exits non-zero when they
disagree. No config, no database, no network, so a non-zero exit is unambiguous.

Healthy — the deployment after the new install command:

$ .venv/bin/keel versions
keel 0.6.0+deb8fa7e978d [release]

keel-broker-api       0.6.0
keel-broker-coinbase  0.6.0
keel-core             0.6.0
keel-trader           0.6.0

ok: 4 keel distributions, all at 0.6.0.
exit=0

Mismatched — same venv, keel-core forced back to 0.5.5 with --no-deps (the pin has to be
bypassed on purpose now):

$ .venv/bin/keel versions
keel 0.6.0+deb8fa7e978d [release]

keel-broker-api       0.6.0
keel-broker-coinbase  0.6.0
keel-core             0.5.5
keel-trader           0.6.0

error: PARTIAL INSTALL: 4 keel distributions at 2 different versions (0.5.5, 0.6.0).
`keel --version` reports keel-trader's version alone and cannot see this. Reinstall
every wheel by path (README, 'Deploying a new version').
exit=1

keel --version on that same venv now says so too, on stderr, leaving its stdout string exactly
what it was (the release workflow greps it):

keel 0.6.0+deb8fa7e978d [release]
warning: PARTIAL INSTALL -- this line reports the keel-trader distribution only, and the
other keel distributions do not agree with it. Run `keel versions`.

It ships in the wheel, not in scripts/. scripts/ is operator tooling and is not packaged;
a deployment is a .venv beside a Release/ directory with no checkout of this repo, so a script
there could not be run without first fetching it. The check has to travel inside the artifact it
is checking. The rules live in keel.version.InstallReport as a pure value, tested without
installing anything; the command is rendering and an exit code. The release workflow now runs
keel versions against the wheels it just built, so a release cannot publish a set that disagrees
with itself.

keel-broker-fake

It should not be there and the check now says so: a release build with keel-broker-fake
installed exits 1 with uv pip uninstall --python .venv keel-broker-fake. A checkout does not
fail — that is exactly where the fake belongs, and a check that cried wolf on every developer's
machine would be ignored by the time it mattered. tests/test_packaging.py also asserts nothing
in the workspace can pull it in as a runtime dependency. The dev group is untouched.

It is inert today (nothing calls load_broker()), but it registers a fake venue under
keel.brokers, and "inert" is a property of this release rather than of the package. The README
and the RELEASING asset table now say not to install it, which is also the reason the install
command names wheels instead of globbing.

Gates

$ uv run ruff check keel tests packages scripts
All checks passed!

$ uv run pytest -q
2696 passed, 1 skipped in 30.51s
SKIPPED [1] packages/keel-broker-api/keel_broker_api/conformance/suite.py:254

$ uv run mypy
Success: no issues found in 224 source files

Baseline on origin/main, re-measured in this worktree, was 2669 passed / 1 skipped; +27 tests,
same single expected skip.

No version bump here — that stays a separate reviewed change. docs/experiments/ untouched.

The documented upgrade installed `keel_trader` alone. Its workspace siblings were
required without a version, so an already-installed older `keel-core` satisfied
`keel-core` and was never touched: `~/keel` ran `keel-trader 0.5.7` against
`keel-core 0.5.5` across two releases. `keel --version` reported the new number
throughout, because it reports that one distribution and nothing else -- a
verification step structurally blind to the failure it is trusted to rule out.

Three changes, each of which fixes it alone:

- README: name all four production wheels by path. A wheel path is a direct
  requirement, installed whatever is already there. Not `Release/*.whl`: the
  release ships every workspace wheel, including the dev-only fake venue and
  Robinhood's Ed25519 stack.
- Pins: every intra-workspace dependency is `==` the workspace version, verified
  to land in the built wheel's `Requires-Dist`. `tests/test_packaging.py` fails
  the build if a bump leaves one behind.
- `keel versions`: prints the build identity plus every keel distribution in the
  venv and exits non-zero when they disagree, or when `keel-broker-fake` is
  present in a release build. `--version` now warns and points at it.
@eaitbrahim eaitbrahim added fix Bug fix (groups under Fixes) docs Documentation (Docs, CI & tooling) labels Aug 11, 2026
@eaitbrahim
eaitbrahim merged commit 6e43c30 into main Aug 11, 2026
1 check passed
@eaitbrahim
eaitbrahim deleted the fix/deploy-upgrades-siblings branch August 11, 2026 19:53
@eaitbrahim eaitbrahim mentioned this pull request Aug 11, 2026
eaitbrahim added a commit that referenced this pull request Aug 11, 2026
Patch bump: packaging and documentation only, no behaviour change.

0.6.0's deploy exposed that the documented install upgraded `keel_trader` alone
and silently left `keel-core` / `keel-broker-*` behind -- the real deployment was
found running keel-trader 0.5.7 against keel-core 0.5.5, across two releases,
because the dependencies were unpinned and `keel --version` reports only the
keel-trader distribution and so could not see it.

#243 pinned every intra-workspace dependency to the exact version, added
`keel versions` (which exits 1 on a partial install), and corrected the README.
This is the first release whose wheels CARRY those pins, so it is also the first
one whose install is self-correcting: `Requires-Dist: keel-core==0.6.1` forces
the sibling upgrade regardless of how the wheel is installed.

`tests/test_packaging.py`, added by the same PR, is what kept this bump honest --
bumping the root version alone failed it twice before the pins were updated.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation (Docs, CI & tooling) fix Bug fix (groups under Fixes)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant