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

Commit 56dd15e

Browse files
authored
Add support for Android libraries
1 parent 04fc95c commit 56dd15e

File tree

14 files changed

+356
-18
lines changed

14 files changed

+356
-18
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ gradle-app.setting
1717

1818
.idea
1919
*.iml
20-
local.properties
20+
local.properties

.travis.yml

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ branches:
1414
except:
1515
- /^v\d/
1616

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

2126
before_script:
2227
- _JAVA_OPTIONS=

build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ allprojects {
3131
repositories {
3232
jcenter()
3333
maven { url "https://plugins.gradle.org/m2/" }
34+
maven { url 'https://maven.google.com' }
3435
}
3536

3637
tasks.withType(Test) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
### Android libraries support
2+
3+
Android Gradle Plugin `3.6.0-beta05` or newer is required.
4+
5+
Configuration specific to Android library projects (using `com.android.library` plugins):
6+
7+
1. Apply `org.shipkit.android-publish` plugin to each Gradle project (submodule) you want to publish
8+
(usually they are not the root projects).
9+
1. Specify `artifactId` in `androidPublish` blocks.
10+
11+
Example:
12+
13+
```Gradle
14+
apply plugin: 'org.shipkit.bintray'
15+
apply plugin: 'org.shipkit.android-publish'
16+
apply plugin: 'com.android.library'
17+
18+
androidPublish {
19+
artifactId = 'shipkit-android'
20+
}
21+
22+
```
23+
24+
Other POM properties which can be set using Gradle API:
25+
* group id - [Project#group](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:group)
26+
* name - [Project#archivesBaseName](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:archivesBaseName)
27+
* description - [Project#description](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:description)

docs/how-shipkit-works.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ How do we:
2626
- [publish binaries](/docs/features/publishing-binaries.md)
2727
- [publishing binaries using maven-publish plugin](/docs/features/publishing-binaries-using-maven-publish-plugin.md)
2828
- [avoid unnecessary releases](/docs/gradle-plugins/release-needed-plugin.md)
29+
- [support Android libraries](/docs/gradle-plugins/android-publish-plugin.md)
2930
- [shipping Javadoc](/docs/features/shipping-javadoc.md)
3031
- [automatically include contributors in pom.xml](/docs/features/celebrating-contributors.md)
3132

@@ -41,8 +42,8 @@ script:
4142
- ./gradlew build -s && ./gradlew ciPerformRelease -s
4243
```
4344
44-
Those lines means the releasing process is two-stage.
45-
First the `build` Gradle task is executed.
45+
Those lines means the releasing process is two-stage.
46+
First the `build` Gradle task is executed.
4647
Shipkit doesn't change there a lot.
4748
More interesting is the second task: `ciPerformRelease`.
4849
This task depends on 3 another tasks: `releaseNeeded`, `ciReleasePrepare` and `performRelease`.
@@ -145,7 +146,7 @@ Text used to create this diagram: https://gist.github.com/mstachniuk/b7cfd3bef9f
145146
| | Info is release needed or not | |------------------------------------| | |
146147
| |<-------------------------------------------| | |
147148
| | | | |
148-
149+
149150
```
150151

151152

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.shipkit.gradle.configuration;
2+
3+
import org.gradle.api.GradleException;
4+
5+
public class AndroidPublishConfiguration {
6+
7+
private String artifactId;
8+
9+
/**
10+
* Artifact id of published AAR
11+
* For example: "shipkit-android"
12+
*/
13+
public String getArtifactId() {
14+
if (artifactId == null || artifactId.isEmpty()) {
15+
throw new GradleException("Please configure artifact id");
16+
}
17+
return artifactId;
18+
}
19+
20+
/**
21+
* See {@link #getArtifactId()} ()}
22+
*/
23+
public void setArtifactId(String artifactId) {
24+
this.artifactId = artifactId;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.shipkit.internal.gradle.android;
2+
3+
import com.jfrog.bintray.gradle.BintrayExtension;
4+
5+
import org.gradle.api.GradleException;
6+
import org.gradle.api.Plugin;
7+
import org.gradle.api.Project;
8+
import org.gradle.api.Task;
9+
import org.gradle.api.component.SoftwareComponent;
10+
import org.gradle.api.logging.Logger;
11+
import org.gradle.api.logging.Logging;
12+
import org.gradle.api.publish.maven.MavenPublication;
13+
import org.shipkit.gradle.configuration.AndroidPublishConfiguration;
14+
import org.shipkit.gradle.configuration.ShipkitConfiguration;
15+
import org.shipkit.internal.gradle.configuration.ShipkitConfigurationPlugin;
16+
import org.shipkit.internal.gradle.snapshot.LocalSnapshotPlugin;
17+
import org.shipkit.internal.gradle.util.GradleDSLHelper;
18+
import org.shipkit.internal.gradle.util.PomCustomizer;
19+
20+
import static org.shipkit.internal.gradle.configuration.DeferredConfiguration.deferredConfiguration;
21+
import static org.shipkit.internal.gradle.java.JavaPublishPlugin.MAVEN_LOCAL_TASK;
22+
import static org.shipkit.internal.gradle.java.JavaPublishPlugin.PUBLICATION_NAME;
23+
24+
/**
25+
* Publishing Android libraries using 'maven-publish' plugin.
26+
* Intended to be applied in individual Android library submodule.
27+
* Applies following plugins and tasks and configures them:
28+
*
29+
* <ul>
30+
* <li>maven-publish</li>
31+
* </ul>
32+
*
33+
* Other features:
34+
* <ul>
35+
* <li>Configures Gradle's publications to publish Android library</li>
36+
* <li>Configures 'build' task to depend on 'publishJavaLibraryToMavenLocal'
37+
* to flesh out publication issues during the build</li>
38+
* <li>Configures 'snapshot' task to depend on 'publishJavaLibraryToMavenLocal'</li>
39+
* </ul>
40+
*/
41+
public class AndroidPublishPlugin implements Plugin<Project> {
42+
43+
private final static Logger LOG = Logging.getLogger(AndroidPublishPlugin.class);
44+
private final static String ANDROID_PUBLISH_EXTENSION = "androidPublish";
45+
46+
public void apply(final Project project) {
47+
final AndroidPublishConfiguration androidPublishConfiguration = project.getExtensions().create(ANDROID_PUBLISH_EXTENSION, AndroidPublishConfiguration.class);
48+
49+
final ShipkitConfiguration conf = project.getPlugins().apply(ShipkitConfigurationPlugin.class).getConfiguration();
50+
51+
project.getPlugins().apply(LocalSnapshotPlugin.class);
52+
Task snapshotTask = project.getTasks().getByName(LocalSnapshotPlugin.SNAPSHOT_TASK);
53+
snapshotTask.dependsOn(MAVEN_LOCAL_TASK);
54+
55+
project.getPlugins().apply("maven-publish");
56+
57+
BintrayExtension bintray = project.getExtensions().getByType(BintrayExtension.class);
58+
bintray.setPublications(PUBLICATION_NAME);
59+
60+
project.getPlugins().withId("com.android.library", plugin -> {
61+
deferredConfiguration(project, () -> {
62+
GradleDSLHelper.publications(project, publications -> {
63+
MavenPublication p = publications.create(PUBLICATION_NAME, MavenPublication.class, publication -> {
64+
publication.setArtifactId(androidPublishConfiguration.getArtifactId());
65+
66+
SoftwareComponent releaseComponent = project.getComponents().findByName("release");
67+
if (releaseComponent == null) {
68+
throw new GradleException("'release' component not found in project. " +
69+
"Make sure you are using Android Gradle Plugin 3.6.0-beta05 or newer.");
70+
}
71+
publication.from(releaseComponent);
72+
PomCustomizer.customizePom(project, conf, publication);
73+
});
74+
LOG.info("{} - configured '{}' publication", project.getPath(), p.getArtifactId());
75+
});
76+
});
77+
78+
//so that we flesh out problems with maven publication during the build process
79+
project.getTasks().getByName("build").dependsOn(MAVEN_LOCAL_TASK);
80+
});
81+
}
82+
}

subprojects/shipkit/src/main/groovy/org/shipkit/internal/gradle/java/JavaPublishPlugin.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* Other features:
3030
* <ul>
3131
* <li>Configures Gradle's publications to publish java library</li>
32-
* <li>Configures 'build' taks to depend on 'publishJavaLibraryToMavenLocal'
32+
* <li>Configures 'build' task to depend on 'publishJavaLibraryToMavenLocal'
3333
* to flesh out publication issues during the build</li>
3434
* <li>Configures 'snapshot' task to depend on 'publishJavaLibraryToMavenLocal'</li>
3535
* </ul>

subprojects/shipkit/src/main/groovy/org/shipkit/internal/gradle/util/PomCustomizer.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ public void execute(XmlProvider xml) {
5656
"\n - Contributors read from GitHub: "
5757
+ StringUtil.join(contributorsFromGitHub.toConfigNotation(), ", "));
5858

59-
customizePom(xml.asNode(), conf, archivesBaseName, project.getDescription(), contributorsFromGitHub);
59+
final boolean isAndroidLibrary = project.getPlugins().hasPlugin("com.android.library");
60+
customizePom(xml.asNode(), conf, archivesBaseName, project.getDescription(), contributorsFromGitHub, isAndroidLibrary);
6061
}
6162
});
6263
}
@@ -66,12 +67,14 @@ public void execute(XmlProvider xml) {
6667
*/
6768
static void customizePom(Node root, ShipkitConfiguration conf,
6869
String projectName, String projectDescription,
69-
ProjectContributorsSet contributorsFromGitHub) {
70-
//Assumes project has java plugin applied. Pretty safe assumption
70+
ProjectContributorsSet contributorsFromGitHub,
71+
boolean isAndroidLibrary) {
7172
//TODO: we need to conditionally append nodes because given node may already be on the root (issue 847)
7273
//TODO: all root.appendNode() need to be conditional
7374
root.appendNode("name", projectName);
74-
if (root.getAt(new QName("packaging")).isEmpty()) {
75+
76+
//Android library publication uses aar packaging
77+
if (!isAndroidLibrary && root.getAt(new QName("packaging")).isEmpty()) {
7578
root.appendNode("packaging", "jar");
7679
}
7780

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
implementation-class=org.shipkit.internal.gradle.android.AndroidPublishPlugin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package org.shipkit.gradle
2+
3+
import org.gradle.testkit.runner.BuildResult
4+
import testutil.GradleSpecification
5+
6+
class ShipkitAndroidIntegTest extends GradleSpecification {
7+
8+
void setup() {
9+
settingsFile << "include 'lib'"
10+
newFile('lib/build.gradle') << """
11+
apply plugin: 'org.shipkit.bintray'
12+
apply plugin: 'org.shipkit.android-publish'
13+
androidPublish {
14+
artifactId = 'shipkit-android'
15+
}
16+
17+
apply plugin: 'com.android.library'
18+
android {
19+
compileSdkVersion 29
20+
defaultConfig {
21+
minSdkVersion 29
22+
}
23+
}
24+
"""
25+
26+
newFile("gradle/shipkit.gradle") << """
27+
shipkit {
28+
gitHub.readOnlyAuthToken = "foo"
29+
gitHub.writeAuthToken = "secret"
30+
releaseNotes.file = "CHANGELOG.md"
31+
git.user = "shipkit"
32+
git.email = "[email protected]"
33+
gitHub.repository = "repo"
34+
}
35+
36+
allprojects {
37+
plugins.withId("com.jfrog.bintray") {
38+
bintray {
39+
user = "szczepiq"
40+
key = "secret"
41+
}
42+
}
43+
}
44+
"""
45+
buildFile << """
46+
apply plugin: 'org.shipkit.java'
47+
buildscript {
48+
repositories {
49+
google()
50+
jcenter()
51+
gradlePluginPortal()
52+
}
53+
}
54+
"""
55+
newFile("src/main/AndroidManifest.xml") << """<manifest package="org.shipkit.android"/>"""
56+
}
57+
58+
def "all tasks in dry run (gradle #gradleVersionToTest) (AGP #agpVersionToTest)"() {
59+
/**
60+
* TODO this test is just a starting point we will make it better and create more integration tests
61+
* Stuff that we should do:
62+
* 1. (Most important) Avoid writing too many integration tests. Most code should be covered by unit tests
63+
* (see testing pyramid)
64+
* 2. Push out complexity to base class GradleSpecification
65+
* so that what remains in the test is the essential part of a tested feature
66+
* 3. Add more specific assertions rather than just a list of tasks in dry run mode
67+
* 4. Use sensible defaults so that we don't need to specify all configuration in the test
68+
* 5. Move integration tests to a separate module
69+
* 6. Dependencies are hardcoded between GradleSpecification and build.gradle of release-tools project
70+
*/
71+
given:
72+
gradleVersion = gradleVersionToTest
73+
74+
and:
75+
buildFile << """
76+
buildscript {
77+
dependencies {
78+
classpath 'com.android.tools.build:gradle:$agpVersionToTest'
79+
}
80+
}
81+
"""
82+
83+
expect:
84+
BuildResult result = pass("performRelease", "-m", "-s")
85+
//git push and bintray upload tasks should run as late as possible
86+
def output = skippedTaskPathsGradleBugWorkaround(result.output).join("\n")
87+
output.startsWith(""":bumpVersionFile
88+
:identifyGitBranch
89+
:fetchContributors
90+
:fetchReleaseNotes
91+
:updateReleaseNotes
92+
:gitCommit
93+
:gitTag
94+
:gitPush
95+
:performGitPush
96+
:updateReleaseNotesOnGitHub
97+
:lib:preBuild""")
98+
99+
and:
100+
output.endsWith(""":lib:bintrayUpload
101+
:bintrayPublish
102+
:performRelease""")
103+
104+
where:
105+
gradleVersionToTest << ["5.6.4", "6.0.1"]
106+
and:
107+
agpVersionToTest << ["3.6.0-beta05", "3.6.0-rc01"]
108+
}
109+
110+
def "fails on unsupported dependency versions (gradle #gradleVersionToTest) (AGP #agpVersionToTest)"() {
111+
given:
112+
gradleVersion = gradleVersionToTest
113+
114+
and:
115+
buildFile << """
116+
buildscript {
117+
dependencies {
118+
classpath 'com.android.tools.build:gradle:$agpVersionToTest'
119+
}
120+
}
121+
"""
122+
123+
expect:
124+
BuildResult result = fail("performRelease", "-m", "-s")
125+
result.output.contains("'release' component not found in project. " +
126+
"Make sure you are using Android Gradle Plugin 3.6.0-beta05 or newer.")
127+
128+
where:
129+
gradleVersionToTest << ["5.6.4", "6.0.1"]
130+
and:
131+
agpVersionToTest << ["3.4.0", "3.5.2"]
132+
}
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.shipkit.gradle.configuration
2+
3+
import org.gradle.api.GradleException
4+
import spock.lang.Specification
5+
6+
class AndroidPublishConfigurationTest extends Specification {
7+
8+
def conf = new AndroidPublishConfiguration();
9+
10+
def "throws when artifact id not configured"() {
11+
when:
12+
conf.artifactId
13+
14+
then:
15+
thrown(GradleException)
16+
}
17+
18+
def "stores artifact id"() {
19+
when:
20+
conf.artifactId = "org.shipkit.android"
21+
22+
then:
23+
conf.artifactId == "org.shipkit.android"
24+
}
25+
}

0 commit comments

Comments
 (0)