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
62 changes: 0 additions & 62 deletions .github/workflows/publish-release.yml

This file was deleted.

82 changes: 82 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Release to Maven Central

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'

permissions:
contents: write # Create the GitHub Release.

concurrency:
group: maven-central-release
cancel-in-progress: false

jobs:
release:
if: github.repository == 'uber/ussi'
runs-on: ubuntu-latest
steps:
- name: Check out release tag
uses: actions/checkout@v7
with:
persist-credentials: false

- name: Verify release tag and version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
if [[ ! "$RELEASE_TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
echo 'Expected a release tag such as v0.1.0.' >&2
exit 1
fi
version="$(sed -n 's/^VERSION_NAME=//p' gradle.properties)"
if [[ "$version" != "${RELEASE_TAG#v}" || "$version" =~ -[Ss][Nn][Aa][Pp][Ss][Hh][Oo][Tt]$ ]]; then
echo 'Release tag must match a non-SNAPSHOT VERSION_NAME.' >&2
exit 1
fi
coordinates="$(sed -n 's/.*maven_coordinates = "com.uber.ussi:ussi:\([^"]*\)".*/\1/p' BUILD.bazel)"
if [[ "$coordinates" != "$version" ]]; then
echo 'BUILD.bazel maven_coordinates must match VERSION_NAME.' >&2
exit 1
fi
printf 'RELEASE_VERSION=%s\n' "$version" >> "$GITHUB_ENV"

- name: Extract release notes
run: |
awk -v ver="$RELEASE_VERSION" '
/^# / { if (found) exit; if ($0 == "# " ver) { found=1; next } }
found
' CHANGELOG.md > release_notes.md
if ! grep -q '[^[:space:]]' release_notes.md; then
echo "Add a # $RELEASE_VERSION section with release notes to CHANGELOG.md" >&2
exit 1
fi

- name: Set up JDK
uses: actions/setup-java@v6
with:
distribution: temurin
java-version: '21'

- name: Set up Gradle and validate wrapper
uses: gradle/actions/setup-gradle@v6
with:
cache-disabled: true

- name: Publish
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USER_TOKEN_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_USER_TOKEN_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.PGP_PRIVATE_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.PGP_SIGNING_KEY_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.PGP_SIGNING_KEY_PASSWORD }}
run: ./gradlew publishToMavenCentral --no-daemon

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
run: |
gh release create "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --verify-tag \
--title "$RELEASE_VERSION" --notes-file release_notes.md
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

Release notes for each version use a top-level `# X.Y.Z` heading (matching the
release workflow and [RELEASING.md](RELEASING.md)).

# 0.1.0

Initial open-source release of Uber Similarity Search Index (USSI).

Published Maven coordinates: `com.uber.ussi:ussi:0.1.0`.
66 changes: 56 additions & 10 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,67 @@ Published artifacts use **`com.uber.ussi:ussi`**.
## CI and local builds

- **Pull requests and main:** Bazel tests (`.github/workflows/tests.yml`).
- **Releases:** Gradle publishes to Maven Central (`.github/workflows/publish-release.yml`).
- **Releases:** [Release to Maven Central](.github/workflows/release.yml) on version tags.

Both build systems share the same `src/main/java` and `src/test/java` trees. Bazel remains supported for day-to-day development; Gradle is the supported release path for Uber open-source JVM libraries.

Gradle requires JDK 21 or later. On Uber-managed machines that inject an internal Artifactory `init.gradle`, use a clean `GRADLE_USER_HOME` (for example `GRADLE_USER_HOME=$PWD/.gradle-home ./gradlew test`) so dependencies resolve from Maven Central only.

## Cut a release
## Release process

1. Set `VERSION_NAME` in `gradle.properties` to the release version (no `-SNAPSHOT` suffix).
2. Update `maven_coordinates` in `BUILD.bazel` to the same version.
3. Merge to `main`.
4. Create and push an annotated tag `vX.Y.Z` matching `VERSION_NAME`.
5. Confirm the **Publish release to Maven Central** workflow succeeds on GitHub Actions.
6. Confirm the artifact on [Maven Central](https://central.sonatype.com/artifact/com.uber.ussi/ussi).
Creating and pushing a release tag to `uber/ussi` automatically triggers the
[Release to Maven Central workflow](.github/workflows/release.yml). Merging a PR
or pushing a commit to `main` does not trigger a release.

If tag `vX.Y.Z` already exists on an older commit, move the tag after the publish workflow is on `main`, or cut the next patch version instead.
The workflow signs, uploads, and releases `com.uber.ussi:ussi` to Maven Central.
The Gradle Maven Publish plugin's `publishToMavenCentral` task handles both upload
and release because `mavenCentralAutomaticPublishing=true` is set in
`gradle.properties`. After publishing, the workflow creates a GitHub Release for
the tag using the matching `CHANGELOG.md` section as its release notes. The tagged
commit should already have passed CI on `main`.

After the release is published, bump `VERSION_NAME` in `gradle.properties` and `maven_coordinates` in `BUILD.bazel` to the next `-SNAPSHOT` development version.
## Prepare and publish a release

1. Set `VERSION_NAME` in `gradle.properties` to the release version without
`-SNAPSHOT`, for example `0.1.0`.
2. Set `maven_coordinates` on `//:src_main` in `BUILD.bazel` to the same version.
3. Add a matching `# 0.1.0` section with release notes to `CHANGELOG.md`.
4. Commit with `Prepare for release 0.1.0.` and merge the PR into `main`.
5. Tag the release commit and push the tag:

```sh
git switch main
git pull --ff-only origin main
git tag -a v0.1.0 -m "Version 0.1.0"
git push origin v0.1.0
```

6. Follow **Release to Maven Central** in GitHub Actions. The workflow validates
the tag, version, Bazel coordinates, and changelog before publishing, then
creates the GitHub Release. Maven Central may take additional time to make a
released version available.
7. Prepare the next development version, such as `0.2.0-SNAPSHOT`, in
`gradle.properties` and `BUILD.bazel`, and submit it in a separate PR.

Release tags use the form `vX.Y.Z`. The tag version (without the `v` prefix) must
exactly match `VERSION_NAME`, and snapshot versions are rejected. The workflow
does not change versions, create commits, or push tags.

## Retry a release

Open the failed tag-push run of **Release to Maven Central** in GitHub Actions
and select **Re-run failed jobs**. Versions already released to Maven Central
cannot be overwritten; use a new version for changed artifacts.

## Repository secrets

These GitHub Actions secrets are configured at the Uber organization level for
`uber/ussi`:

| Secret | Purpose |
| --- | --- |
| `MAVEN_CENTRAL_USER_TOKEN_USERNAME` | Central Portal user token username for `com.uber.ussi`. |
| `MAVEN_CENTRAL_USER_TOKEN_PASSWORD` | Central Portal user token password. |
| `PGP_SIGNING_KEY_PRIVATE_KEY` | ASCII-armored private signing key. |
| `PGP_PRIVATE_KEY_ID` | Signing subkey ID, if required. Optional. |
| `PGP_SIGNING_KEY_PASSWORD` | Private key passphrase. Optional for an unencrypted key. |
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ POM_DEVELOPER_NAME=Uber Technologies
POM_DEVELOPER_URL=https://uber.com

mavenCentralPublishing=true
mavenCentralAutomaticPublishing=true
mavenCentralDeploymentValidation=PUBLISHED
signAllPublications=true
RELEASE_SIGNING_ENABLED=true
Loading