Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Master #2

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Jenkins Build Per Git Flow Branch
This script will allow you to keep your Jenkins jobs in sync with your Git repository (following Git Flow branching model).

**This repository was forked from https://github.com/neoteric-eu/jenkins-build-per-gitflow-branch**
*the build.gradle file was updated to contain the newest dependency versions along with the repository*


### Genesis
This is a variation of a solution we found. Hence, the credit for the idea and initial implementation goes to Entagen and theirs [Jenkins Build Per Branch]. They explained it nicely, so it's advisable to take a look to their page. As stated, Entagen's version would suit better for a [GitHub flow] convention. Our need is to have three different templates for each of the Git Flow branches: features, releases, hotfixes and to sync them all in one 'scanning session' (single Jenkins sync job execution). I found it impossible using the original solution.
So, we reused the concept of Entagen's script, but replaced the synchronization logic with what suited us better.
Expand Down Expand Up @@ -47,7 +51,8 @@ The whole idea is to have a single Jenkins job which executes periodically, chec
##### 2. Add script parameters (provided in Switches box)
- `-DjenkinsUrl` URL of the Jenkins.You should be able to append api/json to the URL to get JSON feed.
- `-DjenkinsUser` Jenkins HTTP basic authorization user name. (optional)
- `-DjenkinsPasswrd` Jenkins HTTP basic authorization password. (optional)
- `-DjenkinsPassword` Jenkins HTTP basic authorization password. (optional)
*The original Usage has this variable as jenkinsPasswrd without the 'o'. This is why proofreading is so important"
- `-DgitUrl` URL of the Git repository to make the synchronization against.
- `-DdryRun` Pass this flag with any value and it won't make any changes to Jenkins (preview mode). It is recommended to use dry run until everything is set up correctly. (optional)
- `-DtemplateJobPrefix` Prefix name of template jobs to use
Expand Down
14 changes: 8 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ apply plugin: 'groovy'
apply plugin: 'eclipse'

repositories {
mavenCentral()
maven {
url "http://dlccasdigdsu08:8081/nexus/content/groups/aig_public/"
}
}

dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.3'
compile 'org.apache.ivy:ivy:2.2.0'
compile 'org.codehaus.groovy:groovy-all:2.4.4'
compile 'org.apache.ivy:ivy:2.4.0'
compile 'commons-cli:commons-cli:1.2' // should be part of groovy, but not available when running for some reason
compile 'org.jooq:joox:1.2.0'
compile 'org.jooq:joox:1.2.0'
testCompile 'junit:junit:4.11'
testCompile 'org.assertj:assertj-core:1.7.0'
testCompile 'com.github.stefanbirkner:system-rules:1.7.0'
testCompile 'com.github.stefanbirkner:system-rules:1.7.0'
compile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
}

Expand All @@ -36,5 +38,5 @@ task syncWithRepo(dependsOn: 'classes', type: JavaExec) {
}

task wrapper(type: Wrapper) {
gradleVersion = '1.12'
gradleVersion = '2.6'
}
1 change: 1 addition & 0 deletions src/main/groovy/com/neoteric/jenkins/BranchView.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class BranchView {
}

public String getSafeBranchName() {
println "---->geting the safe branch name -------->"
return branchName.replaceAll('/', '_')
}

Expand Down
1 change: 1 addition & 0 deletions src/main/groovy/com/neoteric/jenkins/GitApi.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class GitApi {
eachResultLine(command) { String line ->
String branchNameRegex = "^.*\trefs/heads/(.*)\$"
String branchName = line.find(branchNameRegex) { full, branchName -> branchName }
println "----- getBranchNames: branchName "+branchName
Boolean selected = passesFilter(branchName)
println "\t" + (selected ? "* " : " ") + "$line"
// lines are in the format of: <SHA>\trefs/heads/BRANCH_NAME
Expand Down
74 changes: 40 additions & 34 deletions src/main/groovy/com/neoteric/jenkins/JenkinsApi.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import org.apache.http.protocol.HttpContext
import org.apache.http.HttpRequest

class JenkinsApi {


final String SHOULD_START_PARAM_NAME = "startOnCreate"
String jenkinsServerUrl
RESTClient restClient
Expand Down Expand Up @@ -42,34 +42,35 @@ class JenkinsApi {
}

List<String> getJobNames(String prefix = null) {
println "getting project names from " + jenkinsServerUrl + "api/json"

def response = get(path: 'api/json')
def jobNames = response.data.jobs.name
if (prefix) return jobNames.findAll { it.startsWith(prefix) }
return jobNames
}

String getJobConfig(String jobName) {

def response = get(path: "job/${jobName}/config.xml", contentType: TEXT,
headers: [Accept: 'application/xml'])

response.data.text
}

void cloneJobForBranch(String jobPrefix, ConcreteJob missingJob, String createJobInView, String gitUrl) {
void cloneJobForBranch(String templateJob, String missingJob, String branchName, String createJobInView, String gitUrl) {
String createJobInViewPath = resolveViewPath(createJobInView)
println "-----> createInView after" + createJobInView
String missingJobConfig = configForMissingJob(missingJob, gitUrl)
TemplateJob templateJob = missingJob.templateJob
println "-----> createInView after: " + createJobInView
String missingJobConfig = configForMissingJob(templateJob, branchName, gitUrl)

//Copy job with jenkins copy job api, this will make sure jenkins plugins get the call to make a copy if needed (promoted builds plugin needs this)
post(createJobInViewPath + 'createItem', missingJobConfig, [name: missingJob.jobName, mode: 'copy', from: templateJob.jobName], ContentType.XML)
post(createJobInViewPath + 'createItem', missingJobConfig, [name: missingJob, mode: 'copy', from: templateJob], ContentType.XML)

post('job/' + missingJob.jobName + "/config.xml", missingJobConfig, [:], ContentType.XML)
post('job/' + missingJob + "/config.xml", missingJobConfig, [:], ContentType.XML)
//Forced disable enable to work around Jenkins' automatic disabling of clones jobs
//But only if the original job was enabled
post('job/' + missingJob.jobName + '/disable')
post('job/' + missingJob + '/disable')
if (!missingJobConfig.contains("<disabled>true</disabled>")) {
post('job/' + missingJob.jobName + '/enable')
post('job/' + missingJob+ '/enable')
}
}

Expand All @@ -82,57 +83,62 @@ class JenkinsApi {
elements.join();
}

String configForMissingJob(ConcreteJob missingJob, String gitUrl) {
TemplateJob templateJob = missingJob.templateJob
String config = getJobConfig(templateJob.jobName)
return processConfig(config, missingJob.branchName, gitUrl)
String configForMissingJob(String templateJob, String branchName, String gitUrl) {
String config = getJobConfig(templateJob)
return processConfig(config, branchName, gitUrl)
}

public String processConfig(String entryConfig, String branchName, String gitUrl) {

def root = new XmlParser().parseText(entryConfig)

// update branch name
root.scm.branches."hudson.plugins.git.BranchSpec".name[0].value = "*/$branchName"

// update GIT url
root.scm.userRemoteConfigs."hudson.plugins.git.UserRemoteConfig".url[0].value = "$gitUrl"

//update Sonar
if (root.publishers."hudson.plugins.sonar.SonarPublisher".branch[0] != null) {
root.publishers."hudson.plugins.sonar.SonarPublisher".branch[0].value = "$branchName"
}


//remove template build variable
Node startOnCreateParam = findStartOnCreateParameter(root)
if (startOnCreateParam) {
startOnCreateParam.parent().remove(startOnCreateParam)
}

//check if it was the only parameter - if so, remove the enclosing tag, so the project won't be seen as build with parameters
def propertiesNode = root.properties
def parameterDefinitionsProperty = propertiesNode."hudson.model.ParametersDefinitionProperty".parameterDefinitions[0]

if(!parameterDefinitionsProperty.attributes() && !parameterDefinitionsProperty.children() && !parameterDefinitionsProperty.text()) {
root.remove(propertiesNode)
new Node(root, 'properties')
def parameterDefinitionsProperty
//the neoteric people hard coded property names and didn't do defensive coding. This causes an NPE. tisk tisk!!
if(propertiesNode."hudson.model.ParametersDefinitionProperty".parameterDefinitions[0] !=null){

parameterDefinitionsProperty = propertiesNode."hudson.model.ParametersDefinitionProperty".parameterDefinitions[0]

if(!parameterDefinitionsProperty.attributes() && !parameterDefinitionsProperty.children() && !parameterDefinitionsProperty.text()) {
root.remove(propertiesNode)
new Node(root, 'properties')
}
}



def writer = new StringWriter()
XmlNodePrinter xmlPrinter = new XmlNodePrinter(new PrintWriter(writer))
xmlPrinter.setPreserveWhitespace(true)
xmlPrinter.print(root)
return writer.toString()
}
void startJob(ConcreteJob job) {
String templateConfig = getJobConfig(job.templateJob.jobName)

void startJob(String templateJob, String jobName) {
String templateConfig = getJobConfig(templateJob)
if (shouldStartJob(templateConfig)) {
println "Starting job ${job.jobName}."
post('job/' + job.jobName + '/build')
println "Starting job ${jobName}."
post('job/' + jobName + '/build')
}
}

public boolean shouldStartJob(String config) {
Node root = new XmlParser().parseText(config)
Node startOnCreateParam = findStartOnCreateParameter(root)
Expand All @@ -141,7 +147,7 @@ class JenkinsApi {
}
return startOnCreateParam.defaultValue[0]?.text().toBoolean()
}

Node findStartOnCreateParameter(Node root) {
return root.properties."hudson.model.ParametersDefinitionProperty".parameterDefinitions."hudson.model.BooleanParameterDefinition".find {
it.name[0].text() == SHOULD_START_PARAM_NAME
Expand Down
Loading