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
35 changes: 15 additions & 20 deletions .github/workflows/latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ name: latest
# uv.lock moves on a weekly dependabot pull request. A break upstream
# therefore surfaces a week or more later, inside a pull request that has
# to be reviewed anyway, and its cause is one entry in a diff of many.
# btclib_secp256k1 is the entry that matters most here, and it is not a
# release but a branch: pyproject.toml asks for that repository's main
# through a direct reference, so any commit there -- not only a release of
# it -- is a change in another repository that nothing in this one has to
# make for the pair to stop working. suite-bindings-latest below asks
# about that one entry alone, precisely, rather than folding it into
# suite-latest's broader upgrade -- see that job for why.
# btclib_secp256k1 is the entry that matters most here: a release of
# the bindings is a release in another repository, which nothing in this
# one has to change for the pair to stop working. suite-bindings-latest
# below asks about that one entry alone, precisely, rather than folding
# it into suite-latest's broader upgrade -- see that job for why.
#
# The obvious alternative, dropping --locked from test.yml and resolving
# afresh on every run, is what this workflow exists instead of: a pull
Expand Down Expand Up @@ -115,9 +113,8 @@ jobs:
enable-cache: true
python-version: ${{ matrix.python }}
# --upgrade re-resolves every entry, btclib_secp256k1 included:
# this is what carries the bindings to the tip of their main, and
# every other dependency to its newest release, which nothing else
# off a pull request does.
# this is what carries the bindings to their newest release, which
# nothing else off a pull request does.
# It prints one "Update <pkg> vA -> vB" line per package it moves,
# so the log names the suspects without a diff of uv.lock; a run
# that moved nothing says so, and its green means nothing new.
Expand All @@ -133,13 +130,13 @@ jobs:
run: uv run --locked --no-default-groups --group test pytest

# narrower than suite-latest above, and for a different reason: that
# job upgrades every dependency, so a red run there buries a commit
# job upgrades every dependency, so a red run there buries a release
# of btclib_secp256k1 behind a dozen other candidates. This
# upgrades only that one, so a red run here names it without a
# bisect -- worth a job of its own because it is a sibling project
# under the same org, written by the same people for this library,
# which makes "did my own other commit just break this one" a distinct
# and likely enough question to ask separately from "did some
# under the same org, released by the same maintainer, which makes
# "did my own other release just break this one" a distinct and
# likely enough question to ask separately from "did some
# third-party wheel". Moved here from published.yml (issue #397): it
# is a question about drift against a tree not yet published, which
# belongs before publication rather than after
Expand All @@ -164,14 +161,12 @@ jobs:
enable-cache: true
python-version: ${{ matrix.python }}
# upgrades one entry rather than every one of them, which is the
# whole difference from suite-latest above: it prints "Updated
# btclib_secp256k1 vA (sha) -> vB (sha)" when main moved and
# whole difference from suite-latest above: it prints "Update
# btclib_secp256k1 vA -> vB" when a release moved it and
# nothing when it did not, so the log names the suspect without a
# diff of uv.lock -- and with a direct reference the version can be
# the same on both sides, the commit in parentheses being what
# moved. Harmless on an ephemeral checkout; worth
# diff of uv.lock. Harmless on an ephemeral checkout; worth
# knowing before running the same command in a working tree
- name: Upgrade the bindings to the tip of their main
- name: Upgrade the bindings to their latest release
run: uv lock --upgrade-package btclib_secp256k1
- name: Run pytest
run: uv run --locked --no-default-groups --group test pytest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/published.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ name: published
#
# What this is not: a sentinel for whether the newest btclib_secp256k1
# breaks this tree. That question -- issue #397 -- belongs to latest.yml's
# test-bindings-latest job instead: it is about drift against a tree not
# suite-bindings-latest job instead: it is about drift against a tree not
# yet published, asked before publication rather than after, and asking
# it here would be asking it one step too late to matter to what gets
# published next
Expand Down
15 changes: 5 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -393,20 +393,15 @@ jobs:
# wheel and not to the btclib/ source tree.
# Unconstrained, where dist-py pins the runtime dependencies to
# uv.lock: no pull request waits on this job, so it can afford the
# question a required check cannot -- whether what the wheel's own
# requirements resolve to, unpinned, satisfies the wheel about to be
# published, which is what a user installing it resolves. A release
# stopping on that answer is the outcome wanted. Unpinned is the
# newest published release of each once pyproject.toml carries
# floors, and the tip of a branch while it carries a direct
# reference -- which no release may carry: PyPI refuses one, so an
# upload would fail in publish-pypi below, past everything this job
# guards. RELEASING.md's step 1 is where that is caught.
# question a required check cannot -- whether the newest published
# btclib_secp256k1 satisfies the wheel about to be published,
# which is what a user installing it resolves. A release stopping
# on that answer is the outcome wanted.
# The three assertions are dist-py's, and there for the same
# reasons: metadata can be missing (issue #150), `import btclib`
# runs only __init__.py and touches no binding, and a bindings
# release can install and still answer wrongly
- name: Smoke-test the wheel, with its dependencies unpinned
- name: Smoke-test the wheel, with its dependencies from PyPI
run: |
cd "$RUNNER_TEMP"
uv run --isolated --no-project --with "$GITHUB_WORKSPACE"/dist/*.whl \
Expand Down
54 changes: 20 additions & 34 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,14 @@ concurrency:

jobs:
# what the matrix buys: every (os, architecture, interpreter) triple
# builds btclib_secp256k1 from the commit uv.lock pins -- pyproject.toml
# asks for that repository's main through a direct reference -- so each
# cell compiles its own libsecp256k1 with the toolchain of its runner,
# and the pure Python code meets a distinct hashlib and OpenSSL. What
# the same triples used to buy was a different *published* wheel per
# cell; that question is published.yml's now, which installs btclib from
# the index and resolves the wheels a user gets, and it is the one this
# matrix stopped asking when the direct reference went in.
# selects a different published wheel of btclib_secp256k1, each with
# its own compiled libsecp256k1 inside, and the pure Python code meets a
# distinct hashlib and OpenSSL.
# windows-11-arm needs no special handling, and that is worth a word:
# which architecture the interpreter targets there is up to the runner
# image, native arm64 for the versions it caches and emulated x86-64
# for the ones uv has to download, and cffi builds for the interpreter
# rather than for the host, so either one links
# for the ones uv has to download, and the wheel is selected for the
# interpreter rather than for the host, so either one links
suite:
name: Run the suite on ${{ matrix.python }}, ${{ matrix.os }}
# a draft pull request is work in progress: every push to one spends
Expand Down Expand Up @@ -275,30 +270,23 @@ jobs:
# what none of the three above does: install the wheel, and install
# it alone, from an empty directory. The wheel is the only thing
# asked for, so what pulls btclib_secp256k1 in is the
# `Requires-Dist` the wheel itself carries -- which is the question,
# metadata being what the three checks above only read. That
# requirement is a direct reference while pyproject.toml says so, so
# what answers it is the bindings' repository rather than the index,
# and the bindings are built from source here; the metadata is doing
# the same work either way, and does it against the index again once
# a release replaces the reference with a floor.
# `Requires-Dist` the wheel itself carries, resolved from the index
# -- which is the question, metadata being what the three checks
# above only read.
# The lock arrives as constraints and not as requirements: a
# requirements file installs the bindings whether or not the wheel
# asks for them, so a wheel declaring nothing would pass. A
# constraint pins and requests no package, which leaves the wheel's
# metadata doing the work while pinning the outcome to what uv.lock
# resolved.
# Every package but the two direct references, which is what
# --no-emit-package is for here: the lock exports those as the
# commit it resolved, the wheel asks for `main`, and uv unifies a
# branch with a commit only while the branch is still at it --
# after which it refuses the two urls as conflicting and the job
# fails on nothing this repository did. Those branches are where
# this tree's own dependencies are developed, so they move on their
# own schedule, and a pin that has to be refreshed within the hour
# is not determinism. Unpinned, this step resolves what a user
# installing the wheel today resolves, which is the question it
# asks; the rest of the workflow runs --locked and is unaffected.
# constraint binds a version and requests no package, which leaves
# the wheel's metadata doing the work while pinning the outcome to
# what uv.lock resolved -- and pinning it is what keeps a required
# check deterministic, since with the floor alone uv takes the
# newest release and a btclib_secp256k1 published this morning
# would redden every open pull request, none of which caused it.
# Every package, then, including the two siblings: what made them
# an exception was the direct reference, the lock exporting the
# commit it resolved against a wheel asking for `main`, which uv
# refuses as conflicting once the branch has moved past it. A floor
# resolves to a release, and a release does not move.
# Whether the newest bindings still work is asked on a schedule by
# latest.yml, and again by the release workflow before it publishes.
# The assertions are three, because each catches what the others
Expand All @@ -310,11 +298,9 @@ jobs:
# binding, so the bindings could be missing entirely; and a
# signature is verified because an installable release of them with
# an incompatible API imports fine and answers wrongly
- name: Smoke-test the wheel, with the dependencies its metadata asks for
- name: Smoke-test the wheel, with its dependencies from PyPI
run: |
uv export --locked --no-dev --no-emit-project --no-hashes \
--no-emit-package btclib-secp256k1 \
--no-emit-package bitcoin-core-rpc \
-o "$RUNNER_TEMP"/constraints.txt
cd "$RUNNER_TEMP"
uv venv
Expand Down
59 changes: 13 additions & 46 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,35 +244,16 @@ documented at release-notes length in the first place, and are still in

### Packaging, linting and CI

- **`main` is green again, after two failures of one run that have
nothing to do with each other.** `tests/build_system_test.py` read
pyproject.toml with `tomllib`, which is standard library from 3.11 up
and the floor here is 3.10: the module failed to collect on the four
3.10 cells of the matrix and passed everywhere else, so the pull
request that added it (#754) merged with every other cell green. It
reads the `[build-system]` table by regex now, anchored on the header
and stopping at the next one, which is what `tests/copyright_test.py`
- **`tests/build_system_test.py` collects on 3.10** (#767), where it read
pyproject.toml with `tomllib` -- standard library from 3.11 up, and the
floor here is 3.10: the module failed to collect on the four 3.10 cells
of the matrix and passed everywhere else, so the pull request that
added it (#754) merged with every other cell green. It reads the
`[build-system]` table by regex now, anchored on the header and
stopping at the next one, which is what `tests/copyright_test.py`
already does with the same file and for the same reason; both tests
assert what they asserted before.

The `dist` job's smoke test is the other one, and what it failed on was
a design that could only hold for as long as nothing moved. It installs
the wheel with `uv.lock` exported as constraints, and the wheel asks
for `btclib_secp256k1` and `bitcoin-core-rpc` from their repositories'
`main` while the export pins the commit the lock resolved: uv unifies a
branch with a commit while the branch is still at it, and refuses the
two urls as conflicting once it is not. Both repositories had moved, so
the job failed here and in every open pull request, none of which
caused it -- and a lock refresh would only have bought the time until
the next push, which for `bitcoin-core-rpc` was fifteen minutes.
The export now passes `--no-emit-package` for those two, which is the
whole of the fix: everything else stays pinned to the lock, and the two
direct references resolve the way a user installing this wheel resolves
them. Nothing here is left unasked -- `uv run --locked` is what the
rest of the workflow runs, and whether the newest bindings still work
is `latest.yml`'s weekly question and the release workflow's before it
publishes.

- **The package-content policy is stated where an unpacked sdist carries
it** (#735, #734). `docs/source/package-content-policy.md` says what
may be in the wheel and the sdist, what may never be, and what has to
Expand Down Expand Up @@ -453,26 +434,6 @@ documented at release-notes length in the first place, and are still in
rather than by commit SHA, which every third-party action here is: it
is read from the same commit as the workflow calling it, so there is
no owner who could move it.
- **The bindings are required from their `main` rather than from a
release**, which is what `bitcoin-core-rpc` already was: a direct
reference to `btclib-org/btclib-secp256k1@main` in place of
`btclib_secp256k1>=0.8.0`, with `uv.lock` pinning the commit and moving
when that branch does. They exist for this library, so what a branch
here needs of them lands there first and then waits for a release of
another repository to be callable at all — `keys.PubkeyTweakChain`,
written for issue #685, is in `main` and in no release.

Three things it costs. Every environment builds libsecp256k1 from source
where a published wheel used to be resolved: cmake and cffi come from
the bindings' own `[build-system] requires`, a C toolchain is the
machine's, and a cold `uv pip install --no-cache` of the reference takes
about ten seconds on an arm64 laptop, the submodule fetch included. The
test matrix stops selecting a different published wheel per cell and
compiles one instead, so whether the published wheels install and answer
correctly is `published.yml`'s question and now only its. And PyPI
refuses a direct reference in metadata, so a release has to write a
floor back over each of the two — nothing before the upload catches it,
which RELEASING.md's step 1 now says.
- **Their repository is renamed with them**, to
`btclib-org/btclib-secp256k1`, so the urls naming it here move: the
issue templates, `README.md`, `SECURITY.md`'s advisory link, and the
Expand Down Expand Up @@ -526,6 +487,12 @@ documented at release-notes length in the first place, and are still in
both: they are btclib-org projects developed by the same people, which
is what a version ceiling substitutes for when it cannot be. Its `<0.8`
example moves to `<0.9`, the floor having passed it.
- **Both bounds move to the sibling releases of 13 August**,
`btclib_secp256k1>=0.8.0.1` and `bitcoin-core-rpc>=2026.8.13`, each
naming the release that carries what this tree calls of it:
`keys.PubkeyTweakChain` (issue #685) in the first, `assert_chain` and
the two magic lookups `network.py` stopped carrying in the second.
`uv.lock` follows both.
- **`--cov` is in pytest's addopts, so the ratchet is a local gate.** The
100% threshold was reached only by the `coverage` job, which means a
change met it after being pushed: the pull request that added
Expand Down
Loading
Loading