├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── bintray.gradle ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── install.gradle ├── settings.gradle └── src ├── main ├── groovy │ └── com │ │ └── testfunction │ │ ├── GradleGitDroidPlugin.groovy │ │ ├── extensions │ │ ├── dependency │ │ │ └── GitDependencyExt.groovy │ │ └── project │ │ │ ├── GitProjectExt.groovy │ │ │ ├── GitProjectTestExt.groovy │ │ │ └── GitProjectTestRegexExt.groovy │ │ ├── internal │ │ ├── dependencies │ │ │ ├── DepNeedsBuild.groovy │ │ │ ├── DepResolved.groovy │ │ │ └── DepUnresolved.groovy │ │ ├── enums │ │ │ ├── GitDependencyTypes.groovy │ │ │ └── GitReferenceType.groovy │ │ ├── helpers │ │ │ ├── GitDependenciesHelper.groovy │ │ │ └── GitHelper.groovy │ │ └── utils │ │ │ └── Utils.groovy │ │ └── tasks │ │ ├── CompileEachTask.groovy │ │ ├── GitDependenciesTask.groovy │ │ ├── GitDependencyGetTask.groovy │ │ └── ListDependenciesTask.groovy └── resources │ └── META-INF │ └── gradle-plugins │ └── com.testfunction.gradle-gitdroid.properties └── test └── groovy └── com └── testfunction ├── AndroidLibraryTest.groovy └── TestTypes.groovy /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | env: 4 | global: 5 | - TERM=dumb 6 | - ANDROID_SDK_VERSION="r24.4.1" 7 | 8 | sudo: false 9 | before_install: 10 | - chmod +x ./gradlew 11 | - wget http://dl.google.com/android/android-sdk_${ANDROID_SDK_VERSION}-linux.tgz 12 | - tar -zxf android-sdk_${ANDROID_SDK_VERSION}-linux.tgz 13 | - export ANDROID_HOME=`pwd`/android-sdk-linux 14 | - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools 15 | - echo "sdk.dir=$ANDROID_HOME" > local.properties 16 | - echo yes | android update sdk --filter extra-android-support --no-ui --force > /dev/null 17 | - echo yes | android update sdk --filter extra-android-m2repository --no-ui --force > /dev/null 18 | - echo yes | android update sdk --filter extra-google-m2repository --no-ui --force > /dev/null 19 | - echo yes | android update sdk --filter platform-tools --no-ui --force > /dev/null 20 | - echo yes | android update sdk --all --filter build-tools-23.0.2 --no-ui --force > /dev/null 21 | - echo yes | android update sdk --filter android-23 --no-ui --force > /dev/null 22 | 23 | install: 24 | - TERM=dumb ./gradlew assemble --stacktrace --info 25 | 26 | script: 27 | # - travis_wait ./gradlew check --stacktrace --info 28 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.passing test without missing dep" --stacktrace 29 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.tasks list without missing dep" --stacktrace 30 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.tasks list with resolvable dep" --stacktrace 31 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.failing build with missing dep without git extension" --stacktrace 32 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.failing one build with missing dep with git extension" --stacktrace 33 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.passing build twice with missing dep with git extension" --stacktrace 34 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.passing build twice with missing dep with git extension initGitDependencies" --stacktrace 35 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.fail first build missing dep using git direct call initGitDependencies" --stacktrace 36 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.fail build missing dep using git direct call initGitDependencies wrong commit" --stacktrace 37 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.fail build missing dep using git direct call initGitDependencies branch" --stacktrace 38 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.pass build twice missing dep using git direct call listGitDependencies branch" --stacktrace 39 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.pass build twice missing dep using git direct call listGitDependencies tag" --stacktrace 40 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.pass build twice missing dep using git direct call initGitDependencies branch keepUpdated" --stacktrace 41 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.initGitDependencies dep without git gitConfig" --stacktrace 42 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.passing test dep with git gitConfig" --stacktrace 43 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.passing multi dep with one git" --stacktrace 44 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.passing multi dep with both git" --stacktrace 45 | - ./gradlew cleanTest test --tests "com.testfunction.AndroidLibraryTest.failing test dep non existent" --stacktrace -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Brandon Andrews 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gradle GitDroid (gradle-gitdroid) 2 | 3 | This gradle plugin is designed for allowing Android dependencies using git. This allows you to use any Android library 4 | from a git repo based on commit or branch. 5 | 6 | **This project is still in development** 7 | 8 | ### What this plugin does 9 | * Looks for dependencies that have a git extension 10 | * Clone/pull from public or private repo 11 | * Replace repo project library versions 12 | * Compile git project 13 | * Upload compiled git project and all it's dependencies to local maven repo 14 | 15 | ### Usage 16 | To use this plugin: 17 | ####1. Add to project 18 | **Gradle version < 2.1** 19 | `classpath group: 'com.testfunction', name: 'gradle-gitdroid', version: '0.0.3'` to your root `build.gradle` file 20 | in the buildscript dependencies section. 21 | ```groovy 22 | buildscript { 23 | repositories { 24 | jcenter() 25 | } 26 | dependencies { 27 | classpath 'com.android.tools.build:gradle:2.0.0-alpha3' 28 | classpath group: 'com.testfunction', name: 'gradle-gitdroid', version: '0.0.3' 29 | } 30 | } 31 | ``` 32 | 33 | In your module's `build.gradle` file add `apply plugin: 'gradle-gitdroid'` 34 | 35 | **Gradle version >= 2.1** 36 | In your module's `build.gradle` file add plugin: 37 | ```groovy 38 | plugins { 39 | id "com.testfunction.gradle-gitdroid" version "0.0.3" 40 | } 41 | ``` 42 | 43 | ####2. Configure Gradle GitDroid in your module's `build.gradle` file with the `gitDroid` closure: 44 | 45 | ```groovy 46 | gitDroid { 47 | workingDir = "C:/tmp/${rootProject.name}/gitDroid" 48 | localRepo = getRepositories().mavenLocal().url 49 | automaticResolve = false 50 | } 51 | ``` 52 | 53 | If `automaticResolve` is set to `true` then every time the project is evaluated this plugin will look for git 54 | dependencies and if necessary compile them. 55 | 56 | ####3. Add a git extension to a dependency 57 | ```groovy 58 | compile (group: 'com.testfunction', 59 | name:'lumberjill', 60 | version:'HEAD', 61 | ext: 'aar', 62 | transitive: true 63 | ).ext { 64 | git = [ 65 | repo : 'https://github.com/lodlock/LumberJill.git', 66 | remoteModule: 'lumberjill' 67 | ] 68 | } 69 | ``` 70 | 71 | 72 | ### Gradle GitDroid properties 73 | 74 | #####Valid gitDroid properties: 75 | 76 | Property | Expects | Default | Description 77 | ------------- | ------------- | ------------- | ------------- 78 | automaticResolve | boolean | false | Should Gradle GitDroid automatically handle git dependencies 79 | buildModule | boolean | true | Should run build commands against the supplied module or against base project 80 | deleteSourceOnGitFailure | boolean | true | If git command fails delete the current git source 81 | localRepo | URI | repositories.mavenLocal().url | Location to upload to. **Should be included in dependency repositories** 82 | replaceValues | Map> | null | Search and replace strings in git dependency build.gradle file 83 | replaceValuesRegex | Map | null | Search and replace all matching strings in git dependency build.gradle file 84 | test | test closure | | See gitDroid.test 85 | workingDir | Directory | rootProject + "git-tmp" | The directory to clone/pull contents into to work with 86 | 87 | #####Valid gitDroid.test properties: (Most do not need to be messed with) 88 | 89 | Property | Expects | Default | Description 90 | ------------- | ------------- | ------------- | ------------- 91 | filterConfig | boolean | true | Filter dependencies based on config state 92 | filterDependenciesByExternalModule | boolean | true | Filter dependencies restrict to ExternalModuleDependency 93 | forceRunInit | boolean | false | Force init to run every time. (Slow) 94 | pathLengthDieOnFailure | boolean | true | On Windows machines die out if potential file path length would be too long 95 | regexp | regexp closure | | See gitDroid.test.regexp 96 | useLifecycle | boolean | false | Log archive task to lifecycle instead of debug 97 | 98 | #####Valid gitDroid.test.regexp properties: 99 | 100 | Property | Expects | Default | Description 101 | ------------- | ------------- | ------------- | ------------- 102 | flags | String | "g" | g global, i case insensitive, m multiline, s singleline 103 | byLine | boolean | false | Search one line at a time 104 | encoding | String | "UTF-8" | Encoding to use for search / replace 105 | 106 | #####Full Gradle GitDroid DSL: 107 | ```groovy 108 | gitDroid { 109 | automaticResolve = false 110 | buildModule = true 111 | deleteSourceOnGitFailure = true 112 | localRepo = repositories.mavenLocal().url 113 | //optional replace all android build tools alpha1 with alpha3 114 | replaceValues = [ "com.android.tools.build:gradle:2.0.0-alpha3" : [ 115 | "com.android.tools.build:gradle:2.0.0-alpha1" 116 | ] 117 | ] 118 | //optional replace all android build tool versions with alpha3 119 | replaceValuesRegex = [ "com.android.tools.build:gradle:2.0.0-alpha3" : 120 | "(?<=['|\"])com\\.android\\.tools\\.build:gradle:.+?(?=['|\"])" 121 | ] 122 | test { 123 | filterConfig = true 124 | filterDependenciesByExternalModule = true 125 | forceRunInit = false 126 | pathLengthDieOnFailure = true 127 | regexp { 128 | flags = "g" 129 | byLine = false 130 | encoding = "UTF-8" 131 | } 132 | useLifecycle = false 133 | } 134 | workingDir = "C:/tmp/${rootProject.name}/gitDroid" 135 | } 136 | ``` 137 | 138 | ### Declaring dependencies 139 | Declare dependencies and attach git extension. *It is recommended to include aar extension and setting transitive to true* 140 | 141 | ```groovy 142 | compile (group: 'com.testfunction', 143 | name:'lumberjill', 144 | version:'HEAD', 145 | ext: 'aar', 146 | transitive: true 147 | ).ext { 148 | git = [ 149 | repo : 'https://github.com/lodlock/LumberJill.git', 150 | keepUpdated : true, 151 | remoteModule: 'lumberjill', 152 | keepSource : true 153 | ] 154 | } 155 | ``` 156 | 157 | OR 158 | 159 | ```groovy 160 | compile ('com.testfunction:lumberjill:HEAD@aar') { 161 | transitive = true 162 | }.ext { 163 | git = [ 164 | repo : 'https://github.com/lodlock/LumberJill.git', 165 | keepUpdated : true, 166 | remoteModule: 'lumberjill', 167 | keepSource : true 168 | ] 169 | } 170 | ``` 171 | 172 | *Dependency version number can be anything* 173 | 174 | ### Git properties 175 | **Bold** entries are required 176 | 177 | Property | Expects | Default | Description 178 | ------------- | ------------- | ------------- | ------------- 179 | buildJavaDocs | boolean | true | Include javadoc in local repo 180 | buildSource | boolean | true | Include code source in local repo 181 | credentials | Map | | sshKey or username and password 182 | forceBuild | boolean | false | Used internally to force a build of the dependency 183 | keepSource | boolean | false | Keep the cloned source code after completion 184 | keepUpdated | boolean | false | This dependency should regularly check for newer versions 185 | ref | String | | Commit, Tag, or Branch reference desired 186 | refType | Enum | Automatic | Can be Automatic, Branch, Commit, or Tag 187 | **remoteModule** | String | | The module desired in the project 188 | remoteModules | ArrayList | null | Multiple desired modules from the project 189 | **repo** | String | | The repository to clone/pull from 190 | uploadDependencies | boolean | true | Upload a copy of all of the git project dependencies 191 | 192 | 193 | 194 | Gradle GitDroid dependency git DSL: 195 | 196 | ```groovy 197 | git = [ 198 | buildJavaDocs : true, 199 | buildSource : true, 200 | credentials : [ sshKey:'path/to/ssh/key', username:passedUserName, password:passedPassword ], 201 | forceBuild : false, 202 | keepSource : false, 203 | keepUpdated : false, 204 | ref : 'ab0886261d09c867c012647fbccb9342082c7880', //branch, commit, or tag 205 | refType : 'Automatic', 206 | remoteModule : 'lumberjill', //the directory name of the module desired 207 | remoteModules : ['module1', 'module2'], 208 | repo : 'https://github.com/lodlock/LumberJill.git', //the url of the repo 209 | uploadDependencies : true //also upload a copy of dependencies to the gitDroid.localRepo 210 | ] 211 | ``` 212 | 213 | 214 | ### Basic Example 215 | rootProject `build.gradle` 216 | ```groovy 217 | buildscript { 218 | repositories { 219 | jcenter() 220 | } 221 | dependencies { 222 | classpath 'com.android.tools.build:gradle:2.0.0-alpha3' 223 | } 224 | } 225 | 226 | allprojects { 227 | buildDir = "C:/tmp/${rootProject.name}/${project.name}" //short buildDir to avoid Windows path limit 228 | repositories { 229 | jcenter() 230 | maven { 231 | url mavenLocal().url 232 | } 233 | } 234 | } 235 | 236 | task clean(type: Delete) { 237 | delete rootProject.buildDir 238 | } 239 | 240 | ``` 241 | 242 | module `build.gradle` 243 | ```groovy 244 | plugins { 245 | id "com.testfunction.gradle-gitdroid" version "0.0.3" 246 | } 247 | 248 | apply plugin: 'com.android.application' 249 | 250 | android { 251 | compileSdkVersion 23 252 | buildToolsVersion "23.0.2" 253 | 254 | defaultConfig { 255 | applicationId "com.testfunction.testgradleplugin" 256 | minSdkVersion 16 257 | targetSdkVersion 23 258 | versionCode 1 259 | versionName "1.0" 260 | generatedDensities = [] 261 | } 262 | buildTypes { 263 | release { 264 | minifyEnabled false 265 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 266 | } 267 | } 268 | } 269 | 270 | dependencies { 271 | compile fileTree(dir: 'libs', include: ['*.jar']) 272 | testCompile 'junit:junit:4.12' 273 | 274 | compile 'com.android.support:appcompat-v7:23.1.1' 275 | 276 | compile (group: 'com.testfunction', 277 | name:'lumberjill', 278 | version:'HEAD', 279 | ext: 'aar', 280 | transitive: true 281 | ).ext { 282 | git = [ 283 | repo : 'https://github.com/lodlock/LumberJill.git', 284 | buildJavaDocs : true, 285 | keepUpdated : true, 286 | remoteModule: 'lumberjill', 287 | keepSource : true 288 | ] 289 | } 290 | 291 | compile ('com.github.michaldrabik:tapbarmenu:gitDroid@aar') { 292 | transitive = true 293 | }.ext { 294 | git = [ 295 | repo : 'https://github.com/michaldrabik/TapBarMenu.git', 296 | ref : 'd17b48f167f3d121c00dfb5ad7fd7f39b8fc18d2', 297 | remoteModule: 'library', 298 | keepSource: true 299 | ] 300 | } 301 | 302 | compile ("com.github.DeveloperPaul123:FilePickerLibrary:gitDroid@aar") { 303 | transitive = true 304 | }.ext { 305 | git = [ 306 | repo: 'https://github.com/DeveloperPaul123/FilePickerLibrary.git', 307 | ref : '02782b398c6ab590947a3beba6a0d2545c4caf4d', 308 | remoteModule: 'FPlib', 309 | keepSource: true 310 | ] 311 | } 312 | } 313 | 314 | gitDroid { 315 | workingDir = "C:/tmp/${rootProject.name}/gitDroid" 316 | localRepo = getRepositories().mavenLocal().url 317 | automaticResolve = false 318 | test { 319 | pathLengthDieOnFailure = false 320 | } 321 | replaceValuesRegex = [ 322 | "com.android.tools.build:gradle:2.0.0-alpha3" : 323 | "(?<=['|\"])com\\.android\\.tools\\.build:gradle:.+?(?=['|\"])" 324 | ] 325 | } 326 | ``` 327 | 328 | ### Notes 329 | If you are working in Windows you should set a short workingDir path to help avoid reaching the Windows path limit. 330 | -------------------------------------------------------------------------------- /bintray.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | 3 | version = libraryVersion 4 | 5 | task sourcesJar(type: Jar) { 6 | from sourceSets.main.groovy.srcDirs 7 | classifier = 'sources' 8 | } 9 | 10 | task javadocJar(type: Jar, dependsOn: javadoc) { 11 | classifier = 'javadoc' 12 | from javadoc.destinationDir 13 | } 14 | artifacts { 15 | archives javadocJar 16 | archives sourcesJar 17 | } 18 | 19 | 20 | bintray { 21 | 22 | user = property("bintray.user") 23 | key = property("bintray.apikey") 24 | 25 | configurations = ['archives'] 26 | dryRun = false 27 | pkg { 28 | repo = bintrayRepo 29 | name = bintrayName 30 | desc = libraryDescription 31 | websiteUrl = siteUrl 32 | vcsUrl = gitUrl 33 | licenses = allLicenses 34 | dryRun = false 35 | publish = false 36 | publicDownloadNumbers = true 37 | version { 38 | desc = libraryDescription 39 | gpg { 40 | sign = true //Determines whether to GPG sign the files. The default is false 41 | passphrase = property("bintray.gpg.password") 42 | //Optional. The passphrase for GPG signing' 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | mavenCentral() 5 | maven { 6 | url "https://plugins.gradle.org/m2/" 7 | } 8 | } 9 | dependencies { 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.5' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 12 | classpath "com.gradle.publish:plugin-publish-plugin:0.9.2" 13 | } 14 | } 15 | 16 | group 'com.testfunction' 17 | version '0.0.3' 18 | 19 | apply plugin: "com.gradle.plugin-publish" 20 | apply plugin: 'groovy' 21 | apply plugin: 'maven' 22 | apply plugin: 'idea' 23 | 24 | idea { 25 | module { 26 | downloadJavadoc = true 27 | downloadSources = true 28 | true 29 | } 30 | } 31 | 32 | configurations { 33 | provided 34 | magic 35 | } 36 | 37 | task createClasspathManifest { 38 | def outputDir = file("$buildDir/$name") 39 | 40 | inputs.files sourceSets.main.runtimeClasspath 41 | outputs.dir outputDir 42 | 43 | doLast { 44 | outputDir.mkdirs() 45 | file("$outputDir/plugin-classpath.txt").text = sourceSets.main.runtimeClasspath.join("\n") 46 | true 47 | } 48 | } 49 | 50 | dependencies { 51 | repositories { 52 | mavenCentral() 53 | } 54 | 55 | compile gradleApi() 56 | compile localGroovy() 57 | compile 'com.android.tools.build:gradle:1.5.0' 58 | compile 'org.ajoberstar:grgit:1.5.0-rc.1' 59 | 60 | 61 | testCompile group: 'junit', name: 'junit', version: '4.12' 62 | testCompile gradleTestKit() 63 | testCompile ('org.spockframework:spock-core:1.0-groovy-2.4') { 64 | exclude module:'groovy-all' 65 | } 66 | 67 | testCompile 'com.android.tools.build:gradle:1.5.0' 68 | 69 | testRuntime files(createClasspathManifest) 70 | } 71 | 72 | sourceSets { 73 | main { 74 | groovy { 75 | srcDirs = ['src/main/groovy'] 76 | true 77 | } 78 | compileClasspath += configurations.provided 79 | true 80 | } 81 | test { 82 | groovy { 83 | srcDirs = ['src/test/groovy'] 84 | true 85 | } 86 | } 87 | } 88 | 89 | 90 | uploadArchives { 91 | repositories { 92 | mavenDeployer { 93 | repository(url: mavenLocal().url) 94 | } 95 | } 96 | true 97 | } 98 | 99 | task wrapper(type: Wrapper) { 100 | gradleVersion = '2.10' 101 | distributionUrl = "https://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip" 102 | true 103 | } 104 | 105 | ext { 106 | bintrayRepo = 'maven' 107 | bintrayName = 'gradle-gitdroid' 108 | 109 | publishedGroupId = 'com.testfunction' 110 | libraryName = 'gradle-gitdroid' 111 | artifact = 'gradle-gitdroid' 112 | 113 | libraryDescription = 'A gradle plugin for Android projects that allows for dependencies from git repositories' 114 | 115 | siteUrl = 'https://github.com/lodlock/gradle-gitdroid' 116 | gitUrl = 'https://github.com/lodlock/gradle-gitdroid.git' 117 | 118 | libraryVersion = version 119 | 120 | developerId = 'lodlock' 121 | developerName = 'Brandon Andrews' 122 | developerEmail = 'iamhunted@gmail.com' 123 | 124 | licenseName = 'MIT' 125 | licenseUrl = 'http://opensource.org/licenses/mit-license.php' 126 | allLicenses = ["MIT"] 127 | } 128 | 129 | apply from: "install.gradle" 130 | if (hasProperty('bintray.user') && hasProperty('bintray.apikey') && hasProperty('bintray.gpg.password')) { 131 | apply from: "bintray.gradle" 132 | } 133 | 134 | pluginBundle { 135 | website = 'https://github.com/lodlock/gradle-gitdroid' 136 | vcsUrl = 'https://github.com/lodlock/gradle-gitdroid' 137 | description = 'A gradle plugin for Android projects that allows for dependencies from git repositories.' 138 | tags = ['git', 'dependencies', 'repositories', 'android', 'dependency', 'github'] 139 | 140 | plugins { 141 | gradleGitDroidPlugin { 142 | id = 'com.testfunction.gradle-gitdroid' 143 | displayName = 'Gradle GitDroid' 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodlock/gradle-gitdroid/aa10566f330db310a9da42cbaaf35d524eb46173/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jan 04 16:08:29 EST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /install.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | 3 | group = publishedGroupId // Maven Group ID for the artifact 4 | 5 | install { 6 | repositories.mavenInstaller { 7 | // This generates POM.xml with proper parameters 8 | pom { 9 | project { 10 | packaging 'jar' 11 | groupId publishedGroupId 12 | artifactId artifact 13 | 14 | // Add your description here 15 | name libraryName 16 | description libraryDescription 17 | url siteUrl 18 | 19 | // Set your license 20 | licenses { 21 | license { 22 | name licenseName 23 | url licenseUrl 24 | } 25 | } 26 | developers { 27 | developer { 28 | id developerId 29 | name developerName 30 | email developerEmail 31 | } 32 | } 33 | scm { 34 | connection gitUrl 35 | developerConnection gitUrl 36 | url siteUrl 37 | 38 | } 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'gradle-gitdroid' 2 | 3 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/GradleGitDroidPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import com.testfunction.extensions.project.GitProjectExt 5 | import com.testfunction.extensions.project.GitProjectTestExt 6 | import com.testfunction.internal.helpers.GitDependenciesHelper 7 | import com.testfunction.internal.utils.Utils 8 | import com.testfunction.tasks.GitDependenciesTask 9 | import com.testfunction.tasks.GitDependencyGetTask 10 | import com.testfunction.tasks.ListDependenciesTask 11 | import org.gradle.api.Plugin 12 | import org.gradle.api.Project 13 | import org.gradle.api.artifacts.Configuration 14 | import org.gradle.api.artifacts.Dependency 15 | import org.gradle.api.artifacts.ExternalModuleDependency 16 | import org.gradle.api.artifacts.UnresolvedDependency 17 | import org.gradle.api.logging.Logger 18 | import org.gradle.api.logging.Logging 19 | 20 | /** 21 | * Gradle plugin to create tasks for git dependencies. If automaticResolve is true then unresolved or git dependencies 22 | * with errors will be cloned/pulled, and compiled automatically. 23 | * 24 | * Created by Brandon on 1/1/2016. 25 | */ 26 | class GradleGitDroidPlugin implements Plugin { 27 | final Logger log = Logging.getLogger GradleGitDroidPlugin 28 | 29 | @Override 30 | void apply(Project target) { 31 | log.debug("GradleGitDroidPlugin init") 32 | def gitProjectExt = target.extensions.create(GitProjectExt.NAME, GitProjectExt, target); 33 | def testConf = ((GitProjectTestExt) gitProjectExt.test) 34 | 35 | log.debug("Extensions:$target.gitDroid"); 36 | 37 | def gitDependenciesTask = target.tasks.create("initGitDependencies", GitDependenciesTask) 38 | def gitDependenciesGetTask = target.tasks.create("getGitRepo", GitDependencyGetTask) 39 | gitDependenciesGetTask.mustRunAfter(gitDependenciesTask) 40 | 41 | def listGitDependenciesTask = target.tasks.create("listGitDependencies", ListDependenciesTask) 42 | 43 | target.gradle.afterProject { 44 | log.debug "afterProject testSkip" 45 | 46 | log.lifecycle("gitProjectExt.workingDir:$gitProjectExt.workingDir") 47 | Utils.validateWorkingDirPathLength(target, gitProjectExt.workingDir, gitProjectExt.test.pathLengthDieOnFailure) 48 | log.debug "test tasks to run:$target.gradle.startParameter.taskNames" 49 | target.gradle.startParameter.taskNames.each { String taskName -> 50 | println "task to run:$taskName" 51 | if (taskName.equals("initGitDependencies")) { 52 | gitProjectExt.automaticResolve = true 53 | } 54 | } 55 | target.tasks.findByName("build").shouldRunAfter(gitDependenciesTask) 56 | } 57 | 58 | target.afterEvaluate { 59 | def shouldInitGit = false 60 | def buildError = false 61 | def Set confsList 62 | def buildList = [] 63 | if (testConf.filterConfig) { 64 | log.debug("getting configurations that are unresolved or with errors") 65 | confsList = target.configurations.findAll { it.state != Configuration.State.RESOLVED } 66 | } else { 67 | log.debug("getting configurations") 68 | confsList = target.configurations.findAll() 69 | } 70 | confsList.each { Configuration configuration -> 71 | log.debug("configuration:${configuration.name} state is ${configuration.state.name()}") 72 | def Configuration configCopy = configuration.copy() 73 | if (configCopy.resolvedConfiguration.hasError()) { 74 | buildError = true 75 | } 76 | 77 | def Set depsList 78 | if (testConf.filterDependenciesByExternalModule) { 79 | log.debug("getting dependencies that are external module only") 80 | depsList = configuration.dependencies.findAll { it instanceof ExternalModuleDependency } 81 | } else { 82 | log.debug("getting all dependencies") 83 | depsList = configuration.dependencies.findAll() 84 | } 85 | depsList.each { Dependency dependency -> 86 | log.debug("configuration:$configuration.name dependency name:${dependency.name}") 87 | if (dependency instanceof ExternalModuleDependency) { 88 | def ExternalModuleDependency dep = (ExternalModuleDependency) dependency 89 | 90 | if (dep.hasProperty("git")) { 91 | GitDependencyExt gitExt = dep['git'] 92 | log.debug("hasproperty dependency:" + dep + " has git:" + dep['git']); 93 | if (configCopy.resolvedConfiguration.hasError()) { 94 | log.debug("configCopy of $configuration.name hasError:${configCopy.resolvedConfiguration.hasError()}") 95 | configCopy.resolvedConfiguration.lenientConfiguration.unresolvedModuleDependencies.each { UnresolvedDependency unDep -> 96 | log.debug("unresolved dependency:${unDep.problem?.message} caused by ${unDep.problem?.cause?.message}") 97 | if("$unDep.selector.group:$unDep.selector.name:$unDep.selector.version" 98 | .equals("$dep.group:$dep.name:$dep.version")) { 99 | log.debug("dependency with git cause of configuration error. adding shouldInitGit") 100 | shouldInitGit = true 101 | buildList.add("$dep.group:$dep.name:$dep.version") 102 | } 103 | } 104 | } 105 | try { 106 | if (gitExt.keepUpdated || gitExt.forceBuild) { 107 | shouldInitGit = true 108 | if (!buildList.contains("$dep.group:$dep.name:$dep.version")) { 109 | buildList.add("$dep.group:$dep.name:$dep.version") 110 | } 111 | } 112 | } catch (e) { 113 | log.warn("could not set shouldInitGit") 114 | e.printStackTrace() 115 | } 116 | 117 | log.debug("forceRunInit:$testConf.forceRunInit") 118 | } else { 119 | log.debug("NOT GIT:$dep") 120 | } 121 | 122 | } else { 123 | log.debug("not external module dependency") 124 | } 125 | } 126 | } 127 | 128 | log.debug("shouldInitGit:$shouldInitGit") 129 | if (shouldInitGit) { 130 | log.debug("buildError:$buildError && testConf.forceRunInit:$testConf.forceRunInit || gitProjectExt.automaticResolve:$gitProjectExt.automaticResolve") 131 | if (buildError && testConf.forceRunInit || gitProjectExt.automaticResolve) { 132 | try { 133 | GitDependenciesHelper.initGitDependencies(target) 134 | } catch (e) { 135 | log.error("could not run initGitDependencies") 136 | e.printStackTrace() 137 | } 138 | } else if(buildError) { 139 | log.error("gitDroid found git modules $buildList that have not been initialized. Run initGitDependencies or set gitDroid.automaticResolve to true in project build.gradle to automatically run.") 140 | } else { 141 | log.lifecycle("gitDroid found git modules $buildList with keepUpdated set to true. Run initGitDependencies or set gitDroid.automaticResolve to true in project build.gradle to automatically run.") 142 | } 143 | 144 | } 145 | 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/extensions/dependency/GitDependencyExt.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.extensions.dependency 2 | 3 | import com.testfunction.internal.enums.GitReferenceType 4 | import org.gradle.api.tasks.StopExecutionException 5 | 6 | /** 7 | * The git extension for an ExternalModuleDependency. 8 | * Example: 9 | *
10 |  * @code
11 |  * compile (group: 'com.testfunction',
12 |  *  name:'lumberjill',
13 |  *  version:'HEAD',
14 |  *  ext: 'aar',
15 |  *  transitive: true
16 |  * ).ext {
17 |  *  git = [
18 |  *      repo : 'https://github.com/lodlock/LumberJill.git',
19 |  *      buildJavaDocs : true,
20 |  *      keepUpdated : true,
21 |  *      remoteModule: 'lumberjill',
22 |  *      keepSource : true
23 |  *  ]
24 |  * }
25 |  * 
26 | * 27 | * Created by Brandon on 1/1/2016. 28 | */ 29 | class GitDependencyExt { 30 | def String repo = ""; 31 | def String ref = "" 32 | def GitReferenceType refType = GitReferenceType.AUTOMATIC 33 | def String remoteModule = ""; 34 | def LinkedHashMap credentials = null; 35 | def boolean keepUpdated = false; 36 | def boolean keepSource = false; 37 | def ArrayList remoteModules = null; 38 | def boolean uploadDependencies = true; 39 | 40 | def boolean buildSources = true; 41 | def boolean buildJavaDocs = true; 42 | 43 | def boolean forceBuild = false 44 | 45 | def propertyMissing(String name) { 46 | throw new StopExecutionException("gitDroid does not have property:$name") 47 | } 48 | 49 | def propertyMissing(String name, def arg) { 50 | throw new StopExecutionException("gitDroid does not have property:$name with args:$arg") 51 | } 52 | 53 | 54 | def toListString() { 55 | def out = """\ 56 | 57 | |-> repo:$repo 58 | |-> ref:$ref 59 | |-> refType:$refType 60 | |-> remoteModule:$remoteModule 61 | |-> remoteModules:$remoteModules 62 | |-> credentials: 63 | |-> username:${credentials?.username != null} 64 | |-> password:${credentials?.password != null} 65 | |-> sshKey:${credentials?.sshKey != null} 66 | |-> keepUpdated:$keepUpdated 67 | |-> keepSource:$keepSource 68 | |-> uploadDependencies:$uploadDependencies 69 | |-> buildSources:$buildSources 70 | |-> buildJavaDocs:$buildJavaDocs 71 | """ 72 | return out 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/extensions/project/GitProjectExt.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.extensions.project 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import org.gradle.api.Project 5 | import org.gradle.api.artifacts.ExternalModuleDependency 6 | import org.gradle.api.plugins.ExtensionAware 7 | import org.gradle.api.tasks.StopActionException 8 | import org.gradle.api.tasks.StopExecutionException 9 | 10 | /** 11 | * The project extension used by adding gitDroid to the build.gradle file. 12 | * Example: 13 | *
14 |  * @code
15 |  * gitDroid {
16 |  *  workingDir = "C:/tmp/${rootProject.name}/gitDroid"
17 |  *  shouldCompile = true
18 |  *  shouldClone = true
19 |  *  shouldBuildArchivePom = true
20 |  *  useLocalProperties = true
21 |  *  cleanIncludesGitSources = false
22 |  *  localRepo = getRepositories().mavenLocal().url
23 |  *  automaticResolve = false
24 |  * }
25 |  * 
26 | * 27 | * Created by Brandon on 1/1/2016. 28 | */ 29 | class GitProjectExt { 30 | public static final String NAME = "gitDroid"; 31 | def File workingDir; 32 | def boolean shouldBuildArchivePom = true; 33 | def URI localRepo; 34 | def boolean automaticResolve = false; 35 | def GitProjectTestExt test 36 | def Map needsbuild 37 | def boolean deleteSourceOnGitFailure = true 38 | def Map> replaceValues = null 39 | def Map replaceValuesRegex = null 40 | def boolean buildModule = true 41 | private Project target 42 | 43 | 44 | GitProjectExt(Project target) { 45 | workingDir = target.rootProject.file("git-tmp") 46 | localRepo = target.repositories.mavenLocal().url; 47 | test = ((ExtensionAware) this).extensions.create("test", GitProjectTestExt, target) 48 | this.target = target 49 | } 50 | 51 | void setWorkingDir(File workingDir) { 52 | this.workingDir = workingDir 53 | } 54 | 55 | void setWorkingDir(String workingDir) { 56 | this.workingDir = new File(workingDir) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/extensions/project/GitProjectTestExt.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.extensions.project 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.plugins.ExtensionAware 5 | import org.gradle.api.tasks.StopExecutionException 6 | 7 | /** 8 | * gitDroid test extension used for setting test values 9 | * Example: 10 | *
11 |  * @code
12 |  * gitDroid {
13 |  *  test {
14 |  *      pathLengthDieOnFailure = true
15 |  *  }
16 |  * }
17 |  * 
18 | * 19 | * Created by Brandon on 1/6/2016. 20 | */ 21 | class GitProjectTestExt { 22 | def boolean forceRunInit = false 23 | def boolean filterConfig = true 24 | def boolean filterDependenciesByExternalModule = true 25 | def boolean pathLengthDieOnFailure = true 26 | def boolean useLifecycle = false 27 | def GitProjectTestRegexExt regexp 28 | private Project target 29 | 30 | GitProjectTestExt(Project target) { 31 | regexp = ((ExtensionAware) this).extensions.create("regexp", GitProjectTestRegexExt, target) 32 | this.target = target 33 | } 34 | 35 | def propertyMissing(String name) { 36 | if (!target.properties.containsKey(name)) { 37 | throw new StopExecutionException("gitDroid does not have property:$name") 38 | } 39 | } 40 | 41 | def propertyMissing(String name, def arg) { 42 | if (!target.properties.containsKey(name)) { 43 | throw new StopExecutionException("gitDroid does not have property:$name with args:$arg") 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/extensions/project/GitProjectTestRegexExt.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.extensions.project 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.tasks.StopExecutionException 5 | 6 | /** 7 | * gitDroid test.regex extension used for setting regex flags 8 | * Example: 9 | *
10 |  * @code
11 |  * gitDroid {
12 |  *  test {
13 |  *      regexp {
14 |  *          flags = "g"
15 |  *          byLine = true
16 |  *          encoding = "UTF-8"
17 |  *      }
18 |  *  }
19 |  * }
20 |  * 
21 | * 22 | * Created by Brandon on 1/9/2016. 23 | */ 24 | class GitProjectTestRegexExt { 25 | //g global, i case insensitive, m multiline, s singleline 26 | def flags = "g" 27 | def byLine = false 28 | def encoding = "UTF-8" 29 | 30 | 31 | GitProjectTestRegexExt(Project project) { 32 | } 33 | 34 | def propertyMissing(String name) { 35 | throw new StopExecutionException("gitDroid does not have property:$name") 36 | } 37 | 38 | def propertyMissing(String name, def arg) { 39 | throw new StopExecutionException("gitDroid does not have property:$name with args:$arg") 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/dependencies/DepNeedsBuild.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.dependencies 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import com.testfunction.internal.enums.GitDependencyTypes 5 | import org.gradle.api.artifacts.ExternalModuleDependency 6 | 7 | /** 8 | * List of git dependencies that needs to be built 9 | * 10 | * Created by Brandon on 1/3/2016. 11 | */ 12 | class DepNeedsBuild { 13 | def LinkedHashMap list = [:] 14 | def type = GitDependencyTypes.NEEDSUPDATE 15 | } 16 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/dependencies/DepResolved.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.dependencies 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import com.testfunction.internal.enums.GitDependencyTypes 5 | import org.gradle.api.artifacts.ExternalModuleDependency 6 | 7 | /** 8 | * List of git dependencies that have been successfully resolved 9 | * 10 | * Created by Brandon on 1/3/2016. 11 | */ 12 | class DepResolved { 13 | def LinkedHashMap list = [:] 14 | def type = GitDependencyTypes.RESOLVED 15 | } 16 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/dependencies/DepUnresolved.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.dependencies 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import com.testfunction.internal.enums.GitDependencyTypes 5 | import org.gradle.api.artifacts.ExternalModuleDependency 6 | 7 | /** 8 | * List of git dependencies that has not been resolved 9 | * 10 | * Created by Brandon on 1/3/2016. 11 | */ 12 | class DepUnresolved { 13 | def LinkedHashMap list = [:] 14 | def type = GitDependencyTypes.UNRESOLVED 15 | } 16 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/enums/GitDependencyTypes.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.enums 2 | 3 | /** 4 | * GitDependencyTypes used for dependency lists 5 | * 6 | * Created by Brandon on 1/3/2016. 7 | */ 8 | enum GitDependencyTypes { 9 | UNRESOLVED, 10 | RESOLVED, 11 | NEEDSUPDATE 12 | } -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/enums/GitReferenceType.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.enums 2 | 3 | /** 4 | * GitReferenceType type used in GitDependencyExt for determining type of reference that the git ref refers to 5 | * 6 | * Created by Brandon on 1/9/2016. 7 | */ 8 | enum GitReferenceType { 9 | AUTOMATIC, 10 | BRANCH, 11 | COMMIT, 12 | TAG 13 | } -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/helpers/GitDependenciesHelper.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.helpers 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import com.testfunction.extensions.project.GitProjectExt 5 | import com.testfunction.internal.dependencies.DepNeedsBuild 6 | import com.testfunction.internal.dependencies.DepResolved 7 | import com.testfunction.internal.dependencies.DepUnresolved 8 | import com.testfunction.internal.enums.GitDependencyTypes 9 | import com.testfunction.internal.utils.Utils 10 | import com.testfunction.tasks.CompileEachTask 11 | import org.eclipse.jgit.internal.storage.file.WindowCache 12 | import org.eclipse.jgit.storage.file.WindowCacheConfig 13 | import org.gradle.StartParameter 14 | import org.gradle.api.Project 15 | import org.gradle.api.artifacts.Configuration 16 | import org.gradle.api.artifacts.Dependency 17 | import org.gradle.api.artifacts.ExternalModuleDependency 18 | import org.gradle.api.artifacts.UnresolvedDependency 19 | import org.gradle.api.logging.Logger 20 | import org.gradle.api.tasks.StopActionException 21 | 22 | import java.nio.file.Files 23 | import java.nio.file.Path 24 | 25 | /** 26 | * GitDependenciesHelper 27 | * 28 | * Get list of ExternalModuleDependency dependencies which have a git extension. If need be start 29 | * the clone/pull process, a build of the external project with dependencies, and addition to the 30 | * local defined repo along with dependencies required to run external module. 31 | * 32 | * Created by Brandon on 1/3/2016. 33 | */ 34 | class GitDependenciesHelper { 35 | 36 | /** 37 | * Get a list of dependencies which have git extension based on the resolved state of the dependency 38 | * 39 | * @param project the project which contains the dependencies 40 | * @param type the type to sort by RESOLVED, UNRESOLVED, NEEDSUPDATE 41 | * @return Map of dependencies which include git extension 42 | */ 43 | def static getGitDependencies( 44 | def Project project, 45 | def GitDependencyTypes type) { 46 | def log = project.logger 47 | def Map map = [:] 48 | log.debug("getGitDependencies init with project:" + project + " map:" + map + " type:" + type) 49 | 50 | log.debug("type:$type") 51 | project.configurations.findAll().each { Configuration config -> 52 | Configuration configCopy = config.copy() 53 | 54 | log.debug("configCopy of $config.name") 55 | def unresolvedList = [] 56 | if (configCopy.resolvedConfiguration.hasError()) { 57 | log.debug("configCopy of $config.name hasError:${configCopy.resolvedConfiguration.hasError()}") 58 | configCopy.resolvedConfiguration.lenientConfiguration.unresolvedModuleDependencies.each { UnresolvedDependency unDep -> 59 | log.debug("config:$config.name unresolved dependency:${unDep.problem?.message} caused by ${unDep.problem?.cause?.message}") 60 | unresolvedList.add("$unDep.selector.group:$unDep.selector.name:$unDep.selector.version") 61 | } 62 | } 63 | log.debug("unresolvedList:" + unresolvedList + " size:" + unresolvedList.size()) 64 | config.allDependencies.each { Dependency dependency -> 65 | log.debug("type:$type config:$config.name") 66 | if (!(dependency instanceof ExternalModuleDependency)) { 67 | log.debug("dependency:$dependency.name not external module. skipping") 68 | return 69 | } 70 | def dep = dependency as ExternalModuleDependency 71 | if (type == GitDependencyTypes.UNRESOLVED && !configCopy.resolvedConfiguration.hasError()) { 72 | log.debug("no unresolved dependencies") 73 | } else { 74 | if (dep.hasProperty("git")) { 75 | log.debug("$config.name: dependency:$dep.name is of type git:$dep.git") 76 | if (unresolvedList.contains("$dep.group:$dep.name:$dep.version")) { 77 | log.debug("found dep:$dep.name in unresolvedList") 78 | dep['git']['forceBuild'] = true 79 | log.debug("unresolvedList adding to map[$dep] = $dep.git") 80 | map[(dep)] = (GitDependencyExt) dep['git'] 81 | } 82 | if (configCopy.state == Configuration.State.RESOLVED) { 83 | if (type == GitDependencyTypes.NEEDSUPDATE) { 84 | 85 | if (((GitDependencyExt) dep['git']).keepUpdated) { 86 | log.debug("$config.name: is resolved and keepUpdated is true") 87 | log.debug("resolved keepUpdated adding to map[$dep] = $dep.git") 88 | map[(dep)] = (GitDependencyExt) dep['git'] 89 | true 90 | } else { 91 | log.debug("$config.name: state is resolved but keepUpdated is false") 92 | } 93 | } else if (type == GitDependencyTypes.RESOLVED) { 94 | log.debug("resolved and type is resolved adding to map[$dep] = $dep.git") 95 | map[(dep)] = (GitDependencyExt) dep['git'] 96 | true 97 | } 98 | } else { 99 | if (((GitDependencyExt) dep['git']).keepUpdated) { 100 | log.debug("$config.name: is unresolved and keepUpdated is true") 101 | log.debug("unresolved keepUpdated adding to map[$dep] = $dep.git") 102 | map[(dep)] = (GitDependencyExt) dep['git'] 103 | true 104 | } 105 | } 106 | } else { 107 | log.debug("$config.name: dependency:$dep.name not a git dependency") 108 | } 109 | } 110 | } 111 | } 112 | log.debug("returning ${map.size()} dependencies") 113 | return map 114 | } 115 | 116 | /** 117 | * Creates repo directories and calls getGitRepo 118 | * 119 | * @param project 120 | */ 121 | static def initGitDependencies(Project project) { 122 | def log = project.logger 123 | def gitProjectExt = (GitProjectExt) project.(GitProjectExt.NAME) 124 | def dir = gitProjectExt.workingDir 125 | log.debug("dir:$dir") 126 | def repo = gitProjectExt.localRepo 127 | def repoFolder = new File(repo) 128 | if (repoFolder.exists() && repoFolder.directory) { 129 | log.debug("repo exists and is directory") 130 | } else { 131 | log.debug("repo does not exist or is not directory") 132 | if (repoFolder.mkdirs()) { 133 | log.debug("repoFolder created:" + repoFolder.absolutePath) 134 | } else { 135 | log.debug("failed to create repoFolder:" + repoFolder.absolutePath) 136 | } 137 | } 138 | def dependenciesNeedsBuild = DepNeedsBuild.newInstance() 139 | 140 | dependenciesNeedsBuild.list = getGitDependencies(project, dependenciesNeedsBuild.type) 141 | log.lifecycle("git dependencies that need to be built:" + dependenciesNeedsBuild.list) 142 | 143 | if (dependenciesNeedsBuild.list.isEmpty()) { 144 | throw new StopActionException("No dependencies require build") 145 | } 146 | 147 | log.debug("setting gitDroid.needsbuild for dependencies") 148 | ((GitProjectExt) project.extensions.findByName("gitDroid")).needsbuild = dependenciesNeedsBuild.list 149 | 150 | log.debug("executing getGitRepo") 151 | getGitRepo(project) 152 | 153 | log.debug("list of that needs build") 154 | } 155 | 156 | /** 157 | * Checks if repo exists and is valid. If repo already exists a git pull command is called, otherwise 158 | * clone is called. 159 | * 160 | * @param project 161 | * @return 162 | */ 163 | static def getGitRepo(Project project) { 164 | def log = project.logger 165 | def gitDroid = (GitProjectExt) project.extensions.findByName("gitDroid") 166 | def WindowCacheConfig config = new WindowCacheConfig() 167 | config.packedGitMMAP = false 168 | config.install() 169 | def dependenciesNeedsBuild = gitDroid.needsbuild 170 | log.debug("getGitRepo needsbuild:$dependenciesNeedsBuild") 171 | dependenciesNeedsBuild.each { ExternalModuleDependency dependency, GitDependencyExt gitExt -> 172 | log.debug("getGitRepo - dependenciesNeedsBuild dep:${dependency.name} gitExt:$gitExt") 173 | if (!gitExt.repo) { 174 | throw new StopActionException("Missing repo for git") 175 | } 176 | def File destinationDir = new File(gitDroid.workingDir, dependency.name) 177 | def build 178 | 179 | checkRepo(gitDroid, log, destinationDir) 180 | 181 | 182 | if (!destinationDir.exists()) { 183 | log.debug("destinationDir:" + destinationDir + " doesn't exist") 184 | try { 185 | build = GitHelper.cloneGitRepository(project, dependency, gitExt, destinationDir) 186 | } catch (e) { 187 | log.warn("failed to clone for some reason:$e.message") 188 | if (gitDroid.deleteSourceOnGitFailure) { 189 | log.debug("deleting destination dir") 190 | if (destinationDir.deleteDir()) { 191 | log.debug("deleted") 192 | } else { 193 | log.warn("failed to delete $destinationDir.absolutePath") 194 | } 195 | } 196 | build = false 197 | e.printStackTrace() 198 | } 199 | } else { 200 | log.debug("destinationDir:" + destinationDir + " exists") 201 | try { 202 | build = GitHelper.pullGitRepository(project, dependency, gitExt, destinationDir) 203 | if (gitExt.forceBuild) { 204 | log.debug("build was:$build but forceBuild is true") 205 | build = true 206 | } 207 | } catch (e) { 208 | log.warn("failed to pull for some reason:$e.message") 209 | if (gitDroid.deleteSourceOnGitFailure) { 210 | log.debug("deleting destination dir") 211 | if (destinationDir.deleteDir()) { 212 | log.debug("deleted") 213 | } else { 214 | log.warn("failed to delete $destinationDir.absolutePath") 215 | } 216 | } 217 | build = false 218 | e.printStackTrace() 219 | } 220 | } 221 | log.debug("build:$build") 222 | if (build) { 223 | handleCompile(project, dependency, gitExt) 224 | } 225 | } 226 | } 227 | 228 | /** 229 | * Checks if destinationDir exists. If it does tests the repo. If repo fails and gitDroid.deleteSourceOnGitFailure 230 | * is true then attempts to delete destinationDir. 231 | * 232 | * @param gitDroid the project's GitProjectExt value 233 | * @param log logger to use for logging 234 | * @param destinationDir the directory to check for a valid repo in 235 | * @return 236 | */ 237 | static def checkRepo(GitProjectExt gitDroid, Logger log, File destinationDir) { 238 | if (destinationDir.exists()) { 239 | if (!GitHelper.testRepo(log, destinationDir)) { 240 | log.debug("not a valid repo. checking if gitDroid.deleteSourceOnGitFailure is true") 241 | if (gitDroid.deleteSourceOnGitFailure) { 242 | log.lifecycle("deleting destination dir") 243 | if (destinationDir.deleteDir()) { 244 | log.lifecycle("deleted") 245 | } else { 246 | log.lifecycle("failed to delete") 247 | } 248 | } 249 | } 250 | } 251 | } 252 | 253 | /** 254 | * Creates gitDroid.gradle file and sets module build file to apply it. Creates local.properties in git 255 | * project if it does not exist. Replaces build file versions of libraries based on replaceValues 256 | * and replaceValuesRegex. Creates, runs, then deletes compile tasks for git project. Deletes git 257 | * sources keepSource is false. 258 | * 259 | * @param project the project containing git dependencies 260 | * @param dependency the git dependency 261 | * @param gitExt the GitDependencyExt for provided dependency 262 | * @return 263 | */ 264 | def static handleCompile(Project project, ExternalModuleDependency dependency, GitDependencyExt gitExt) { 265 | def log = project.logger 266 | def gitProjectExt = ((GitProjectExt) project.extensions.findByName(GitProjectExt.NAME)) 267 | def archivePom = gitProjectExt.shouldBuildArchivePom 268 | def workingDir = gitProjectExt.workingDir 269 | if (workingDir.exists()) { 270 | log.debug("workingDir found at:$workingDir.absolutePath") 271 | } else { 272 | throw new StopActionException("Git working directory missing") 273 | } 274 | def dependencyDir = new File(workingDir, dependency.name) 275 | def buildFileBase = new File(dependencyDir, "build.gradle") 276 | log.debug("remoteModule:" + gitExt.remoteModule) 277 | log.debug("remoteModules:" + gitExt.remoteModules) 278 | def moduleDir = new File(dependencyDir, (gitExt.remoteModule) ? gitExt.remoteModule : dependency.name) 279 | def buildFileModule = new File(moduleDir, "build.gradle") 280 | def gitDroid = new File(dependencyDir, "gitDroid.gradle") 281 | def localProperties = new File(project.rootDir, "local.properties") 282 | log.debug("checking if gitDroid:" + gitDroid.absolutePath + " exists:" + gitDroid.exists()) 283 | if (gitDroid.exists()) { 284 | if (gitDroid.delete()) { 285 | log.debug("previous gitDroid deleted") 286 | } else { 287 | log.debug("previous gitDroid failed to be deleted") 288 | } 289 | } 290 | if (buildFileBase.exists()) { 291 | log.debug("buildFileBase found at $buildFileBase.absolutePath") 292 | } else { 293 | log.debug("not a gradle project") 294 | throw new StopActionException("not a gradle project") 295 | } 296 | 297 | if (!gitDroid.exists()) { 298 | log.debug("gitDroid.gradle does not exist archivePom:" + archivePom) 299 | def isAndroidLibrary = Utils.isAndroidLibrary(project, buildFileModule) 300 | log.debug("isAndroidLibrary:" + isAndroidLibrary) 301 | if (archivePom) { 302 | log.debug("creating archive file") 303 | addArchiveFile(log, gitProjectExt, dependency, gitExt, dependencyDir) 304 | def depLocalProperties = new File(dependencyDir, "local.properties") 305 | log.debug("dependencyLocalProperties exists:" + depLocalProperties.exists()) 306 | if (localProperties.exists() && !depLocalProperties.exists()) { 307 | addLocalFile(log, dependencyDir, localProperties) 308 | } 309 | appendArchiveFile(project, buildFileModule.exists() ? buildFileModule : buildFileBase, buildFileModule.exists()) 310 | } 311 | 312 | if (buildFileBase.exists()) { 313 | log.debug("buildFile found at:" + buildFileBase.absolutePath) 314 | 315 | if (gitProjectExt.replaceValues != null) { 316 | log.debug("replacing files using $gitProjectExt.replaceValues") 317 | replaceValues(project, buildFileBase, gitProjectExt.replaceValues) 318 | replaceValues(project, buildFileModule, gitProjectExt.replaceValues) 319 | } 320 | 321 | if (gitProjectExt.replaceValuesRegex != null) { 322 | log.debug("replacing files using regex $gitProjectExt.replaceValuesRegex") 323 | replaceValuesRegex(project, buildFileBase, gitProjectExt.replaceValuesRegex) 324 | replaceValuesRegex(project, buildFileModule, gitProjectExt.replaceValuesRegex) 325 | } 326 | 327 | try { 328 | def compileEachTask = (CompileEachTask) project.tasks.maybeCreate("compileEach-$dependency.name", CompileEachTask) 329 | compileEachTask.buildFile = gitProjectExt.buildModule ? buildFileModule : buildFileBase 330 | compileEachTask.dir = dependencyDir 331 | 332 | HashMap projectProperties = new HashMap(); 333 | projectProperties["RELEASE_REPOSITORY_URL"] = gitProjectExt.localRepo.toString(); 334 | projectProperties["SNAPSHOT_REPOSITORY_URL"] = gitProjectExt.localRepo.toString(); 335 | 336 | StartParameter startParameter = compileEachTask.startParameter 337 | startParameter.projectProperties = projectProperties 338 | 339 | compileEachTask.startParameter = startParameter 340 | if (gitExt.uploadDependencies) { 341 | compileEachTask.tasks = Arrays.asList("clean", "assembleRelease", "uploadArchives", "uploadArchivesDeps") 342 | } else { 343 | compileEachTask.tasks = Arrays.asList("clean", "assembleRelease", "uploadArchives") 344 | } 345 | 346 | compileEachTask.run = true 347 | 348 | log.debug("compileEachTask starting") 349 | compileEachTask.execute() 350 | 351 | log.debug("execute completed. deleting task") 352 | project.tasks.remove(compileEachTask) 353 | } catch (e) { 354 | log.warn("failed to compile:$e.message") 355 | e.printStackTrace() 356 | } 357 | 358 | } else { 359 | log.debug("no buildFile found at:" + buildFileBase.absolutePath) 360 | } 361 | log.debug("checking if should keep source:$gitExt.keepSource") 362 | if (gitExt.keepSource) { 363 | log.debug("keepSource is true") 364 | } else { 365 | log.debug("deleting source:" + dependencyDir.absolutePath) 366 | if (dependencyDir.deleteDir()) { 367 | log.debug("deleted") 368 | } else { 369 | log.warn("could not delete dependency directory:$dependencyDir.absolutePath") 370 | } 371 | } 372 | } 373 | } 374 | 375 | /** 376 | * Used to replace specific included libraries' included library versions with the version defined 377 | * Example: 378 | *
379 |      * @code
380 |      * gitDroid{
381 |      *  replaceValues = [
382 |      *      "com.android.tools.build:gradle:2.0.0-alpha2" : [
383 |      *          "com.android.tools.build:gradle:2.0.0-alpha1"
384 |      *      ]
385 |      *  ]
386 |      * }
387 |      * 
388 | * 389 | * Will replace com.android.tools.build:gradle:2.0.0-alpha1 with com.android.tools.build:gradle:2.0.0-alpha2 390 | * 391 | * @param project project with git dependencies 392 | * @param buildFile the build file of the git dependency module or base project 393 | * @param replaceMap a mapping of what to replace 394 | * @return 395 | */ 396 | static def replaceValues(Project project, File buildFile, Map> replaceMap) { 397 | if (replaceMap == null) { 398 | return 399 | } 400 | if (buildFile.exists()) { 401 | replaceMap.each {k, v -> 402 | if (v == null || v.empty) { 403 | project.logger.debug("buildFile:$buildFile.name list for $k is null or empty:${v?.size()}") 404 | return 405 | } 406 | v?.each {r -> 407 | if (r == null || r.empty) { 408 | project.logger.debug("buildFile:$buildFile.name item for $k is null or empty:$r") 409 | return 410 | } 411 | 412 | project.logger.debug("buildFile:$buildFile.name replacing $r with $k") 413 | project.ant.replace(file:buildFile, token:r, value:k) 414 | } 415 | } 416 | } 417 | } 418 | 419 | /** 420 | * Used to replace a regex range of included libraries' included library versions with the version defined 421 | * Example: 422 | *
423 |      * @code
424 |      * gitDroid{
425 |      *  replaceValuesRegex = [
426 |      *      "com.android.tools.build:gradle:2.0.0-alpha3" :
427 |      *          "(?<=['|\"])com\\.android\\.tools\\.build:gradle:.+?(?=['|\"])"
428 |      *      ]
429 |      *  ]
430 |      * }
431 |      * 
432 | * 433 | * Will replace com.android.tools.build:gradle:ANYTHING with com.android.tools.build:gradle:2.0.0-alpha3 434 | * 435 | * @param project project with git dependencies 436 | * @param buildFile the build file of the git dependency module or base project 437 | * @param replaceMapRegex a mapping of what to replace 438 | * @return 439 | */ 440 | static def replaceValuesRegex(Project project, File buildFile, Map replaceMapRegex) { 441 | if (replaceMapRegex == null) { 442 | project.logger.debug("replaceMapRegex is null") 443 | return 444 | } 445 | def gitProjectExt = (GitProjectExt) project.extensions.getByName("gitDroid") 446 | 447 | if (buildFile.exists()) { 448 | replaceMapRegex.each {k, v -> 449 | if (!v) { 450 | project.logger.debug("buildFile:$buildFile.name list for $k is null or empty:${v}") 451 | return 452 | } 453 | project.logger.debug("buildFile:$buildFile.name regex replacing "+v+" with $k") 454 | project.ant.replaceregexp(file:buildFile, 455 | match:v, 456 | replace:k, 457 | flags:gitProjectExt.test.regexp.flags, 458 | byline:gitProjectExt.test.regexp.byLine, 459 | encoding:gitProjectExt.test.regexp.encoding 460 | ) 461 | } 462 | } else { 463 | project.logger.debug("buildFile doesn't exist") 464 | } 465 | } 466 | 467 | /** 468 | * Create the local.properties file for the newly created git project 469 | * @param log logger to use 470 | * @param dependencyDir git project directory 471 | * @param localProperties local.properties file to copy into dependencyDir 472 | * @return 473 | */ 474 | def static addLocalFile(Logger log, File dependencyDir, File localProperties) { 475 | log.debug("addLocalFile init depdendencyDir:$dependencyDir.absolutePath localProperties:$localProperties.absolutePath") 476 | Path source = localProperties.toPath() 477 | Path target = new File(dependencyDir, "local.properties").toPath() 478 | Files.copy(source, target) 479 | } 480 | 481 | /** 482 | * Creates gitDroid.gradle file which adds tasks to git dependency gradle to add to local maven repo and 483 | * includes creating necessary dependencies into same local maven repo. 484 | * 485 | * @param log logger to use 486 | * @param gitProjectExt the main project GitProjectExt (gitDroid) 487 | * @param dependency the external dependency that is a git dependency 488 | * @param gitExt the git extension of the external dependency 489 | * @param dependencyDir the temp directory created for the git dependency project 490 | * @return 491 | */ 492 | def static addArchiveFile( 493 | Logger log, 494 | GitProjectExt gitProjectExt, 495 | ExternalModuleDependency dependency, 496 | GitDependencyExt gitExt, 497 | File dependencyDir 498 | ) { 499 | log.debug("addArchiveFile init with dependency:$dependency.name and dir:$dependencyDir.absolutePath") 500 | log.debug("setting archive repo to:" + gitProjectExt.localRepo) 501 | 502 | def clearArtifact = "" 503 | if (!gitExt.buildJavaDocs && !gitExt.buildSources) { 504 | clearArtifact = """ 505 | configurations.archives.artifacts.with { archives -> 506 | removeFromArchives(archives, 'javadoc.jar') 507 | removeFromArchives(archives, 'sources.jar') 508 | } 509 | """ 510 | } else if (!gitExt.buildJavaDocs) { 511 | clearArtifact = """ 512 | configurations.archives.artifacts.with { archives -> 513 | removeFromArchives(archives, 'javadoc.jar') 514 | } 515 | """ 516 | } else if (!gitExt.buildSources) { 517 | clearArtifact = """ 518 | configurations.archives.artifacts.with { archives -> 519 | removeFromArchives(archives, 'sources.jar') 520 | } 521 | """ 522 | } 523 | 524 | def clearJavadocsSource = """ 525 | def removeFromArchives(def archives, def search) { 526 | def jarArtifact 527 | archives.each { 528 | if (it.file =~ search) { 529 | jarArtifact = it 530 | } 531 | } 532 | println "JAR to delete: \${jarArtifact}" 533 | if (jarArtifact) { 534 | archives.remove(jarArtifact) 535 | } 536 | } 537 | $clearArtifact 538 | """ 539 | 540 | new File(dependencyDir, "gitDroid.gradle") << """\ 541 | apply plugin: 'maven' 542 | 543 | if (project.hasProperty('android')) { 544 | afterEvaluate { 545 | android.libraryVariants.all { variant -> 546 | def name = variant.buildType.name 547 | if (name.equals("debug")) { 548 | return; // Skip debug builds. 549 | } 550 | def task = project.tasks.create "gitDroidJar\${variant.name.capitalize()}", Jar 551 | task.dependsOn variant.javaCompile 552 | //Include Java classes 553 | task.from variant.javaCompile.destinationDir 554 | //Include dependent jars with some exceptions 555 | task.from configurations.compile.findAll { 556 | it.getName() != 'android.jar' && !it.getName().startsWith('junit') && !it.getName().startsWith('hamcrest') 557 | }.collect { 558 | it.isDirectory() ? it : zipTree(it) 559 | } 560 | artifacts.add('archives', task); 561 | } 562 | } 563 | } 564 | 565 | afterEvaluate { 566 | uploadArchives { 567 | repositories { 568 | mavenDeployer { 569 | repository(url: "$gitProjectExt.localRepo") 570 | pom.groupId = '$dependency.group' 571 | pom.artifactId = '$dependency.name' 572 | pom.version = '$dependency.version' 573 | pom.project { 574 | packaging = 'aar' 575 | properties { 576 | gitDroid { 577 | ref = "$gitExt.ref" 578 | } 579 | } 580 | } 581 | } 582 | } 583 | } 584 | } 585 | 586 | 587 | $clearJavadocsSource 588 | 589 | task uploadArchivesDeps (dependsOn:uploadArchives) { 590 | doLast { 591 | def configurationName = 'compile' 592 | def repoDir = "$gitProjectExt.localRepo" 593 | def useLifecycle = $gitProjectExt.test.useLifecycle 594 | def log = (useLifecycle) ? project.logger.&lifecycle : project.logger.&debug 595 | def Configuration configuration = project.configurations.getByName(configurationName) 596 | configuration.resolvedConfiguration.resolvedArtifacts.each { artifact -> 597 | def moduleVersionId = artifact.moduleVersion.id 598 | 599 | def moduleDir = file(repoDir + File.separator + "\${(moduleVersionId.group.replaceAll('\\\\.', '/'))}/\${moduleVersionId.name}/\${moduleVersionId.version}") 600 | log("artifact moduleDir:\$moduleDir.absolutePath") 601 | moduleDir.mkdirs() 602 | log("artifact moduleDir exists:\${moduleDir.exists()}") 603 | log("artifact.file:\$artifact.file exists:\${artifact.file.exists()} artifact.file.name:\$artifact.file.name artifact.file.isDirectory:\${artifact.file.isDirectory()}") 604 | copy { 605 | from artifact.file 606 | into moduleDir.absolutePath 607 | } 608 | } 609 | def componentIds = configuration.incoming.resolutionResult.allDependencies.collect { it.selected.id } 610 | 611 | def result = project.dependencies.createArtifactResolutionQuery() 612 | .forComponents(componentIds) 613 | .withArtifacts(MavenModule, MavenPomArtifact) 614 | .execute() 615 | 616 | result.resolvedComponents.each { component -> 617 | def componentId = component.id 618 | if (componentId instanceof ModuleComponentIdentifier) { 619 | def moduleDir = file(repoDir + File.separator + "\${(componentId.group.replaceAll('\\\\.', '/'))}/\${componentId.module}/\${componentId.version}") 620 | log("pom moduleDir:\$moduleDir.absolutePath") 621 | moduleDir.mkdirs() 622 | log("pom moduleDir exists:\${moduleDir.exists()}") 623 | def pomFile = component.getArtifacts(MavenPomArtifact)[0].file 624 | log("pomFile:\$pomFile.absolutePath exists:\${pomFile.exists()} name:\$pomFile.name isDirectory:\${pomFile.isDirectory()}") 625 | copy { 626 | from pomFile 627 | into moduleDir.absolutePath 628 | } 629 | } 630 | } 631 | 632 | def resultsource = project.dependencies.createArtifactResolutionQuery() 633 | .forComponents(componentIds) 634 | .withArtifacts(JvmLibrary, SourcesArtifact, JavadocArtifact) 635 | .execute() 636 | 637 | resultsource.resolvedComponents.each { component -> 638 | if (!component) { 639 | return 640 | } 641 | def componentId = component.id 642 | if (componentId instanceof ModuleComponentIdentifier) { 643 | def moduleDir = file(repoDir + File.separator + "\${(componentId.group.replaceAll('\\\\.', '/'))}/\${componentId.module}/\${componentId.version}") 644 | log("sources javadoc moduleDir:\$moduleDir.absolutePath") 645 | moduleDir.mkdirs() 646 | log("sources javadoc moduleDir exists:\${moduleDir.exists()}") 647 | if (component.getArtifacts(SourcesArtifact)[0] != null) { 648 | log "sources not null" 649 | 650 | def sourcesFile = component.getArtifacts(SourcesArtifact)[0].file 651 | log("sourcesFile:\$sourcesFile.absolutePath exists:\${sourcesFile.exists()} name:\$sourcesFile.name isDirectory:\${sourcesFile.isDirectory()}") 652 | if (sourcesFile.exists()) { 653 | copy { 654 | from sourcesFile 655 | into moduleDir.absolutePath 656 | } 657 | } else { 658 | log("sourcesFile does not exist") 659 | } 660 | } else { 661 | log "sources are null" 662 | } 663 | if (component.getArtifacts(JavadocArtifact)[0] != null) { 664 | def javadocFile = component.getArtifacts(JavadocArtifact)[0].file 665 | log("javadocFile:\$javadocFile.absolutePath exists:\${javadocFile.exists()} name:\$javadocFile.name isDirectory:\${javadocFile.isDirectory()}") 666 | if (javadocFile.exists()) { 667 | copy { 668 | from javadocFile 669 | into moduleDir.absolutePath 670 | } 671 | } else { 672 | log("javadocFile does not exist") 673 | } 674 | } else { 675 | log("javadoc is null") 676 | } 677 | } 678 | } 679 | } 680 | } 681 | 682 | """ 683 | 684 | } 685 | 686 | /** 687 | * Adds "apply from: (../)gitDroid.gradle" to the git dependency module 688 | * 689 | * @param project the main project 690 | * @param buildFile the buildFile to add the apply call to 691 | * @param parentPath if the buildFile exists in parent or a sub module 692 | * @return 693 | */ 694 | def static appendArchiveFile(Project project, File buildFile, boolean parentPath) { 695 | def log = project.logger 696 | log.debug("init with buildFile:" + buildFile.absolutePath + " parentPath:$parentPath") 697 | if (!Utils.containsGitDroid(project, buildFile)) { 698 | buildFile.append("\napply from: '" + ((parentPath) ? "../" : "") + "gitDroid.gradle'") 699 | } 700 | } 701 | 702 | def static appendArchiveFileSubProject(Project project, File buildFileModule, File dependencyDir) { 703 | def log = project.logger 704 | log.debug("checking for project dependencies in:" + buildFileModule.absolutePath) 705 | } 706 | 707 | /** 708 | * List all git dependencies in the project to log.lifecycle 709 | * 710 | * @param project the main project 711 | * @return 712 | */ 713 | static def listGitDependencies(Project project) { 714 | def log = project.logger 715 | 716 | def dependenciesResolved = DepResolved.newInstance() 717 | dependenciesResolved.list = getGitDependencies(project, dependenciesResolved.type) 718 | 719 | def dependenciesUnresolved = DepUnresolved.newInstance() 720 | dependenciesUnresolved.list = getGitDependencies(project, dependenciesUnresolved.type) 721 | 722 | def dependenciesNeedsBuild = DepNeedsBuild.newInstance() 723 | dependenciesNeedsBuild.list = getGitDependencies(project, dependenciesNeedsBuild.type) 724 | 725 | 726 | log.lifecycle("Dependencies Unresolved:") 727 | dependenciesUnresolved.list.each { ExternalModuleDependency dep, GitDependencyExt gitExt -> 728 | log.lifecycle("$dep.name${gitExt.toListString()}") 729 | } 730 | log.lifecycle("Dependencies Resolved:") 731 | dependenciesResolved.list.each { ExternalModuleDependency dep, GitDependencyExt gitExt -> 732 | log.lifecycle("$dep.name${gitExt.toListString()}") 733 | } 734 | log.lifecycle("Dependencies Which Need Build:") 735 | dependenciesNeedsBuild.list.each { ExternalModuleDependency dep, GitDependencyExt gitExt -> 736 | log.lifecycle("$dep.name${gitExt.toListString()}") 737 | } 738 | } 739 | } 740 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/helpers/GitHelper.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.helpers 2 | 3 | import com.testfunction.extensions.dependency.GitDependencyExt 4 | import org.ajoberstar.grgit.Grgit 5 | import org.ajoberstar.grgit.Repository 6 | import org.ajoberstar.grgit.auth.AuthConfig 7 | import org.ajoberstar.grgit.exception.GrgitException 8 | import org.eclipse.jgit.storage.file.WindowCacheConfig 9 | import org.gradle.api.Project 10 | import org.gradle.api.artifacts.ExternalModuleDependency 11 | import org.gradle.api.logging.Logger 12 | import org.gradle.api.tasks.StopActionException 13 | 14 | /** 15 | * GitHelper 16 | * Git calls are all made through this class and passed to Grgit or jgit depending on need. 17 | * 18 | * Created by Brandon on 1/6/2016. 19 | */ 20 | class GitHelper { 21 | /** 22 | * Clone the dependency git repository provided by gitExt. 23 | * 24 | * @param project the main project 25 | * @param dependency the git dependency 26 | * @param gitExt the git extension of the dependency 27 | * @param destinationDir the temp directory to clone into 28 | * @return 29 | */ 30 | def static boolean cloneGitRepository( 31 | Project project, 32 | ExternalModuleDependency dependency, 33 | GitDependencyExt gitExt, 34 | File destinationDir) { 35 | 36 | def log = project.logger; 37 | log.debug("init cloneGitRepository") 38 | 39 | def build = true 40 | 41 | def grgit 42 | if (gitExt.ref) { 43 | grgit = Grgit.clone(dir: destinationDir.absolutePath, uri: gitExt.repo, checkout: true, refToCheckout: gitExt.ref) 44 | } else { 45 | grgit = Grgit.clone(dir: destinationDir.absolutePath, uri: gitExt.repo) 46 | } 47 | 48 | log.debug("clone command created checking for branch $gitExt.ref $gitExt.refType") 49 | log.debug("calling git") 50 | 51 | closeGit(log, grgit) 52 | build = testRepo(log, destinationDir) 53 | return build 54 | } 55 | 56 | /** 57 | * Close the git repository 58 | * 59 | * @param log the logger to be used 60 | * @param git current instance of Grgit 61 | * @return 62 | */ 63 | static def closeGit(Logger log, Grgit git) { 64 | log.debug("closing repository") 65 | git.repository.jgit.repository.close() 66 | git.repository.jgit.close() 67 | git.close() 68 | } 69 | 70 | /** 71 | * Use existing repository and call a pull command. 72 | * 73 | * @param project the main project 74 | * @param dependency the git dependency 75 | * @param gitExt the git extension for the dependency 76 | * @param destinationDir the directory containing the git repository 77 | * @return 78 | * @throws IOException 79 | * @throws GrgitException 80 | */ 81 | def static boolean pullGitRepository(Project project, 82 | ExternalModuleDependency dependency, 83 | GitDependencyExt gitExt, 84 | File destinationDir) throws IOException, GrgitException { 85 | def log = project.logger 86 | log.debug("pullGitRepository init with dependency:$dependency gitExt:$gitExt dependencyDir:$destinationDir") 87 | def build = true 88 | def grgit = Grgit.open(dir:destinationDir.absolutePath) 89 | 90 | try { 91 | if (hasAtLeastOneReference(log, grgit)) { 92 | log.debug("has at least one ref") 93 | Repository repo = grgit.repository 94 | log.debug("!gitExt.branch:$gitExt.refType && repo.branch != master : $repo.jgit.repository.branch") 95 | 96 | if (repo.jgit.repository.branch != "master") { 97 | log.debug("checkout master") 98 | repo.jgit.stashCreate().call() 99 | def cmd = repo.jgit.checkout() 100 | cmd.name = "origin/master" 101 | cmd.force = true 102 | cmd.call() 103 | } 104 | 105 | log.debug("git pull") 106 | handleGitCreds(log, gitExt, grgit) 107 | grgit.fetch(prune: true) 108 | 109 | grgit.branch.remove(names: ['gitDroid-branch'], force: true) 110 | if (gitExt.ref) { 111 | grgit.checkout(branch: "gitDroid-branch", startPoint: gitExt.ref, createBranch: true) 112 | } else { 113 | grgit.checkout(branch: "gitDroid-branch", createBranch: true) 114 | } 115 | } else { 116 | log.warn("does not have any refs") 117 | closeGit(log, grgit) 118 | throw new StopActionException("not a valid local git repository") 119 | } 120 | closeGit(log, grgit) 121 | } catch (e) { 122 | e.printStackTrace() 123 | if (grgit != null) { 124 | closeGit(log, grgit) 125 | } 126 | throw new StopActionException("Error pulling repository") 127 | } 128 | 129 | build = testRepo(log, destinationDir) 130 | return build 131 | } 132 | 133 | /** 134 | * Sets credentials for use with Grgit instance to clone/pull from a private repo 135 | * 136 | * @param log the logger to use 137 | * @param gitExt the git extension of the dependency 138 | * @param grgit the current instance of Grgit 139 | * @return 140 | */ 141 | def static handleGitCreds(Logger log, GitDependencyExt gitExt, Grgit grgit) { 142 | clearOriginalCreds() 143 | if (gitExt.credentials != null && !gitExt.credentials.isEmpty()) { 144 | log.debug("credentials found") 145 | 146 | def username = (String) gitExt.credentials["username"] 147 | if (username) { 148 | System.properties[AuthConfig.USERNAME_OPTION] = username 149 | } 150 | def password = (String) gitExt.credentials["password"] 151 | if (password) { 152 | log.debug("password found") 153 | System.properties[AuthConfig.PASSWORD_OPTION] = password 154 | } else { 155 | log.warn("credentials missing username or password") 156 | } 157 | 158 | def sshKey = (String) gitExt.credentials["sshKey"] 159 | if (sshKey) { 160 | System.properties[AuthConfig.SSH_PRIVATE_KEY_OPTION] = sshKey 161 | } 162 | } 163 | return true 164 | } 165 | 166 | /** 167 | * Removes current credentials if they exists from a prior Grgit call 168 | * 169 | * @return 170 | */ 171 | static def clearOriginalCreds() { 172 | if (System.properties.hasProperty(AuthConfig.USERNAME_OPTION)) { 173 | System.properties.remove(AuthConfig.USERNAME_OPTION) 174 | } 175 | if (System.properties.hasProperty(AuthConfig.PASSWORD_OPTION)) { 176 | System.properties.remove(AuthConfig.PASSWORD_OPTION) 177 | } 178 | if (System.properties.hasProperty(AuthConfig.SSH_PRIVATE_KEY_OPTION)) { 179 | System.properties.remove(AuthConfig.SSH_PRIVATE_KEY_OPTION) 180 | } 181 | return true 182 | } 183 | 184 | /** 185 | * Test that the repository in the destinationDir is valid 186 | * 187 | * @param log the logger to use 188 | * @param destinationDir the temp directory that contains the git repo 189 | * @return true if valid repo is found, false if not 190 | */ 191 | def static boolean testRepo(Logger log, File destinationDir) { 192 | log.debug("testing repo") 193 | def ret 194 | def Grgit grgit = null 195 | try { 196 | grgit = Grgit.open(dir:destinationDir.absolutePath) 197 | log.debug("grgit:"+grgit) 198 | if (hasAtLeastOneReference(log, grgit)) { 199 | log.debug("has at least one reference") 200 | closeGit(log, grgit) 201 | ret = true 202 | } else { 203 | log.warn("does not have any references") 204 | closeGit(log, grgit) 205 | ret = false 206 | } 207 | } catch (e) { 208 | ret = false 209 | log.warn("git failure:$e.message") 210 | e.printStackTrace() 211 | if (grgit != null) { 212 | closeGit(log, grgit) 213 | } 214 | } 215 | 216 | log.debug("done testing repo") 217 | return ret 218 | } 219 | 220 | /** 221 | * Validate that the repository passed contains at least one reference 222 | * 223 | * @param log the logger to use 224 | * @param grgit the current instance of Grgit 225 | * @return 226 | */ 227 | def static boolean hasAtLeastOneReference(Logger log, Grgit grgit) { 228 | def hasRef = grgit.repository.jgit.repository.allRefs.find { key, ref -> ref.objectId != null } 229 | log.debug "hasRef:" + hasRef 230 | return hasRef != null 231 | } 232 | } -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/internal/utils/Utils.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.internal.utils 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.tasks.StopExecutionException 5 | 6 | import static com.android.SdkConstants.PLATFORM_WINDOWS 7 | import static com.android.SdkConstants.currentPlatform 8 | 9 | /** 10 | * Utils 11 | * Basic file checks such as checking that a build file is an android library, contains gitDroid, or isn't too 12 | * long for windows file systems. 13 | * 14 | * Created by Brandon on 1/14/2016. 15 | */ 16 | class Utils { 17 | /** 18 | * Check that file contains "com.android.library" 19 | * 20 | * @param project the main project 21 | * @param file the file to check 22 | * @return true if is library, false if not 23 | */ 24 | static def isAndroidLibrary(Project project, File file) { 25 | def log = project.logger 26 | log.debug("isAndroidLibrary init checking file:"+file.absolutePath) 27 | if (!file.exists()) { 28 | log.warn("file does not exist") 29 | } 30 | return (file.exists()) ? file.getText('UTF-8').contains("com.android.library") : false 31 | } 32 | 33 | /** 34 | * Check that file contains "gitDroid.gradle" 35 | * 36 | * @param project the main project 37 | * @param file the file to check 38 | * @return true if contain gitDroid.gradle, false if not 39 | */ 40 | def static boolean containsGitDroid(Project project, File file) { 41 | def log = project.logger 42 | log.debug("containsGitDroid init with file:"+file.absolutePath) 43 | if (!file.exists()) { 44 | log.warn("file does not exist") 45 | } 46 | return (file.exists()) ? file.getText('UTF-8').contains("gitDroid.gradle") : false 47 | 48 | } 49 | 50 | /** 51 | * Check if project's gitDroid.workingDir is too long for a Windows platform. 52 | * It is common for android compile dependency libraries to have a length > 160 53 | * characters. Due to this we need to ensure that the workingDir path is <= 64 54 | * characters in length. This only applies to Windows 55 | * 56 | * @param project target project used for log 57 | * @param workingDir the directory to test 58 | * @param dieOnFailure true if should throw exception on failure false to log 59 | * @return 60 | * @throws org.gradle.api.tasks.StopExecutionException if workingDir length > 65 and dieOnFailure is true 61 | */ 62 | @SuppressWarnings("GroovyLocalVariableNamingConvention") 63 | static def validateWorkingDirPathLength(Project project, File workingDir, boolean dieOnFailure) 64 | throws StopExecutionException { 65 | def log = project.logger 66 | log.debug("validateWorkingDirPathLength init with workingDir:"+workingDir.absolutePath) 67 | if (currentPlatform() == PLATFORM_WINDOWS) { 68 | log.debug("platform is windows") 69 | if (workingDir.absolutePath.toCharArray().length > 65) { 70 | def reason = "Unlikely to build git projects with current long gitDroid workingDir as Windows path length limit will likely be reached. Change gitDroid workingDir to shorter path. e.g., 'C:\\tmp' length: 5 vs current workingDir:'$workingDir.absolutePath' length:${workingDir.absolutePath.length()}" 71 | if (dieOnFailure) { 72 | throw new StopExecutionException(reason) 73 | } else { 74 | log.warn(reason) 75 | } 76 | } 77 | 78 | } else { 79 | log.debug("not a windows platform, ignoring path length") 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/tasks/CompileEachTask.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.tasks 2 | 3 | import org.gradle.api.tasks.GradleBuild 4 | import org.gradle.api.tasks.StopActionException 5 | import org.gradle.api.tasks.TaskAction 6 | 7 | /** 8 | * This task should never be visible by the user. It is created on the fly and deleted when done. 9 | * 10 | * Created by Brandon on 1/6/2016. 11 | */ 12 | class CompileEachTask extends GradleBuild { 13 | def run = false 14 | 15 | @Override 16 | def String getDescription() { 17 | "Cannot be run directly. Used as a template task to create individual build tasks for each git project." 18 | } 19 | 20 | @Override 21 | def String getGroup() { 22 | "GitDroid" 23 | } 24 | 25 | @TaskAction 26 | def compileEach() { 27 | def log = project.logger 28 | log.debug("compileEach init") 29 | if (!run) { 30 | throw new StopActionException("Cannot run compileEach directly") 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/tasks/GitDependenciesTask.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.tasks 2 | 3 | import com.testfunction.internal.helpers.GitDependenciesHelper 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.logging.Logger 6 | import org.gradle.api.logging.Logging 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | /** 10 | * Gradle task for initializing git dependencies 11 | * 12 | * Created by Brandon on 1/3/2016. 13 | */ 14 | class GitDependenciesTask extends DefaultTask { 15 | private static Logger LOG = Logging.getLogger(GitDependenciesTask) 16 | @Override 17 | def String getDescription() { 18 | "Initialize git-based dependencies." 19 | } 20 | 21 | @Override 22 | def String getGroup() { 23 | "GitDroid" 24 | } 25 | 26 | 27 | @TaskAction 28 | def initGitDependencies() { 29 | LOG.debug("initGitDependencies init") 30 | GitDependenciesHelper.initGitDependencies(project) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/tasks/GitDependencyGetTask.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.tasks 2 | 3 | import com.testfunction.internal.helpers.GitDependenciesHelper 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.logging.Logger 6 | import org.gradle.api.logging.Logging 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | /** 10 | * Gradle task for clone/pull of git dependency repositories 11 | * 12 | * Created by Brandon on 1/6/2016. 13 | */ 14 | class GitDependencyGetTask extends DefaultTask { 15 | def static Logger LOG = Logging.getLogger(GitDependenciesTask) 16 | 17 | @Override 18 | def String getDescription() { 19 | "Get projects via a git clone or pull" 20 | } 21 | 22 | @Override 23 | def String getGroup() { 24 | "GitDroid" 25 | } 26 | 27 | @TaskAction 28 | def getGitRepo() { 29 | LOG.debug("getGitRepo init") 30 | GitDependenciesHelper.getGitRepo(project) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/groovy/com/testfunction/tasks/ListDependenciesTask.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction.tasks 2 | 3 | import com.testfunction.internal.helpers.GitDependenciesHelper 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.logging.Logger 6 | import org.gradle.api.logging.Logging 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | /** 10 | * Gradle task to list git dependencies grouped by resolved, unresolved, and needsbuild 11 | * 12 | * Created by Brandon on 1/3/2016. 13 | */ 14 | class ListDependenciesTask extends DefaultTask { 15 | private static Logger LOG = Logging.getLogger(ListDependenciesTask) 16 | @Override 17 | def String getDescription() { 18 | "Create list of git-based dependencies." 19 | } 20 | 21 | @Override 22 | def String getGroup() { 23 | "GitDroid" 24 | } 25 | 26 | 27 | @TaskAction 28 | def listGitDependencies() { 29 | LOG.debug("listGitDependencies init") 30 | GitDependenciesHelper.listGitDependencies(project) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/com.testfunction.gradle-gitdroid.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.testfunction.GradleGitDroidPlugin -------------------------------------------------------------------------------- /src/test/groovy/com/testfunction/AndroidLibraryTest.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction 2 | 3 | import org.gradle.api.logging.Logger 4 | import org.gradle.api.logging.Logging 5 | import org.gradle.testkit.runner.GradleRunner 6 | import spock.lang.Specification 7 | 8 | import static com.testfunction.TestTypes.* 9 | import static org.gradle.testkit.runner.TaskOutcome.FAILED 10 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS 11 | import static com.android.SdkConstants.ANDROID_HOME_ENV 12 | import static com.android.SdkConstants.FN_LOCAL_PROPERTIES 13 | import static com.android.SdkConstants.PLATFORM_WINDOWS 14 | import static com.android.SdkConstants.PLATFORM_DARWIN 15 | import static com.android.SdkConstants.PLATFORM_LINUX 16 | import static com.android.SdkConstants.SDK_DIR_PROPERTY 17 | import static com.android.SdkConstants.currentPlatform 18 | 19 | /** 20 | * Android library tests 21 | * 22 | * Created by Brandon on 1/1/2016. 23 | */ 24 | class AndroidLibraryTest extends Specification { 25 | final private static Logger LOG = Logging.getLogger GradleGitDroidPlugin 26 | def File buildFile; 27 | def File localPropertiesFile; 28 | def File srcMainDir; 29 | def File manifest; 30 | def List pluginClasspath 31 | def File classPath 32 | def File testProjectDir 33 | def classPathString 34 | def final extraArgumentsDebug = "--debug" 35 | def final extraArgumentsStack = "--stacktrace" 36 | def URI localRepo 37 | def boolean shouldClean = true 38 | 39 | def setup() { 40 | testProjectDir = File.createTempDir("gitDroid",""); 41 | buildFile = new File(testProjectDir, "build.gradle") 42 | LOG.lifecycle("buildFile:" + buildFile + " exists:" + buildFile.exists()); 43 | localPropertiesFile = new File(testProjectDir, "local.properties") 44 | def srcMainSubPath = File.separator + "src" + File.separator + "main" 45 | srcMainDir = new File(testProjectDir.absolutePath, srcMainSubPath) 46 | srcMainDir.mkdirs(); 47 | manifest = new File(srcMainDir, "AndroidManifest.xml"); 48 | 49 | LOG.lifecycle("manifest:" + manifest + " exists:" + manifest.exists() + " isdir:" + manifest.directory) 50 | 51 | def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") 52 | if (pluginClasspathResource == null) { 53 | throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") 54 | } 55 | classPath = new File("build/classes/main"); 56 | LOG.lifecycle("file:" + classPath.absolutePath + " exists:" + classPath.exists()); 57 | pluginClasspath = pluginClasspathResource.readLines().collect { new File(it) } 58 | populateLocalProperties() 59 | 60 | def manifestContent = ''' 62 | 63 | ''' 64 | manifest << manifestContent 65 | 66 | LOG.lifecycle("manifest:" + manifest); 67 | 68 | classPathString = pluginClasspath 69 | .collect { it.absolutePath.replace('\\', '\\\\') } 70 | .collect { "'$it'" } 71 | .join(", ") 72 | 73 | LOG.lifecycle("classPathString:" + classPathString); 74 | } 75 | 76 | 77 | def "passing test without missing dep"() { 78 | given: 79 | populateBuildFile(DEPENDENCY_NONE) 80 | LOG.lifecycle("buildFile:" + buildFile); 81 | 82 | when: 83 | def buildResult = GradleRunner.create() 84 | .withProjectDir(testProjectDir) 85 | .withTestKitDir(testKitDir) 86 | .withPluginClasspath(pluginClasspath) 87 | .withArguments(populateArguments(false, true, "initGitDependencies")) 88 | .build(); 89 | 90 | then: 91 | LOG.lifecycle("buildResult output:" + buildResult.output) 92 | } 93 | 94 | def "tasks list without missing dep"() { 95 | given: 96 | populateBuildFile(DEPENDENCY_NONE) 97 | LOG.lifecycle("buildFile:" + buildFile); 98 | 99 | when: 100 | def buildResult = GradleRunner.create() 101 | .withProjectDir(testProjectDir) 102 | .withTestKitDir(testKitDir) 103 | .withPluginClasspath(pluginClasspath) 104 | .withArguments(populateArguments(false, true, "tasks")) 105 | .build(); 106 | then: 107 | LOG.lifecycle("buildResult output:" + buildResult.output) 108 | } 109 | 110 | def "tasks list with resolvable dep"() { 111 | given: 112 | populateBuildFile(DEPENDENCY_NONE) 113 | LOG.lifecycle("buildFile:" + buildFile); 114 | 115 | when: 116 | def buildResult = GradleRunner.create() 117 | .withProjectDir(testProjectDir) 118 | .withTestKitDir(testKitDir) 119 | .withPluginClasspath(pluginClasspath) 120 | .withArguments(populateArguments(false, true, "tasks")) 121 | .build(); 122 | then: 123 | LOG.lifecycle("buildResult output:" + buildResult.output) 124 | } 125 | 126 | def "failing build with missing dep without git extension"() { 127 | given: 128 | populateBuildFile(DEPENDENCY_WITHOUT_GIT) 129 | LOG.lifecycle("buildFile:" + buildFile); 130 | 131 | when: 132 | def buildResult = GradleRunner.create() 133 | .withProjectDir(testProjectDir) 134 | .withTestKitDir(testKitDir) 135 | .withPluginClasspath(pluginClasspath) 136 | .withArguments(populateArguments(false, true)) 137 | .buildAndFail(); 138 | buildResult.tasks(FAILED) 139 | then: 140 | LOG.lifecycle("buildResult output:" + buildResult.output) 141 | } 142 | 143 | def "failing one build with missing dep with git extension"() { 144 | given: 145 | populateBuildFile(DEPENDENCY_WITH_GIT) 146 | LOG.lifecycle("buildFile:" + buildFile); 147 | 148 | when: 149 | def buildResult = GradleRunner.create() 150 | .withProjectDir(testProjectDir) 151 | .withTestKitDir(testKitDir) 152 | .withPluginClasspath(pluginClasspath) 153 | .withArguments(populateArguments(false, true)) 154 | .buildAndFail(); 155 | buildResult.tasks(FAILED) 156 | then: 157 | LOG.lifecycle("buildResult output:" + buildResult.output) 158 | } 159 | 160 | def "passing build twice with missing dep with git extension"() { 161 | given: 162 | populateBuildFile(DEPENDENCY_WITH_GIT_AUTOMATIC_RESOLVE) 163 | LOG.lifecycle("buildFile:" + buildFile); 164 | 165 | def runner = GradleRunner.create() 166 | .withProjectDir(testProjectDir) 167 | .withTestKitDir(testKitDir) 168 | .withPluginClasspath(pluginClasspath) 169 | .withArguments(populateArguments(false, true)) 170 | 171 | when: 172 | def buildResult = runner.buildAndFail(); 173 | buildResult.tasks(FAILED) 174 | then: 175 | LOG.lifecycle("buildResult failed as expected output:" + buildResult.output) 176 | 177 | when: 178 | def buildResult2 = runner.build() 179 | buildResult2.tasks(SUCCESS) 180 | then: 181 | LOG.lifecycle("buildResult success:"+buildResult2.output) 182 | } 183 | 184 | def "passing build twice with missing dep with git extension initGitDependencies"() { 185 | 186 | given: 187 | populateBuildFile(DEPENDENCY_WITH_GIT) 188 | LOG.lifecycle("buildFile:" + buildFile); 189 | 190 | def runner = GradleRunner.create() 191 | .withProjectDir(testProjectDir) 192 | .withTestKitDir(testKitDir) 193 | .withPluginClasspath(pluginClasspath) 194 | .withArguments(populateArguments(true, true, "initGitDependencies")) 195 | 196 | when: 197 | def buildResult = runner.buildAndFail(); 198 | buildResult.tasks(FAILED) 199 | then: 200 | LOG.lifecycle("buildResult failed as expected output:" + buildResult.output) 201 | 202 | when: 203 | def buildResult2 = runner.build() 204 | buildResult2.tasks(SUCCESS) 205 | then: 206 | LOG.lifecycle("buildResult success:"+buildResult2.output) 207 | } 208 | 209 | def "fail first build missing dep using git direct call initGitDependencies"() { 210 | given: 211 | populateBuildFile(DEPENDENCY_WITH_GIT_COMMIT) 212 | LOG.lifecycle("buildFile:" + buildFile); 213 | 214 | when: 215 | def buildResult = GradleRunner.create() 216 | .withProjectDir(testProjectDir) 217 | .withTestKitDir(testKitDir) 218 | .withPluginClasspath(pluginClasspath) 219 | .withArguments(populateArguments(false, true, "initGitDependencies")) 220 | .buildAndFail(); 221 | buildResult.tasks(FAILED) 222 | then: 223 | LOG.lifecycle("buildResult output:" + buildResult.output) 224 | } 225 | 226 | def "fail build missing dep using git direct call initGitDependencies wrong commit"() { 227 | given: 228 | populateBuildFile(DEPENDENCY_WITH_GIT_COMMIT_WRONG) 229 | LOG.lifecycle("buildFile:" + buildFile); 230 | 231 | when: 232 | def buildResult = GradleRunner.create() 233 | .withProjectDir(testProjectDir) 234 | .withTestKitDir(testKitDir) 235 | .withPluginClasspath(pluginClasspath) 236 | .withArguments(populateArguments(false, true, "initGitDependencies")) 237 | .buildAndFail(); 238 | buildResult.tasks(FAILED) 239 | then: 240 | LOG.lifecycle("buildResult output:" + buildResult.output) 241 | } 242 | 243 | def "fail build missing dep using git direct call initGitDependencies branch"() { 244 | 245 | given: 246 | populateBuildFile(DEPENDENCY_WITH_GIT_BRANCH) 247 | LOG.lifecycle("buildFile:" + buildFile); 248 | 249 | when: 250 | def buildResult = GradleRunner.create() 251 | .withProjectDir(testProjectDir) 252 | .withTestKitDir(testKitDir) 253 | .withPluginClasspath(pluginClasspath) 254 | .withArguments(populateArguments(false, true, "initGitDependencies")) 255 | .buildAndFail(); 256 | buildResult.tasks(FAILED) 257 | then: 258 | LOG.lifecycle("buildResult output:" + buildResult.output) 259 | } 260 | 261 | def "pass build twice missing dep using git direct call listGitDependencies branch"() { 262 | 263 | given: 264 | populateBuildFile(DEPENDENCY_WITH_GIT_BRANCH) 265 | LOG.lifecycle("buildFile:" + buildFile); 266 | 267 | when: 268 | def buildResult = GradleRunner.create() 269 | .withProjectDir(testProjectDir) 270 | .withTestKitDir(testKitDir) 271 | .withPluginClasspath(pluginClasspath) 272 | .withArguments(populateArguments(false, true, "initGitDependencies")) 273 | .buildAndFail(); 274 | buildResult.tasks(FAILED) 275 | then: 276 | LOG.lifecycle("buildResult output:" + buildResult.output) 277 | 278 | when: 279 | def buildResult2 = GradleRunner.create() 280 | .withProjectDir(testProjectDir) 281 | .withTestKitDir(testKitDir) 282 | .withPluginClasspath(pluginClasspath) 283 | .withArguments(populateArguments(false, true, "listGitDependencies")) 284 | .build(); 285 | buildResult2.tasks(SUCCESS) 286 | then: 287 | LOG.lifecycle("buildResult2 output:" + buildResult2.output) 288 | } 289 | 290 | def "pass build twice missing dep using git direct call listGitDependencies tag"() { 291 | 292 | given: 293 | populateBuildFile(DEPENDENCY_WITH_GIT_TAG) 294 | LOG.lifecycle("buildFile:" + buildFile); 295 | 296 | when: 297 | def buildResult = GradleRunner.create() 298 | .withProjectDir(testProjectDir) 299 | .withTestKitDir(testKitDir) 300 | .withPluginClasspath(pluginClasspath) 301 | .withArguments(populateArguments(false, true, "initGitDependencies")) 302 | .buildAndFail(); 303 | buildResult.tasks(FAILED) 304 | then: 305 | LOG.lifecycle("buildResult output:" + buildResult.output) 306 | 307 | when: 308 | def buildResult2 = GradleRunner.create() 309 | .withProjectDir(testProjectDir) 310 | .withTestKitDir(testKitDir) 311 | .withPluginClasspath(pluginClasspath) 312 | .withArguments(populateArguments(false, true, "listGitDependencies")) 313 | .build(); 314 | buildResult2.tasks(SUCCESS) 315 | then: 316 | LOG.lifecycle("buildResult2 output:" + buildResult2.output) 317 | } 318 | 319 | def "pass build twice missing dep using git direct call initGitDependencies branch"() { 320 | 321 | given: 322 | populateBuildFile(DEPENDENCY_WITH_GIT_BRANCH) 323 | LOG.lifecycle("buildFile:" + buildFile); 324 | 325 | when: 326 | def buildResult = GradleRunner.create() 327 | .withProjectDir(testProjectDir) 328 | .withTestKitDir(testKitDir) 329 | .withPluginClasspath(pluginClasspath) 330 | .withArguments(populateArguments(false, true, "initGitDependencies")) 331 | .buildAndFail(); 332 | buildResult.tasks(FAILED) 333 | then: 334 | LOG.lifecycle("buildResult output:" + buildResult.output) 335 | 336 | when: 337 | def buildResult2 = GradleRunner.create() 338 | .withProjectDir(testProjectDir) 339 | .withTestKitDir(testKitDir) 340 | .withPluginClasspath(pluginClasspath) 341 | .withArguments(populateArguments(false, true, "initGitDependencies")) 342 | .build(); 343 | buildResult2.tasks(SUCCESS) 344 | then: 345 | LOG.lifecycle("buildResult2 output:" + buildResult2.output) 346 | } 347 | 348 | def "pass build twice missing dep using git direct call initGitDependencies branch keepUpdated"() { 349 | 350 | given: 351 | populateBuildFile(DEPENDENCY_WITH_GIT_BRANCH_KEEP_UPDATED) 352 | LOG.lifecycle("buildFile:" + buildFile); 353 | 354 | when: 355 | def buildResult = GradleRunner.create() 356 | .withProjectDir(testProjectDir) 357 | .withTestKitDir(testKitDir) 358 | .withPluginClasspath(pluginClasspath) 359 | .withArguments(populateArguments(false, true, "initGitDependencies")) 360 | .buildAndFail(); 361 | buildResult.tasks(FAILED) 362 | then: 363 | LOG.lifecycle("buildResult output:" + buildResult.output) 364 | 365 | when: 366 | def buildResult2 = GradleRunner.create() 367 | .withProjectDir(testProjectDir) 368 | .withTestKitDir(testKitDir) 369 | .withPluginClasspath(pluginClasspath) 370 | .withArguments(populateArguments(false, true, "initGitDependencies")) 371 | .build(); 372 | buildResult2.tasks(SUCCESS) 373 | then: 374 | LOG.lifecycle("buildResult2 output:" + buildResult2.output) 375 | } 376 | 377 | def "initGitDependencies dep without git gitConfig"() { 378 | given: 379 | populateBuildFile(DEPENDENCY_WITHOUT_GIT_DIFF_CONFIG) 380 | LOG.lifecycle("buildFile:" + buildFile); 381 | 382 | when: 383 | def buildResult = GradleRunner.create() 384 | .withProjectDir(testProjectDir) 385 | .withTestKitDir(testKitDir) 386 | .withPluginClasspath(pluginClasspath) 387 | .withArguments(populateArguments(false, true, "initGitDependencies")) 388 | .build(); 389 | buildResult.tasks(SUCCESS) 390 | then: 391 | LOG.lifecycle("buildResult output:" + buildResult.output) 392 | buildResult.output.contains('BUILD SUCCESSFUL') 393 | buildResult.task(":initGitDependencies").outcome == SUCCESS 394 | } 395 | 396 | def "passing test dep with git gitConfig"() { 397 | given: 398 | populateBuildFile(DEPENDENCY_WITH_GIT_DIFF_CONFIG) 399 | LOG.lifecycle("buildFile:" + buildFile); 400 | 401 | when: 402 | def buildResult = GradleRunner.create() 403 | .withProjectDir(testProjectDir) 404 | .withTestKitDir(testKitDir) 405 | .withPluginClasspath(pluginClasspath) 406 | .withArguments(populateArguments(false, true, "initGitDependencies")) 407 | .build(); 408 | buildResult.tasks(SUCCESS) 409 | then: 410 | LOG.lifecycle("buildResult output:" + buildResult.output) 411 | } 412 | 413 | def "passing multi dep with one git"() { 414 | given: 415 | populateBuildFile(DEPENDENCY_MULTIPLE_WITH_ONE_GIT) 416 | LOG.lifecycle("buildFile:" + buildFile); 417 | 418 | when: 419 | def buildResult = GradleRunner.create() 420 | .withProjectDir(testProjectDir) 421 | .withTestKitDir(testKitDir) 422 | .withPluginClasspath(pluginClasspath) 423 | .withArguments(populateArguments(false, true, "initGitDependencies")) 424 | .buildAndFail(); 425 | buildResult.tasks(FAILED) 426 | then: 427 | LOG.lifecycle("buildResult output:" + buildResult.output) 428 | when: 429 | def buildResult2 = GradleRunner.create() 430 | .withProjectDir(testProjectDir) 431 | .withTestKitDir(testKitDir) 432 | .withPluginClasspath(pluginClasspath) 433 | .withArguments(populateArguments(false, true, "initGitDependencies")) 434 | .build(); 435 | buildResult2.tasks(SUCCESS) 436 | then: 437 | LOG.lifecycle("buildResult2 output:" + buildResult2.output) 438 | } 439 | 440 | def "passing multi dep with both git"() { 441 | given: 442 | populateBuildFile(DEPENDENCY_MULTIPLE_WITH_ALL_GIT) 443 | LOG.lifecycle("buildFile:" + buildFile) 444 | 445 | when: 446 | def buildResult = GradleRunner.create() 447 | .withProjectDir(testProjectDir) 448 | .withTestKitDir(testKitDir) 449 | .withPluginClasspath(pluginClasspath) 450 | .withArguments(populateArguments(false, true, "initGitDependencies")) 451 | .buildAndFail() 452 | buildResult.tasks(FAILED) 453 | then: 454 | LOG.lifecycle("buildResult output:" + buildResult.output) 455 | when: 456 | def buildResult2 = GradleRunner.create() 457 | .withProjectDir(testProjectDir) 458 | .withTestKitDir(testKitDir) 459 | .withPluginClasspath(pluginClasspath) 460 | .withArguments(populateArguments(false, true, "initGitDependencies")) 461 | .build() 462 | buildResult2.tasks(SUCCESS) 463 | then: 464 | LOG.lifecycle("buildResult2 output:" + buildResult2.output) 465 | } 466 | 467 | def "failing test dep non existent"() { 468 | given: 469 | populateBuildFile(DEPENDENCY_NON_EXISTENT) 470 | LOG.lifecycle("buildFile:" + buildFile); 471 | 472 | when: 473 | def buildResult = GradleRunner.create() 474 | .withProjectDir(testProjectDir) 475 | .withTestKitDir(testKitDir) 476 | .withPluginClasspath(pluginClasspath) 477 | .withArguments(populateArguments(false, true, "initGitDependencies")) 478 | .buildAndFail(); 479 | buildResult.tasks(FAILED) 480 | then: 481 | LOG.lifecycle("buildResult output:" + buildResult.output) 482 | } 483 | 484 | def cleanup() { 485 | LOG.lifecycle("cleanup init for dir:" + testProjectDir.absolutePath) 486 | if (shouldClean) { 487 | if (testProjectDir.deleteDir()) { 488 | LOG.lifecycle("$testProjectDir.absolutePath deleted") 489 | } else { 490 | LOG.lifecycle("$testProjectDir.absolutePath could not be deleted") 491 | } 492 | } 493 | } 494 | 495 | private void populateBuildFile(def TestTypes testType) { 496 | LOG.lifecycle("populateBuildFile with TestTypes:${testType.name()}") 497 | def dependencyNone = "" 498 | 499 | def dependencyNoGit = """\ 500 | compile (group: 'com.testfunction', 501 | name:'lumberjill', 502 | version:'ASDF', 503 | ext: 'aar' 504 | ) 505 | """ 506 | 507 | def dependencyWithGit = """\ 508 | compile (group: 'com.testfunction', 509 | name:'lumberjill', 510 | version:'FAKE', 511 | ext: 'aar' 512 | ).ext { 513 | git = [ 514 | repo : 'https://github.com/lodlock/LumberJill.git', 515 | buildJavaDocs : true, 516 | keepUpdated : true, 517 | remoteModule: 'lumberjill', 518 | keepSource : true 519 | ] 520 | } 521 | """ 522 | 523 | 524 | def dependencyWithGitCommit = """\ 525 | compile (group: 'com.testfunction', 526 | name:'lumberjill', 527 | version:'FAKE', 528 | ext: 'aar' 529 | ).ext { 530 | git = [ 531 | repo : 'https://github.com/lodlock/LumberJill.git', 532 | ref : '4ff48898569e96c1c3074214e3226730d99e4df2', 533 | buildJavaDocs : true, 534 | keepUpdated : true, 535 | remoteModule: 'lumberjill', 536 | keepSource : true 537 | ] 538 | } 539 | """ 540 | 541 | def dependencyWithGitCommitWrong = """\ 542 | compile (group: 'com.testfunction', 543 | name:'lumberjill', 544 | version:'FAKE', 545 | ext: 'aar' 546 | ).ext { 547 | git = [ 548 | repo : 'https://github.com/lodlock/LumberJill.git', 549 | ref : '4ff48898569e96c1c3074214e3226730d99e4df2_no', 550 | buildJavaDocs : true, 551 | keepUpdated : true, 552 | remoteModule: 'lumberjill', 553 | keepSource : true 554 | ] 555 | } 556 | """ 557 | 558 | def dependencyWithGitCommitTag = """\ 559 | compile (group: 'com.nostra13', 560 | name:'universalimageloader', 561 | version:'FAKE', 562 | ext: 'aar' 563 | ).ext { 564 | git = [ 565 | repo : 'https://github.com/nostra13/Android-Universal-Image-Loader.git', 566 | ref : 'v1.9.4', 567 | buildJavaDocs : true, 568 | keepUpdated : false, 569 | remoteModule: 'library', 570 | keepSource : true 571 | ] 572 | } 573 | """ 574 | 575 | def dependencyWithGitCommitBranch = """\ 576 | compile (group: 'com.nostra13', 577 | name:'universalimageloader', 578 | version:'FAKE', 579 | ext: 'aar' 580 | ).ext { 581 | git = [ 582 | repo : 'https://github.com/nostra13/Android-Universal-Image-Loader.git', 583 | ref : 'new_memory_cache_api', 584 | buildJavaDocs : true, 585 | keepUpdated : false, 586 | remoteModule: 'library', 587 | keepSource : true 588 | ] 589 | } 590 | """ 591 | 592 | def dependencyWithGitCommitBranchKeepUpdated = """\ 593 | compile (group: 'com.nostra13', 594 | name:'universalimageloader', 595 | version:'FAKE', 596 | ext: 'aar' 597 | ).ext { 598 | git = [ 599 | repo : 'https://github.com/nostra13/Android-Universal-Image-Loader.git', 600 | ref : 'new_memory_cache_api', 601 | buildJavaDocs : true, 602 | keepUpdated : true, 603 | remoteModule: 'library', 604 | keepSource : true 605 | ] 606 | } 607 | """ 608 | 609 | def dependencyMultipleWithGit = """\ 610 | compile (group: 'com.testfunction', 611 | name:'lumberjill', 612 | version:'FAKE', 613 | ext: 'aar' 614 | ).ext { 615 | git = [ 616 | repo : 'https://github.com/lodlock/LumberJill.git', 617 | buildJavaDocs : true, 618 | keepUpdated : true, 619 | remoteModule: 'lumberjill', 620 | keepSource : true 621 | ] 622 | } 623 | compile (group: 'com.kaopiz', 624 | name:'kprogresshud', 625 | version:'FAKE', 626 | ext: 'aar' 627 | ).ext { 628 | git = [ 629 | repo : 'https://github.com/Kaopiz/KProgressHUD.git', 630 | buildJavaDocs : true, 631 | keepUpdated : true, 632 | remoteModule: 'kprogresshud', 633 | keepSource : true 634 | ] 635 | } 636 | """ 637 | 638 | def dependencyMultipleOneWithGit = """\ 639 | compile 'com.kaopiz:kprogresshud:1.0.1' 640 | compile (group: 'com.testfunction', 641 | name:'lumberjill', 642 | version:'FAKE', 643 | ext: 'aar' 644 | ).ext { 645 | git = [ 646 | repo : 'https://github.com/lodlock/LumberJill.git', 647 | buildJavaDocs : true, 648 | keepUpdated : true, 649 | remoteModule: 'lumberjill', 650 | keepSource : true 651 | ] 652 | } 653 | """ 654 | 655 | def dependencyNonExistent = """\ 656 | compile (group: 'com.testfunction-non-existent_fdsa8023fja', 657 | name:'non-existent_ifa8vb0gjnfj', 658 | version:'BLAH', 659 | ext: 'aar') 660 | """ 661 | 662 | def dependencyNoGitDiffConfig = """\ 663 | gitConfig (group: 'com.testfunction', 664 | name:'lumberjill', 665 | version:'ADF', 666 | ext: 'aar') 667 | """ 668 | 669 | def dependencyWithGitDiffConfig = """\ 670 | gitConfig (group: 'com.testfunction', 671 | name:'lumberjill', 672 | version:'FAKE', 673 | ext: 'aar' 674 | ).ext { 675 | git = [ 676 | repo : 'https://github.com/lodlock/LumberJill.git', 677 | buildJavaDocs : true, 678 | keepUpdated : true, 679 | remoteModule: 'lumberjill', 680 | keepSource : true 681 | ] 682 | } 683 | """ 684 | 685 | def gitConfig = """\ 686 | configurations { 687 | gitConfig 688 | } 689 | """ 690 | 691 | def config = "" 692 | def depStr = "" 693 | 694 | switch (testType) { 695 | case DEPENDENCY_NONE: 696 | depStr += dependencyNone 697 | break 698 | case DEPENDENCY_WITHOUT_GIT: 699 | depStr += dependencyNoGit 700 | break 701 | case DEPENDENCY_WITH_GIT: 702 | depStr += dependencyWithGit 703 | break 704 | case DEPENDENCY_WITH_GIT_COMMIT: 705 | depStr += dependencyWithGitCommit 706 | break 707 | case DEPENDENCY_WITH_GIT_COMMIT_WRONG: 708 | depStr += dependencyWithGitCommitWrong 709 | break 710 | case DEPENDENCY_WITH_GIT_TAG: 711 | depStr += dependencyWithGitCommitTag 712 | break 713 | case DEPENDENCY_WITH_GIT_BRANCH: 714 | depStr += dependencyWithGitCommitBranch 715 | break 716 | case DEPENDENCY_WITH_GIT_BRANCH_KEEP_UPDATED: 717 | depStr += dependencyWithGitCommitBranchKeepUpdated 718 | break 719 | case DEPENDENCY_WITH_GIT_AUTOMATIC_RESOLVE: 720 | depStr += dependencyWithGit 721 | break 722 | case DEPENDENCY_WITHOUT_GIT_DIFF_CONFIG: 723 | depStr += dependencyNoGitDiffConfig 724 | config = gitConfig 725 | break 726 | case DEPENDENCY_WITH_GIT_DIFF_CONFIG: 727 | depStr += dependencyWithGitDiffConfig 728 | config = gitConfig 729 | break 730 | case DEPENDENCY_MULTIPLE_WITH_ONE_GIT: 731 | depStr += dependencyMultipleOneWithGit 732 | break; 733 | case DEPENDENCY_MULTIPLE_WITH_ALL_GIT: 734 | depStr += dependencyMultipleWithGit 735 | break; 736 | case DEPENDENCY_NON_EXISTENT: 737 | depStr += dependencyNonExistent 738 | break; 739 | default: 740 | depStr += dependencyNone 741 | } 742 | 743 | def buildFileContent = """\ 744 | buildscript { 745 | repositories { 746 | jcenter() 747 | } 748 | dependencies { 749 | classpath 'com.android.tools.build:gradle:1.5.0' 750 | } 751 | } 752 | plugins { 753 | id 'com.testfunction.gradle-gitdroid' 754 | } 755 | 756 | allprojects { 757 | repositories { 758 | jcenter() 759 | maven { 760 | url mavenLocal().url 761 | } 762 | maven { 763 | url gitDroid.localRepo 764 | } 765 | } 766 | } 767 | 768 | gitDroid { 769 | workingDir = getRootDir().absolutePath + File.separator + "tmp" 770 | localRepo = uri getRootDir().absolutePath + File.separator + "local-repo" 771 | automaticResolve = ${testType == DEPENDENCY_WITH_GIT_AUTOMATIC_RESOLVE} 772 | test { 773 | pathLengthDieOnFailure = false 774 | } 775 | replaceValuesRegex = [ 776 | "com.android.tools.build:gradle:2.0.0-alpha3" : 777 | "(?<=['|\\"])com\\\\.android\\\\.tools\\\\.build:gradle:.+?(?=['|\\"])" 778 | ] 779 | } 780 | 781 | apply plugin: 'com.android.library' 782 | 783 | android { 784 | compileSdkVersion 23 785 | buildToolsVersion "23.0.2" 786 | 787 | defaultConfig { 788 | minSdkVersion 16 789 | targetSdkVersion 23 790 | versionCode 1 791 | versionName "1.0" 792 | } 793 | buildTypes { 794 | release { 795 | minifyEnabled false 796 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 797 | } 798 | } 799 | } 800 | 801 | $config 802 | 803 | repositories { 804 | jcenter() 805 | maven { 806 | url mavenLocal().url 807 | } 808 | maven { 809 | url gitDroid.localRepo 810 | } 811 | } 812 | 813 | dependencies { 814 | $depStr 815 | } 816 | """; 817 | LOG.lifecycle("buildFileContent:$buildFileContent") 818 | buildFile << buildFileContent 819 | } 820 | 821 | def populateLocalProperties() { 822 | def path = '' 823 | 824 | def isWindows = false 825 | if (currentPlatform() == PLATFORM_WINDOWS) { 826 | LOG.lifecycle("isWindows setting to true") 827 | isWindows = true 828 | } 829 | 830 | def androidHome = System.getenv(ANDROID_HOME_ENV) 831 | LOG.lifecycle("androidHome:$androidHome") 832 | if (androidHome) { 833 | def sdkDir = new File(androidHome) 834 | if (sdkDir.exists()) { 835 | LOG.lifecycle("Found $ANDROID_HOME_ENV at '$androidHome'. Writing to $FN_LOCAL_PROPERTIES.") 836 | path = androidHome 837 | } else { 838 | LOG.lifecycle("Found $ANDROID_HOME_ENV at '$androidHome' but directory is missing.") 839 | } 840 | } 841 | 842 | LOG.lifecycle("path:$path") 843 | 844 | if (!path) { 845 | LOG.lifecycle("path empty") 846 | def home = '' 847 | def androidSdk = null 848 | LOG.lifecycle("home:$home") 849 | if (currentPlatform() == PLATFORM_WINDOWS) { 850 | home = System.getenv("HOMEDRIVE") + System.getenv("HOMEPATH") 851 | androidSdk = new File(home, 852 | File.separator + "AppData" + 853 | File.separator + "Local" + 854 | File.separator + "Android" + 855 | File.separator + "sdk") 856 | 857 | } else if (currentPlatform() == PLATFORM_DARWIN) { 858 | home = System.getenv("HOME") 859 | androidSdk = new File(home, 860 | File.separator + "Library" + 861 | File.separator + "Android" + 862 | File.separator + "sdk") 863 | 864 | } else if (currentPlatform() == PLATFORM_LINUX) { 865 | home = System.getenv("HOME") 866 | androidSdk = new File(File.separator + "usr" + 867 | File.separator + "local" + 868 | File.separator + "android-sdk") 869 | 870 | } 871 | LOG.lifecycle("home:$home androidSdk:"+androidSdk) 872 | if (androidSdk?.exists() && androidSdk?.isDirectory()) { 873 | path = androidSdk.absolutePath 874 | } else { 875 | if (home) { 876 | androidSdk = new File(home, ".android-sdk") 877 | if (androidSdk?.exists() && androidSdk?.isDirectory()) { 878 | path = androidSdk.absolutePath 879 | } 880 | } else { 881 | LOG.lifecycle("could not get home environment") 882 | } 883 | } 884 | } 885 | 886 | if (!path) { 887 | LOG.lifecycle("failing to make local.properties") 888 | return 889 | } 890 | 891 | 892 | if (isWindows) { 893 | path = path.replace "\\", "\\\\" 894 | path = path.replace ":", "\\:" 895 | } 896 | 897 | localPropertiesFile << "$SDK_DIR_PROPERTY=$path\n" 898 | LOG.lifecycle("localPropertiesFile created :"+localPropertiesFile+" exists:"+localPropertiesFile.exists()) 899 | } 900 | 901 | def populateArguments(boolean debug, boolean stacktrace, String... args) { 902 | LOG.lifecycle("populateArguments init with debug:$debug stacktrace:$stacktrace and args:$args") 903 | def out = []; 904 | args.each { 905 | LOG.lifecycle("populateArguments adding arg:$it") 906 | out << it 907 | } 908 | if (debug) { 909 | LOG.lifecycle("populateArguments debug is true") 910 | out << extraArgumentsDebug 911 | } 912 | if (stacktrace) { 913 | LOG.lifecycle("populateArguments stacktrace is true") 914 | out << extraArgumentsStack 915 | } 916 | LOG.lifecycle("populateArguments returning args:$out") 917 | return out 918 | } 919 | 920 | 921 | private static File getTestKitDir() { 922 | def gradleUserHome = System.getenv("GRADLE_USER_HOME") 923 | if (!gradleUserHome) { 924 | gradleUserHome = new File(System.getProperty("user.home"), ".gradle").absolutePath 925 | } 926 | return new File(gradleUserHome, "testkit") 927 | } 928 | 929 | private static void writeFile(File destination, String content) throws IOException { 930 | BufferedWriter output = null; 931 | try { 932 | output = new BufferedWriter(new FileWriter(destination)); 933 | output.write(content); 934 | } finally { 935 | if (output != null) { 936 | output.close(); 937 | } 938 | } 939 | } 940 | } 941 | -------------------------------------------------------------------------------- /src/test/groovy/com/testfunction/TestTypes.groovy: -------------------------------------------------------------------------------- 1 | package com.testfunction 2 | 3 | /** 4 | * Test dependency types 5 | * 6 | * Created by Brandon on 1/4/2016. 7 | */ 8 | enum TestTypes { 9 | DEPENDENCY_NONE, 10 | DEPENDENCY_WITHOUT_GIT, 11 | DEPENDENCY_WITH_GIT, 12 | DEPENDENCY_WITH_GIT_COMMIT, 13 | DEPENDENCY_WITH_GIT_COMMIT_WRONG, 14 | DEPENDENCY_WITH_GIT_TAG, 15 | DEPENDENCY_WITH_GIT_BRANCH, 16 | DEPENDENCY_WITH_GIT_BRANCH_KEEP_UPDATED, 17 | DEPENDENCY_WITH_GIT_BRANCH_LIST_DEPENDENCIES, 18 | DEPENDENCY_WITH_GIT_AUTOMATIC_RESOLVE, 19 | DEPENDENCY_WITHOUT_GIT_DIFF_CONFIG, 20 | DEPENDENCY_WITH_GIT_DIFF_CONFIG, 21 | DEPENDENCY_MULTIPLE_WITH_ONE_GIT, 22 | DEPENDENCY_MULTIPLE_WITH_ALL_GIT, 23 | DEPENDENCY_NON_EXISTENT 24 | 25 | } --------------------------------------------------------------------------------