Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
230 changes: 162 additions & 68 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,97 @@

## Prerequisites

### Setup OSSRH and Signing
### Setup Maven Central Portal Publishing

If you haven't deployed artifacts to Maven Central before, you need to set up
your OSSRH (OSS Repository Hosting) account and signing keys.
> [!IMPORTANT]
> The OSSRH service will reach the end-of-life sunset date on June 30th, 2025.
> After this, it is recommended to only use Sonatype's Central Publisher Portal
> to publish artifacts to Maven Central. See
> [the official notice](https://central.sonatype.org/news/20250326_ossrh_sunset/)
> for more details.

If you do not have a Central Portal account on Sonatype, you need to set up your
account to publish via the Central Portal.

- Follow the instructions on [this
page](http://central.sonatype.org/pages/ossrh-guide.html) to set up an account
with OSSRH.
- You only need to create the account, not set up a new project
- Contact an OpenTelemetry Operations Java maintainer to add your account
after you have created it.
- (For release deployment only) [Install
GnuPG](http://central.sonatype.org/pages/working-with-pgp-signatures.html#installing-gnupg)
and [generate your key
pair](http://central.sonatype.org/pages/working-with-pgp-signatures.html#generating-a-key-pair).
You'll also need to [publish your public
key](http://central.sonatype.org/pages/working-with-pgp-signatures.html#distributing-your-public-key)
to make it visible to the Sonatype servers. For gpg 2.1 or newer, you also
need to [export the
keys](https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials)
with command `gpg --keyring secring.gpg --export-secret-keys >
~/.gnupg/secring.gpg`.
- Put your GnuPG key password and OSSRH account information in
`<your-home-directory>/.gradle/gradle.properties`:
page](https://central.sonatype.org/register/central-portal/) to set up an
account with Central Portal.
- You only need to create the account, not set up a new project.
- Contact an OpenTelemetry Operations Java maintainer to add your account
after you have created it.

### Setup artifact signing

The artifacts must be signed before being published for consumption. Follow
these steps to set up artifact signing:
- [Install
GnuPG](http://central.sonatype.org/pages/working-with-pgp-signatures.html#installing-gnupg)
and [generate your key
pair](http://central.sonatype.org/pages/working-with-pgp-signatures.html#generating-a-key-pair).
- You'll also need to [publish your public
key](http://central.sonatype.org/pages/working-with-pgp-signatures.html#distributing-your-public-key)
to make it visible to the Sonatype servers. For gpg 2.1 or newer, you also
need to [export the
keys](https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials)
with command
`gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg`.
Comment thread
psx95 marked this conversation as resolved.
Outdated
- Put your GnuPG key password and Central Portal account information in
`<your-home-directory>/.gradle/gradle.properties`:

```text
# You need the signing properties only if you are making release deployment
signing.keyId=<8-character-public-key-id>
signing.password=<key-password>
signing.secretKeyRingFile=<your-home-directory>/.gnupg/secring.gpg

ossrhUsername=<ossrh-username>
ossrhPassword=<ossrh-password>
centralPortalUsername=<ossrh-username>
centralPortalPassword=<ossrh-password>
checkstyle.ignoreFailures=false
```

> [!TIP]
> If your key-generation is failing, checkout the [help section](#help-timeout-during-key-generation-process) at the bottom of this document.
> If your key-generation is failing, checkout the
> [help section](#help-timeout-during-key-generation-process) at the bottom of
> this document.

### Using GPG-Agent for artifact signing

> [!NOTE]
> These instructions are for modern linux where `gpg` refers to the 2.0 version.

If you're running in linux and would like to use the GPG agent to remember your PGP key passwords instead of keeping them in a plain-text file on your home directory,
you can configure the following in `<your-home-directory>/.gradle/gradle.properties`:
If you're running in linux and would like to use the GPG agent to remember your
PGP key passwords instead of keeping them in a plain-text file on your home
directory, you can configure the following in
`<your-home-directory>/.gradle/gradle.properties`:

```text
ossrhUsername=<generated-token-user>
ossrhPassword=<generated-token-key>
centralPortalUsername=<generated-token-user>
centralPortalPassword=<generated-token-key>

signingUseGpgCmd=true
signing.gnupg.executable=gpg
signing.gnupg.keyName=<secret key id (large hash)>
```
Note: You can retrieve the list of previously created GPG keys on your machine by using `gpg --list-secret-keys`.

Note: You can retrieve the list of previously created GPG keys on your machine
by using `gpg --list-secret-keys`. Additionally, you might still be asked for
the GPG key's passphrase while signing the artifact, you can store the key in a
password manager (or in the built-in Keyring) to avoid entering the password
manually.\
For more details, checkout the
[help section](#help-timeout-while-singing-during-release-process) on the bottom
of this guide.

> [!IMPORTANT]
> Starting June 2024, due to a change to the OSSRH authentication backend, the maven publish plugin now requires [a user token](https://central.sonatype.org/publish/generate-token/) instead of a typical username and password used in the Nexus UI.
> Follow the steps in the [link](https://central.sonatype.org/publish/generate-token/) to generate a user token, if not done already - this will provide you with a `tokenuser` and `tokenkey`. Replace `<generated-token-user>` and `<generated-token-key>` with the generated `tokenuser` and `tokenkey` in your `gradle.properties` file to successfully publish artifacts.
> The user tokens for publishing to the Central Portal are different from those
> used for OSSRH. If you haven't already, you must generate a new Portal Token
> to publish to the Central Portal.
> Follow the steps in this
> [link](https://central.sonatype.org/publish/generate-portal-token/) to
> generate a user token - this will provide you with a Portal token containing a
> `username` and `password`. Replace `<generated-token-user>` and
> `<generated-token-key>` with the generated `username` and `password` in your
> `gradle.properties` file to successfully publish artifacts.

### Ensuring you can push tags to GitHub upstream

Expand All @@ -71,15 +102,33 @@ token](https://help.github.com/articles/creating-a-personal-access-token-for-the

## Release a Snapshot

If you've followed the above steps, you can release snapshots for consumption using the following:
If you've followed the above steps, you can release snapshots for consumption
using the following:

```bash
$ ./gradlew snapshot
```

## Releasing a Candidate (Optional)
SNAPSHOT releases are intended for developers to make pre-release versions of
their projects available for testing. Published snapshots should be visible
using the
[directory listing for com.google.cloud.opentelemetry](https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/google/cloud/opentelemetry/)
namespace.

See
[Publishing Snapshot Releases](https://central.sonatype.org/publish/publish-portal-snapshots/#publishing-snapshot-releases)
for more details.

## Preparing a release candidate (Optional)

> [!TIP]
> Preparing a release candidate involves the same steps as preparing a final
> version. The only difference is in how a release candidate is tagged.\
> Release candidates are pre-release version of libraries, close to the final
> stable release, this is typically not required for this repository.

After following the above steps, you can release candidates from `main` or `v<major>.<minor>.x` branches.
After following the above steps, you can release candidates from `main` or
`v<major>.<minor>.x` branches.

For example, to release the v0.14.0-RC1 candidate, do the following:

Expand All @@ -93,12 +142,18 @@ $ git push origin v0.14.0-RC1
Next follow [Releasing on Maven Central](#releasing-on-maven-central) to close + publish the
[repository on OSSRH](https://oss.sonatype.org/#stagingRepositories).


Note: In the future, the `-Prelease.version` flag should not be required.

## Release a final verison
## Preparing a final verison

> [!IMPORTANT]
> The nebula-release plugin automatically tags the current release with the
> appropriate number based on the previous release. Make sure that the release
> version being provided in the argument for the candidate task matches the
> latest tag.

After following the above steps, you can release candidates from `main` or `v<major>.<minor>.x` branches.
After following the above steps, you can release candidates from `main` or
`v<major>.<minor>.x` branches.

For example, to release the v0.14.0 candidate, do the following:

Expand All @@ -109,14 +164,11 @@ $ ./gradlew candidate -Prelease.version=0.14.0
$ git push origin v0.14.0
```

*Note: If you do not have a CredentialsProvider registered for GitHub, the `candidate` task may fail to upload tags to the GitHub repository and the overall command may take a long time to report completion on the task. In this case, before moving forward - check if tags were pushed to GitHub. If not, manually push the tags before continuing.*\
*Next, check if the staging repository is created on the [nexus repository manager](https://oss.sonatype.org/#stagingRepositories). If the repository is already created, continue with the next steps.*

Follow [Releasing on Maven Central](#releasing-on-maven-central) to close + publish the
[repository on OSSRH](https://oss.sonatype.org/#stagingRepositories).

After this, follow the [Announcment](#Announcement) documentation to advertise the release and update README files.

*Note: If you do not have a CredentialsProvider registered for GitHub, the
`candidate` task may fail to upload tags to the GitHub repository and the
overall command may take a long time to report completion on the task.
In this case, before moving forward - check if tags were pushed to GitHub.
If not, manually push the tags before continuing.*\

Note: In the future, the `-Prelease.version` flag should not be required.

Expand All @@ -130,37 +182,61 @@ gone through code review. For the current release use:
$ git checkout -b v$MAJOR.$MINOR.$PATCH tags/v$MAJOR.$MINOR.$PATCH
```

## Releasing on Maven Central
## Uploading the release artifacts to Central Portal

Once all the artifacts have been pushed to the staging repository, the
repository must first be `closed`, which will trigger several sanity checks on
the repository. If this completes successfully, the repository can then be
`released`, which will begin the process of pushing the new artifacts to Maven
Central (the staging repository will be destroyed in the process). You can see
the complete process for releasing to Maven Central on the [OSSRH
site](http://central.sonatype.org/pages/releasing-the-deployment.html).
> [!IMPORTANT]
> This task will create a deployment on the Central Portal, visible on their UI.
> It should only be run after the release is prepared.

After preparing the release, the release artifacts need to be uploaded to
Central Portal so that they can be released on Maven Central.\
To upload the prepared release artifacts, run the following gradle task:

Note: This can/will be automated in the future.
```shell
./gradlew sonatypeUploadDefaultRepository
```

### Things to check before 'closing' on Maven Central
The task will respond with an HTTP status code. If the status code is 200, the
artifacts are successfully uploaded on the Central Portal and should be visible
on the UI.

Before closing the staging repository, it is important to verify that the contents of all the
published modules are looking good. Particularly, the version numbers should be what are expected,
and they include any custom release qualifiers (like 'alpha') which are set. Make sure that:
- The generated POM files for the individual module have the correct version number.
- The dependencies for an individual module in the POM file are the expected ones & they dependencies have the correct versions.
- The module content includes all the artifacts that are expected to be published - for instance, sourcesJar, javadocs, additional variants like a shaded JAR in some cases, etc.
## Releasing on Maven Central

Once all the artifacts have been pushed to the Central Portal, a `deployment`
will be created in the Central Portal. This deployment is visible under the
"Deployments" tab on https://central.sonatype.com/publishing (you will have to
log in with your account).\
At this point, you can either manually 'Drop' or 'Publish' the deployment.
- Publishing the deployment will make the new release available on Maven
Central.
- Dropping the deployment will close the deployment and abandon the release.
You should drop the deployment if you do not wish to proceed with release
process for any reason.

### Things to check before 'Publishing' on Maven Central
Comment thread
psx95 marked this conversation as resolved.

Before publishing the release, it is important to verify that the
contents of all the published modules are looking good. Particularly, the
version numbers should be what are expected, and they include any custom release
qualifiers (like 'alpha') which are set. Make sure that:
- The generated POM files for the individual module have the correct version
number.
- The dependencies for an individual module in the POM file are the expected
ones & they dependencies have the correct versions.
Comment thread
psx95 marked this conversation as resolved.
Outdated
- The module content includes all the artifacts that are expected to be
published - for instance, sourcesJar, javadocs, additional variants like a
shaded JAR in some cases, etc.
- The file sizes for the published artifacts should seem reasonable.

## Announcement

Once deployment finishes, go to GitHub [release
page](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java/releases),
Once deployment finishes, go to GitHub
[release page](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java/releases),
press `Draft a new release` to write release notes about the new release.

You can use `git log upstream/v$MAJOR.$((MINOR-1)).x..upstream/v$MAJOR.$MINOR.x
--graph --first-parent` or the GitHub [compare
tool](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java/compare/)
--graph --first-parent` or the GitHub
[compare tool](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java/compare/)
to view a summary of all commits since last release as a reference.

Please pick major or important user-visible changes only.
Expand All @@ -177,9 +253,27 @@ $ git cherry-pick -x $COMMIT
```

### Help: Timeout during key-generation process
If you see timeout errors when you run `gpg --gen-key` to generate your keys, it maybe because you are running the command on a server and do not have access to a UI.
If you see timeout errors when you run `gpg --gen-key` to generate your keys,
it maybe because you are running the command on a server and do not have access
to a UI.\
A common example is - running this command on a remote machine over ssh.

The issue here is that this command opens up a UI dialog asking for you to set a passphrase, waiting for input for a fixed time.
The issue here is that this command opens up a UI dialog asking for you to set a
passphrase, waiting for input for a fixed time.

The easiest way to fix this is to run it on a machine for which you have UI
access.
Comment thread
psx95 marked this conversation as resolved.
Outdated

### Help: Timeout while signing during release process
If you see timeout errors when you run `./gradlew snapshot` or
`./gradlew candidate` this could be because the process is expecting your GPGKey
passphrase and timed out waiting on it.
This is expected if you do not have access to the UI of the machine which runs
the gradle task.\
A common example is - running this command on a remote machine over ssh.

The issue here is that this command opens up a UI dialog asking for you to set a
passphrase, waiting for input for a fixed time.

The easiest way to fix this is to run it on a machine for which you have UI access.
The easiest way to fix this is to run it on a machine for which you have UI
access.
58 changes: 54 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
import nebula.plugin.release.git.opinion.Strategies

import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

plugins {
id "com.diffplug.spotless"
id 'nebula.release'
Expand Down Expand Up @@ -314,12 +318,14 @@ subprojects {
}
repositories {
maven {
def ossrhRelease = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
def ossrhSnapshot = "https://oss.sonatype.org/content/repositories/snapshots/"
// Publish using Portal OSSRH Staging API.
// For more information see https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/#publishing-by-using-the-portal-ossrh-staging-api
def ossrhRelease = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/"
def ossrhSnapshot = "https://central.sonatype.com/repository/maven-snapshots/"
url = isReleaseVersion ? ossrhRelease : ossrhSnapshot
credentials {
username = rootProject.hasProperty('ossrhUsername') ? rootProject.ossrhUsername : "Unknown user"
password = rootProject.hasProperty('ossrhPassword') ? rootProject.ossrhPassword : "Unknown password"
username = rootProject.hasProperty('centralPortalUsername') ? rootProject.centralPortalUsername : "Unknown user"
password = rootProject.hasProperty('centralPortalPassword') ? rootProject.centralPortalPassword : "Unknown password"
}
}
}
Expand All @@ -335,3 +341,47 @@ subprojects {
}
}
}

tasks.register('sonatypeUploadDefaultRepository') {
group = 'Deployment'
description = 'Uploads the artifact repository published by the maven publish plugin to Central Portal so that it is visible in the UI.'

if (!rootProject.hasProperty('centralPortalUsername') || !rootProject.hasProperty('centralPortalPassword')) {
throw new GradleException("Unable to find the username and password. Please check ~/.gradle/gradle.properties file.")
}

// The logic is placed in a doLast block to ensure it runs during the execution phase.
doLast {
// Create authentication string to be used in the bearer token for the API.
// See https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/#manual-api-endpoints
String authString = "${rootProject.centralPortalUsername}:${rootProject.centralPortalPassword}"

// Encode the auth string and construct the bearer token to be passed in the auth header.
String encodedAuthString = Base64.getEncoder().encodeToString(authString.getBytes('UTF-8'))

String centralPortalUploadEndpoint = 'https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/com.google.cloud.opentelemetry?publishing_type=user_managed'

// Make a manual POST request to the Central Portal Endpoint
def client = HttpClient.newHttpClient()
def request = HttpRequest.newBuilder()
.uri(URI.create(centralPortalUploadEndpoint))
.header('Authorization', "Bearer ${encodedAuthString}")
.header('accept', '*/*')
.POST(HttpRequest.BodyPublishers.ofString(''))
.build()

println "Sending POST request to: ${centralPortalUploadEndpoint}"

// The response body will be handled as a String.
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString())
println "Response Status Code: ${response.statusCode()}"
println "Response Body: ${response.body()}"

// Optionally, fail the build if the request was not successful
if (response.statusCode() >= 400) {
throw new GradleException("Deployment failed! Received HTTP status ${response.statusCode()}")
} else {
println "Default repository uploaded successfully! Please visit https://central.sonatype.com/publishing to complete release process."
}
}
}
Loading