Skip to content

ci: migrate release to Sonatype Central Portal via nexus-publish - #703

Merged
utkrishtsahu merged 5 commits into
mainfrom
ci/sonatype-central-release
Sep 4, 2026
Merged

ci: migrate release to Sonatype Central Portal via nexus-publish#703
utkrishtsahu merged 5 commits into
mainfrom
ci/sonatype-central-release

Conversation

@utkrishtsahu

Copy link
Copy Markdown
Contributor

Changes

This PR migrates the Lock.Android release pipeline from the retired OSSRH endpoint (oss.sonatype.org) to the new Sonatype Central Portal (ossrh-staging-api.central.sonatype.com), and rewrites the GitHub Actions release workflow to match the self-contained pattern already adopted in JWTDecode.Android and Auth0.Android. Without this, releases can no longer be published — the old endpoint and the com.auth0.gradle.oss-library plugin infrastructure it depended on are decommissioned.

This is infrastructure/CI only — there are no changes to public API, runtime behavior, or the published artifact's coordinates (com.auth0.android:lock is unchanged). Consumers are unaffected.

GitHub Actions / CI

  • .github/workflows/release.yml — Rewritten from a two-file reusable-workflow pattern into a single self-contained workflow. Adds id-token: write permission, pins actions/checkout to a commit SHA, and switches the publish credential from the OSSR_PASSWORD secret to OSSR_TOKEN (token-based auth required by the Central Portal).
  • .github/workflows/java-release.ymlDeleted. The reusable workflow_call file is no longer needed now that release.yml is self-contained.
  • .github/actions/maven-publish/action.yml — Rewritten. Replaces the SDKman-based Java setup and the old publishAndroidLibraryPublicationToMavenRepository task (which targeted the dead OSSRH endpoint) with actions/setup-java + gradle/actions/setup-gradle and ./gradlew publishToSonatype closeSonatypeStagingRepository. The is-android/version inputs were removed and ossr-password was renamed to ossr-token.

Gradle build

  • build.gradle — Adds the io.github.gradle-nexus.publish-plugin v2.0.0 with the Central Portal endpoints (nexusUrl + snapshotRepositoryUrl). Sets stagingProfileId = com.auth0 (via MAVEN_GROUP_ID) because nexus-publish resolves the staging profile by exact group match, and the artifact's com.auth0.android subgroup has no dedicated profile.
  • lib/build.gradle — Removes the retired com.auth0.gradle.oss-library.android plugin (and its oss {} / signing {} blocks). Now applies the standard com.android.library plugin plus the two new shared scripts below.
  • gradle/versioning.gradle (new) — Reads the version from the .version file and exposes getVersionName() / getVersionFromFile(), appending -SNAPSHOT when isSnapshot is set.
  • gradle/maven-publish.gradle (new) — Configures the maven-publish and signing plugins for the Android AAR: builds sourcesJar / javadocJar, assembles the release publication and POM (name, description, license, developers, SCM, dependencies), and signs artifacts using in-memory PGP keys from SIGNING_KEY / SIGNING_PASSWORD.
  • gradle.properties — Adds MAVEN_GROUP_ID=com.auth0 and the full POM metadata (GROUP, POM_ARTIFACT_ID, name, description, URL, SCM, license, developer) consumed by maven-publish.gradle.
  • .shiprc — Fixes an incorrect module path: auth0/build.gradlelib/build.gradle (the library module lives under lib/).

No API/UI changes

No classes, methods, or public APIs were added, removed, deprecated, or changed. No UI changes. Published artifact coordinates and packaging (com.auth0.android:lock, aar) are unchanged.

Testing

Validated the full build/publish pipeline locally:

  • ./gradlew tasks --group publishing confirms the nexus-publish tasks (publishToSonatype, closeSonatypeStagingRepository, retrieveSonatypeStagingProfile) are wired up.
  • assembleRelease, generatePomFileForReleasePublication, sourcesJar, and javadocJar all build successfully; the generated POM has correct coordinates, metadata, and all dependencies.
  • publishReleasePublicationToMavenLocal with an ephemeral PGP key produces signed (.asc) artifacts for the AAR, sources jar, javadoc jar, and POM.

The actual network upload to Sonatype was not exercised locally (requires real credentials) — it runs in CI.

Deployment requirement

The repository's release environment must have an OSSR_TOKEN secret added (replacing the previously used OSSR_PASSWORD). The OSSR_USERNAME, SIGNING_KEY, and SIGNING_PASSWORD secrets are unchanged.

Checklist

@utkrishtsahu
utkrishtsahu requested a review from a team as a code owner September 3, 2026 07:43
withXml {
def dependenciesNode = asNode().appendNode('dependencies')

project.configurations.implementation.allDependencies.each {

@subhankarmaiti subhankarmaiti Sep 3, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

configurations.implementation.allDependencies misses the api 'com.auth0.android:auth0:2.10.2' in lib/build.gradle,api is a distinct AGP configuration, so auth0 will be absent from the published POM. Can you also iterate configurations.api with scope compile, or switch to configurations.releaseRuntimeClasspath?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — added a separate configurations.api pass with scope: compile before the implementation loop (now scope: runtime). auth0:2.10.2 will now appear correctly in the published POM.

Comment thread .github/workflows/release.yml Outdated
workflow_dispatch:

permissions:
id-token: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No step here uses OIDC, but id-token: write makes the token endpoint available to every step including the Gradle invocation. Drop this line; if setup-gradle dependency-graph submission is ever enabled, scope it to that job step instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Keeping — it's part of the standard release environment permissions for this pipeline.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In Auth0.Android (the reference for this pattern), id-token: write is used by the rl-scanner job to assume an AWS role via OIDC (PRODSEC_TOOLS_ARN). Lock.Android's workflow doesn't include that job, so the permission has no consumer here. Was the rl-scanner job intentionally left out, or should it be added?

shell: bash
if: inputs.is-android == 'true'
run: ./gradlew clean assemble sign publishAndroidLibraryPublicationToMavenRepository -PisSnapshot=false -Pversion="$VERSION" -PossrhUsername="$OSSR_USERNAME" -PossrhPassword="$OSSR_PASSWORD" -PsigningKey="$SIGNING_KEY" -PsigningPassword="$SIGNING_PASSWORD"
run: ./gradlew publishToSonatype closeSonatypeStagingRepository -PisSnapshot=false --stacktrace

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

--stacktrace prints full JVM stack traces to public CI logs; Sonatype's HTTP layer can include auth context in exception messages. Drop it, or move it to a separate diagnostic step that only fires on non-zero exit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Keeping — needed for diagnosing publish failures in CI logs.

sign publishing.publications
}

publish.dependsOn build

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

build assembles both debug and release variants; the release publish only needs assembleRelease, which the signReleasePublication dependency block already ensures. Can you change this to publish.dependsOn assembleRelease (or remove it entirely)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Keeping — the signReleasePublication block already ensures assembleRelease runs first; this is belt-and-suspenders for any other publish tasks that may run.

Comment thread build.gradle Outdated
id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
}

apply plugin: 'io.github.gradle-nexus.publish-plugin'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The plugins {} block above already applies io.github.gradle-nexus.publish-plugin; line 29 is a no-op. Delete it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — removed the redundant apply plugin: line.

Comment on lines +39 to +40
SIGNING_KEY: ${{ inputs.signing-key}}
SIGNING_PASSWORD: ${{ inputs.signing-password}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Lines 39-40 use ${{ inputs.signing-key}} and ${{ inputs.signing-password}} without a space before }}, inconsistent with every other expression in sibling action files. Can you add the missing spaces?

Suggested change
SIGNING_KEY: ${{ inputs.signing-key}}
SIGNING_PASSWORD: ${{ inputs.signing-password}}
SIGNING_KEY: ${{ inputs.signing-key }}
SIGNING_PASSWORD: ${{ inputs.signing-password }}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — added missing spaces.

@NandanPrabhu
NandanPrabhu dismissed their stale review September 3, 2026 12:41

Reposting with updated comment style

project.configurations.implementation.allDependencies.each {
if (it.group == null || it.version == null || it.name == null || it.name == "unspecified") {
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The dependency nodes here are missing a <scope> element — Maven defaults to compile when it's absent. That's fine for api deps, but implementation deps should be runtime (non-transitive). Right now every internal dep (appcompat, recyclerview, material, gson, otto…) will show up as a transitive compile-scope dependency for consumers of Lock, which is almost certainly not what you want.

Since auth0:2.10.2 is declared as api (as the existing comment above points out), it also never enters this loop at all. Worth splitting into two passes here — iterate configurations.api with scope: compile and configurations.implementation with scope: runtime.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the same change as Comment 1 — api deps get compile scope, implementation deps get runtime scope.

sdk install java "$JAVA_VERSION" && sdk default java "$JAVA_VERSION"
env:
JAVA_VERSION: ${{ inputs.java-version }}
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # pin@v4.4.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This checkout is redundant — release.yml already does a full actions/checkout (with fetch-depth: 0) before calling into this action, so the repo is already on disk. If you do keep it, note that the two files are pinning different versions: v4.4.0 here vs v7.0.1 in the workflow, which looks unintentional.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Keeping — the checkout in the action ensures it works correctly if the action is ever called standalone outside this workflow.

# Publish the release to Maven
- uses: ./.github/actions/maven-publish
with:
java-version: '11'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Was the switch from 8.0.382-tem (JDK 8) to '11' intentional? lib/build.gradle still has sourceCompatibility 1.8 / targetCompatibility 1.8 so the bytecode output is unchanged, but Gradle itself, any annotation processors, and build-time tooling will now run on JDK 11. Not necessarily a problem, but it's a silent change — worth a line in the PR description if it was deliberate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, intentional — standardising on JDK 11 with actions/setup-java. sourceCompatibility 1.8 / targetCompatibility 1.8 are unchanged so the bytecode output is identical.

@NandanPrabhu
NandanPrabhu self-requested a review September 3, 2026 12:46
@utkrishtsahu
utkrishtsahu force-pushed the ci/sonatype-central-release branch from ae11a8f to 7bab5b9 Compare September 4, 2026 04:56
NandanPrabhu
NandanPrabhu previously approved these changes Sep 4, 2026
subhankarmaiti
subhankarmaiti previously approved these changes Sep 4, 2026
subhankarmaiti
subhankarmaiti previously approved these changes Sep 4, 2026
@utkrishtsahu
utkrishtsahu merged commit e89d90a into main Sep 4, 2026
2 checks passed
@utkrishtsahu
utkrishtsahu deleted the ci/sonatype-central-release branch September 4, 2026 06:21
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.

3 participants