diff --git a/.gitignore b/.gitignore
index c2e4dd6..e749f84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -106,3 +106,8 @@ build-log.txt
# Generated files for local development (configured via local.properties)
sample/src/main/res/raw/debug_ca.crt
sample/src/main/res/xml/network_security_config.xml
+
+# Release artifacts (temporary files created by release script)
+device-sdk-*.aar
+device-sdk-*.jar
+*.asc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c4d7fd..338386f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,5 @@
# Changelog
-## 0.1.0 (unreleased)
+## 0.1.0 (2026-01-09)
- Initial release
diff --git a/CLAUDE.md b/CLAUDE.md
index d72a99d..e17d0bd 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -87,9 +87,11 @@ If a commit fails due to formatting, run `precious tidy -g` and retry.
### Publishing
+See `README.dev.md` for the full release process. For manual publishing:
+
```bash
-# Publish to Maven Central (requires credentials in local.properties)
-./gradlew :device-sdk:publishReleasePublicationToMavenCentralRepository
+# Publish to Maven Central via Central Portal (requires credentials in local.properties)
+./gradlew :device-sdk:publishAndReleaseToMavenCentral
```
## Architecture
@@ -298,21 +300,16 @@ Access in build files: `libs.ktor.client.core`, `libs.plugins.kotlin.android`
## Maven Publishing Configuration
-Located in `device-sdk/build.gradle.kts`:
-
-- POM metadata from `gradle.properties` (POM_NAME, POM_URL, etc.)
-- Signing configuration placeholders in `gradle.properties`
-- Actual credentials should be in `local.properties` (gitignored)
+Publishing uses the
+[Vanniktech Maven Publish](https://github.com/vanniktech/gradle-maven-publish-plugin)
+plugin configured in `device-sdk/build.gradle.kts`. The plugin publishes to
+Maven Central via Central Portal with automatic release.
-**Required for publishing:**
+Credentials are read from `~/.m2/settings.xml` (server id `central`) to share
+credentials with other MaxMind Maven projects. GPG signing uses the system `gpg`
+command, so existing `~/.gnupg` configuration is used automatically.
-```properties
-signing.keyId=...
-signing.password=...
-signing.secretKeyRingFile=...
-mavenCentralUsername=...
-mavenCentralPassword=...
-```
+See `README.dev.md` for the full release process and credential setup.
## Module Structure
diff --git a/README.dev.md b/README.dev.md
new file mode 100644
index 0000000..19f2017
--- /dev/null
+++ b/README.dev.md
@@ -0,0 +1,149 @@
+# Preparing your environment for a release
+
+- Ensure you have access to publish to the repository on
+ [Central Portal](https://central.sonatype.com/).
+ - See the section about Central Portal access.
+- You need a GPG secret key. You need to publish it as well.
+ - See the section about setting up GPG.
+- Ensure the SSH key you use on GitHub.com is available.
+ - e.g., `~/.ssh/id_rsa`.
+- Ensure an appropriate `~/.gitconfig` is set up.
+ - The release process generates commits.
+- Ensure you have the necessary dependencies available:
+ - Java 17+ (Android Studio JDK recommended)
+ - Android SDK with API 34+
+- Ensure [gh](https://github.com/cli/cli) is set up and in your `PATH`.
+
+## Setting up Central Portal access
+
+To get this access, first create a Central Portal account at
+[central.sonatype.com](https://central.sonatype.com/).
+
+You will need access to the `com.maxmind` namespace. Contact MaxMind operations
+to request access to the namespace.
+
+### Configuring credentials
+
+This project reads credentials from Maven's `~/.m2/settings.xml`, allowing you
+to share credentials with other MaxMind Java/Maven projects.
+
+Configure your `~/.m2/settings.xml` for Central Portal. See
+[these instructions](https://central.sonatype.org/publish/publish-portal-maven/#credentials):
+
+```xml
+
+
+
+ central
+
+
+
+
+
+```
+
+GPG signing uses the system `gpg` command (same as Maven projects), so your
+existing `~/.gnupg` configuration will be used automatically.
+
+**Alternative: local.properties**
+
+You can also configure credentials in `local.properties` (takes precedence over
+settings.xml):
+
+```properties
+mavenCentralUsername=
+mavenCentralPassword=
+```
+
+**For CI/CD**, set environment variables:
+
+- `ORG_GRADLE_PROJECT_mavenCentralUsername`
+- `ORG_GRADLE_PROJECT_mavenCentralPassword`
+
+Some links about Central Portal:
+
+- [Maven Central Repository homepage](https://central.sonatype.com/)
+- [Publishing guide](https://central.sonatype.org/publish/publish-portal-maven/)
+
+## Setting up GPG
+
+You need a key. It is fine to create/use your own, but you'll probably want one
+with your MaxMind email address.
+
+If you need to generate a key: `gpg --gen-key`.
+
+If you have one and need to export/import it:
+
+ gpg --export-secret-keys --armor > secretkey.gpg
+ gpg --import secretkey.gpg
+ gpg --edit-key
+
+and enter `trust` and choose ultimate.
+
+Make sure the key shows up in `gpg --list-secret-keys`.
+
+Make sure you publish it to a keyserver. See
+[here](http://central.sonatype.org/pages/working-with-pgp-signatures.html) for
+more info.
+
+### gpg "inappropriate ioctl" errors
+
+Add this to `~/.gnupg/gpg.conf`:
+
+ use-agent
+ pinentry-mode loopback
+
+Add this to `~/.gnupg/gpg-agent.conf`:
+
+ allow-loopback-pinentry
+
+# Releasing
+
+## Steps
+
+1. Ensure you can run `./gradlew :device-sdk:test` and
+ `./gradlew :device-sdk:assemble` successfully. Run `./gradlew clean` after.
+
+2. Create a release branch off `main`. Ensure you have a clean checkout.
+ - We'll be generating commits.
+ - When the release is complete, deliver the release PR for review.
+
+3. Review open issues and PRs to see if any can easily be fixed, closed, or
+ merged.
+
+4. Review `CHANGELOG.md` for completeness and correctness.
+
+5. Set a version and a date in `CHANGELOG.md` and commit that.
+ - The format must be: `## X.Y.Z (YYYY-MM-DD)` (markdown heading).
+ - It gets used in the release process.
+
+6. Bump copyright year in `README.md` if appropriate.
+ - You don't need to update the version. `./dev-bin/release.sh` does this.
+
+7. Run `./dev-bin/release.sh`
+ - This will run tests, update versions, publish to Maven Central, and create
+ a GitHub release.
+
+8. This will prompt you several times. Generally you need to say `y` or `n`.
+
+9. You may be prompted about dependency updates. Review them and decide if you
+ want to update before releasing.
+
+10. If you get HTTP 401 errors from Central Portal, check your credentials in
+ `~/.m2/settings.xml` (or `local.properties`).
+
+11. After completion, a release will be on GitHub and Maven Central.
+
+12. Create a PR to merge the release branch back to main.
+
+## Updating dependencies
+
+Review the versions from the dependency update check. If you want to update:
+
+1. Make a branch
+2. Update versions in `gradle/libs.versions.toml`
+3. Run `./gradlew :device-sdk:test` and fix any errors
+4. Push and ensure CI completes successfully
+5. Merge
+
+If you did this in the middle of releasing, start the release process over.
diff --git a/README.md b/README.md
index 58f458f..75f5f0d 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Add the dependency to your app's `build.gradle.kts`:
```kotlin
dependencies {
- implementation("com.maxmind.device:device-sdk:0.1.0-SNAPSHOT")
+ implementation("com.maxmind.device:device-sdk:0.1.0")
}
```
@@ -24,7 +24,7 @@ dependencies {
```groovy
dependencies {
- implementation 'com.maxmind.device:device-sdk:0.1.0-SNAPSHOT'
+ implementation 'com.maxmind.device:device-sdk:0.1.0'
}
```
diff --git a/build.gradle.kts b/build.gradle.kts
index f63e1b5..c1f449a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,11 +6,13 @@ plugins {
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.dokka) apply false
alias(libs.plugins.detekt) apply false
+ alias(libs.plugins.maven.publish) apply false
+ alias(libs.plugins.dependency.updates)
}
allprojects {
group = "com.maxmind.device"
- version = "0.1.0-SNAPSHOT"
+ version = "0.1.0"
}
tasks.register("clean", Delete::class) {
diff --git a/dev-bin/release.sh b/dev-bin/release.sh
new file mode 100755
index 0000000..62902cd
--- /dev/null
+++ b/dev-bin/release.sh
@@ -0,0 +1,243 @@
+#!/bin/bash
+
+set -eu -o pipefail
+
+# Pre-flight checks - verify all required tools are available and configured
+# before making any changes to the repository
+
+check_command() {
+ if ! command -v "$1" &>/dev/null; then
+ echo "Error: $1 is not installed or not in PATH"
+ exit 1
+ fi
+}
+
+# Verify gh CLI is authenticated
+if ! gh auth status &>/dev/null; then
+ echo "Error: gh CLI is not authenticated. Run 'gh auth login' first."
+ exit 1
+fi
+
+# Verify we can access this repository via gh
+if ! gh repo view --json name &>/dev/null; then
+ echo "Error: Cannot access repository via gh. Check your authentication and repository access."
+ exit 1
+fi
+
+# Verify git can connect to the remote (catches SSH key issues, etc.)
+if ! git ls-remote origin &>/dev/null; then
+ echo "Error: Cannot connect to git remote. Check your git credentials/SSH keys."
+ exit 1
+fi
+
+check_command perl
+check_command ./gradlew
+check_command mise
+
+# Ensure mise is activated and all tools from mise.toml are installed
+if ! mise current &>/dev/null; then
+ echo "Error: mise is not activated in your shell."
+ echo "Run 'eval \"\$(mise activate bash)\"' or add it to your shell config."
+ exit 1
+fi
+mise install --quiet
+
+# Check that we're not on the main branch
+current_branch=$(git branch --show-current)
+if [ "$current_branch" = "main" ]; then
+ echo "Error: Releases should not be done directly on the main branch."
+ echo "Please create a release branch and run this script from there."
+ exit 1
+fi
+
+# Fetch latest changes and check that we're not behind origin/main
+echo "Fetching from origin..."
+git fetch origin
+
+if ! git merge-base --is-ancestor origin/main HEAD; then
+ echo "Error: Current branch is behind origin/main."
+ echo "Please merge or rebase with origin/main before releasing."
+ exit 1
+fi
+
+changelog=$(cat CHANGELOG.md)
+
+# Parse changelog in format: ## X.Y.Z (YYYY-MM-DD)
+regex='## ([0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9\-]*) \(([0-9]{4}-[0-9]{2}-[0-9]{2})\)
+
+((.|
+)*)'
+
+if [[ ! $changelog =~ $regex ]]; then
+ echo "Could not find date line in change log!"
+ echo "Expected format: ## X.Y.Z (YYYY-MM-DD)"
+ exit 1
+fi
+
+version="${BASH_REMATCH[1]}"
+date="${BASH_REMATCH[2]}"
+# Extract notes until the next version header or end of file
+notes="$(echo "${BASH_REMATCH[3]}" | sed -n -e '/^## [0-9]\+\.[0-9]\+\.[0-9]\+/,$!p')"
+
+if [[ "$date" != "$(date +"%Y-%m-%d")" ]]; then
+ echo "$date is not today!"
+ exit 1
+fi
+
+tag="v$version"
+
+if [ -n "$(git status --porcelain)" ]; then
+ echo ". is not clean." >&2
+ exit 1
+fi
+
+# Check for dependency updates
+# Note: --no-parallel is required for Gradle 9+ compatibility
+echo ""
+echo "Checking for dependency updates..."
+./gradlew dependencyUpdates -Drevision=release --no-parallel --no-daemon
+
+read -r -n 1 -p "Continue given above dependencies? (y/n) " should_continue
+echo ""
+
+if [ "$should_continue" != "y" ]; then
+ echo "Aborting"
+ exit 1
+fi
+
+# Run tests
+echo ""
+echo "Running tests..."
+./gradlew :device-sdk:test --no-daemon
+
+read -r -n 1 -p "Continue given above tests? (y/n) " should_continue
+echo ""
+
+if [ "$should_continue" != "y" ]; then
+ echo "Aborting"
+ exit 1
+fi
+
+# Update version in build.gradle.kts
+echo ""
+echo "Updating version to $version..."
+perl -pi -e "s/version = \"[^\"]+\"/version = \"$version\"/" build.gradle.kts
+
+# Update version in README.md
+perl -pi -e "s/com\.maxmind\.device:device-sdk:[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9\-]*/com.maxmind.device:device-sdk:$version/g" README.md
+
+git diff
+
+read -r -n 1 -p "Commit changes? (y/n) " should_commit
+echo ""
+if [ "$should_commit" != "y" ]; then
+ echo "Aborting"
+ exit 1
+fi
+
+git add build.gradle.kts README.md
+git commit -m "Preparing for $version"
+
+# Build and publish to Maven Central
+echo ""
+echo "Building and publishing to Maven Central..."
+
+# Read credentials from ~/.m2/settings.xml and export as env vars
+# (Gradle properties from settings.xml don't work with Vanniktech plugin)
+if [ -z "${ORG_GRADLE_PROJECT_mavenCentralUsername:-}" ]; then
+ settings_xml="$HOME/.m2/settings.xml"
+ if [ -f "$settings_xml" ]; then
+ # Extract username and password for server id "central" using yq
+ # The [.settings.servers.server] | flatten handles both single server (object) and multiple servers (array)
+ maven_username=$(yq -p xml -oy '[.settings.servers.server] | flatten | .[] | select(.id == "central") | .username' "$settings_xml" 2>/dev/null)
+ maven_password=$(yq -p xml -oy '[.settings.servers.server] | flatten | .[] | select(.id == "central") | .password' "$settings_xml" 2>/dev/null)
+
+ if [ -n "$maven_username" ] && [ -n "$maven_password" ]; then
+ export ORG_GRADLE_PROJECT_mavenCentralUsername="$maven_username"
+ export ORG_GRADLE_PROJECT_mavenCentralPassword="$maven_password"
+ echo "Using Maven Central credentials from ~/.m2/settings.xml"
+ else
+ echo "Error: Maven Central credentials not found in ~/.m2/settings.xml (server id 'central')"
+ exit 1
+ fi
+ else
+ echo "Error: ~/.m2/settings.xml not found and ORG_GRADLE_PROJECT_mavenCentralUsername not set"
+ exit 1
+ fi
+fi
+
+./gradlew :device-sdk:publishAndReleaseToMavenCentral --no-daemon
+
+echo ""
+echo "Release notes for $version:"
+echo ""
+echo "$notes"
+echo ""
+
+read -r -n 1 -p "Push to origin? (y/n) " should_push
+echo ""
+
+if [ "$should_push" != "y" ]; then
+ echo "Aborting"
+ exit 1
+fi
+
+git push
+
+# Create GitHub release with artifacts
+echo ""
+echo "Creating GitHub release..."
+
+# Build outputs location
+build_dir="device-sdk/build/outputs/aar"
+
+# Clean up any previous release artifacts
+rm -f device-sdk-*.aar device-sdk-*.jar device-sdk-*.asc 2>/dev/null || true
+
+# Collect release artifacts
+release_files=()
+
+# AAR file
+aar_file="$build_dir/device-sdk-release.aar"
+if [ -f "$aar_file" ]; then
+ cp "$aar_file" "device-sdk-$version.aar"
+ release_files+=("device-sdk-$version.aar")
+fi
+
+# Sources JAR (from publications)
+sources_jar=$(find device-sdk/build -name "*-sources.jar" -type f 2>/dev/null | head -1)
+if [ -n "$sources_jar" ]; then
+ cp "$sources_jar" "device-sdk-$version-sources.jar"
+ release_files+=("device-sdk-$version-sources.jar")
+fi
+
+# Javadoc JAR
+javadoc_jar=$(find device-sdk/build -name "*-javadoc.jar" -type f 2>/dev/null | head -1)
+if [ -n "$javadoc_jar" ]; then
+ cp "$javadoc_jar" "device-sdk-$version-javadoc.jar"
+ release_files+=("device-sdk-$version-javadoc.jar")
+fi
+
+# Sign artifacts for GitHub release
+for file in "${release_files[@]}"; do
+ gpg --armor --detach-sign "$file"
+done
+
+# Add signature files to release
+for file in "${release_files[@]}"; do
+ if [ -f "$file.asc" ]; then
+ release_files+=("$file.asc")
+ fi
+done
+
+gh release create --target "$(git branch --show-current)" -t "$version" -n "$notes" "$tag" "${release_files[@]}"
+
+# Cleanup temporary files
+for file in device-sdk-*.aar device-sdk-*.jar device-sdk-*.asc; do
+ rm -f "$file" 2>/dev/null || true
+done
+
+echo ""
+echo "Release $version complete!"
+echo "GitHub release: https://github.com/maxmind/device-android/releases/tag/$tag"
+echo "Maven Central: https://central.sonatype.com/artifact/com.maxmind.device/device-sdk/$version"
diff --git a/device-sdk/build.gradle.kts b/device-sdk/build.gradle.kts
index 65d0291..7c73861 100644
--- a/device-sdk/build.gradle.kts
+++ b/device-sdk/build.gradle.kts
@@ -5,8 +5,8 @@ plugins {
alias(libs.plugins.dokka)
alias(libs.plugins.detekt)
alias(libs.plugins.ktlint)
- id("maven-publish")
- id("signing")
+ alias(libs.plugins.maven.publish)
+ signing
id("tech.apter.junit5.jupiter.robolectric-extension-gradle-plugin") version "0.9.0"
}
@@ -46,17 +46,6 @@ android {
targetCompatibility = JavaVersion.VERSION_17
}
- kotlinOptions {
- jvmTarget = "17"
-
- // Enable explicit API mode for better library API design
- freeCompilerArgs +=
- listOf(
- "-Xexplicit-api=strict",
- "-opt-in=kotlin.RequiresOptIn",
- )
- }
-
buildFeatures {
buildConfig = true
}
@@ -66,12 +55,17 @@ android {
isIncludeAndroidResources = true
}
}
+}
- publishing {
- singleVariant("release") {
- withSourcesJar()
- withJavadocJar()
- }
+kotlin {
+ compilerOptions {
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
+
+ // Enable explicit API mode for better library API design
+ freeCompilerArgs.addAll(
+ "-Xexplicit-api=strict",
+ "-opt-in=kotlin.RequiresOptIn",
+ )
}
}
@@ -113,81 +107,64 @@ detekt {
buildUponDefaultConfig = true
}
-// Dokka configuration
-tasks.dokkaHtml.configure {
- outputDirectory.set(layout.buildDirectory.dir("dokka"))
+// Dokka configuration (V2 API)
+dokka {
+ dokkaPublications.html {
+ outputDirectory.set(layout.buildDirectory.dir("dokka"))
+ }
}
-// Maven publishing configuration
-publishing {
- publications {
- create("release") {
- groupId = project.group.toString()
- artifactId = "device-sdk"
- version = project.version.toString()
+// Maven Central publishing configuration (using Vanniktech plugin)
+// Only configure Maven Central upload when real credentials are available.
+// Credentials come from env vars (set by release.sh from ~/.m2/settings.xml)
+// or can be provided directly via ORG_GRADLE_PROJECT_mavenCentralUsername
+val mavenCentralUsername = providers.gradleProperty("mavenCentralUsername").orNull ?: ""
+val hasMavenCentralCredentials = mavenCentralUsername.isNotEmpty()
- afterEvaluate {
- from(components["release"])
+mavenPublishing {
+ if (hasMavenCentralCredentials) {
+ publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)
+ }
+ signAllPublications()
+
+ coordinates(
+ groupId = "com.maxmind.device",
+ artifactId = "device-sdk",
+ version = project.version.toString(),
+ )
+
+ pom {
+ name.set("MaxMind Device SDK")
+ description.set("Android SDK for collecting and reporting device data to MaxMind")
+ inceptionYear.set("2025")
+ url.set("https://github.com/maxmind/device-android")
+
+ licenses {
+ license {
+ name.set("Apache License, Version 2.0")
+ url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
+ distribution.set("repo")
}
+ }
- pom {
- name.set(findProperty("POM_NAME")?.toString() ?: "MaxMind Device SDK")
- description.set(
- findProperty("POM_DESCRIPTION")?.toString()
- ?: "Android SDK for collecting and reporting device data to MaxMind",
- )
- url.set(findProperty("POM_URL")?.toString() ?: "")
- inceptionYear.set(findProperty("POM_INCEPTION_YEAR")?.toString() ?: "2025")
-
- licenses {
- license {
- name.set(findProperty("POM_LICENSE_NAME")?.toString() ?: "")
- url.set(findProperty("POM_LICENSE_URL")?.toString() ?: "")
- distribution.set(findProperty("POM_LICENSE_DIST")?.toString() ?: "")
- }
- }
-
- developers {
- developer {
- id.set(findProperty("POM_DEVELOPER_ID")?.toString() ?: "")
- name.set(findProperty("POM_DEVELOPER_NAME")?.toString() ?: "")
- url.set(findProperty("POM_DEVELOPER_URL")?.toString() ?: "")
- }
- }
-
- scm {
- url.set(findProperty("POM_SCM_URL")?.toString() ?: "")
- connection.set(findProperty("POM_SCM_CONNECTION")?.toString() ?: "")
- developerConnection.set(
- findProperty("POM_SCM_DEV_CONNECTION")?.toString() ?: "",
- )
- }
+ developers {
+ developer {
+ id.set("maxmind")
+ name.set("MaxMind, Inc.")
+ url.set("https://www.maxmind.com/")
}
}
- }
- repositories {
- maven {
- name = "mavenCentral"
- url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
- credentials {
- username = findProperty("mavenCentralUsername")?.toString() ?: ""
- password = findProperty("mavenCentralPassword")?.toString() ?: ""
- }
+ scm {
+ url.set("https://github.com/maxmind/device-android")
+ connection.set("scm:git:git://github.com/maxmind/device-android.git")
+ developerConnection.set("scm:git:ssh://git@github.com/maxmind/device-android.git")
}
}
}
-// Signing configuration
+// Configure signing to use GPG command (like Maven) instead of in-memory keys
+// This respects ~/.gnupg configuration and gpg-agent
signing {
- if (findProperty("signing.keyId") != null) {
- sign(publishing.publications["release"])
- }
-}
-
-// Task to generate Javadoc from Dokka
-tasks.register("javadocJar") {
- dependsOn(tasks.dokkaHtml)
- archiveClassifier.set("javadoc")
- from(tasks.dokkaHtml.get().outputDirectory)
+ useGpgCmd()
}
diff --git a/gradle.properties b/gradle.properties
index a419b64..debd080 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -24,27 +24,24 @@ android.defaults.buildfeatures.renderscript=false
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
-# Maven Publishing Configuration (set these in local.properties or CI environment)
-# signing.keyId=
-# signing.password=
-# signing.secretKeyRingFile=
-# mavenCentralUsername=
-# mavenCentralPassword=
-
-# Library metadata
-POM_NAME=MaxMind Device SDK
-POM_DESCRIPTION=Android SDK for collecting and reporting device data to MaxMind
-POM_INCEPTION_YEAR=2025
-POM_URL=https://github.com/maxmind/device-android
-
-POM_LICENSE_NAME=Apache License, Version 2.0
-POM_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
-POM_LICENSE_DIST=repo
-
-POM_SCM_URL=https://github.com/maxmind/device-android
-POM_SCM_CONNECTION=scm:git:git://github.com/maxmind/device-android.git
-POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/maxmind/device-android.git
-
-POM_DEVELOPER_ID=maxmind
-POM_DEVELOPER_NAME=MaxMind, Inc.
-POM_DEVELOPER_URL=https://www.maxmind.com/
+# Maven Central Publishing Configuration (Central Portal)
+#
+# Credentials are read from ~/.m2/settings.xml (server id "central")
+# to share credentials with other MaxMind Maven projects.
+#
+# GPG signing uses the system gpg command (~/.gnupg configuration).
+#
+# Alternatively, set in local.properties (takes precedence):
+# mavenCentralUsername=
+# mavenCentralPassword=
+#
+# For CI/CD, set environment variables:
+# ORG_GRADLE_PROJECT_mavenCentralUsername
+# ORG_GRADLE_PROJECT_mavenCentralPassword
+#
+# See README.dev.md for full setup instructions.
+
+# Empty placeholders for Vanniktech maven-publish plugin (prevents build service errors)
+# Real credentials are provided via env vars set by release.sh from ~/.m2/settings.xml
+mavenCentralUsername=
+mavenCentralPassword=
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a34882e..017210f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -39,6 +39,10 @@ turbine = "1.2.1"
# Material (for sample app)
material = "1.12.0"
+# Publishing
+mavenPublish = "0.30.0"
+dependencyUpdates = "0.53.0"
+
[libraries]
# Kotlin
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
@@ -87,6 +91,8 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
+maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
+dependency-updates = { id = "com.github.ben-manes.versions", version.ref = "dependencyUpdates" }
[bundles]
ktor = ["ktor-client-core", "ktor-client-android", "ktor-client-content-negotiation", "ktor-serialization-kotlinx-json", "ktor-client-logging"]
diff --git a/mise.toml b/mise.toml
new file mode 100644
index 0000000..641898a
--- /dev/null
+++ b/mise.toml
@@ -0,0 +1,5 @@
+[tools]
+# Java 21 is required - Dokka doesn't support Java 25+ yet
+java = "temurin-21"
+# yq is used by release.sh to parse ~/.m2/settings.xml for Maven Central credentials
+yq = "latest"
diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts
index 34d531c..f5b08dc 100644
--- a/sample/build.gradle.kts
+++ b/sample/build.gradle.kts
@@ -62,16 +62,18 @@ android {
targetCompatibility = JavaVersion.VERSION_17
}
- kotlinOptions {
- jvmTarget = "17"
- }
-
buildFeatures {
viewBinding = true
buildConfig = true
}
}
+kotlin {
+ compilerOptions {
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
+ }
+}
+
dependencies {
// SDK module
implementation(project(":device-sdk"))