
com.github.goldin.plugins.about.AboutMojo.groovy Maven / Gradle / Ivy
The newest version!
package com.github.goldin.plugins.about
import static com.github.goldin.plugins.common.GMojoUtils.*
import com.github.goldin.gcommons.beans.ExecOption
import com.github.goldin.plugins.common.BaseGroovyMojo
import org.apache.maven.artifact.Artifact
import org.apache.maven.plugin.MojoExecutionException
import org.gcontracts.annotations.Ensures
import org.gcontracts.annotations.Requires
import org.jfrog.maven.annomojo.annotations.MojoGoal
import org.jfrog.maven.annomojo.annotations.MojoParameter
import org.jfrog.maven.annomojo.annotations.MojoPhase
import org.jfrog.maven.annomojo.annotations.MojoRequiresDependencyResolution
import java.text.SimpleDateFormat
/**
* Updates files specified with "about" build metadata
*/
@MojoGoal( 'create-about' )
@MojoPhase( 'package' )
@MojoRequiresDependencyResolution( 'test' )
@SuppressWarnings( [ 'StatelessClass', 'PublicInstanceField', 'NonFinalPublicField' ] )
class AboutMojo extends BaseGroovyMojo
{
public static final String SEPARATOR = '|==============================================================================='
@MojoParameter
public boolean updateArchives = true
@MojoParameter
public String prefix = 'META-INF'
@MojoParameter ( defaultValue = 'about-${project.groupId}-${project.artifactId}-${project.version}.txt' )
public String fileName
@MojoParameter
public boolean dumpSCM = true
@MojoParameter
public boolean dumpMaven = false
@MojoParameter
public boolean dumpEnv = false
@MojoParameter
public boolean dumpSystem = false
@MojoParameter
public boolean dumpPaths = false
@MojoParameter
public boolean dumpDependencies = false
@MojoParameter
public String addContent = ''
private String evaluateAddContent()
{
addContent.trim().with { ( startsWith( '{{' ) && endsWith( '}}' )) ? eval(( String ) delegate, String ) : delegate }
}
@MojoParameter
public String endOfLine = 'windows'
@MojoParameter ( defaultValue = '${project.build.directory}' )
public File directory
@MojoParameter
public String include = '*.jar'
@MojoParameter
public String exclude
@MojoParameter
public boolean failOnError = true
@MojoParameter
public boolean failIfNotFound = true
private env = System.getenv()
@Requires({ ( s != null ) && prefix })
@Ensures({ result != null })
private String padLines ( String s, String prefix )
{
List lines = s.readLines()
( lines ? ( lines[ 0 ] + (( lines.size() > 1 ) ? '\n' + lines[ 1 .. -1 ].collect { '|' + ( ' ' * prefix.size()) + it }.join( '\n' ) :
'' )) :
'' )
}
private String exec ( String command,
File directory = basedir,
boolean failOnError = true,
boolean failIfEmpty = true,
int minimalListSize = -1 )
{
assert command && directory
if ( log.isDebugEnabled()) { log.debug( "Running [$command] in [$directory.canonicalPath]" ) }
String result = general().executeWithResult( command, ExecOption.Runtime, failOnError, -1, directory )
if ( log.isDebugEnabled()) { log.debug( "Running [$command] in [$directory.canonicalPath] - result is [$result]" ) }
if ( minimalListSize > 0 )
{
List lines = result.readLines()
assert lines.size() >= minimalListSize, \
"Received not enough data when running [$command] in [$directory.canonicalPath] - " +
"expected list of size [$minimalListSize] at least, received [$result]$lines of size [${ lines.size() }]"
}
assert ( result || ( ! failIfEmpty )), \
"Failed to run [$command] in [$directory.canonicalPath] - result is empty [$result]"
result
}
private String find ( String prefix, String command ) { find( prefix, exec( command ).readLines()) }
private String find ( String prefix, List l ) { l.find{ it.startsWith( prefix ) }?.replace( prefix, '' )?.trim() ?: '' }
private String sort ( Map,?> map )
{
def maxKey = maxKeyLength( map ) + 3
map.sort().collect { key, value -> " [$key]".padRight( maxKey ) + ":[$value]" }.
join( '\n' )
}
/**
* Retrieves result of running "mvn dependency:tree" for the current project.
*
* @return Result of running "mvn dependency:tree" for the current project.
*/
@Ensures({ result })
private String dependencyTree()
{
if ( project.collectedProjects )
{
return 'Aggregate project, no dependencies shown'
}
String coordinates = "${project.groupId}:${project.artifactId}:${project.packaging}:${project.version}"
String plugin = 'maven-dependency-plugin:2.4:tree'
String mvnHome = env[ 'M2_HOME' ]
assert mvnHome, "'M2_HOME' environment variable is not defined"
File mvn = new File( new File( mvnHome ), 'bin/mvn' + ( isWindows ? '.bat' : '' )).canonicalFile
String mavenRepo = System.getProperty( 'maven.repo.local' )
String command = "$mvn -e -B -f ${ project.file.canonicalPath } org.apache.maven.plugins:$plugin" +
( mavenRepo ? " -Dmaven.repo.local=$mavenRepo" : '' )
long t = System.currentTimeMillis()
assert mvn.file, "[$mvn] - not found"
log.info( "Running [$command]" )
def mdt = exec( command )
log.info( "Running [$command] - done, [${ System.currentTimeMillis() - t }] ms" )
assert [ plugin, coordinates ].every { mdt.contains( it ) }, \
"Failed to run [$plugin] - data received doesn't contain enough information: [$mdt]"
def mdtStripped = mdt.replace( '[INFO] ', '' ).
replaceAll( /(?s)^.+?@.+?---/, '' ). // Removing Maven 3 header
replaceAll( /(?s)^.+\[dependency:tree.+?]/, '' ). // Removing Maven 2 header
replaceAll( /(?m)Downloading: .+$/, '' ). // Removing Maven 3 download progress indicator
replaceAll( /(?m)Downloaded: .+$/, '' ). // Removing Maven 3 download progress indicator
replaceAll( /(?s)----+.+$/, '' ). // Removing footer
trim()
assert mdtStripped.startsWith( coordinates ), \
"Failed to run [$plugin] - cleaned up data should start with [$coordinates]: [$mdtStripped]"
project.artifacts.each {
Artifact a ->
if ( ! a.groupId.startsWith( IVY_PREFIX ))
{
"$a.groupId:$a.artifactId".with {
assert mdtStripped.contains(( String ) delegate ), \
"Failed to run [$plugin] - cleaned up data should contain [$delegate]: [$mdtStripped]"
}
}
}
mdtStripped
}
String jenkinsContent()
{
// https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
"""
$SEPARATOR
| Jenkins Info
$SEPARATOR
| Server : [${ env[ 'JENKINS_URL' ] }]
| Job : [${ env[ 'JENKINS_URL' ] }job/${ env[ 'JOB_NAME' ] }/${ env[ 'BUILD_NUMBER' ]}/]
| Log : [${ env[ 'JENKINS_URL' ] }job/${ env[ 'JOB_NAME' ] }/${ env[ 'BUILD_NUMBER' ]}/console]"""
}
String hudsonContent()
{
// http://weblogs.java.net/blog/johnsmart/archive/2008/03/using_hudson_en.html
"""
$SEPARATOR
| Hudson Info
$SEPARATOR
| Server : [${ env[ 'HUDSON_URL' ] }]
| Job : [${ env[ 'HUDSON_URL' ] }job/${ env[ 'JOB_NAME' ] }/${ env[ 'BUILD_NUMBER' ]}/]
| Log : [${ env[ 'HUDSON_URL' ] }job/${ env[ 'JOB_NAME' ] }/${ env[ 'BUILD_NUMBER' ]}/console]"""
}
String teamcityContent()
{
// http://confluence.jetbrains.net/display/TCD65/Predefined+Build+Parameters
// http://confluence.jetbrains.net/display/TCD7/Predefined+Build+Parameters
def urlMessage = 'Define \'TEAMCITY_URL\' environment variable and make sure \'-Dteamcity.build.id\' specified when job starts'
def buildId = System.getProperty( 'teamcity.build.id' )
def teamCityUrl = ( env[ 'TEAMCITY_URL' ]?.replaceAll( /(? svnInfo = exec( "svn info ${basedir.canonicalPath}", basedir, true, true, 2 ).readLines()
/**
* ------------------------------------------------------------------------
* r39087 | Evgeny | 2011-08-24 09:28:06 +0300 (Wed, 24 Aug 2011) | 1 line
*
* About removed
* ------------------------------------------------------------------------
*/
List commitLines = exec( "svn log ${basedir.canonicalPath} -l 1", basedir, true, true, 3 ).readLines().grep()
assert [ commitLines[ 0 ], commitLines[ -1 ]].each { it.with { startsWith( '---' ) && endsWith( '---' ) }}, \
"Unknown commit format:\n$commitLines"
String commit = commitLines[ 1 ]
List commitMessage = ( commitLines.size() > 3 ) ? commitLines[ 2 .. -2 ]*.trim() : []
"""
$SEPARATOR
| SVN Info
$SEPARATOR
| Repository : [${ find( 'URL:', svnInfo )}]
| Revision : [${ find( 'Revision:', svnInfo )}]
| Status : [${ padLines( svnStatus, ' Status : [' ) }]
| Commit : [$commit]
| Commit Date : [${ split( commit, '\\|' )[ 2 ].trim() }]
| Commit Author : [${ split( commit, '\\|' )[ 1 ].trim() }]
| Commit Message : [${ padLines( commitMessage.join( '\n' ), ' Commit Message : [' ) }]"""
}
String gitContent( String gitStatus )
{
/**
* http://schacon.github.com/git/git-log.html
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* dc38954
* dc389541c4aa7f72f07f11236b1c632a919de61c
* Fri, 28 Oct 2011 15:40:03 +0200
* Evgeny Goldin
* [email protected]
* 0.2.3.5-SNAPSHOT
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
List log = exec( 'git log -1 --format=format:%h%n%H%n%cD%n%cN%n%ce%n%B', basedir, true, true, 5 ).readLines()*.trim()
"""
$SEPARATOR
| Git Info
$SEPARATOR
| Repositories : [${ padLines( exec( 'git remote -v' ), ' Repositories : [' ) }]
| Branch : [${ find( '# On branch', 'git status' ) }]
| Git Status : [${ padLines( gitStatus, ' Git Status : [' ) }]
| Commit : [${ log[ 0 ] }][${ log[ 1 ] }]
| Commit Date : [${ log[ 2 ] }]
| Commit Author : [${ log[ 3 ] } <${ log[ 4 ] }>]
| Commit Message : [${ log.size() > 5 ? padLines( log[ 5 .. -1 ].join( '\n' ), ' Commit Message : [' ) : '' }]"""
}
String allContent()
{
def version = properties( 'META-INF/maven/com.github.goldin/about-maven-plugin/pom.properties', AboutMojo.classLoader ).
getProperty( 'version', '' )
( " Created with http://evgeny-goldin.com/wiki/Maven-about-plugin${ version ? ', version "' + version + '"' : '' }\n" +
serverContent() +
scmContent() +
buildContent() +
optionalContent() + '\n' +
SEPARATOR ).
stripMargin().readLines()*.replaceAll( /\s+$/, '' ).grep(). // Deleting empty lines
join(( 'windows' == endOfLine ) ? '\r\n' : '\n' )
}
@Requires({ aboutFile })
@Ensures({ result == aboutFile })
File writeAboutFile( File aboutFile )
{
file().delete( aboutFile )
long t = System.currentTimeMillis()
log.info( "Generating \"about\" in [$aboutFile.canonicalPath], basedir is [${ basedir.canonicalPath }]" )
file().mkdirs( aboutFile.parentFile )
aboutFile.write( allContent())
log.info( "Generated \"about\" in [$aboutFile.canonicalPath] (${ System.currentTimeMillis() - t } ms)" )
aboutFile
}
@Override
void doExecute ()
{
try
{
if ( updateArchives )
{
if ( ! directory.directory )
{
assert ( ! failIfNotFound ), "Directory [$directory.canonicalPath] is not available, consider using "
log.warn( "Directory [$directory.canonicalPath] is not available, \"about\" is not created" )
return
}
def split = { String s -> s ? split( s ) : null }
def files = file().files( directory, split( include ), split( exclude ), false, false, failIfNotFound )
if ( files )
{
def aboutFile = new File( outputDirectory(), fileName )
def prefix = (( prefix == '/' ) ? '' : prefix )
writeAboutFile( aboutFile )
for ( f in files )
{
def aboutPath = "$f.canonicalPath/$prefix${ prefix ? '/' : '' }$fileName"
log.info( "Adding \"about\" to [$aboutPath]" )
file().pack( aboutFile.parentFile, f, [ aboutFile.name ], null, false, true, true, null, null, prefix )
log.info( "Added \"about\" to [$aboutPath]" )
}
file().delete( aboutFile )
}
else
{
log.warn( "No files found in [$directory.canonicalPath] and include/exclude patterns [${ include ?: '' }]/[${ exclude ?: '' }]" )
}
}
else
{
def aboutFile = ( File ) new File( fileName ).with{ absolute ? delegate : new File( outputDirectory(), fileName )}
writeAboutFile( aboutFile )
}
}
catch ( Throwable e )
{
def message = 'Failed to create "about" file'
if ( failOnError ) { throw new MojoExecutionException( message, e ) }
log.error( "$message:", e )
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy