Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.goldin.plugins.jenkins.Job.groovy Maven / Gradle / Ivy
package com.github.goldin.plugins.jenkins
import static com.github.goldin.plugins.common.GMojoUtils.*
import org.apache.maven.plugin.MojoExecutionException
import org.gcontracts.annotations.Requires
import com.github.goldin.plugins.jenkins.beans.*
/**
* Class describing a Jenkins job
*/
@SuppressWarnings([ 'StatelessClass' ])
class Job
{
/**
* Folder names that are illegal on Windows:
*
* "Illegal Characters on Various Operating Systems"
* http://support.grouplogic.com/?p=1607
* http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
*/
private static final Set ILLEGAL_NAMES = new HashSet
([ 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
'con', 'nul', 'prn' ])
/**
* Error messages to display when jobs is not properly configured
*/
private static String notConfigured( String errorMessage ){ "[${ this }] is not configured correctly: $errorMessage" }
private static String misConfigured( String errorMessage ){ "[${ this }] is mis-configured: $errorMessage" }
/**
* Default Maven goals used in Maven projects and Maven tasks in free-style projects.
*/
static final String DEFAULT_MAVEN_GOALS = '-B -e clean install'
/**
* Job types supported
*/
static enum JobType
{
free ( 'Free-Style' ),
maven ( 'Maven' )
final String description
@Requires({ description })
JobType ( String description )
{
this.description = description
}
}
/**
* "runPostStepsIfResult" types supported
*/
static enum PostStepResult
{
success ( 'SUCCESS', 0, 'BLUE' ),
unstable ( 'UNSTABLE', 1, 'YELLOW' ),
all ( 'FAILURE', 2, 'RED' )
final String name
final int ordinal
final String color
@Requires({ name && color })
PostStepResult( String name, int ordinal, String color )
{
this.name = name
this.ordinal = ordinal
this.color = color
}
}
/**
* Individual property, not inherited from parent job
* @see #extend
*/
String id // Job's ID when a folder is created, has illegal characters "fixed"
String originalId // Job's ID used for logging
void setId( String id )
{
assert id?.trim()?.length()
/**
* Job Id should never have any illegal characters in it since it becomes a folder name later
* (in Jenkins workspace - '.jenkins/jobs/JobId' )
*/
this.originalId = id
this.id = fixIllegalChars( id, 'Job id' )
}
/**
* Individual properties, *not inherited* from parent job
* @see #extend
*/
boolean isAbstract = false
void setAbstract( boolean isAbstract ) { this.isAbstract = isAbstract }
boolean disabled = false
String displayName = ''
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* When adding fields:
* 1) Update {@link #extend(Job)} where current job is "extended" with the "parent Job"
* 2) DO NOT specify default values, let {@link #extend(Job)} inheritance take care of it.
* 3) Update {@link #validate} where job configuration is validated for correctness.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
JobType jobType
Boolean buildOnSNAPSHOT
Boolean useUpdate
Boolean doRevert
Boolean privateRepository
Boolean privateRepositoryPerExecutor
Boolean archivingDisabled
Boolean blockBuildWhenDownstreamBuilding
Boolean blockBuildWhenUpstreamBuilding
Boolean appendTasks
Boolean incrementalBuild
String parent
String description
DescriptionRow[] descriptionTable
Integer daysToKeep
Integer numToKeep
Integer artifactDaysToKeep
Integer artifactNumToKeep
Task[] tasks
Task[] prebuildersTasks
Task[] postbuildersTasks
String node
String pom
String localRepoBase
String localRepo
void setLocalRepo ( String localRepo ){ this.localRepo = fixIllegalChars( localRepo, 'Local repo' ) }
String localRepoPath
Artifactory artifactory
String mavenGoals
String mavenName
String jdkName
String mavenOpts
String quietPeriod
String scmCheckoutRetryCount
String gitHubUrl
Deploy deploy
Mail mail
Invoke invoke
String authToken
PostStepResult runPostStepsIfResult
Groovy[] groovys
Groovy groovy
List groovys(){ general().list( groovys, groovy )}
Trigger[] triggers
Trigger trigger
List triggers() { general().list( triggers, trigger ) }
Parameter[] parameters
Parameter parameter
List parameters() { general().list( parameters, parameter ) }
Repository[] repositories
Repository repository
List repositories() { general().list( repositories, repository ) }
String scmType
String getScmMarkup()
{
if ( repositories() || ( 'none' == scmType ))
{
assert scmType
Scm scm = ( scmType == 'none' ? new None() :
scmType == 'cvs' ? new Cvs() :
scmType == 'svn' ? new Svn() :
scmType == 'hg' ? new Hg() :
scmType == 'git' ? new Git() :
null )
assert scm, "Unknown ${ scmType } "
scm.job = this
scm.repositories = repositories()
scm.markup
}
else
{
''
}
}
/**
* Extension points - tags accepting raw XML content wrapped in
*/
String scm
String reporters
String publishers
String buildWrappers
String properties
String prebuilders
String postbuilders
/**
* Groovy extension point
*/
String process
/**
* Set by {@link JenkinsMojo#configureJobs}
*/
String jenkinsUrl
String generationPom
Boolean parentIsReal
Job[] childJobs
Job[] invokedBy
/**
* Retrieves job description table
*/
String getDescriptionTableMarkup() { makeTemplate( '/descriptionTable.html', [ job : this, jenkinsUrl : jenkinsUrl ] ) }
/**
* Checks that String specifies contains no illegal characters and can be used for creating a folder
*/
String fixIllegalChars( String s, String title )
{
if ( ! s )
{
return s
}
if ( ILLEGAL_NAMES.contains( s.toLowerCase()))
{
throw new MojoExecutionException( "$title [${ id }] is illegal! " +
"It becomes a folder name and the following names are illegal on Windows: ${ ILLEGAL_NAMES.sort() }" )
}
/**
*
* Leaving only letters/digits, '-', '.' and '_' characters:
* \w = word character: [a-zA-Z_0-9]
*/
s.replaceAll( /[^\w\.-]+/, '-' )
}
@Override
String toString () { "Job \"${ originalId }\"" }
/**
* Sets properties specified by calling {@link #set} for each one.
*
* @param propertyNames name of the properties to set
* @param parentJob job to copy property value from if current job has this property undefined
* @param override whether current property should be overridden in any case
*/
private void setMany( List propertyNames,
Job parentJob,
boolean override )
{
assert propertyNames && parentJob
propertyNames.each { set( it, parentJob, override ) }
}
/**
* Sets the property specified using the value of another job or default value.
*
* @param propertyName name of the property to set
* @param parentJob job to copy property value from if current job has this property undefined
* @param override whether current property should be overridden in any case
* @param defaultValue default value to set to the property if other job has it undefined as well,
* if unspecified or null, then {@code ''} for {@code String},
* {@code false} for {@code Boolean} and {@code -1} for {@code Integer} properties are used
* @param verifyClosure closure to pass the resulting property values, it can verify its correctness
*/
private void set( String propertyName,
Job parentJob,
boolean override,
Object defaultValue = null,
Closure verifyClosure = null )
{
assert propertyName && parentJob
final propertyType = metaClass.getMetaProperty( propertyName ).type
// noinspection GroovyAssignmentToMethodParameter
defaultValue = ( defaultValue != null ) ? defaultValue :
( propertyType == String ) ? '' :
( propertyType == Boolean ) ? false :
( propertyType == Integer ) ? -1 :
null
assert ( defaultValue != null ), "Default value should be specified for unknown property type [$propertyType]"
if (( this[ propertyName ] == null ) || override )
{
this[ propertyName ] = parentJob[ propertyName ] ?: defaultValue
}
assert ( this[ propertyName ] != null ), "[$this] has null [$propertyName]"
if ( verifyClosure ) { verifyClosure( this[ propertyName ] ) }
}
/**
* Sets job's tasks using property name and parent job specified.
*
* @param propertyName name of the property to set
* @param parentJob job to copy tasks from if current job has tasks undefined
* @param override whether current job tasks should be overridden in any case
*/
private void setTasks( String propertyName,
Job parentJob,
boolean override )
{
assert ( propertyName && parentJob )
Task[] parentTasks = ( Task[] ) ( parentJob[ propertyName ] ?: [] as Task[] )
Task[] ourTasks = ( Task[] ) ( this[ propertyName ] ?: [] as Task[] )
this[ propertyName ] = ( override ) ? parentTasks :
( appendTasks ) ? ( parentTasks.toList() + ourTasks.toList()) as Task[] :
ourTasks ?: parentTasks
assert ( this[ propertyName ] != null ), "[$this] has null [$propertyName]"
}
/**
* Extends job definition using the parent job specified
* (completes missing data with that inherited from the parent job)
*
* @param parentJob parent job to take the missing data from
* @param override whether or not parentJob data is of higher priority than this job data,
* usually it's not - only used when we want to "override" this job data
*/
@SuppressWarnings( 'AbcComplexity' )
void extend ( Job parentJob, boolean override = false )
{
set( 'description', parentJob, override, ' ', { String s -> assert s } )
set( 'scmType', parentJob, override, 'svn', { String s -> assert s } )
set( 'jobType', parentJob, override, JobType.maven, { JobType t -> assert t } )
set( 'node', parentJob, override, 'master', { String s -> assert s } )
set( 'jdkName', parentJob, override, '(Default)', { String s -> assert s } )
set( 'mail', parentJob, override, new Mail())
set( 'invoke', parentJob, override, new Invoke())
set( 'descriptionTable', parentJob, override, new DescriptionRow[ 0 ])
setMany( split( '|authToken|scm|buildWrappers|properties|publishers|process|quietPeriod|scmCheckoutRetryCount|gitHubUrl' +
'|useUpdate|doRevert|blockBuildWhenDownstreamBuilding|blockBuildWhenUpstreamBuilding|appendTasks|daysToKeep' +
'|numToKeep|artifactDaysToKeep|artifactNumToKeep', '\\|' ),
parentJob, override )
if ((( ! triggers()) || ( override )) && parentJob.triggers())
{
triggers = parentJob.triggers() as Trigger[]
}
if ((( ! parameters()) || ( override )) && parentJob.parameters())
{
parameters = parentJob.parameters() as Parameter[]
}
else if ( parentJob.parameters())
{ /**
* Set gives a lower priority to parentJob parameters - parameters having the
* same name and type *are not taken*, see {@link Parameter#equals(Object)}
*/
parameters = joinParameters( parentJob.parameters(), parameters()) as Parameter[]
}
if ((( ! repositories()) || ( override )) && parentJob.repositories())
{
repositories = parentJob.repositories() as Repository[]
}
if ((( ! groovys()) || ( override )) && parentJob.groovys())
{
groovys = parentJob.groovys() as Groovy[]
}
if ( jobType == JobType.free )
{
setTasks( 'tasks', parentJob, override )
}
if ( jobType == JobType.maven )
{
set( 'pom', parentJob, override, 'pom.xml', { String s -> assert s } )
set( 'mavenGoals', parentJob, override, DEFAULT_MAVEN_GOALS, { String s -> assert s } )
set( 'runPostStepsIfResult', parentJob, override, PostStepResult.all, { PostStepResult r -> assert r } )
set( 'deploy', parentJob, override, new Deploy())
set( 'artifactory', parentJob, override, new Artifactory())
setTasks( 'prebuildersTasks', parentJob, override )
setTasks( 'postbuildersTasks', parentJob, override )
setMany( split( '|mavenName|mavenOpts|reporters|localRepoBase|localRepo|buildOnSNAPSHOT' +
'|privateRepository|privateRepositoryPerExecutor|archivingDisabled' +
'|prebuilders|postbuilders|incrementalBuild', '\\|' ),
parentJob, override )
}
}
/**
* Joins two set of parameters, those inhered from the parent job and those of the current job.
*
* @param parentParameters parameters inherited from the parent job.
* @param currentParameters parameters of the current job.
* @return new set of parameters
*/
private static List joinParameters( List parentParameters, List currentParameters )
{
List parentNames = parentParameters*.name
List currentNames = currentParameters*.name
if ( parentNames.intersect( currentNames ))
{
parentParameters.findAll { ! currentNames.contains( it.name ) } + currentParameters
}
else
{
parentParameters + currentParameters
}
}
/**
* Updates job's {@link #mavenGoals}:
* - replaces "{...}" with "${...}"
* - updates "-Dmaven.repo.local" value
*/
void updateMavenGoals()
{
assert jobType == JobType.maven
/**
* {..} => ${..}
*/
mavenGoals = verify().notNullOrEmpty( mavenGoals ).addDollar()
if ( privateRepository || privateRepositoryPerExecutor )
{
assert ( ! ( localRepoBase || localRepo )), "[${this}] has or set, " +
" and shouldn't be specified"
}
else if ( localRepoBase || localRepo )
{
/**
* Adding "-Dmaven.repo.local=/x/y/z" using {@link #localRepoBase} and {@link #localRepo}
* or replacing it with updated value, if already exists
*/
localRepoPath = (( localRepoBase ?: "${ constants().USER_HOME }/.m2/repository" ) +
'/' +
( localRepo ?: '.' )).
replace( '\\', '/' )
String localRepoArg = "-Dmaven.repo.local="${ localRepoPath }""
mavenGoals = ( mavenGoals.contains( '-Dmaven.repo.local' )) ?
( mavenGoals.replaceAll( /-Dmaven.repo.local\S+/, localRepoArg )) :
( mavenGoals + ' ' + localRepoArg )
}
}
/**
* Validates job is fully configured.
*
* @return validated {@link Job} instance
*/
@SuppressWarnings( 'AbcComplexity' )
Job validate ()
{
if ( isAbstract ) { return this }
assert id, notConfigured( 'missing ' )
assert jenkinsUrl, notConfigured( 'missing ' )
assert generationPom, notConfigured( 'missing ' )
assert description, notConfigured( 'missing ' )
assert jobType, notConfigured( 'missing ' )
assert scmType, notConfigured( 'missing ' )
assert node, notConfigured( 'missing ' )
assert jdkName, notConfigured( 'missing ' )
assert ( scm != null ), notConfigured( '"scm" is null' )
assert ( authToken != null ), notConfigured( '"authToken" is null' )
assert ( properties != null ), notConfigured( '"properties" is null' )
assert ( publishers != null ), notConfigured( '"publishers" is null' )
assert ( buildWrappers != null ), notConfigured( '"buildWrappers" is null' )
assert ( process != null ), notConfigured( '"process" is null' )
assert ( useUpdate != null ), notConfigured( '"useUpdate" is null' )
assert ( doRevert != null ), notConfigured( '"doRevert" is null' )
assert ( daysToKeep != null ), notConfigured( '"daysToKeep" is null' )
assert ( numToKeep != null ), notConfigured( '"numToKeep" is null' )
assert ( artifactDaysToKeep != null ), notConfigured( '"artifactDaysToKeep" is null' )
assert ( artifactNumToKeep != null ), notConfigured( '"artifactNumToKeep" is null' )
assert ( descriptionTable != null ), notConfigured( '"descriptionTable" is null' )
assert ( mail != null ), notConfigured( '"mail" is null' )
assert ( invoke != null ), notConfigured( '"invoke" is null' )
assert ( quietPeriod != null ), notConfigured( '"quietPeriod" is null' )
assert ( scmCheckoutRetryCount != null ), notConfigured( '"scmCheckoutRetryCount" is null' )
assert ( gitHubUrl != null ), notConfigured( '"gitHubUrl" is null' )
assert ( blockBuildWhenDownstreamBuilding != null ), notConfigured( '"blockBuildWhenDownstreamBuilding" is null' )
assert ( blockBuildWhenUpstreamBuilding != null ), notConfigured( '"blockBuildWhenUpstreamBuilding" is null' )
assert ( appendTasks != null ), notConfigured( '"appendTasks" is null' )
validateRepositories()
if ( jobType == JobType.free )
{
assert tasks, notConfigured( 'missing ""' )
assert ! pom, misConfigured( ' is not active in free-style jobs' )
assert ! mavenGoals, misConfigured( ' is not active in free-style jobs' )
assert ! mavenName, misConfigured( ' is not active in free-style jobs' )
assert ( mavenOpts == null ), misConfigured( ' is not active in free-style jobs' )
assert ( buildOnSNAPSHOT == null ), misConfigured( ' is not active in free-style jobs' )
assert ( incrementalBuild == null ), misConfigured( ' is not active in free-style jobs' )
assert ( privateRepository == null ), misConfigured( ' is not active in free-style jobs' )
assert ( privateRepositoryPerExecutor == null ), misConfigured( ' is not active in free-style jobs' )
assert ( archivingDisabled == null ), misConfigured( ' is not active in free-style jobs' )
assert ( reporters == null ), misConfigured( ' is not active in free-style jobs' )
assert ( localRepoBase == null ), misConfigured( ' is not active in free-style jobs' )
assert ( localRepo == null ), misConfigured( ' is not active in free-style jobs' )
assert ( deploy == null ), misConfigured( ' is not active in free-style jobs' )
assert ( artifactory == null ), misConfigured( ' is not active in free-style jobs' )
assert ( prebuilders == null ), misConfigured( ' is not active in free-style jobs' )
assert ( postbuilders == null ), misConfigured( ' is not active in free-style jobs' )
assert ( prebuildersTasks == null ), misConfigured( ' is not active in free-style jobs' )
assert ( postbuildersTasks == null ), misConfigured( ' is not active in free-style jobs' )
assert ( runPostStepsIfResult == null ), misConfigured( ' is not active in free-style jobs' )
}
else if ( jobType == JobType.maven )
{
assert ! tasks, misConfigured( ' is not active in maven jobs' )
assert pom, notConfigured( 'missing ' )
assert mavenGoals, notConfigured( 'missing ' )
assert mavenName, notConfigured( 'missing ' )
assert ( mavenOpts != null ), notConfigured( '"mavenOpts" is null' )
assert ( buildOnSNAPSHOT != null ), notConfigured( '"buildOnSNAPSHOT" is null' )
assert ( incrementalBuild != null ), notConfigured( '"incrementalBuild" is null' )
assert ( privateRepository != null ), notConfigured( '"privateRepository" is null' )
assert ( privateRepositoryPerExecutor != null ), notConfigured( '"privateRepositoryPerExecutor" is null' )
assert ( archivingDisabled != null ), notConfigured( '"archivingDisabled" is null' )
assert ( reporters != null ), notConfigured( '"reporters" is null' )
assert ( localRepoBase != null ), notConfigured( '"localRepoBase" is null' )
assert ( localRepo != null ), notConfigured( '"localRepo" is null' )
assert ( deploy != null ), notConfigured( '"deploy" is null' )
assert ( artifactory != null ), notConfigured( '"artifactory" is null' )
assert ( prebuilders != null ), notConfigured( '"prebuilders" is null' )
assert ( postbuilders != null ), notConfigured( '"postbuilders" is null' )
assert ( prebuildersTasks != null ), notConfigured( '"prebuildersTasks" is null' )
assert ( postbuildersTasks != null ), notConfigured( '"postbuildersTasks" is null' )
assert ( runPostStepsIfResult != null ), notConfigured( '"runPostStepsIfResult" is null' )
if ( deploy?.url || artifactory?.name )
{
assert ( ! archivingDisabled ), \
"[${ this }] has archiving disabled - artifacts deploy to Maven or Artifactory repository can not be used"
}
assert ( ! ( privateRepository && privateRepositoryPerExecutor )), \
"[${ this }] - both and can't be set to \"true\""
if ( privateRepository || privateRepositoryPerExecutor )
{
assert ( ! ( localRepoBase || localRepo || localRepoPath )), \
"[${ this }] has or specified, " +
"no , , or should be defined"
}
}
else
{
throw new IllegalArgumentException ( "Unknown job type [${ jobType }]. " +
"Known types are \"${ JobType.free.name() }\" and \"${ JobType.maven.name() }\"" )
}
this
}
/**
* Validates remote repositories for correctness.
*/
void validateRepositories ()
{
if ( gitHubUrl )
{
assert repositories(), "[${ this }]: Missing or "
}
if ( repositories())
{
assert scmType, "[${ this }]: Missing "
}
for ( repo in repositories().remote )
{
int counter = 0
repositories().remote.each
{
String otherRepo ->
if (( repo == otherRepo ) && (( ++counter ) != 1 ))
{
/**
* Repository should only equal to itself once
*/
throw new MojoExecutionException( "[${ this }]: Repo [$repo] is duplicated" )
}
if (( ! ( repo == otherRepo )) && ( otherRepo.toLowerCase().contains( repo.toLowerCase() + '/' )))
{
throw new MojoExecutionException(
"[${ this }]: Repo [$repo] is duplicated in [$otherRepo] - you should remove [$otherRepo]" )
}
}
}
}
}