Skip to content
This repository has been archived by the owner on Jan 18, 2021. It is now read-only.

Commit

Permalink
Add support for Android libraries
Browse files Browse the repository at this point in the history
  • Loading branch information
koral-- authored Jan 18, 2020
1 parent 04fc95c commit 56dd15e
Show file tree
Hide file tree
Showing 14 changed files with 356 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ gradle-app.setting

.idea
*.iml
local.properties
local.properties
9 changes: 7 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ branches:
except:
- /^v\d/

#Below skips the installation step completely (https://docs.travis-ci.com/user/customizing-the-build/#Skipping-the-Installation-Step)
install:
- true
- export ANDROID_HOME=~/android-sdk-linux
- wget -q "https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip" -O android-sdk-tools.zip
- unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
- PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools
- yes | sdkmanager --update
- yes | sdkmanager --licenses
- sdkmanager "tools" "ndk-bundle" "build-tools;29.0.0" "platforms;android-29" > /dev/null

before_script:
- _JAVA_OPTIONS=
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ allprojects {
repositories {
jcenter()
maven { url "https://plugins.gradle.org/m2/" }
maven { url 'https://maven.google.com' }
}

tasks.withType(Test) {
Expand Down
27 changes: 27 additions & 0 deletions docs/gradle-plugins/android-publish-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
### Android libraries support

Android Gradle Plugin `3.6.0-beta05` or newer is required.

Configuration specific to Android library projects (using `com.android.library` plugins):

1. Apply `org.shipkit.android-publish` plugin to each Gradle project (submodule) you want to publish
(usually they are not the root projects).
1. Specify `artifactId` in `androidPublish` blocks.

Example:

```Gradle
apply plugin: 'org.shipkit.bintray'
apply plugin: 'org.shipkit.android-publish'
apply plugin: 'com.android.library'
androidPublish {
artifactId = 'shipkit-android'
}
```

Other POM properties which can be set using Gradle API:
* group id - [Project#group](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:group)
* name - [Project#archivesBaseName](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:archivesBaseName)
* description - [Project#description](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:description)
7 changes: 4 additions & 3 deletions docs/how-shipkit-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ How do we:
- [publish binaries](/docs/features/publishing-binaries.md)
- [publishing binaries using maven-publish plugin](/docs/features/publishing-binaries-using-maven-publish-plugin.md)
- [avoid unnecessary releases](/docs/gradle-plugins/release-needed-plugin.md)
- [support Android libraries](/docs/gradle-plugins/android-publish-plugin.md)
- [shipping Javadoc](/docs/features/shipping-javadoc.md)
- [automatically include contributors in pom.xml](/docs/features/celebrating-contributors.md)

Expand All @@ -41,8 +42,8 @@ script:
- ./gradlew build -s && ./gradlew ciPerformRelease -s
```
Those lines means the releasing process is two-stage.
First the `build` Gradle task is executed.
Those lines means the releasing process is two-stage.
First the `build` Gradle task is executed.
Shipkit doesn't change there a lot.
More interesting is the second task: `ciPerformRelease`.
This task depends on 3 another tasks: `releaseNeeded`, `ciReleasePrepare` and `performRelease`.
Expand Down Expand Up @@ -145,7 +146,7 @@ Text used to create this diagram: https://gist.github.com/mstachniuk/b7cfd3bef9f
| | Info is release needed or not | |------------------------------------| | |
| |<-------------------------------------------| | |
| | | | |
```


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.shipkit.gradle.configuration;

import org.gradle.api.GradleException;

public class AndroidPublishConfiguration {

private String artifactId;

/**
* Artifact id of published AAR
* For example: "shipkit-android"
*/
public String getArtifactId() {
if (artifactId == null || artifactId.isEmpty()) {
throw new GradleException("Please configure artifact id");
}
return artifactId;
}

/**
* See {@link #getArtifactId()} ()}
*/
public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.shipkit.internal.gradle.android;

import com.jfrog.bintray.gradle.BintrayExtension;

import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.component.SoftwareComponent;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.publish.maven.MavenPublication;
import org.shipkit.gradle.configuration.AndroidPublishConfiguration;
import org.shipkit.gradle.configuration.ShipkitConfiguration;
import org.shipkit.internal.gradle.configuration.ShipkitConfigurationPlugin;
import org.shipkit.internal.gradle.snapshot.LocalSnapshotPlugin;
import org.shipkit.internal.gradle.util.GradleDSLHelper;
import org.shipkit.internal.gradle.util.PomCustomizer;

import static org.shipkit.internal.gradle.configuration.DeferredConfiguration.deferredConfiguration;
import static org.shipkit.internal.gradle.java.JavaPublishPlugin.MAVEN_LOCAL_TASK;
import static org.shipkit.internal.gradle.java.JavaPublishPlugin.PUBLICATION_NAME;

/**
* Publishing Android libraries using 'maven-publish' plugin.
* Intended to be applied in individual Android library submodule.
* Applies following plugins and tasks and configures them:
*
* <ul>
* <li>maven-publish</li>
* </ul>
*
* Other features:
* <ul>
* <li>Configures Gradle's publications to publish Android library</li>
* <li>Configures 'build' task to depend on 'publishJavaLibraryToMavenLocal'
* to flesh out publication issues during the build</li>
* <li>Configures 'snapshot' task to depend on 'publishJavaLibraryToMavenLocal'</li>
* </ul>
*/
public class AndroidPublishPlugin implements Plugin<Project> {

private final static Logger LOG = Logging.getLogger(AndroidPublishPlugin.class);
private final static String ANDROID_PUBLISH_EXTENSION = "androidPublish";

public void apply(final Project project) {
final AndroidPublishConfiguration androidPublishConfiguration = project.getExtensions().create(ANDROID_PUBLISH_EXTENSION, AndroidPublishConfiguration.class);

final ShipkitConfiguration conf = project.getPlugins().apply(ShipkitConfigurationPlugin.class).getConfiguration();

project.getPlugins().apply(LocalSnapshotPlugin.class);
Task snapshotTask = project.getTasks().getByName(LocalSnapshotPlugin.SNAPSHOT_TASK);
snapshotTask.dependsOn(MAVEN_LOCAL_TASK);

project.getPlugins().apply("maven-publish");

BintrayExtension bintray = project.getExtensions().getByType(BintrayExtension.class);
bintray.setPublications(PUBLICATION_NAME);

project.getPlugins().withId("com.android.library", plugin -> {
deferredConfiguration(project, () -> {
GradleDSLHelper.publications(project, publications -> {
MavenPublication p = publications.create(PUBLICATION_NAME, MavenPublication.class, publication -> {
publication.setArtifactId(androidPublishConfiguration.getArtifactId());

SoftwareComponent releaseComponent = project.getComponents().findByName("release");
if (releaseComponent == null) {
throw new GradleException("'release' component not found in project. " +
"Make sure you are using Android Gradle Plugin 3.6.0-beta05 or newer.");
}
publication.from(releaseComponent);
PomCustomizer.customizePom(project, conf, publication);
});
LOG.info("{} - configured '{}' publication", project.getPath(), p.getArtifactId());
});
});

//so that we flesh out problems with maven publication during the build process
project.getTasks().getByName("build").dependsOn(MAVEN_LOCAL_TASK);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* Other features:
* <ul>
* <li>Configures Gradle's publications to publish java library</li>
* <li>Configures 'build' taks to depend on 'publishJavaLibraryToMavenLocal'
* <li>Configures 'build' task to depend on 'publishJavaLibraryToMavenLocal'
* to flesh out publication issues during the build</li>
* <li>Configures 'snapshot' task to depend on 'publishJavaLibraryToMavenLocal'</li>
* </ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public void execute(XmlProvider xml) {
"\n - Contributors read from GitHub: "
+ StringUtil.join(contributorsFromGitHub.toConfigNotation(), ", "));

customizePom(xml.asNode(), conf, archivesBaseName, project.getDescription(), contributorsFromGitHub);
final boolean isAndroidLibrary = project.getPlugins().hasPlugin("com.android.library");
customizePom(xml.asNode(), conf, archivesBaseName, project.getDescription(), contributorsFromGitHub, isAndroidLibrary);
}
});
}
Expand All @@ -66,12 +67,14 @@ public void execute(XmlProvider xml) {
*/
static void customizePom(Node root, ShipkitConfiguration conf,
String projectName, String projectDescription,
ProjectContributorsSet contributorsFromGitHub) {
//Assumes project has java plugin applied. Pretty safe assumption
ProjectContributorsSet contributorsFromGitHub,
boolean isAndroidLibrary) {
//TODO: we need to conditionally append nodes because given node may already be on the root (issue 847)
//TODO: all root.appendNode() need to be conditional
root.appendNode("name", projectName);
if (root.getAt(new QName("packaging")).isEmpty()) {

//Android library publication uses aar packaging
if (!isAndroidLibrary && root.getAt(new QName("packaging")).isEmpty()) {
root.appendNode("packaging", "jar");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
implementation-class=org.shipkit.internal.gradle.android.AndroidPublishPlugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.shipkit.gradle

import org.gradle.testkit.runner.BuildResult
import testutil.GradleSpecification

class ShipkitAndroidIntegTest extends GradleSpecification {

void setup() {
settingsFile << "include 'lib'"
newFile('lib/build.gradle') << """
apply plugin: 'org.shipkit.bintray'
apply plugin: 'org.shipkit.android-publish'
androidPublish {
artifactId = 'shipkit-android'
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 29
}
}
"""

newFile("gradle/shipkit.gradle") << """
shipkit {
gitHub.readOnlyAuthToken = "foo"
gitHub.writeAuthToken = "secret"
releaseNotes.file = "CHANGELOG.md"
git.user = "shipkit"
git.email = "[email protected]"
gitHub.repository = "repo"
}
allprojects {
plugins.withId("com.jfrog.bintray") {
bintray {
user = "szczepiq"
key = "secret"
}
}
}
"""
buildFile << """
apply plugin: 'org.shipkit.java'
buildscript {
repositories {
google()
jcenter()
gradlePluginPortal()
}
}
"""
newFile("src/main/AndroidManifest.xml") << """<manifest package="org.shipkit.android"/>"""
}

def "all tasks in dry run (gradle #gradleVersionToTest) (AGP #agpVersionToTest)"() {
/**
* TODO this test is just a starting point we will make it better and create more integration tests
* Stuff that we should do:
* 1. (Most important) Avoid writing too many integration tests. Most code should be covered by unit tests
* (see testing pyramid)
* 2. Push out complexity to base class GradleSpecification
* so that what remains in the test is the essential part of a tested feature
* 3. Add more specific assertions rather than just a list of tasks in dry run mode
* 4. Use sensible defaults so that we don't need to specify all configuration in the test
* 5. Move integration tests to a separate module
* 6. Dependencies are hardcoded between GradleSpecification and build.gradle of release-tools project
*/
given:
gradleVersion = gradleVersionToTest
and:
buildFile << """
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:$agpVersionToTest'
}
}
"""
expect:
BuildResult result = pass("performRelease", "-m", "-s")
//git push and bintray upload tasks should run as late as possible
def output = skippedTaskPathsGradleBugWorkaround(result.output).join("\n")
output.startsWith(""":bumpVersionFile
:identifyGitBranch
:fetchContributors
:fetchReleaseNotes
:updateReleaseNotes
:gitCommit
:gitTag
:gitPush
:performGitPush
:updateReleaseNotesOnGitHub
:lib:preBuild""")
and:
output.endsWith(""":lib:bintrayUpload
:bintrayPublish
:performRelease""")
where:
gradleVersionToTest << ["5.6.4", "6.0.1"]
and:
agpVersionToTest << ["3.6.0-beta05", "3.6.0-rc01"]
}
def "fails on unsupported dependency versions (gradle #gradleVersionToTest) (AGP #agpVersionToTest)"() {
given:
gradleVersion = gradleVersionToTest
and:
buildFile << """
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:$agpVersionToTest'
}
}
"""
expect:
BuildResult result = fail("performRelease", "-m", "-s")
result.output.contains("'release' component not found in project. " +
"Make sure you are using Android Gradle Plugin 3.6.0-beta05 or newer.")
where:
gradleVersionToTest << ["5.6.4", "6.0.1"]
and:
agpVersionToTest << ["3.4.0", "3.5.2"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.shipkit.gradle.configuration

import org.gradle.api.GradleException
import spock.lang.Specification

class AndroidPublishConfigurationTest extends Specification {

def conf = new AndroidPublishConfiguration();

def "throws when artifact id not configured"() {
when:
conf.artifactId

then:
thrown(GradleException)
}

def "stores artifact id"() {
when:
conf.artifactId = "org.shipkit.android"

then:
conf.artifactId == "org.shipkit.android"
}
}
Loading

0 comments on commit 56dd15e

Please sign in to comment.