diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml new file mode 100644 index 0000000..5500ebb --- /dev/null +++ b/.github/workflows/deploy-snapshot.yml @@ -0,0 +1,35 @@ +name: Deploy snapshot +on: + push: + branches: + - master +jobs: + publish: + runs-on: ubuntu-latest + if: ${{ !contains(github.event.head_commit.message, 'Prepare for release') }} + steps: + - uses: actions/checkout@v2 + + - name: Cache Gradle Files + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches/ + ~/.gradle/wrapper/ + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Set up Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Build + run: ./gradlew build + + - name: Publish package + run: ./gradlew publishAllPublicationsToSonatypeSnapshotRepository + env: + SONATYPE_USER: ${{ secrets.SONATYPE_USER }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/.github/workflows/publish-maven-central.yml b/.github/workflows/publish-maven-central.yml new file mode 100644 index 0000000..ee388b6 --- /dev/null +++ b/.github/workflows/publish-maven-central.yml @@ -0,0 +1,27 @@ +name: Publish package to the Maven Central Repository +on: + release: + types: [published] + branches: + - master +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Build + run: ./gradlew build + + - name: Publish package + run: ./gradlew publishAllPublicationsToMavenCentralRepository + env: + SONATYPE_USER: ${{ secrets.SONATYPE_USER }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SHAKY_ANDROID_GPG_PRIVATE_KEY: ${{ secrets.SHAKY_ANDROID_GPG_PRIVATE_KEY }} + SHAKY_ANDROID_GPG_PRIVATE_KEY_PASSWORD: ${{ secrets.SHAKY_ANDROID_GPG_PRIVATE_KEY_PASSWORD }} diff --git a/README.md b/README.md index 704a246..c84cac1 100644 --- a/README.md +++ b/README.md @@ -80,3 +80,19 @@ to collect extra app data including device logs, user data, etc. You will also n If you want to programmatically trigger the feedback collection flow, rather than listening for shake events, you can call `Shaky#startFeedbackFlow()` on the object returned by `Shaky.with()`. See the demo app for a full example of how to do this. + +## Snapshots + +You can use snapshot builds to test the latest unreleased changes. A new snapshot is published +after every merge to the main branch by the [Deploy Snapshot Github Action workflow](.github/workflows/deploy-snapshot.yml). + +Just add the Sonatype snapshot repository to your Gradle scripts: +```gradle +repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } +} +``` + +You can find the latest snapshot version to use in the [gradle.properties](gradle.properties) file. diff --git a/RELEASING.md b/RELEASING.md index 35d2385..018446c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,21 +1,24 @@ -Releasing -======== +# Releasing - 1. Change the version in `gradle.properties` to a non-SNAPSHOT version. - 2. Update the `CHANGELOG.md` for the impending release. - 3. Update the `README.md` with the new version. - 4. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version) - 5. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version) - 6. `./gradlew clean distributeBuild` - 7. Update the `gradle.properties` to the next SNAPSHOT version. - 8. `git commit -am "Prepare next development version."` - 9. `git push && git push --tags` +1. Change the version in `gradle.properties` to a non-SNAPSHOT version. +2. Update the `CHANGELOG.md` for the impending release. +3. Update the `README.md` with the new version. +4. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version) +5. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version) +6. Update the `gradle.properties` to the next SNAPSHOT version. +7. `git commit -am "Prepare next development version."` +8. `git push && git push --tags` +9. Create a new release in the releases tab on GitHub +10. Wait for the [publish-maven-central.yml](.github/workflows/publish-maven-central.yml) action to complete. +11. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact. +## How it works -Prerequisites -------------- +The [deploy-snapshot.yml](.github/workflows/deploy-snapshot.yml) workflow runs on every +push to the main branch as long as the commit message does not contain `Prepare for release`. This +workflow calls Gradle to publish to the Sonatype snapshot repository. -Set the following environment variables: - - * `ARTIFACTORY_USER` - LinkedIn artifactory username for releasing to `com.linkedin.shaky`. - * `ARTIFACTORY_KEY` - LinkedIn artifactory API key for releasing to `com.linkedin.shaky`. +For actual releases, there is a separate [publish-maven-central.yml](.github/workflows/publish-maven-central.yml) +workflow which runs after a new release is created in the GitHub UI. This will call Gradle on the +tagged release commit and upload to the staging repository. After that completes, you will need to +go and promote the artifacts to production. diff --git a/build.gradle b/build.gradle index 65db881..f06c2b0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,12 @@ -import com.linkedin.gradle.DistributeTask - buildscript { repositories { google() + mavenCentral() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' - classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.8.1" + classpath 'com.android.tools.build:gradle:4.1.2' } } @@ -19,10 +15,12 @@ subprojects { repositories { google() + mavenCentral() jcenter() } - tasks.withType(Javadoc).all { enabled = false } + group = GROUP_ID + version = VERSION_NAME } ext { @@ -31,24 +29,3 @@ ext { minSdkVersion = 21 targetSdkVersion = 27 } - -apply plugin: 'com.jfrog.artifactory' - -artifactory { - contextUrl = 'https://linkedin.jfrog.io/linkedin' - publish { - repoKey = 'shaky-android' - username = System.getenv('ARTIFACTORY_USER') ?: '' - password = System.getenv('ARTIFACTORY_KEY') ?: '' - - defaults { - publications ('aar') - publishArtifacts = true - } - } - clientConfig.setIncludeEnvVars(false) -} - -task distributeBuild(type: DistributeTask) { - dependsOn ':artifactoryPublish', 'shaky:artifactoryPublish' -} \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index 7bf7fe2..0000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -plugins { - id 'groovy' -} - -// Our dependencies are pulling in older version of guava, -// but agp3 requires guava 23. So, we force it here -configurations.runtimeClasspath { - resolutionStrategy { - force 'com.google.guava:guava:23.0' - } -} - -repositories { - mavenLocal() - jcenter() -} - -/* - maven plug-in transitively brings in guava 18.0 which creates errors in build due to missing method. - We will have to force the version to at least 22 to resolve it. - */ -configurations.runtimeClasspath { - resolutionStrategy.eachDependency { DependencyResolveDetails details -> - if (details.requested.group == 'com.google.guava' && details.requested.name == 'guava') { - details.useVersion '22.0' - } - } -} - -dependencies { - implementation gradleApi() - implementation localGroovy() - - implementation 'org.ajoberstar:gradle-git:1.2.0' - implementation group: 'org.apache.httpcomponents', name: 'fluent-hc', version: '4.5.2' - implementation ('org.jfrog.buildinfo:build-info-extractor-gradle:4.8.1') { - exclude module: 'groovy-all' - } -} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/com/linkedin/gradle/DistributeTask.groovy b/buildSrc/src/main/groovy/com/linkedin/gradle/DistributeTask.groovy deleted file mode 100644 index 17b17e6..0000000 --- a/buildSrc/src/main/groovy/com/linkedin/gradle/DistributeTask.groovy +++ /dev/null @@ -1,39 +0,0 @@ -package com.linkedin.gradle - -import groovy.json.JsonBuilder -import org.apache.http.client.fluent.Request -import org.apache.http.entity.ContentType -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction -import org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention - -class DistributeTask extends DefaultTask { - - @TaskAction - public void distributeBuild() { - ArtifactoryPluginConvention convention = project.convention.plugins.artifactory - def buildNumber = convention.clientConfig.info.buildNumber - def buildName = convention.clientConfig.info.buildName - def context = convention.clientConfig.publisher.contextUrl - def password = convention.clientConfig.publisher.password - - def body = [ - "publish" : "true", - "overrideExistingFiles": "false", - "async" : "true", - "targetRepo" : "maven", - "sourceRepos" : ["shaky-android"], - "dryRun" : "false" - ] - - def bodyString = new JsonBuilder(body).toString() - - def content = Request.Post("$context/api/build/distribute/$buildName/$buildNumber") - .bodyString(bodyString, ContentType.APPLICATION_JSON) - .addHeader("X-JFrog-Art-Api", password) - .execute() - .returnContent() - - logger.lifecycle("Distribute Response: {}", content.asString()) - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9052e46..3674d77 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,22 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true + GROUP_ID=com.linkedin.shaky -ARTIFACT=shaky VERSION_NAME=3.0.2-SNAPSHOT -LIBRARY_NAME=Shaky -LIBRARY_DESCRIPTION=Shake-to-send-feedback library for Android -android.useAndroidX=true diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle new file mode 100644 index 0000000..7562bae --- /dev/null +++ b/gradle/publishing.gradle @@ -0,0 +1,91 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' + +// Ideally AGP should provide sources and javadoc integration for their components: +// https://issuetracker.google.com/issues/145670440 +tasks.register("sourcesJar", Jar) { + classifier 'sources' + from android.sourceSets.main.java.srcDirs +} + +tasks.register("javadoc", Javadoc) { + failOnError false + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +tasks.register("javadocJar", Jar) { + dependsOn javadoc + classifier 'javadoc' + from javadoc.destinationDir +} + +// AGP creates the components in afterEvaluate, so we need to use it too +// https://developer.android.com/studio/build/maven-publish-plugin +afterEvaluate { + publishing { + publications { + maven(MavenPublication) { + from components.release + + artifact sourcesJar + artifact javadocJar + + pom { + name = 'Shaky' + description = 'Shake-to-send-feedback library for Android' + url = 'https://github.com/linkedin/shaky-android' + licenses { + license { + name = 'The Apache Software License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = 'com.linkedin' + name = 'LinkedIn Corp' + } + } + scm { + connection = 'scm:git:git://github.com/linkedin/shaky-android.git' + developerConnection = 'scm:git:ssh://github.com:linkedin/shaky-android.git' + url = 'https://github.com/linkedin/shaky-android/tree/master' + } + } + + repositories { + def sonatypeUsername = System.getenv("SONATYPE_USER") + def sonatypePassword = System.getenv("SONATYPE_PASSWORD") + maven { + name = "sonatypeSnapshot" + url = "https://oss.sonatype.org/content/repositories/snapshots" + credentials { + username = sonatypeUsername + password = sonatypePassword + } + } + maven { + name = "mavenCentral" + url = "https://oss.sonatype.org/service/local/staging/deploy/maven2" + credentials { + username = sonatypeUsername + password = sonatypePassword + } + } + } + } + } + } + + // SHAKY_ANDROID_GPG_PRIVATE_KEY should contain the armoured private key that + // starts with -----BEGIN PGP PRIVATE KEY BLOCK----- + // It can be obtained with gpg --armour --export-secret-keys KEY_ID + def signingKey = System.getenv("SHAKY_ANDROID_GPG_PRIVATE_KEY") + def signingPassword = System.getenv("SHAKY_ANDROID_GPG_PRIVATE_KEY_PASSWORD") + signing { + required { signingKey != null && signingPassword != null } + useInMemoryPgpKeys(signingKey, signingPassword) + sign publishing.publications.maven + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d3b8398..29953ea 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d96bc38..2a56324 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sat Nov 10 11:59:22 PST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/gradlew b/gradlew index 27309d9..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f6d5974..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -49,7 +49,6 @@ goto fail @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/shaky/build.gradle b/shaky/build.gradle index fdb6666..21d3133 100644 --- a/shaky/build.gradle +++ b/shaky/build.gradle @@ -1,8 +1,5 @@ -apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.android.library' -apply plugin: 'maven-publish' - -group = GROUP_ID +apply from: "$rootDir/gradle/publishing.gradle" android { compileSdkVersion rootProject.ext.compileSdkVersion @@ -23,10 +20,6 @@ android { lintOptions { abortOnError true - - // Lint check is catching false positives. Fixed in AGP 3.4 - // https://issuetracker.google.com/issues/118518625 - disable 'WrongThread' } resourcePrefix 'shaky_' @@ -45,80 +38,3 @@ dependencies { testImplementation 'org.mockito:mockito-core:2.19.0' testImplementation 'org.robolectric:robolectric:3.7.1' } - -version = VERSION_NAME - -task sourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.srcDirs -} - -task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -publishing { - publications { - aar(MavenPublication) { - - artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") { - builtBy assemble - } - artifact sourcesJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - - description = LIBRARY_DESCRIPTION - url 'https://github.com/linkedin/shaky-android' - scm { - url 'https://github.com/linkedin/shaky-android' - connection 'scm:git:git://github.com/linkedin/shaky-android.git' - developerConnection 'https://github.com/linkedin/shaky-android.git' - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/license/LICENSE-2.0.txt' - distribution 'repo' - } - } - - developers { - developer { - id 'com.linkedin' - name 'LinkedIn Corp.' - email '' - } - } - - dependencies { - configurations.implementation.allDependencies.each { ModuleDependency dp -> - dependency { - resolveStrategy = Closure.DELEGATE_ONLY - groupId dp.group - artifactId dp.name - version dp.version - } - } - } - } - } - - //For local debugging of maven publications run './gradlew publish' and check 'build/repo' - repositories { - maven { - url = "$rootProject.buildDir/repo" - } - } - } - } -} \ No newline at end of file