Skip to content

Commit

Permalink
Merge branch 'release-1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mahnkong committed Mar 6, 2016
2 parents f4e6bbc + ebe59f4 commit 570ee3b
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 48 deletions.
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
language: groovy

jdk:
- oraclejdk8
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
136 changes: 136 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# gradle-teg2neo4j-plugin
![Travis build status](https://api.travis-ci.org/mahnkong/gradle-teg2neo4j-plugin.svg?branch=develop)

"gradle-teg2neo4j-plugin" (or in short: "gteg2neo4j") is a Gradle plugin providing functionality to store the [Task Execution Graph](https://docs.gradle.org/2.11/javadoc/org/gradle/api/execution/TaskExecutionGraph.html "TaskExecutionGraph interface") (including information like success, failure, etc. for each task of the current build) of a project's gradle build inside a [Neo4j](http://neo4j.com/ "Neo4j Home") instance.

Once imported, the graph can be seen directly inside the Neo4j browser and detailed data can be queried using the Neo4j [Cypher](http://neo4j.com/developer/cypher-query-language/ "Cypher documentation") language.

The following image shows such a visualizion. The source was a build execution of the "gradle-teg2neo4j-plugin" project itself (which was actually failing).

![Task Graph Visualization](https://drive.google.com/uc?export=download&id=0B2Bgx0RONdwIYU9RY04tSS1yWlE)

## Usage

### Configuration of the plugin
The build.gradle file of the project, which will use the plugin, must be extended with the configuration below:

```gradle
//fetch plugin jar and dependencies
buildscript {
repositories {
//only relevant if the plugin is located in the local maven repo
mavenLocal()
jcenter()
//required for the neo4j relevant dependencies
maven { url "http://m2.neo4j.org/content/groups/public" }
}
dependencies {
classpath 'mahnkong:gradle-teg2neo4j-plugin:$VERSION'
}
}
//apply the plugin
apply plugin: 'mahnkong.gteg2neo4j'
//configuration of the plugin
gteg2neo4j {
//required: server url
neo4jServer "http://localhost:7474"
//optional: authentication parameters
neo4jUser "neo4j"
neo4jPassword "password"
//optional: disable plugin
disabled false
}
```

### Execution of a build with the plugin configured

Just execute the build as always. At the end of the build the plugin prints the build id used for the storing of the data in the neo4j instance

```
$ ./gradlew build
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
:createClasspathManifest UP-TO-DATE
:compileTestJava UP-TO-DATE
:compileTestGroovy UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build UP-TO-DATE
BUILD SUCCESSFUL
Total time: 5.091 secs
gteg2neo4j :: This build has the id: gradle-teg2neo4j-plugin::71592037-771a-4391-b9db-825df301d6a4
```

The plugin execution can be disabled during a build by providing the system property "gteg2neo4j.disabled"

```
$ ./gradlew build -Dgteg2neo4j.disabled
...
BUILD SUCCESSFUL
Total time: 3.488 secs
'gteg2neo4j' has been disabled - won't send data to neo4j!
```

### Query the database using the build id

With the build id, the stored task graph and its task data can be accessed using Neo4j.

Example query for the complete task graph of one specific build:

```
MATCH (t:BuildTask {build:'gradle-teg2neo4j-plugin::71592037-771a-4391-b9db-825df301d6a4'}) RETURN t
```

Example query for all tasks stored during the last 10 minutes:

```
MATCH (t:BuildTask) WHERE t.insertedAt + 600000 >= timestamp() RETURN t
```

## Node and Relationship reference

The following labels exist for the task nodes:

- BuildTask: All task nodes
- SucessfulTask: All successful task nodes
- FailureTask: All failed task nodes

The following attributes are set for each task node (based on the task's state after the build finished):

- name: The task's path
- build: the build id (set by the "gradle-teg2neo4j-plugin")
- executed: bool indicating that the task was executed
- didWork: bool indicating that the task did some work
- upToDate: bool indicating that the task was up to date
- failureMsg: the thrown exception's message in case the task failed
- insertedAt: timestamp of the data insertion (set by the "gradle-teg2neo4j-plugin")

The following relationships are set between the task nodes depending on the task execution graph

- DEPENDS_ON: indicates, that one task depends on another task
- FINALIZES: indicates, that one task finalizes another task

## How to build the plugin

To build, simply execute the build task from within the project's directory:

```
$ ./gradlew build
```

To publish the build to the local maven repository, execute:

```
$ ./gradlew publishAPTMLR
```
33 changes: 18 additions & 15 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
repositories {
jcenter()
maven {
url "http://m2.neo4j.org/content/groups/public"
}
maven { url "http://m2.neo4j.org/content/groups/public" }
}

group = 'mahnkong'
version = '1.0.0-SNAPSHOT'
version = '1.0.0'

apply plugin: 'mahnkong.gteg2neo4j'
apply plugin: 'groovy'
apply plugin: 'maven'

gteg2neo4j {
neo4jServer "http://localhost:7474"
neo4jUser "neo4j"
neo4jPassword "password"
}
apply plugin: 'maven-publish'

ext {
neo4jVersion = '2.3.2'
neo4jTestInstance = null
}

sourceCompatibility = 1.7
targetCompatibility = 1.7


// Write the plugin's classpath to a file to share with the tests
task createClasspathManifest {
def outputDir = file("$buildDir/$name")
Expand All @@ -39,7 +34,7 @@ task createClasspathManifest {
dependencies {
compile gradleApi()
compile localGroovy()
compile "org.neo4j:neo4j-jdbc:${neo4jVersion}:jar-with-dependencies"
compile "org.neo4j:neo4j-jdbc:${neo4jVersion}"

testCompile "org.neo4j.test:neo4j-harness:${neo4jVersion}"
testCompile 'junit:junit:4.12'
Expand All @@ -48,10 +43,18 @@ dependencies {
testRuntime files(createClasspathManifest)
}

uploadArchives() {
jar {
from "LICENSE"
}

publishing {
publications {
artifacts(MavenPublication) {
from components.java
}
}
repositories {
mavenLocal()
}
}

uploadArchives.dependsOn('test')
Empty file modified gradlew
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ include 'api'
include 'services:webservice'
*/

rootProject.name = 'gteg2neo4j'
rootProject.name = 'gradle-teg2neo4j-plugin'
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Created by mahnkong on 28.02.2016.
*/
public enum Gteg2Neo4jConstants {
EXTENSION_NAME("gteg2neo4j");
EXTENSION_NAME("gteg2neo4j"), DISABLE_GTEG2NEO2J_PROPERTY("gteg2neo4j.disabled");

private String value;
Gteg2Neo4jConstants(String value) {
Expand Down
43 changes: 25 additions & 18 deletions src/main/groovy/mahnkong/gteg2neo4j/Gteg2Neo4jPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionGraph

import java.time.Instant

/**
* Created by mahnkong on 27.02.2016.
*/
class Gteg2Neo4jPlugin implements Plugin<Project> {

final
static CONFIG_INCOMPLETE_ERROR = "neo4jServer must be defined for '${Gteg2Neo4jConstants.EXTENSION_NAME.value}' plugin!"
final static CONFIG_INCOMPLETE_ERROR = "neo4jServer must be defined for '${Gteg2Neo4jConstants.EXTENSION_NAME.value}' plugin!"
final static BUILD_ID_OUTPUT_PREFIX = "${Gteg2Neo4jConstants.EXTENSION_NAME.value} :: This build has the id:"
final static EXCEPTION_OCCURED_ERROR = "${Gteg2Neo4jConstants.EXTENSION_NAME.value} :: An exception occured while trying to store the Gradle Task execution data!"

def Map getTaskMapFromTaskGraph(TaskExecutionGraph graph) {
def Map<Task, Map<TaskRelationships, Set<Task>>> taskMap = [:]
Expand All @@ -26,7 +28,7 @@ class Gteg2Neo4jPlugin implements Plugin<Project> {
}

def validateParams(extension, logger) {
if (extension.disabled) {
if (extension.disabled || System.properties.containsKey(Gteg2Neo4jConstants.DISABLE_GTEG2NEO2J_PROPERTY.value)) {
logger.info("'${Gteg2Neo4jConstants.EXTENSION_NAME.value}' has been disabled - won't send data to neo4j!")
return false
} else if (!extension.neo4jServer) {
Expand All @@ -45,28 +47,33 @@ class Gteg2Neo4jPlugin implements Plugin<Project> {

project.gradle.buildFinished {
def extension = project.extensions.getByName(Gteg2Neo4jConstants.EXTENSION_NAME.value)
if (!validateParams(extension, project.logger))
if (!validateParams(extension, project.logger)) {
return
}

def neo4jClient = new Neo4jClient(extension.neo4jServer, extension.neo4jUser, extension.neo4jPassword, project.logger)
String buildId = new String("${project.name}::${UUID.randomUUID().toString()}")
println "${BUILD_ID_OUTPUT_PREFIX} ${buildId}"
try {
def neo4jClient = new Neo4jClient(extension.neo4jServer, extension.neo4jUser, extension.neo4jPassword, project.logger)
String buildId = new String("${project.name}::${UUID.randomUUID().toString()}")
println "${BUILD_ID_OUTPUT_PREFIX} ${buildId}"

taskMap.each { task, relationships ->
neo4jClient.createTaskNode(task, buildId)
relationships.each { relationshipType, tasks ->
tasks.each {
neo4jClient.createTaskNode(it, buildId)
if (relationshipType.equals(TaskRelationships.DEPENDS_ON)) {
neo4jClient.createTaskDependsOnRelationship(task, it, buildId)
}
if (relationshipType.equals(TaskRelationships.FINALIZED_BY)) {
neo4jClient.createTaskFinalizesRelationship(it, task, buildId)
taskMap.each { task, relationships ->
neo4jClient.createTaskNode(task, buildId)
relationships.each { relationshipType, tasks ->
tasks.each {
neo4jClient.createTaskNode(it, buildId)
if (relationshipType.equals(TaskRelationships.DEPENDS_ON)) {
neo4jClient.createTaskDependsOnRelationship(task, it, buildId)
}
if (relationshipType.equals(TaskRelationships.FINALIZED_BY)) {
neo4jClient.createTaskFinalizesRelationship(it, task, buildId)
}
}
}
}
neo4jClient.commitAndClose()
} catch (Exception e) {
project.logger.error("${EXCEPTION_OCCURED_ERROR} [${e.getMessage()}", e)
}
neo4jClient.commitAndClose()
}
}
}
5 changes: 4 additions & 1 deletion src/main/groovy/mahnkong/gteg2neo4j/Neo4jClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.Instant;
import java.util.Properties;

/**
* Created by mahnkong on 28.02.2016.
*/
public class Neo4jClient {

private static final String CYPHER_CREATE_NOTE = "MERGE (n:%s {name: ?, build: ?, executed: ?, didWork: ?, skipped: ?, upToDate: ?, failureMsg: ?}) RETURN n";
private static final String CYPHER_CREATE_NOTE = "MERGE (n:%s {name: ?, build: ?, executed: ?, didWork: ?, skipped: ?, upToDate: ?, failureMsg: ?, insertedAt: ?}) RETURN n";
private static final String CYPHER_CREATE_DEPENDSON_RELATIONSHIP = "MATCH (a:BuildTask), (b:BuildTask) WHERE a.name = ? AND a.build = ? AND b.name = ? AND b.build = ? CREATE (a)-[r:DEPENDS_ON]->(b) RETURN r";
private static final String CYPHER_CREATE_FINALIZES_RELATIONSHIP = "MATCH (a:BuildTask), (b:BuildTask) WHERE a.name = ? AND a.build = ? AND b.name = ? AND b.build = ? CREATE (a)-[r:FINALIZES]->(b) RETURN r";

Neo4jConnection connection;
Logger logger;
private long insertTimestamp = Instant.now().toEpochMilli();

public Neo4jClient(String server, String user, String password, Logger logger) throws SQLException {
this.connection = createConnection(server, user, password);
Expand All @@ -42,6 +44,7 @@ void createTaskNode(Task task, String buildId) throws SQLException {
createTaskStmt.setBoolean(5, task.getState().getSkipped());
createTaskStmt.setBoolean(6, task.getState().getUpToDate());
createTaskStmt.setString(7, (task.getState().getFailure() != null ? task.getState().getFailure().getMessage() : ""));
createTaskStmt.setLong(8, insertTimestamp);
createTaskStmt.execute();
}
}
Expand Down
Loading

0 comments on commit 570ee3b

Please sign in to comment.