diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..02ecbd1a45 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,27 @@ +on: + workflow_dispatch + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + cache: 'gradle' + - name: Change wrapper permission + run: chmod +x ./gradlew + + - name: Publish Maven + run: ./.github/workflows/scripts/publish-maven.sh + env: + NEXUS_USERNAME: ${{ secrets.SONATYPE_OSSRH_USERNAME }} + NEXUS_PASSWORD: ${{ secrets.SONATYPE_OSSRH_PASSWORD }} + SIGNING_PRIVATE_KEY: ${{ secrets.PGP_PRIVATE_KEY }} + SIGNING_PASSWORD: ${{ secrets.PGP_PASSPHRASE }} diff --git a/.github/workflows/scripts/publish-maven.sh b/.github/workflows/scripts/publish-maven.sh new file mode 100755 index 0000000000..ec06a0cca5 --- /dev/null +++ b/.github/workflows/scripts/publish-maven.sh @@ -0,0 +1,9 @@ +set -x + +branch=$(git rev-parse --abbrev-ref HEAD) + +if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then + ./gradlew :core:publishToSonatype closeAndReleaseSonatypeStagingRepository -PremoveSnapshotSuffix +else + ./gradlew :core:publishToSonatype +fi diff --git a/Jenkinsfile b/Jenkinsfile index 09a700c2e0..0a4af585f6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -76,7 +76,6 @@ pipeline { // Do not deploy on PR builds expression { env.CHANGE_ID == null } anyOf { - expression { env.GIT_BRANCH == "master" } expression { env.GIT_BRANCH == "develop" } expression { env.GIT_BRANCH ==~ /[0-9]+\.[0-9]+\.[0-9]+-rc/ } } @@ -85,13 +84,10 @@ pipeline { environment { NEXUS_USERNAME = credentials('android-sonatype-nexus-username') NEXUS_PASSWORD = credentials('android-sonatype-nexus-password') - GPG_KEY_ID = credentials('android-sdk-signing-public-key-id') - GPG_PASSPHRASE = credentials('android-sdk-signing-private-key-password') - GPG_KEY_LOCATION = credentials('android-sdk-signing-private-key-ring-file') } steps { echo 'Deploy to Sonatype nexus' - sh './gradlew :core:publish' + sh './gradlew :core:publishToSonatype' } } } diff --git a/build.gradle.kts b/build.gradle.kts index 9477128f76..f79d71db1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,16 @@ import org.jlleitschuh.gradle.ktlint.reporter.ReporterType +group = "org.hisp.dhis" +version = libs.versions.dhis2AndroidSdkVersion.get() + +/** + * Property from the Gradle command line. To remove the snapshot suffix from the version. + */ +if (project.hasProperty("removeSnapshotSuffix")) { + val mainVersion = (version as String).split("-SNAPSHOT")[0] + version = mainVersion +} + buildscript { repositories { google() @@ -18,6 +29,7 @@ buildscript { plugins { alias(libs.plugins.sonarqube) alias(libs.plugins.dokka) + alias(libs.plugins.nexus.publish) } sonarqube { @@ -58,9 +70,6 @@ subprojects { apply(plugin = "org.jlleitschuh.gradle.ktlint") apply(plugin = "org.jetbrains.dokka") - //group = GROUP - //version = VERSION_NAME - configure { version.set("0.50.0") android.set(true) @@ -70,4 +79,16 @@ subprojects { reporter(ReporterType.CHECKSTYLE) } } +} + +val nexusUsername: String? = System.getenv("NEXUS_USERNAME") +val nexusPassword: String? = System.getenv("NEXUS_PASSWORD") + +nexusPublishing { + this.repositories { + sonatype { + username.set(nexusUsername) + password.set(nexusPassword) + } + } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Props.kt b/buildSrc/src/main/kotlin/Props.kt index fe472edfc6..7e47175ec2 100644 --- a/buildSrc/src/main/kotlin/Props.kt +++ b/buildSrc/src/main/kotlin/Props.kt @@ -27,18 +27,17 @@ */ object Props { - val GROUP = "org.hisp.dhis" - val POM_NAME = "Core" - val POM_ARTIFACT_ID = "android-core" - val POM_PACKAGING = "aar" - val POM_DESCRIPTION = "Android SDK for DHIS 2." - val POM_URL = "https://github.com/dhis2/dhis2-android-sdk" - val POM_SCM_URL = "https://github.com/dhis2/dhis2-android-sdk" - val POM_SCM_CONNECTION = "scm:git:git://github.com/dhis2/dhis2-android-sdk.git" - val POM_SCM_DEV_CONNECTION = "scm:git:ssh://git@github.com/dhis2/dhis2-android-sdk.git" - val POM_LICENCE_NAME = "BSD" - val POM_LICENCE_URL = "https://opensource.org/licenses/BSD-3-Clause" - val POM_LICENCE_DIST = "repo" - val POM_DEVELOPER_ID = "DHIS 2" - val POM_DEVELOPER_NAME = "DHIS 2" + const val POM_NAME = "Core" + const val POM_ARTIFACT_ID = "android-core" + const val POM_PACKAGING = "aar" + const val POM_DESCRIPTION = "Android SDK for DHIS 2." + const val POM_URL = "https://github.com/dhis2/dhis2-android-sdk" + const val POM_SCM_URL = "https://github.com/dhis2/dhis2-android-sdk" + const val POM_SCM_CONNECTION = "scm:git:git://github.com/dhis2/dhis2-android-sdk.git" + const val POM_SCM_DEV_CONNECTION = "scm:git:ssh://git@github.com/dhis2/dhis2-android-sdk.git" + const val POM_LICENCE_NAME = "BSD" + const val POM_LICENCE_URL = "https://opensource.org/licenses/BSD-3-Clause" + const val POM_LICENCE_DIST = "repo" + const val POM_DEVELOPER_ID = "DHIS 2" + const val POM_DEVELOPER_NAME = "DHIS 2" } diff --git a/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts b/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts index 3180cb0905..aeaa3f77e6 100644 --- a/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts @@ -26,50 +26,17 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import java.net.URI - plugins { `maven-publish` signing } -val VERSION_NAME: String by project - fun isReleaseBuild(): Boolean { - return !VERSION_NAME.contains("SNAPSHOT") -} - -val releaseRepositoryUrl: String = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - -val snapshotRepositoryUrl: String = "https://oss.sonatype.org/content/repositories/snapshots/" - -fun getRepositoryUsername(): String? { - return System.getenv("NEXUS_USERNAME") + return !version.toString().endsWith("-SNAPSHOT") } -fun getRepositoryPassword(): String? { - return System.getenv("NEXUS_PASSWORD") -} - -fun gpgKeyId(): String? { - return System.getenv("GPG_KEY_ID") -} - -fun gpgKeyLocation(): String? { - return System.getenv("GPG_KEY_LOCATION") -} - -fun gpgPassphrase(): String? { - return System.getenv("GPG_PASSPHRASE") -} - -gradle.taskGraph.whenReady(closureOf { - if (gradle.taskGraph.allTasks.any { it is Sign }) { - allprojects { ext["signing.keyId"] = gpgKeyId() } - allprojects { ext["signing.secretKeyRingFile"] = gpgKeyLocation() } - allprojects { ext["signing.password"] = gpgPassphrase() } - } -}) +val signingPrivateKey: String? = System.getenv("SIGNING_PRIVATE_KEY") +val signingPassword: String? = System.getenv("SIGNING_PASSWORD") val androidJavadocsJar = tasks.register("androidJavadocsJar", Jar::class) { archiveClassifier.set("javadoc") @@ -85,9 +52,7 @@ afterEvaluate { from(components["release"]) artifact(androidJavadocsJar) - groupId = Props.GROUP artifactId = Props.POM_ARTIFACT_ID - version = VERSION_NAME pom { name = Props.POM_NAME @@ -114,22 +79,12 @@ afterEvaluate { } } } - - repositories { - maven { - url = if (isReleaseBuild()) URI(releaseRepositoryUrl) else URI(snapshotRepositoryUrl) - - credentials { - username = getRepositoryUsername() - password = getRepositoryPassword() - } - } - } } + } - signing { - setRequired({ isReleaseBuild() && gradle.taskGraph.hasTask("publishing") }) - sign(publishing.publications) - } + signing { + setRequired({ isReleaseBuild() && gradle.taskGraph.hasTask("publishing") }) + useInMemoryPgpKeys(signingPrivateKey, signingPassword) + sign(publishing.publications) } -} \ No newline at end of file +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5628fd60b4..6fc0b7e1ba 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -44,8 +44,8 @@ repositories { maven(url = "https://oss.sonatype.org/content/repositories/snapshots") } -val VERSION_CODE: String by project -val VERSION_NAME: String by project +group = rootProject.group +version = rootProject.version android { compileSdk = libs.versions.targetSdkVersion.get().toInt() @@ -57,8 +57,8 @@ android { multiDexEnabled = true vectorDrawables.useSupportLibrary = true - buildConfigField("long", "VERSION_CODE", VERSION_CODE) - buildConfigField("String", "VERSION_NAME", "\"${VERSION_NAME}\"") + buildConfigField("long", "VERSION_CODE", libs.versions.dhis2AndroidSdkCode.get()) + buildConfigField("String", "VERSION_NAME", "\"$version\"") } compileOptions { diff --git a/core/gradle.properties b/core/gradle.properties deleted file mode 100644 index 4231d960fb..0000000000 --- a/core/gradle.properties +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2016, University of Oslo -# -# All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# Neither the name of the HISP project nor the names of its contributors may -# be used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -# Properties which are consumed by plugins/gradle-mvn-push.gradle plugin. -# They are used for publishing artifact to snapshot repository. - -VERSION_NAME=1.10.1-SNAPSHOT -VERSION_CODE=293 diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt index ee3073f305..83ad09e136 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt @@ -185,7 +185,7 @@ internal abstract class IdentifiableDataHandlerImpl( relatives: RelationshipItemRelatives?, ) - protected fun beforeCollectionHandled( + protected open fun beforeCollectionHandled( oCollection: Collection, params: IdentifiableDataHandlerParams, ): Collection { diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventHandler.kt index 39e8e207f1..0f014418bc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventHandler.kt @@ -40,6 +40,8 @@ import org.hisp.dhis.android.core.note.Note import org.hisp.dhis.android.core.note.internal.NoteDHISVersionManager import org.hisp.dhis.android.core.note.internal.NoteHandler import org.hisp.dhis.android.core.note.internal.NoteUniquenessManager +import org.hisp.dhis.android.core.program.ProgramType +import org.hisp.dhis.android.core.program.internal.ProgramStore import org.hisp.dhis.android.core.relationship.internal.EventRelationshipOrphanCleaner import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager import org.hisp.dhis.android.core.relationship.internal.RelationshipHandler @@ -53,6 +55,7 @@ internal class EventHandler( relationshipVersionManager: RelationshipDHISVersionManager, relationshipHandler: RelationshipHandler, eventStore: EventStore, + private val programStore: ProgramStore, private val trackedEntityDataValueHandler: TrackedEntityDataValueHandler, private val noteHandler: NoteHandler, private val noteVersionManager: NoteDHISVersionManager, @@ -60,11 +63,25 @@ internal class EventHandler( private val relationshipOrphanCleaner: EventRelationshipOrphanCleaner, ) : IdentifiableDataHandlerImpl(eventStore, relationshipVersionManager, relationshipHandler) { + override fun beforeCollectionHandled( + oCollection: Collection, + params: IdentifiableDataHandlerParams, + ): Collection { + val programTypes = programStore.selectAll().associate { it.uid() to it.programType() } + + val updatedEvents = oCollection.map { + it.takeUnless { programTypes[it.program()] == ProgramType.WITHOUT_REGISTRATION } + ?: it.toBuilder().enrollment(null).build() + } + + return super.beforeCollectionHandled(updatedEvents, params) + } + override fun beforeObjectHandled(o: Event, params: IdentifiableDataHandlerParams): Event { return if (GeometryHelper.isValid(o.geometry())) { o } else { - Log.i(this.javaClass.simpleName, "Event " + o.uid() + " has invalid geometry value") + Log.i(this::class.simpleName, "Event ${o.uid()} has an invalid geometry value") o.toBuilder().geometry(null).build() } } diff --git a/core/src/sharedTest/resources/event/new_tracker_importer_events.json b/core/src/sharedTest/resources/event/new_tracker_importer_events.json index 8f0c0dccbe..ea755b4054 100644 --- a/core/src/sharedTest/resources/event/new_tracker_importer_events.json +++ b/core/src/sharedTest/resources/event/new_tracker_importer_events.json @@ -10,6 +10,7 @@ "program": "lxAQ7Zs9VYR", "event": "single1", "status": "COMPLETED", + "enrollment": "u9mt5Z8assp", "occurredAt": "2018-02-27T00:00:00.000", "createdAt": "2017-08-07T15:47:25.959", "completedAt": "2016-02-27T00:00:00.000", @@ -112,6 +113,7 @@ "program": "lxAQ7Zs9VYR", "event": "single2", "status": "ACTIVE", + "enrollment": "u9mt5Z8assp", "occurredAt": "2017-02-27T00:00:00.000", "createdAt": "2018-08-07T15:47:25.959", "completedAt": "2018-02-27T00:00:00.000", diff --git a/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventHandlerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventHandlerShould.kt index ab677fbae6..d81357bcea 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventHandlerShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventHandlerShould.kt @@ -37,6 +37,7 @@ import org.hisp.dhis.android.core.note.Note import org.hisp.dhis.android.core.note.internal.NoteDHISVersionManager import org.hisp.dhis.android.core.note.internal.NoteHandler import org.hisp.dhis.android.core.note.internal.NoteUniquenessManager +import org.hisp.dhis.android.core.program.internal.ProgramStore import org.hisp.dhis.android.core.relationship.internal.EventRelationshipOrphanCleaner import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager import org.hisp.dhis.android.core.relationship.internal.RelationshipHandler @@ -51,6 +52,7 @@ import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class EventHandlerShould { private val eventStore: EventStore = mock() + private val programStore: ProgramStore = mock() private val trackedEntityDataValueHandler: TrackedEntityDataValueHandler = mock() private val trackedEntityDataValue: TrackedEntityDataValue = mock() private val noteHandler: NoteHandler = mock() @@ -84,6 +86,7 @@ class EventHandlerShould { relationshipVersionManager, relationshipHandler, eventStore, + programStore, trackedEntityDataValueHandler, noteHandler, noteVersionManager, diff --git a/docs/content/developer/getting-started.md b/docs/content/developer/getting-started.md index 51aa430148..271f91bae7 100644 --- a/docs/content/developer/getting-started.md +++ b/docs/content/developer/getting-started.md @@ -6,7 +6,7 @@ Include dependency in build.gradle. ```gradle dependencies { - implementation "org.hisp.dhis:android-core:1.10.0" + implementation "org.hisp.dhis:android-core:1.10.0.1" ... } ``` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f1ebb1edc..2cecef8339 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,14 @@ [versions] +dhis2AndroidSdkVersion = "1.10.1-SNAPSHOT" +dhis2AndroidSdkCode = "294" + gradle = "8.3.1" kotlin = "1.9.21" ktlint = "11.5.1" sonarqube = "3.3" detekt = "1.18.0" dokka = "1.9.20" +nexusPublish = "1.3.0" targetSdkVersion = "34" minSdkVersion = "21" @@ -125,5 +129,6 @@ facebook-soloader = { group = "com.facebook.soloader", name = "soloader", versio sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } +nexus-publish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexusPublish"} [bundles]