STF-1287: Build and publish PIE precompiled binaries on tag - #2
Draft
claude[bot] wants to merge 8 commits into
Draft
STF-1287: Build and publish PIE precompiled binaries on tag#2claude[bot] wants to merge 8 commits into
claude[bot] wants to merge 8 commits into
Conversation
Once ext/libmaxminddb exists, a bare glob sweeps the whole vendored checkout -- its gitfile, CI configuration, test suite and nested test-data submodules -- into a published asset, so list the members explicitly and assert the layout.
The Linux and macOS lanes ran near-identical build, gate, verify and staging shell as per-OS heredocs. Sharing one copy of each cuts the workflow by a third and makes them runnable outside Actions.
Apache-2.0 4(d) requires a redistribution to carry the NOTICE content when the original work publishes one, and libmaxminddb does: a single NOTICE at its repository root, present at the pinned 1.13.3.
The one existing release is tagged v1.13.1 and titled 1.13.1, matching what dev-bin/release.sh passed. Asset filenames still use the tag verbatim, since PIE matches on Composer's v-prefixed pretty version.
Every binary lane builds from the submodule tree, so a libmaxminddb reshuffle -- headers moved into a subdirectory, a new source file added -- would produce a tarball that fails only for source-installing users while every gate stayed green. This builds what those users download, in a container where the extraction directory is the only thing mounted and no system libmaxminddb can stand in for a source the tarball failed to ship. publish waits for it so a tarball that cannot be built is not published. The tarball carries no libmaxminddb sources until ext/libmaxminddb is part of the submodule tree, so until then the build is skipped with a warning rather than failed.
The tarball is built from inside ext/, so it carried libmaxminddb's LICENSE and NOTICE but not the extension's own licence, which lives a level up at the repository root. Append it with tar's -C so it lands flat at the archive root next to config.m4.
The bundled sources measure a 2.38 floor when built on Ubuntu 24.04, which reads as a contradiction of the 2.36 gate. It is not: glibc 2.38 headers redirect strtol to __isoc23_strtol and PHP's -D_GNU_SOURCE turns that redirect on, so the number to keep is bookworm's, not the number a failing gate reports.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Greg Oschwald · Slack thread
Today
pie install maxmind-db/reader-extalways compiles from source, so a userneeds a toolchain and
libmaxminddb-devheaders. After this, PIE downloads aprebuilt
maxminddb.soon Linux (x86_64/arm64) and macOS (arm64) for PHP8.2–8.5, NTS and ZTS, plus Windows — and still falls back to building from
source everywhere else, unchanged.
The one line that enables it is
"download-url-method": ["pre-packaged-binary", "pre-packaged-source"]. PIE tries the methods in order, so an uncoveredplatform misses the binary and falls through to the tarball.
.github/workflows/release.ymlruns onv*tags (orworkflow_dispatchwith ataginput, to re-cut assets without re-tagging). It stages everything onto adraft release — assets cannot be added to a published, immutable release — and
only un-drafts at the end. Each Linux/macOS build is gated before it is staged:
no
libmaxminddbinNEEDED, no RUNPATH/RPATH, no undefinedMMDB_symbols, ameasured glibc floor within the documented maximum, and a load-and-query of a
real database in a clean container.
The source tarball PIE falls back to is built from an explicit member list: the
extension's own sources and
LICENSE, plus, onceext/libmaxminddbexists,only the libmaxminddb sources
config.m4compiles, the headers it puts on theinclude path, and that library's
LICENSEandNOTICE, which Apache-2.0 §4(d)requires a redistribution to carry. A bare glob would publish the vendored
checkout's gitfile, CI configuration, test suite and nested test-data
submodules — 101 entries where 16 are needed — and the verification step now
asserts that. The layout stays flat, with
config.m4at the archive root,because that is where PIE looks; the extension's
LICENSElives a level aboveext/, sotarappends it with-C ..and it lands flat there too.Every binary lane builds from the submodule tree, so a
source-buildjobbuilds the staged tarball itself: extracted into an empty directory, in a
container with only that directory mounted and no system libmaxminddb to stand
in for a source the tarball failed to ship, then loaded for a real lookup.
Without it, a libmaxminddb reshuffle would break source-installing users while
every other gate stayed green. It runs alongside the binary lanes, and
publishwaits for it.publishasserts the exact expected asset count and refuses to publish if itdoes not match. That assertion is deliberate: the Python repo once shipped a
release with an sdist and zero wheels because an artifact glob silently matched
nothing.
The build, gate, load-and-query and staging shell is shared by both lanes from
dev-bin/, rather than duplicated per-OS in the workflow, so there is one copyof each and it can be run outside Actions.
Linux binaries are built in digest-pinned
php:{version}-{cli,zts}-bookwormcontainers rather than on the runner, so the glibc floor (2.36) is a reviewable
choice that cannot drift with runner images. The 2.38 floor reported for the
bundled sources is what building outside those containers costs: glibc 2.38
headers redirect the
strtollibmaxminddb calls to__isoc23_strtol@GLIBC_2.38and PHP compiles with-D_GNU_SOURCE, whichenables the redirect. Inside bookworm that symbol does not exist, and the gate
fails closed if a build ever escapes. arm64 comes from a native
ubuntu-24.04-armrunner, not qemu. macOS has no container equivalent, so thefloor there is a pinned
MACOSX_DEPLOYMENT_TARGET, asserted againstLC_BUILD_VERSION.Dependencies, both in
maxmind/MaxMind-DB-Reader-php:--with-maxminddb-bundledconfigure flag(PR in flight, branch
stf-1287-…), which compiles libmaxminddb's vendoredsources into the extension so the
.soneeds only libc. Until the submodulepoints at a tag that has it, those jobs fail fast with an explicit message.
config.w32accept thelib-prefixedname that PHP's
libmaxminddb-1.5.0-vs16-*.zipactually ships. Windows keepsthe system-library path and does not use the bundled flag.
dev-bin/release.shthere must also stop building the tarball and creating therelease itself and just push the tag, or the two will collide. That is a
follow-up PR in the other repo, not this one.
This workflow is unexercised — it cannot run until a tag is pushed. The
cheapest way to smoke it before relying on it is a
workflow_dispatchrunagainst an existing tag. What was verified statically:
actionlintandzizmor(auditor persona) are both clean; every pinned action SHA was checked against
the tag named in its comment;
composer.jsonis valid JSON and thephp-extblock matches PIE's documented schema; all eight pinned image digests were
confirmed to exist as multi-arch indexes with amd64 and arm64; the tarball was
reproduced in both submodule states, before and after the bundled-sources PR,
with
LICENSEasserted at the archive root in each, and thesource-buildjobwas run against both — building and querying in the post-PR state, skipping
with a warning in today's, and failing as intended when a header is withheld
from the tarball; the glibc claim was measured rather than assumed, compiling
libmaxminddb with and without
-D_GNU_SOURCE(strtol@GLIBC_2.2.5versus__isoc23_strtol@GLIBC_2.38, the only symbol in the built object above 2.36)and against a bookworm glibc 2.36 sysroot, where the floor drops to 2.33; and
the asset-count assertion (including the zero-binaries case), the tag
sanitisation, the staging cross-checks and the lookup assertion were each
exercised locally, negative cases included.
Generated by Claude Code