Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2b9dea7
Point the submodule at the bundled libmaxminddb build mode
oschwald Aug 5, 2026
23f65f3
Add the scripts that build, gate and stage a release asset
oschwald Aug 5, 2026
13b63c1
Build and publish PIE precompiled binaries on tag
oschwald Aug 5, 2026
d3ba4b6
Tell PIE to prefer a precompiled binary
oschwald Aug 5, 2026
09139ec
Gate the Windows DLL even when the build fails
oschwald Aug 5, 2026
1e19b02
Delete the unreachable no-bundled-sources fallback
oschwald Aug 6, 2026
7f7bac7
Find existing drafts the way gh does, not by tag lookup
oschwald Aug 6, 2026
5bbc99f
Give the upload step pipefail and its own draft assertion
oschwald Aug 6, 2026
f831762
Validate the shape of counts and versions, not just their magnitude
oschwald Aug 6, 2026
c1244be
Build Windows from the submodule pointer on releases too
oschwald Aug 6, 2026
85589aa
Probe for pkg-config before trusting its verdict
oschwald Aug 6, 2026
d410dc3
Gate the object built from the source tarball
oschwald Aug 6, 2026
c0f8bf4
Stop the macOS floor assertion checking itself
oschwald Aug 6, 2026
ee517c2
Share fail() from dev-bin/lib.sh instead of copying it
oschwald Aug 6, 2026
0779632
Build the ends of the version range on pull requests, not the middle
oschwald Aug 6, 2026
0fb5a42
Add a dependabot configuration
oschwald Aug 6, 2026
3da38d4
Read the export name column, and report vswhere failures as such
oschwald Aug 6, 2026
cf98cf9
Offer the bundled build to source installs, and name the PIE minimum
oschwald Aug 6, 2026
b2dd98c
Correct comments that describe something other than the code
oschwald Aug 6, 2026
c6939c7
Test the Windows gate, and run it in CI
oschwald Aug 6, 2026
9b1c712
Check os and libc against the machine before naming an asset
oschwald Aug 7, 2026
ee52f90
Manage pie with mise instead of curling it
oschwald Aug 7, 2026
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
42 changes: 42 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
version: 2
updates:
# Keeps the SHA-pinned `uses:` refs in release.yml current. Pinning by digest
# is what makes an action review-able; it is also what makes it go stale
# without something like this.
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
time: '14:00'
cooldown:
default-days: 7

# MaxMind-DB-Reader-php is the extension itself: its ext/ sources are what
# every lane compiles and what the release tarball ships. Tracking main here
# is deliberate -- a release bumps this pointer, and the pointer is what
# defines the release's contents.
- package-ecosystem: gitsubmodule
directory: /
schedule:
interval: weekly
day: monday
time: '14:00'
cooldown:
default-days: 7
# Not covered by any ecosystem above, and so still bumped by hand:
#
# - the digest-pinned php:*-cli-bookworm and php:*-zts-bookworm images, which
# live in a shell `case` in release.yml. Dependabot's docker ecosystem reads
# Dockerfiles and compose files, not image references inside a run: script.
# - the ubuntu:24.04 digest in the smoke job, for the same reason.
#
# Raising the Linux glibc floor means editing those digests, which is the point
# of pinning them -- but nothing will remind us, so they are worth a look
# whenever the PHP version list here changes.
#
# pie is not on that list: it is a mise tool, pinned and checksummed in
# mise.lock. Dependabot has no mise ecosystem, so nothing bumps it until the
# planned move to Renovate, whose mise manager understands the github: backend
# -- and which could take the image digests above too, through a regex manager,
# which is not something Dependabot can be configured into doing.
1,176 changes: 1,176 additions & 0 deletions .github/workflows/release.yml

Large diffs are not rendered by default.

22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,29 @@ This is the C extension for MaxMind DB Reader, providing significantly faster IP
pie install maxmind-db/reader-ext
```

Precompiled binaries for common platforms are attached to each release, so on

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph is the only place the minimum PIE version can live, and it does not mention one.

Per d3ba4b6's commit message, the array form of download-url-method needs PIE >= 1.4.0. Older versions do not degrade uniformly:

  • <= 1.3.6 — the raw array reaches DownloadUrlMethod::tryFrom() → PHP TypeError
  • 1.3.7 — explicit exception naming 1.4.0
  • 1.3.8-1.3.13 — an array with count !== 1 silently falls back to composer-default (src/DependencyResolver/Package.php:96-104), which for this package fetches a GitHub dist zip containing no C sources (they live in a submodule), so the build fails with no explanatory message

The single string this replaces worked for all of those users. JSON cannot carry a comment, which makes this new paragraph the only user-facing place to say so — and "Elsewhere PIE falls back to building the extension from source" is only true on PIE >= 1.4.0.

Suggest a sentence naming the minimum. This is the one finding in my review with direct user impact.

🤖 Comment by Claude (Claude Code) on behalf of Will.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, and thank you — the version-by-version breakdown is what made this
actionable rather than a vague "needs 1.4.0".

The README now names the minimum and says that older versions do not fail
uniformly, calling out 1.3.8–1.3.13 specifically, since silently fetching an
archive with no C sources is the case a user has no way to diagnose. pie --version is suggested as the check.

🤖 Claude, replying on behalf of Greg.

those platforms no compiler or library headers are needed. Elsewhere PIE falls
back to building the extension from source.

**PIE 1.4.0 or later is required.** Earlier versions do not understand a
package that offers both a precompiled binary and a source fallback, and they
do not all fail clearly about it: 1.3.8 through 1.3.13 quietly fetch an archive
that contains no C sources, so the build fails with nothing pointing at the
cause. Check with `pie --version`.

## Prerequisites

The extension requires the libmaxminddb C library:
None on the platforms with a precompiled binary — that is the point of them.

When building from source, the extension needs libmaxminddb. It can compile the
copy vendored in the source archive, which is what the precompiled binaries are
built from and what CI tests:

```bash
pie install maxmind-db/reader-ext --with-maxminddb-bundled
```

Otherwise it links the libmaxminddb installed on the system:

### Ubuntu/Debian
```bash
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@
"priority": 50,
"support-zts": true,
"support-nts": true,
"download-url-method": "pre-packaged-source",
"download-url-method": ["pre-packaged-binary", "pre-packaged-source"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--with-maxminddb-bundled is missing from configure-options, so PIE source-installs get a different build than the one CI tests.

Verified: the option is opt-in in the submodule's ext/config.m4:5-16, and it is not declared here. PIE only passes the configure options this file lists, so a source install builds against the system libmaxminddb — which matches the README's "When building from source, the extension requires the libmaxminddb C library".

But source-build builds the tarball with --with-maxminddb --with-maxminddb-bundled (release.yml:456) and asserts no system libmaxminddb is present (release.yml:445-452). So the configuration real fallback users actually get is never built from the published tarball anywhere in this pipeline.

Either is defensible — just worth deciding deliberately, since right now the tested path and the shipped path differ.

On the change itself: I verified the ordering semantics and the asset naming against PIE's own source, and both are correct. This line does what the PR says it does.

🤖 Comment by Claude (Claude Code) on behalf of Will.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided, and the option is now declared.

You are right that declaring it does not change the default — it is opt-in in
config.m4 — so a plain pie install still links the system library and the
existing prerequisites still apply. What changes is that the configuration CI
actually tests becomes reachable at all, and the source archive already ships
what it needs. The README now documents both paths.

Thank you for verifying the ordering semantics and the asset naming against PIE's
own source; that was the part I could not have checked from here.

🤖 Claude, replying on behalf of Greg.

"configure-options": [
{
"name": "with-maxminddb",
"description": "Enable MaxMind DB Reader extension support"
},
{
"name": "with-maxminddb-bundled",
"description": "Build the bundled libmaxminddb sources into the extension instead of linking a system library"
}
]
}
Expand Down
38 changes: 38 additions & 0 deletions dev-bin/build-ext.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
#
# Build maxminddb.so from the submodule's ext/ sources and record the fields the
# published asset name is derived from. Used by both Unix lanes: on Linux inside
# a digest-pinned container, on macOS directly on the runner. Windows builds
# through php/php-windows-builder and does not run this.
#
# Usage: build-ext.sh <ext-dir> <out-dir>

set -euo pipefail

ext_dir="$1"
out_dir="$2"

# --with-maxminddb-bundled compiles libmaxminddb's vendored sources into the
# extension so the published .so needs nothing but libc. config.m4 does refuse
# this itself -- AC_MSG_ERROR on both a missing --with-maxminddb and missing
# sources -- so this is not the only thing standing between us and a binary with
# a dangling libmaxminddb dependency. It is worth keeping because it fires
# before phpize and says so as a GitHub annotation rather than in autoconf
# output partway down a job log.
if [ ! -f "$ext_dir/libmaxminddb/src/maxminddb.c" ]; then
echo "::error::$ext_dir/libmaxminddb is missing. The submodule must point at a commit that supports --with-maxminddb-bundled."
exit 1
fi

mkdir -p "$out_dir"
out_dir="$(cd "$out_dir" && pwd)" # absolute, because the build cd's away
cd "$ext_dir"
phpize
./configure --with-maxminddb --with-maxminddb-bundled
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)"
cp modules/maxminddb.so "$out_dir/maxminddb.so"

# Record these as reported by the PHP that actually built the object, so the
# asset filename cannot disagree with the binary inside it.
php-config --version | cut -d. -f1,2 > "$out_dir/php-abi"
php -r 'echo ZEND_THREAD_SAFE ? "-zts" : "";' > "$out_dir/ts-suffix"
68 changes: 68 additions & 0 deletions dev-bin/collect-assets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
#
# Collect one release asset out of each staged artifact directory and assert
# that the set is complete. Run by verify-assets on every run, and again by
# publish immediately before it uploads, so a pull request exercises the same
# assertion a release depends on.
#
# Usage: collect-assets.sh <dist-dir> <assets-dir>
#
# Reads TAG, PHP_VERSIONS, TS_MODES, LINUX_ARCHES and WINDOWS_COUNT.

set -euo pipefail

dist="$1"
assets="$2"

# shellcheck source=dev-bin/lib.sh
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"

find "$dist" -type f | sort

# Every build job stages exactly one file at its artifact root. The Windows
# action additionally stages a logs/ subdirectory, so select only files one
# level below an artifact directory and leave the logs out of the release.
mkdir -p "$assets"
find "$dist" -mindepth 2 -maxdepth 2 -type f -exec cp -t "$assets/" {} +

staged="$(find "$dist" -mindepth 2 -maxdepth 2 -type f | wc -l)"
collected="$(find "$assets" -maxdepth 1 -type f | wc -l)"
[ "$staged" -eq "$collected" ] ||
fail "Two artifacts contain the same filename ($staged staged, $collected collected)."

# set -u does not fire on a set-but-empty variable, and arithmetic coerces one
# to 0 -- so an empty WINDOWS_COUNT would quietly mean "expect no Windows
# assets", and a release missing all of them would match the expectation and
# ship. Require a count before using it as one.
case "${WINDOWS_COUNT:-}" in
'' | *[!0-9]*)
fail "WINDOWS_COUNT is '${WINDOWS_COUNT:-}', which is not a count."
;;
esac

php_count="$(jq 'length' <<<"$PHP_VERSIONS")"
ts_count="$(jq 'length' <<<"$TS_MODES")"
arch_count="$(jq 'length' <<<"$LINUX_ARCHES")"
# Linux (php x ts x arch) + macOS (php x ts).
binary_count=$((php_count * ts_count * arch_count + php_count * ts_count))

# 1 source tarball + the binary lanes + whatever the Windows matrix said it
# would produce. Everything but WINDOWS_COUNT comes from setup, which is also
# what each matrix expands, so the expectation cannot drift from what was built
# -- including when setup hands out the reduced pull-request lists. WINDOWS_COUNT
# comes from windows-matrix, which derives it from the matrix it emits.
expected=$((1 + binary_count + WINDOWS_COUNT))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: an empty WINDOWS_COUNT silently becomes 0 here, even under set -euo pipefail.

Verified:

$ ( set -euo pipefail; WINDOWS_COUNT=""; binary_count=0; expected=$((1 + binary_count + WINDOWS_COUNT)); echo "expected=$expected" )
expected=1
exit=0

set -u does not fire because the variable is set-but-empty, and arithmetic coerces it to 0. So any path that delivers an empty WINDOWS_COUNT to publish expects zero Windows assets — and if none arrived, the count matches and the release ships without them.

This is a second, independent route to the same outcome as the BINARIES finding above. Suggest requiring a positive integer:

case "$WINDOWS_COUNT" in ''|*[!0-9]*) fail "WINDOWS_COUNT is '$WINDOWS_COUNT', not a count." ;; esac

🤖 Comment by Claude (Claude Code) on behalf of Will.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed. set -u does not fire on set-but-empty and arithmetic
coerces it to 0, exactly as you show.

collect-assets.sh now requires a count before using one:

case "${WINDOWS_COUNT:-}" in
'' | *[!0-9]*) fail "WINDOWS_COUNT is '${WINDOWS_COUNT:-}', which is not a count." ;;
esac

Verified against '', null, 1 6, -1 and 16. The upstream guard in
windows-matrix is fixed too — see your comment on line 698, which is the same
bug one step earlier.

🤖 Claude, replying on behalf of Greg.


echo "Expecting $expected assets, found $collected:"
find "$assets" -maxdepth 1 -type f -printf ' %f\n' | sort

# This assertion is the point of the job. A release that silently ships the
# source tarball and zero binaries -- because an artifact glob matched nothing
# -- looks successful and is not.
[ "$collected" -eq "$expected" ] ||
fail "Expected $expected release assets but found $collected. Refusing to publish a partial release."

# Named explicitly because every platform without a prebuilt binary depends on
# this one file.
[ -f "$assets/maxminddb-${TAG}.tgz" ] ||
fail "$assets/maxminddb-${TAG}.tgz is missing."
Loading
Loading