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
110 changes: 110 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Publishing to Maven Central.
#
# A tag is the trigger, and the tag is the version: v0.11.0 publishes
# 0.11.0. The pom on main stays a snapshot, because a repository whose
# version has to be bumped in a commit before every release is a
# repository where the version in a checkout is a lie between the bump
# and the tag.
#
# The engine and this client move on one version number, so the same tag
# names the engine release the libraries are downloaded from. If that
# release does not exist yet, this job fails at the staging step rather
# than publishing a client with no engine in it.
#
# Nothing is published without a human: autoPublish is off, so this puts
# a deployment in the portal and stops. Dropping it there is how a
# mistake is undone, and there is no undoing a version that went out.
name: Release

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: The engine release to take libraries from, for example v0.11.0
required: true
publish:
description: Upload to Central, rather than only building what would be uploaded
type: boolean
default: false

env:
MAVEN_ARGS: -B -ntp

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5

- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "25"
cache: maven
# Writes a settings.xml whose `central` server credentials come
# from these two, which is the id the publishing plugin is
# configured with.
server-id: central
server-username: CENTRAL_USERNAME
server-password: CENTRAL_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: GPG_PASSPHRASE

- name: Which version this is
run: |
set -eu
tag="${{ inputs.tag || github.ref_name }}"
case "$tag" in
v*) ;;
*) echo "$tag is not a version tag"; exit 1 ;;
esac
echo "TAG=$tag" >> "$GITHUB_ENV"
echo "VERSION=${tag#v}" >> "$GITHUB_ENV"

# The version lives in the tag rather than in the pom, so it is set
# here and never committed.
- name: Set the version
run: mvn $MAVEN_ARGS versions:set -DnewVersion="$VERSION" -DgenerateBackupPoms=false

- name: Stage the libraries
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./scripts/stage-natives.sh "$TAG"

# The suite needs an engine, and one of the seven that was just
# staged is the platform this runner is, so it runs against the
# library that is about to be published rather than against a build
# of main. A release that cannot answer a query does not go out.
- name: The library that is about to be published answers
run: |
set -eu
echo "ZU_LIBRARY=$PWD/zudb-native/lib/linux-amd64/libzu.so" >> "$GITHUB_ENV"

- run: mvn $MAVEN_ARGS -Pnatives test

- name: Build, sign and upload
if: github.event_name == 'push' || inputs.publish
env:
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: mvn $MAVEN_ARGS -Prelease,natives -DskipTests deploy

# What a dispatch with publish off is for: everything up to the
# upload, so that a change to the packaging can be checked without
# a version being spent on it.
- name: Build what would have been uploaded
if: github.event_name != 'push' && !inputs.publish
run: mvn $MAVEN_ARGS -Prelease,natives -DskipTests -Dgpg.skip=true package

- uses: actions/upload-artifact@v4
with:
name: artifacts
path: |
*/target/*.jar
*/target/*.pom
if-no-files-found: error
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ ZU_LIBRARY=/path/to/libzu.dylib java -jar zudb-bench/target/benchmarks.jar

`ZU_LIBRARY` rather than `-Dzu.library` there, because JMH forks a JVM of its own and a fork inherits the environment rather than the system properties.

A release is a tag. `v0.11.0` builds `0.11.0`, takes its libraries from the engine release of the same name, runs the suite against the very library it is about to publish, and puts one signed deployment of the whole reactor in the Central portal. The version is never committed: a pom that has to be bumped before a release is a pom that is wrong between the bump and the tag. Nothing is published without a human pressing the button, and dropping a deployment in the portal is the only way a mistake is undone, because a version that went out cannot be taken back.

## Beyond Java

- **Kotlin** (`dev.zudb:zudb-kotlin`, tier 2), a thin extension layer, not a second binding. `use`, `Sequence`, `Flow` with cancellation wired to `interrupt()`, and an inline `transaction { }`.
Expand Down
39 changes: 39 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,47 @@
and a local build has no use for. -->
<profile>
<id>release</id>
<properties>
<!-- gpg reads a passphrase from a terminal, and a release runs
where there is none. Loopback is what makes it read the one
the plugin hands it instead of trying to open a tty and
hanging until the job is cancelled. -->
<gpg.args>--pinentry-mode=loopback</gpg.args>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven.gpg.plugin.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals><goal>sign</goal></goals>
</execution>
</executions>
</plugin>
<!-- One upload for the whole reactor rather than one per
module, so that a release either lands complete or does
not land: Central validates a deployment as a unit, and a
half-published version is a version nobody can use and
nobody can replace. autoPublish is off, so a deployment
waits in the portal until a human looks at it. -->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>${central.publishing.plugin.version}</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>false</autoPublish>
<waitUntil>validated</waitUntil>
<excludeArtifacts>
<excludeArtifact>zudb-bench</excludeArtifact>
</excludeArtifacts>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
Expand Down
39 changes: 39 additions & 0 deletions zudb-native/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,43 @@
</plugin>
</plugins>
</build>

<profiles>
<!-- Central wants a sources jar and a javadoc jar beside every jar
it publishes, and has no way of being told that an artifact made
of shared libraries has neither. The inherited source and javadoc
plugins produce nothing here, because there is no Java, so both
are attached by hand and both hold one file saying why they are
empty. That is more honest than a jar with nothing in it at all,
which reads like a build that went wrong. -->
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>sources</id>
<goals><goal>jar</goal></goals>
<configuration>
<classifier>sources</classifier>
<classesDirectory>${project.basedir}/src/empty</classesDirectory>
</configuration>
</execution>
<execution>
<id>javadoc</id>
<goals><goal>jar</goal></goals>
<configuration>
<classifier>javadoc</classifier>
<classesDirectory>${project.basedir}/src/empty</classesDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
7 changes: 7 additions & 0 deletions zudb-native/src/empty/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# There is no source here, and no documentation either

`zudb-native` is a jar of shared libraries. Nothing in it is compiled from Java and nothing in it has an API, so a sources jar and a javadoc jar have nothing to hold.

Maven Central requires both of them beside every published jar, and there is no way to say that one of them is meaningless for an artifact. So both are published and both contain this file, which is at least an answer to whoever opens one looking for something.

The source for the libraries is the engine, at https://github.com/tamnd/zu. The API this artifact serves is `dev.zudb`, whose javadoc is published beside the `zudb` artifact.
Loading