Skip to content

chore(build): build release binaries in ci#36

Draft
sklarsa wants to merge 14 commits into
mainfrom
build-binaries-in-ci
Draft

chore(build): build release binaries in ci#36
sklarsa wants to merge 14 commits into
mainfrom
build-binaries-in-ci

Conversation

@sklarsa
Copy link
Copy Markdown
Contributor

@sklarsa sklarsa commented May 28, 2026

What this does

Reworks the Maven Central release into a single manually-triggered workflow that proves the release is good before doing anything irreversible. Native libraries are built and tested in CI (not committed by hand), and the git tag / Central publish only happen after the full test suite and the Central Portal validation have passed.

Release flow (abridged)

Trigger Release to Maven Central from the Actions tab, then approve the gated publish step when prompted:

resolve → build ×5 → verify → publish (gated) → open-bump-pr
  1. resolve — infers the release version from the current -SNAPSHOT POM; fails fast if the tag or Central version already exists.
  2. build ×5 — builds each platform's native lib (darwin-aarch64/x86-64, linux-x86-64/aarch64, windows-x86-64). The macOS and Linux libs are load-tested on their own runners; the cross-compiled Windows DLL is objdump-checked for a clean runtime-dependency list.
  3. verify — bundles all five libs and runs the full test suite (exercises the linux-x86-64 lib directly). No credentials. This is the quality gate.
  4. publish (gated by the maven-release environment) — after approval: signs and uploads a droppable VALIDATED deployment, pushes the release tag, then performs the single irreversible step of publishing the deployment via the Central Portal API. If anything fails, the tag is rolled back so reruns stay clean.
  5. open-bump-pr — opens the next-development-version bump PR (post-release; merge it before the next release).

Key properties: validation happens before the tag is pushed, and the Central publish is the single irreversible step and runs last — a tag-push failure leaves nothing published and a clean rerun. The tag pins the exact verified tree. We never push commits to main (the snapshot bump is a PR, so main keeps its PR-only protection). The run does not block on Maven Central's asynchronous propagation (which can take minutes to an hour+).

Required one-time setup

  • Tag ruleset bypass: add github-actions[bot] as a bypass actor on the org-wide restrict-tag-pushing ruleset, so the publish job can push (and roll back) the release tag. (Do not bypass the main branch ruleset — the snapshot bump goes through a PR.)
  • maven-release environment: configure required reviewers (the human approval gate before any credentials are used).
  • AWS release secret (MAVEN_RELEASE_AWS_SECRET_ARN) must define: MAVEN_GPG_PRIVATE_KEY, MAVEN_CENTRAL_USERNAME, MAVEN_CENTRAL_PASSWORD, and optionally MAVEN_GPG_PASSPHRASE (empty/absent for a passphrase-less key).

Full procedure: artifacts/release/README.md.

Follow-ups (not blocking)

  • Pin the GraalVM JDK 25 download (currently /25/latest/) and the Windows jni_md.h (currently jdk25u@master) to exact builds + checksums — marked with TODO(pin) in the workflow.
  • On the first real run, confirm the central-publishing-maven-plugin 0.9.0 log line matches the deploymentId: <uuid> capture, and that the GPG key imports cleanly from the AWS secret.

Evidence

Verified that the include-native-artifacts profile bundles staged binaries into the jar by mocking libquestdb.so with a test string, packaging, and confirming the jar contains the mocked version:

mvn -B -ntp -pl core clean
mkdir -p core/target/native-libs/io/questdb/client/bin/linux-x86-64
printf 'test-linux-x86-64\n' \
    > core/target/native-libs/io/questdb/client/bin/linux-x86-64/libquestdb.so
mvn -B -ntp -pl core package -Pinclude-native-artifacts -DskipTests
$ unzip -p core/target/questdb-client-1.3.2-SNAPSHOT.jar io/questdb/client/bin/linux-x86-64/libquestdb.so
test-linux-x86-64

sklarsa and others added 14 commits May 27, 2026 11:13
Restructure the Maven Central release workflow into a pipeline that
proves the release good before doing anything irreversible: resolve
versions, build the five native libraries, run the full test suite
against them, and validate the signed bundle with the Central Portal
before pushing the tag or publishing.

Push only the release tag (never a commit to main); land the next
snapshot bump as a follow-up PR. Upload as a droppable VALIDATED
deployment, publish it through the Portal API, and do not block on the
asynchronous propagation to Maven Central. Add a shared native-artifact
staging script, harden the shell (pipefail, guarded objdump), pin the
versions-set plugin, and flip central-publishing to autoPublish=false.

Rewrite the release docs for the new flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address self-review findings on the release workflow:

- Push the release tag before the irreversible Central publish POST, not
  after. Validation already proved the bundle good; a tag is deletable, a
  publish is not, so a tag-push failure (e.g. missing bot bypass) now
  leaves nothing published and a clean rerun.
- Guard deployment-id capture and the status peek with || true so a
  no-match or transient curl/jq error cannot abort the step under
  errexit; the status peek timeout stays non-fatal.
- Only commit the version bump when versions:set actually changed the
  poms, so a no-op override does not abort after upload.
- Make open-bump-pr idempotent: force-with-lease the branch and skip PR
  creation when one already exists.
- Re-check the Central version (not just the tag) in publish after the
  environment gate.
- Read the release version from core/pom.xml, the shipped artifact.
- Build both macOS targets to completion (fail-fast: false).
- Fix the README Maven coordinate (org.questdb:questdb-client) and align
  the release docs with the tag-before-publish order and test coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Second self-review pass:

- open-bump-pr pushed the bump branch with --force-with-lease, but the
  job never fetches that branch, so there is no lease reference and the
  push is rejected ("stale info") whenever the branch already exists (and
  on first creation). Use plain --force; the branch is a throwaway owned
  solely by this workflow.
- Tag is now pushed before the publish POST, so a POST failure used to
  leave a tag that blocks reruns while nothing was published. Add an
  if: failure() step that deletes the tag; the publish job fails only
  when nothing reached Central, so this never drops a real release's tag.
- Correct the release doc: the Windows DLL is cross-compiled on Linux and
  only objdump-checked, not load-tested like the macOS/Linux libraries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tag-rollback step was gated only on if: failure(), which can fire
after the publish POST already returned 2xx (the deployment is then
irreversibly Sonatype's) -- via the status peek exiting on FAILED or a
job timeout mid-peek -- deleting the tag of a release that will publish.

Set a published=true step output immediately after the 2xx accept and
gate the rollback on `failure() && steps.central-publish.outputs.published
!= 'true'`. Outputs persist even if the step later fails, so the tag can
never be rolled back once the publish was accepted. Rollback now runs only
for failures before the POST, where nothing reached Central. Also warn,
instead of silently swallowing, when the tag deletion itself fails.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- open-bump-pr opened the PR with --base "${SOURCE_REF}", which fails for
  a tag or SHA ref and would redden a run whose release already
  succeeded. Skip the bump with a notice when source_ref is not a branch;
  the bump is post-release housekeeping and can be done manually.
- Document that MAVEN_GPG_PRIVATE_KEY must be the ASCII-armored key as a
  JSON-escaped string (newlines as \n) so parse-json-secrets restores
  real newlines for gpg --import -- the most common Central release
  failure.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant