Last active
July 10, 2020 06:49
-
-
Save kozaxinan/7762a22eed488e4634199e293f5f2e35 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
implementation "com.android.tools.build:gradle:3.6.3" | |
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" | |
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // https://issuetracker.google.com/issues/123491449 | |
implementation "org.eclipse.jgit:org.eclipse.jgit:5.5.1.201910021850-r" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.is24.git.diff | |
import org.eclipse.jgit.api.Git | |
import org.eclipse.jgit.lib.Repository | |
import org.eclipse.jgit.revwalk.RevWalk | |
import org.eclipse.jgit.treewalk.AbstractTreeIterator | |
import org.eclipse.jgit.treewalk.CanonicalTreeParser | |
import org.gradle.api.DefaultTask | |
import org.gradle.api.Project | |
import org.gradle.api.Task | |
import org.gradle.api.artifacts.Dependency | |
import org.gradle.api.artifacts.UnknownConfigurationException | |
import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency | |
import org.gradle.api.tasks.TaskAction | |
import org.gradle.api.tasks.testing.Test | |
import java.io.File | |
abstract class TestDiff : DefaultTask() { // Keep the file open otherwise gradle won't be able to generate the proxy class. | |
private val branchProperty = "branch" | |
private val nameOftestTasks = arrayOf("test", "testDebugUnitTest") | |
init { | |
if (project.hasProperty(branchProperty)) { | |
val branchName = project | |
.property(branchProperty) | |
.toString() | |
println("Running tests for changes of $branchName") | |
val affectedProjects = projectsAffectedByBranch(branchName, project.rootProject) | |
val testTasks = mutableListOf<Task>() | |
affectedProjects.forEach { project -> | |
val testTasksOfModule = project.tasks | |
.withType(Test::class.java) | |
.filter { task -> | |
task.name in nameOftestTasks | |
} | |
val testDebugUnitTest = testTasksOfModule.find { it.name == "testDebugUnitTest" } | |
if (testDebugUnitTest != null) { | |
println("${project.name}:testDebugUnitTest module will be tested") | |
testTasks += testDebugUnitTest | |
} else { | |
testTasksOfModule.forEach { | |
println("${project.name}:${it.name} module will be tested") | |
} | |
testTasks += testTasksOfModule | |
} | |
} | |
if (testTasks.isNotEmpty()) { | |
dependsOn(testTasks) | |
} else { | |
println("Full codebase will be tested") | |
dependsOn("ciUnitTests") | |
} | |
} | |
} | |
@TaskAction // Marks a function as the action to run when the task is executed. | |
fun run() { | |
if (!project.hasProperty(branchProperty)) { | |
throw IllegalArgumentException("please specify branch property with -P$branchProperty=<BranchName>") | |
} | |
} | |
private fun projectsAffectedByBranch(branchName: String, rootProject: Project): Collection<Project> { | |
val diffFilePaths = getDiffFilePaths(rootProject, branchName) | |
val affectedProjects = getProjectsForFilePaths(rootProject, diffFilePaths) | |
return affectedProjects | |
.flatMap { getDependantProjects(rootProject.subprojects, it) + it } | |
.distinct() | |
} | |
private fun getGitReferenceTree(repository: Repository, ref: String): AbstractTreeIterator { | |
val head = repository.exactRef(ref) | |
RevWalk(repository).use { walk -> | |
val commit = walk.parseCommit(head.objectId) | |
val tree = walk.parseTree(commit.tree.id) | |
val treeParser = CanonicalTreeParser() | |
repository | |
.newObjectReader() | |
.use { reader -> treeParser.reset(reader, tree.id) } | |
walk.dispose() | |
return treeParser | |
} | |
} | |
private fun getDiffFilePaths(rootProject: Project, branchName: String): Iterable<String> { | |
val git = Git.open(File(rootProject.projectDir.path)) | |
val masterTree = getGitReferenceTree(git.repository, "refs/remotes/origin/develop") | |
val branchTree = getGitReferenceTree(git.repository, "refs/remotes/origin/$branchName") | |
val diffEntries = git | |
.diff() | |
.setOldTree(masterTree) | |
.setNewTree(branchTree) | |
.call() | |
return diffEntries.flatMap { diffEntry -> listOf(diffEntry.oldPath, diffEntry.newPath) } | |
} | |
private fun getProjectsForFilePaths(rootProject: Project, filePaths: Iterable<String>): Iterable<Project> = | |
rootProject.subprojects.filter { subproject -> | |
filePaths.find { filePath -> filePath.startsWith(subproject.projectDir.toRelativeString(rootProject.projectDir)) } != null | |
} | |
private fun getDependantProjects(allProjects: Iterable<Project>, dependencyProject: Project): Iterable<Project> { | |
val dependants = allProjects | |
.filter { subproject -> | |
try { | |
val implementationDependencies: Set<Dependency> = try { | |
subproject | |
.configurations | |
.getByName("implementation") | |
.dependencies | |
} catch (ignored: UnknownConfigurationException) { | |
emptySet() | |
} | |
val apiDependencies: Set<Dependency> = try { | |
subproject | |
.configurations | |
.getByName("api") | |
.dependencies | |
} catch (ignored: UnknownConfigurationException) { | |
emptySet() | |
} | |
val combinedDepSet = implementationDependencies + apiDependencies | |
val dependencies = combinedDepSet | |
.distinct() | |
.filterIsInstance<DefaultProjectDependency>() | |
.find { dependency -> dependency.dependencyProject == dependencyProject } | |
dependencies != null | |
} catch (exception: UnknownConfigurationException) { | |
false | |
} | |
} | |
.distinct() | |
return dependants + dependants.flatMap { getDependantProjects(allProjects, it) } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tasks.register("ciUnitTestsForBranch", TestDiff) { | |
group = 'Continuous Integration' | |
description = 'Executes unit test for branch' | |
} | |
./gradlew ciUnitTestsForBranch -Pbranch=\"${getGitBranchName()}\" | |
(Branch name of current pr, it is `env.BRANCH_NAME` for our jenkins setup) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment