Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
af61006
Fix dependencyResolutionManagement and pin plugin versions for standa…
akfreas May 30, 2026
1ed0048
Add Gradle wrapper pinned to 8.10.2 for the Android library module
akfreas May 30, 2026
0ad5dcb
Add Maven Central publishing config to the Android library (vanniktec…
akfreas May 30, 2026
83517f7
Add Maven Central credential setup script with self-verification
akfreas May 30, 2026
c9cd49b
Add Android Maven Central publish workflow alongside the Swift one
akfreas May 30, 2026
06b30a7
Merge branch 'main' of github.com:contentful/optimization into NT-326…
akfreas May 30, 2026
8e1957f
Use vanniktech maven-publish 0.30.0 for AGP 8.7 compatibility
akfreas May 30, 2026
d38e915
Add the Windows Gradle wrapper script for the Android library module
akfreas May 30, 2026
967e85e
Align the Android publish workflow setup-android pin with CI
akfreas May 30, 2026
676625e
Smoke-test Android Maven publishing assembly in CI
akfreas May 30, 2026
cd99eea
Document the Android Maven Central release flow
akfreas May 30, 2026
0f117b6
Merge branch 'main' into NT-3269-create-android-distribution-channel
akfreas May 30, 2026
76c0521
Merge branch 'main' into NT-3269-create-android-distribution-channel
akfreas May 30, 2026
0f8daac
Declare the vanniktech plugin version inline so the module builds as …
akfreas May 30, 2026
b04c1b0
Resolve merge conflict markers in Android module Gradle config
akfreas May 30, 2026
6ffcaba
Check out the dispatched tag, not the default branch, on manual Andro…
akfreas May 30, 2026
8f51102
Harden Maven credential script: stdin secrets, positive token check, …
akfreas May 30, 2026
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
63 changes: 63 additions & 0 deletions .github/workflows/main-pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
e2e_android: ${{ steps.filter.outputs.e2e_android }}
e2e_ios: ${{ steps.filter.outputs.e2e_ios }}
swift_package: ${{ steps.filter.outputs.swift_package }}
android_library: ${{ steps.filter.outputs.android_library }}
steps:
- uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1

Expand Down Expand Up @@ -153,6 +154,13 @@ jobs:
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/workflows/main-pipeline.yaml'
# Android library scope (the AAR published to Maven Central).
android_library:
- 'packages/android/ContentfulOptimization/**'
- 'packages/universal/optimization-js-bridge/**'
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/workflows/main-pipeline.yaml'

setup:
name: 🛠️ pnpm install
Expand Down Expand Up @@ -1071,6 +1079,61 @@ jobs:
- name: Swift test
run: swift test --package-path packages/ios/ContentfulOptimization

android-library-build:
name: 🤖 Android Library Build & Publish-Local Smoke
runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal
timeout-minutes: 30
needs: [setup, changes]
if: needs.changes.outputs.android_library == 'true'
steps:
- uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: '.nvmrc'
package-manager-cache: false

- uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6.0.3

- name: Set Android SDK environment variables
run: |
echo "ANDROID_SDK_ROOT=$HOME/.android/sdk" >> "$GITHUB_ENV"
echo "ANDROID_HOME=$HOME/.android/sdk" >> "$GITHUB_ENV"

- name: Prepare cache directories
run: mkdir -p "$HOME/.android/sdk"

- name: Set up caches (Namespace)
uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3
with:
cache: pnpm
path: |
~/.android/sdk
~/.gradle/caches
~/.gradle/wrapper

- run: pnpm install --prefer-offline --frozen-lockfile

- name: Build the JS bridge
run: pnpm --filter @contentful/optimization-js-bridge build

- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: '17'

- name: Set up Android SDK
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1

# Smoke-test packaging: assemble the AAR + sources/javadoc/POM the release would publish
# (no Central access, no signing) so packaging breaks are caught on PRs.
- name: Verify Maven publishing assembles
working-directory: packages/android/ContentfulOptimization
run: |
./gradlew publishToMavenLocal -Pcontentful.optimization.version=0.0.0-ci \
--no-configuration-cache --no-daemon --console=plain

e2e-react-native-android:
name: 📱 E2E React Native Android (shard ${{ matrix.shard }}/2)
runs-on: namespace-profile-linux-16-vcpu-32-gb-ram-optimal
Expand Down
70 changes: 70 additions & 0 deletions .github/workflows/publish-android.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Publish Android Library

permissions:
contents: read

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Existing release tag (e.g. v1.2.3)'
required: true

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Derive release version
run: |
if [ "${{ github.event_name }}" = "release" ]; then
TAG='${{ github.event.release.tag_name }}'
else
TAG='${{ inputs.tag }}'
fi
echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV"
echo "RELEASE_VERSION=${TAG#v}" >> "$GITHUB_ENV"

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.tag }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: '.nvmrc'
package-manager-cache: false

- uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6.0.3

- run: pnpm install --prefer-offline --frozen-lockfile

- name: Build the JS bridge (stamps the version into the UMD)
run: pnpm --filter @contentful/optimization-js-bridge build
env:
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}

- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: '17'

- uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1

# Build the signed AAR (+ sources/javadoc/POM) and publish+release it to Maven Central via the
# Sonatype Central Portal. vanniktech reads credentials and the in-memory GPG key from the
# ORG_GRADLE_PROJECT_* env vars below, populated from the Actions secrets that
# scripts/setup-maven-central-credential.sh provisions.
- name: Publish to Maven Central
working-directory: packages/android/ContentfulOptimization
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_SIGNING_PASSWORD }}
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}
run: |
./gradlew publishAndReleaseToMavenCentral \
-Pcontentful.optimization.version="$RELEASE_VERSION" \
--no-configuration-cache --no-daemon --stacktrace
19 changes: 17 additions & 2 deletions packages/android/ContentfulOptimization/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ polyfill bindings (URLSession/OkHttp/timers/UUID), and public API surface.

## Commands

- Gradle build commands require Android SDK. Use `./gradlew build` from this directory.
- Gradle build commands require Android SDK. Use `./gradlew build` from this directory (the module
ships its own pinned wrapper, 8.10.2, and pins its plugin versions in `settings.gradle.kts`, so it
builds standalone — not only inside the demo's composite build).
- Run `pnpm --filter @contentful/optimization-js-bridge build` to rebuild the JS bridge bundle
before Gradle build.
before Gradle build. `buildJsBridge` (wired into `preBuild`) also does this automatically.

## Releasing

- Published to Maven Central (Sonatype Central Portal) as `com.contentful.java:optimization-android`
by `.github/workflows/publish-android.yaml` on each `v*` release, in parallel with the Swift
package. Version comes from the tag (`-Pcontentful.optimization.version` / `RELEASE_VERSION`); the
group reuses Contentful's existing verified namespace `com.contentful.java`.
- Credentials are GitHub Actions secrets on `contentful/optimization`, provisioned and self-verified
by `scripts/setup-maven-central-credential.sh` (Central Portal token + GPG signing key). The
published artifacts are generated; nobody edits them by hand.
- Smoke-test packaging locally with
`./gradlew publishToMavenLocal -Pcontentful.optimization.version=0.0.0-local` and consume it from
a real app via `mavenLocal()` — this is how the Android demo is verified before a real release.
70 changes: 70 additions & 0 deletions packages/android/ContentfulOptimization/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import com.vanniktech.maven.publish.SonatypeHost

plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
// Version inline so the module builds when included as a subproject (parent builds don't pin it).
id("com.vanniktech.maven.publish") version "0.30.0"
}

// Published coordinate: com.contentful.java:optimization-android. We reuse Contentful's existing,
// already-verified Maven Central namespace (com.contentful.java) rather than registering a new one.
// The Android package namespace stays com.contentful.optimization (group != package, no conflict).
// Version flows from the release tag in CI (-Pcontentful.optimization.version / RELEASE_VERSION),
// matching the npm and SPM release versions cut from the same tag.
group = "com.contentful.java"
version = (project.findProperty("contentful.optimization.version") as String?)
?: System.getenv("RELEASE_VERSION")
?: "0.0.0-SNAPSHOT"

android {
namespace = "com.contentful.optimization"
compileSdk = 36
Expand Down Expand Up @@ -70,6 +85,61 @@ val buildJsBridge = tasks.register<Exec>("buildJsBridge") {
}
tasks.named("preBuild").configure { dependsOn(buildJsBridge) }

// Maven Central publishing via the Sonatype Central Portal. The vanniktech plugin configures the
// AGP single-variant ("release") publication, including sources and javadoc jars, so we do NOT add
// an android { publishing { singleVariant(...) } } block ourselves (that would double-configure it).
mavenPublishing {
configure(
AndroidSingleVariantLibrary(
variant = "release",
sourcesJar = true,
publishJavadocJar = true,
)
)

publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)

// Sign with the in-memory GPG key supplied by CI (ORG_GRADLE_PROJECT_signingInMemoryKey*).
// Skipped automatically when no key is configured (e.g. local publishToMavenLocal smoke tests),
// so the artifact can be assembled and consumed locally without GPG.
if (project.findProperty("signingInMemoryKey") != null) {
signAllPublications()
}

coordinates("com.contentful.java", "optimization-android", version.toString())

pom {
name.set("Contentful Optimization Android SDK")
description.set(
"Native Android (Kotlin) SDK for the Contentful Optimization product: " +
"personalization, audience qualification, view/click tracking, and preview overrides."
)
inceptionYear.set("2026")
url.set("https://github.com/contentful/optimization")
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
distribution.set("repo")
}
}
developers {
developer {
id.set("contentful")
name.set("Contentful")
url.set("https://github.com/contentful")
organization.set("Contentful")
organizationUrl.set("https://www.contentful.com/")
}
}
scm {
url.set("https://github.com/contentful/optimization")
connection.set("scm:git:git://github.com/contentful/optimization.git")
developerConnection.set("scm:git:ssh://git@github.com/contentful/optimization.git")
}
}
}

dependencies {
implementation("io.github.dokar3:quickjs-kt:1.0.5")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading