Skip to content

Commit

Permalink
Version 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
gmegidish committed Aug 31, 2017
1 parent 0b72a04 commit c31707b
Show file tree
Hide file tree
Showing 37 changed files with 229 additions and 918 deletions.
16 changes: 2 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,13 @@ branches:
- master

jdk:
- oraclejdk7
- openjdk7

before_install:
- sudo apt-get update
- sudo apt-get install php5
- curl -sS https://getcomposer.org/installer | php
- sudo mv composer.phar /usr/bin/composer

install:
- composer install --no-interaction

android:
components:
- build-tools-19.0.0
- build-tools-19.1.0
- android-19
- android-25
- build-tools-25.0.2

script:
- bash gradlew uploadArchives
- ./vendor/bin/phpunit tests/gradle-uploader

5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
compile 'org.apache.httpcomponents:httpmime:4.2.5'
compile 'commons-io:commons-io:2.4'
compile 'org.apache.httpcomponents:httpmime:4.2.5'
compile 'org.apache.commons:commons-compress:1.9'
compile 'com.android.tools.build:gradle:1.5.0'
}

apply plugin: 'maven'
Expand Down Expand Up @@ -33,7 +34,7 @@ uploadArchives {

pom.project {
name 'TestFairy Uploader Plugin for Gradle'
description 'Upload signed builds directly to TestFairy platform.'
description 'Upload apps to TestFairy for distribution.'
url 'https://www.testfairy.com'
inceptionYear '2013'

Expand Down
9 changes: 0 additions & 9 deletions composer.json

This file was deleted.

Binary file added example/TestApplication/random.keystore
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class TestFairyExtension {

private String apiKey
private String video = "on"
private String videoQuality = "high"
private String videoQuality = "medium"
private String videoRate = "1.0"
private String testersGroups
private String maxDuration
Expand Down
247 changes: 15 additions & 232 deletions src/main/groovy/com/testfairy/plugins/gradle/TestFairyPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import org.apache.commons.io.FilenameUtils
import org.apache.commons.compress.archivers.zip.*
import groovy.json.JsonSlurper

import com.android.build.gradle.AppExtension
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.api.ApplicationVariant

class TestFairyPlugin implements Plugin<Project> {

private String apiKey
Expand All @@ -27,240 +31,19 @@ class TestFairyPlugin implements Plugin<Project> {
// create an extension where the apiKey and such settings reside
def extension = project.extensions.create("testfairyConfig", TestFairyExtension, project)

project.configure(project) {
if (it.hasProperty("android")) {

tasks.whenTaskAdded { task ->

project.("android").applicationVariants.all { variant ->

// locate packageRelease and packageDebug tasks
def expectingTask = "package${variant.name.capitalize()}".toString()
if (expectingTask.equals(task.name)) {

def variantName = variant.name

// create new task with name such as testfairyRelease and testfairyDebug
def newTaskName = "testfairy${variantName.capitalize()}"

project.task(newTaskName) << {

assertValidApiKey(extension)

String apiKey = extension.getApiKey()
String serverEndpoint = extension.getServerEndpoint()

// use outputFile from packageApp task
String apkFilename = task.outputFile.toString()
project.logger.info("Instrumenting ${apkFilename} using apiKey ${apiKey} and server ${serverEndpoint}")

def tempDir = task.temporaryDir.getAbsolutePath()
project.logger.debug("Saving temporary files to ${tempDir}")

String proguardMappingFilename = null
if (isMinifyEnabledCompat(variant.buildType) && extension.uploadProguardMapping) {
// proguard-mapping.txt upload is enabled

proguardMappingFilename = getMappingFileCompat(variant)
project.logger.debug("Using proguard mapping file at ${proguardMappingFilename}")
}

def json = uploadApk(project, extension, apkFilename, proguardMappingFilename)

println ""
println "Successfully uploaded to TestFairy, build is available at:"
println json.build_url
}

project.(newTaskName.toString()).dependsOn(expectingTask)
project.(newTaskName.toString()).group = "TestFairy"
project.(newTaskName.toString()).description = "Uploads the ${variantName.capitalize()} build to TestFairy"
}
}
}
if (project.plugins.hasPlugin(AppPlugin)) {

AppExtension android = project.android
android.applicationVariants.all { ApplicationVariant variant ->
TestFairyUploadTask task = project.tasks.create("testfairy${variant.name.capitalize()}", TestFairyUploadTask)
task.group = "TestFairy"
task.description = "Upload '${variant.name}' to TestFairy"
task.applicationVariant = variant
task.extension = extension
task.outputs.upToDateWhen { false }
task.dependsOn variant.assemble
}
}
}

/**
* Make sure ApiKey is configured and not empty.
*
* @param extension
*/
private void assertValidApiKey(extension) {
if (extension.getApiKey() == null || extension.getApiKey().equals("")) {
throw new GradleException("Please configure your TestFairy apiKey before building")
}
}

/**
* Returns true if code minification is enabled for this build type.
* Added to work around runProguard property being renamed to isMinifyEnabled in Android Gradle Plugin 0.14.0
*
* @param buildType
* @return boolean
*/
private boolean isMinifyEnabledCompat(buildType) {
if (buildType.respondsTo("isMinifyEnabled")) {
return buildType.isMinifyEnabled()
} else {
return buildType.runProguard
}
}

private String getMappingFileCompat(variant) {

if (variant.metaClass.respondsTo(variant, "getMappingFile")) {
// getMappingFile was added in Android Plugin 0.13
return variant.getMappingFile().toString()
}

// fallback to getProcessResources
File f = new File(variant.processResources.proguardOutputFile.parent, 'mapping.txt')
if (f.exists()) {
// found as mapping.txt using getProguardOutputFile
return f.absolutePath.toString()
}

f = new File(variant.packageApplication.outputFile.parent)
f = new File(f.parent, "proguard/${variant.name}/mapping.txt")
if (f.exists()) {
// found through getPackageApplication
return f.absolutePath.toString()
}

// any other ways to find mapping file?
return null
}

private DefaultHttpClient buildHttpClient() {
DefaultHttpClient httpClient = new DefaultHttpClient()

// configure proxy (patched by timothy-volvo, https://github.com/timothy-volvo/testfairy-gradle-plugin)
def proxyHost = System.getProperty("http.proxyHost")
if (proxyHost != null) {
def proxyPort = Integer.parseInt(System.getProperty("http.proxyPort"))
HttpHost proxy = new HttpHost(proxyHost, proxyPort)
def proxyUser = System.getProperty("http.proxyUser")
if (proxyUser != null) {
AuthScope authScope = new AuthScope(proxyUser, proxyPort)
Credentials credentials = new UsernamePasswordCredentials(proxyUser, System.getProperty("http.proxyPassword"))
httpClient.getCredentialsProvider().setCredentials(authScope, credentials)
}

httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy)
}

return httpClient
}

private Object post(String url, MultipartEntity entity, String via) {
DefaultHttpClient httpClient = buildHttpClient()
HttpPost post = new HttpPost(url)
String userAgent = "TestFairy Gradle Plugin" + via;
post.addHeader("User-Agent", userAgent)
post.setEntity(entity)
HttpResponse response = httpClient.execute(post)

String json = EntityUtils.toString(response.getEntity())
def parser = new JsonSlurper()
def parsed = parser.parseText(json)
if (!parsed.status.equals("ok")) {
throw new GradleException("Failed with json: " + json)
}

return parsed
}

/**
* Upload an APK using /api/upload REST service.
*
* @param project
* @param extension
* @param apkFilename
* @return Object parsed json
*/
private Object uploadApk(Project project, TestFairyExtension extension, String apkFilename, String mappingFilename) {
String serverEndpoint = extension.getServerEndpoint()
String url = "${serverEndpoint}/api/upload"
MultipartEntity entity = buildEntity(extension, apkFilename, mappingFilename)
String via = ""

if (project.hasProperty("testfairyChangelog")) {
// optional: testfairyChangelog, as passed through -P
String changelog = project.property("testfairyChangelog")
entity.addPart('changelog', new StringBody(changelog))
}

if (project.hasProperty("testfairyUploadedBy")){
via = " via " + project.property("testfairyUploadedBy")
}

// since testfairy gradle plugin 2.0, we no longer support instrumentation
entity.addPart('instrumentation', new StringBody("off"))

// sent to testers groups, as defined
if (extension.getTestersGroups()) {
entity.addPart('testers-groups', new StringBody(extension.getTestersGroups()))
}

// add notify "on" or "off"
entity.addPart('notify', new StringBody(extension.getNotify() ? "on" : "off"))

// add auto-update "on" or "off"
entity.addPart('auto-update', new StringBody(extension.getAutoUpdate() ? "on" : "off"))

return post(url, entity, via)
}

/**
* Build MultipartEntity for API parameters on Upload of an APK
*
* @param extension
* @return MultipartEntity
*/
private MultipartEntity buildEntity(TestFairyExtension extension, String apkFilename, String mappingFilename) {
String apiKey = extension.getApiKey()

MultipartEntity entity = new MultipartEntity()
entity.addPart('api_key', new StringBody(apiKey))
entity.addPart('apk_file', new FileBody(new File(apkFilename)))

if (mappingFilename != null) {
entity.addPart('symbols_file', new FileBody(new File(mappingFilename)))
}

if (extension.getVideo()) {
// if omitted, default value is "on"
entity.addPart('video', new StringBody(extension.getVideo()))
}

if (extension.getVideoQuality()) {
// if omitted, default value is "high"
entity.addPart('video-quality', new StringBody(extension.getVideoQuality()))
}

if (extension.getVideoRate()) {
// if omitted, default is 1 frame per second (videoRate = 1.0)
entity.addPart('video-rate', new StringBody(extension.getVideoRate()))
}

if (extension.getMetrics()) {
// if omitted, by default will record as much as possible
entity.addPart('metrics', new StringBody(extension.getMetrics()))
}

if (extension.getMaxDuration()) {
// override default value
entity.addPart('max-duration', new StringBody(extension.getMaxDuration()))
}

if (extension.getRecordOnBackground()) {
// enable record on background option
entity.addPart('record-on-background', new StringBody("on"))
}

return entity
}
}

Loading

0 comments on commit c31707b

Please sign in to comment.