| /******************************************************************************* |
| * Copyright (c) 23.08.2011 Aaron Digulla. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Aaron Digulla - initial API and implementation and/or initial documentation |
| *******************************************************************************/ |
| package m4e |
| |
| import groovy.xml.MarkupBuilder; |
| import java.io.File; |
| import java.text.SimpleDateFormat; |
| import java.util.regex.Pattern; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| class AnalyzeCmd extends AbstractCommand { |
| |
| static final String DESCRIPTION = '''\ |
| repository [ ignore ] |
| - Check a converted Maven 2 repository for various problems |
| ''' |
| |
| void run( String... args ) { |
| if( args.size() <= 1 ) { |
| throw new UserError( 'Missing path to repository to analyze' ) |
| } |
| |
| File repo = new File( args[1] ).absoluteFile |
| if( !repo.exists() ) { |
| throw new UserError( "Directory ${repo} doesn't exist" ) |
| } |
| |
| File ignoreList = null |
| if( args.size() >= 3 ) { |
| ignoreList = new File( args[2] ).absoluteFile |
| if( !ignoreList.exists() ) { |
| throw new UserError( "File with ignore options ${ignoreList} doesn't exist" ) |
| } |
| } |
| |
| def tool = new Analyzer( repo, Calendar.getInstance() ) |
| |
| if( ignoreList ) { |
| tool.loadIgnores( ignoreList ) |
| } |
| |
| tool.run() |
| } |
| } |
| |
| class Analyzer { |
| |
| static final Logger log = LoggerFactory.getLogger( ConsoleUtils ) |
| |
| File repo |
| File reportFile |
| Calendar timestamp |
| Set<Glob> ignores = new HashSet() |
| Set<Glob> ignoreMissingSources = new HashSet() |
| |
| Analyzer( File repo, Calendar timestamp ) { |
| this.repo = repo.canonicalFile |
| this.timestamp = timestamp |
| |
| SimpleDateFormat formatter = new SimpleDateFormat( 'yyyyMMdd-HHmmss' ) |
| formatter.setTimeZone( timestamp.getTimeZone() ) |
| |
| String now = formatter.format( timestamp.getTime() ) |
| reportFile = new File( repo.absolutePath + "-analysis-${now}.html" ) |
| } |
| |
| void loadIgnores( File file ) { |
| String manyRegexp = '[^ :]*' |
| |
| file.eachLine { |
| String line = it.substringBefore( '#' ).trim() |
| |
| if( !line ) { |
| return |
| } |
| |
| line = line.replaceAll( '\\s+', ' ' ) |
| |
| if( line.startsWith( 'MissingSources ' ) ) { |
| line = line.substringAfter( ' ' ) |
| ignoreMissingSources << new Glob( line, manyRegexp ) |
| } else { |
| ignores << new Glob( line, manyRegexp ) |
| } |
| } |
| } |
| |
| void run() { |
| log.info( 'Analyzing {}...', repo ) |
| MavenRepositoryTools.eachPom( repo ) { |
| analyzePom( it ) |
| } |
| |
| sortEverything() |
| |
| if( missingSource ) { |
| def l = missingSource.findResults { |
| def key = it.key() |
| |
| for( Glob g : ignoreMissingSources ) { |
| if( g.matches( key ) ) { |
| return null |
| } |
| } |
| |
| return it |
| } |
| |
| problems << new MissingSources( l ) |
| } |
| |
| log.info( 'Found {} POM files. Looking for problems...', poms.size() ) |
| validate() |
| |
| log.info( 'Found {} problems. Generating report...', problems.size() ) |
| report() |
| } |
| |
| void report() { |
| // textReport() |
| htmlReport() |
| } |
| |
| void textReport() { |
| println "Found ${poms.size()} POM files" |
| println "Found ${problems.size()} problems" |
| |
| for( def p in problems ) { |
| println p |
| } |
| } |
| |
| void htmlReport() { |
| log.info( 'Writing HTML report to {}', reportFile ) |
| |
| reportFile.withWriter('utf-8') { writer -> |
| htmlReport( writer ) |
| } |
| } |
| |
| void htmlReport( Writer writer ) { |
| MarkupBuilder builder = new MarkupBuilder( writer ) |
| |
| SimpleDateFormat formatter = new SimpleDateFormat( 'yyyy.MM.dd HH:mm:ss' ) |
| formatter.setTimeZone( timestamp.getTimeZone() ) |
| |
| String now = formatter.format( timestamp.getTime() ) |
| |
| String titleText = "Analysis of ${repo} (${now})" |
| |
| builder.html { |
| head { |
| title titleText |
| |
| style( type: 'text/css', ''' |
| html, body { background: white; } |
| .pom { font-weight: bold; color: #7F0055; font-family: monospace; } |
| .dependency { font-weight: bold; color: #55007F; font-family: monospace; } |
| .version { font-weight: bold; color: #007F55; font-family: monospace; } |
| .file { font-weight: bold; color: #00557F; font-family: monospace; } |
| .files { font-style: italic; } |
| .padLeft { padding-left: 10px; } |
| tr:hover { background-color: #D0E0FF; } |
| .hidden { color: white; } |
| .error { font-weight: bold; color: red; } |
| .problem { border-left: 3px solid white; border-bottom: 1px solid #ccc; padding-left: 3px; } |
| .problem:hover { border-left-color: #cccccc; } |
| .ignoreKey { color: #ccc; } |
| ''' |
| ) |
| |
| } |
| body { |
| h1 titleText |
| |
| p "Found ${poms.size()} POM files" |
| p "Found ${problems.size()} problems" |
| |
| renderProblemsAsHtml( builder ) |
| |
| renderRepoAsHtml( builder ) |
| |
| // Add some empty space below the page to make sure anchors can always scroll to the top |
| div( style: 'height: 20em;' ) { |
| yield( ' ', false ) |
| } |
| } |
| } |
| } |
| |
| Map<String, String> renderToc( MarkupBuilder builder, List<ProblemType> keys, Map<ProblemType, List<Problem>> map ) { |
| |
| Map<ProblemType, String> problemTitle2Anchor = [:] |
| |
| builder.h2 'Table of Contents' |
| |
| int index = 1 |
| |
| builder.ul( 'class': 'toc' ) { |
| for( def key in keys ) { |
| String anchor = "toc${index}" |
| index ++ |
| |
| problemTitle2Anchor[key] = anchor |
| |
| li { |
| a( href: "#${anchor}", "${key.title} (${map[key].size()})" ) |
| } |
| } |
| |
| li { |
| a( href: "#poms", "${poms.size()} POMs in the repository" ) |
| } |
| } |
| |
| return problemTitle2Anchor |
| } |
| |
| void renderProblemsAsHtml( MarkupBuilder builder ) { |
| Map<ProblemType, List<Problem>> map = [:] |
| |
| for( def p in problems ) { |
| ProblemType type = ProblemType.byClass( p.class ) |
| def list = map.get( type, [] ) |
| list << p |
| } |
| |
| List<ProblemType> keys = new ArrayList( map.keySet() ) |
| keys.sort() |
| |
| def problemTitle2Anchor = renderToc( builder, keys, map ) |
| |
| for( def key in keys ) { |
| def list = map[key] |
| |
| builder.h2( id: problemTitle2Anchor[key], key.title ) |
| |
| if( key.description ) { |
| builder.p key.description |
| } |
| |
| String s = list.size() == 1 ? '' : 's' |
| builder.p "${list.size()} time${s}" |
| |
| for( def p in list ) { |
| p.render( builder ) |
| } |
| } |
| } |
| |
| void renderRepoAsHtml( MarkupBuilder builder ) { |
| builder.h2( id: 'poms', "${poms.size()} POMs in the repository" ) |
| |
| def pomShortKeys = new ArrayList( pomByShortKey.keySet() ) |
| pomShortKeys.sort() |
| |
| builder.table( border: '0', cellspacing: '0', cellpadding: '0' ) { |
| String currentLabel = '' |
| |
| tr { |
| th 'Group ID' |
| th 'Artifact ID + Version' |
| th 'Files' |
| } |
| |
| for( def shortKey in pomShortKeys ) { |
| String label = shortKey.substringBefore( ':' ) |
| |
| if( label == currentLabel ) { |
| label = '' |
| } else { |
| currentLabel = label |
| } |
| |
| tr { |
| td { |
| builder.yield( label, true ) |
| |
| if( label ) { |
| builder.yield( '''<span class='hidden'>:</span>''', false ) |
| } |
| } |
| |
| def pom = pomByShortKey[shortKey] |
| def artifactId = pom.value( Pom.ARTIFACT_ID ) |
| def version = pom.version() |
| |
| td { |
| span( 'class': 'pom', artifactId ) |
| builder.yield( '''<span class='hidden'>:</span>''', false ) |
| |
| span( 'class': 'version', version ) |
| } |
| |
| td( 'class': 'padLeft' ) { |
| def files = pom.files() |
| |
| if( files ) { |
| span( 'class': 'files' ) { |
| builder.yield( ' ', true ) |
| builder.yield( pom.files().join( ' ' ), true ) |
| } |
| } else { |
| span( 'class': 'error', 'No files found; check problems above' ) |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void validate() { |
| checkDifferentVersions() |
| checkMissingDependencies() |
| |
| postProcessProblemSameKeyDifferentVersion() |
| |
| applyIgnores() |
| } |
| |
| void applyIgnores() { |
| Set<Glob> unused = new HashSet( ignores ) |
| |
| problems = problems.findResults { |
| String key = it.key() |
| |
| for( Glob g : ignores ) { |
| if( g.matches( key ) ) { |
| unused.remove( g ) |
| return null |
| } |
| } |
| |
| return it |
| } |
| |
| |
| if( unused ) { |
| log.warn( "Not all ignores were necessary:" ) |
| unused.each { |
| log.warn( ' {}', it ) |
| } |
| } |
| |
| } |
| |
| void postProcessProblemSameKeyDifferentVersion() { |
| for( def p in problems ) { |
| if( !(p instanceof ProblemSameKeyDifferentVersion ) ) { |
| continue |
| } |
| |
| Set<Pom> set = new HashSet<Pom>( nullToEmpty( dependencyUsage[p.pom.shortKey()] ) ) |
| set.addAll( ( nullToEmpty( dependencyUsage[p.other.shortKey()] ) ) ) |
| |
| List<Pom> list = new ArrayList( set ) |
| list.sort() { it.key() } |
| |
| p.usedIn = list |
| } |
| } |
| |
| List nullToEmpty( def list ) { |
| return null == list ? [] : list |
| } |
| |
| void checkDifferentVersions() { |
| for( def entry in versionBackRefsMap.entrySet() ) { |
| // println "${entry.key} -> ${entry.value.keySet()}" |
| |
| if( entry.value.size() <= 1 ) { |
| continue |
| } |
| |
| problems << new ProblemDifferentVersions( entry.key, entry.value ) |
| } |
| } |
| |
| void checkMissingDependencies() { |
| List<String> keys = new ArrayList( dependencyUsage.keySet() ) |
| keys.sort() |
| |
| for( def key in keys ) { |
| def pom = pomByShortKey[key] |
| |
| if( !pom ) { |
| problems << new MissingDependency( key, dependencyUsage[key] ) |
| } |
| } |
| } |
| |
| /** List of all POMs in the repo */ |
| List<Pom> poms = [] |
| |
| /** shortKey -> POM */ |
| Map<String, Pom> pomByShortKey = [:] |
| |
| /** shortKey or dependency -> list of POMs in which it is used */ |
| Map<String, List<Pom>> dependencyUsage = [:] |
| |
| /** All found problems */ |
| List<Problem> problems = [] |
| |
| /** All versions of a dependency */ |
| Map<String, Set<String>> versions = [:] |
| |
| /** short key -> versions -> poms */ |
| Map<String, Map<String, List<Pom>>> versionBackRefsMap = [:] |
| |
| /** List of artifacts without source */ |
| List<Pom> missingSource = [] |
| |
| void sortEverything() { |
| poms.sort() { it.key() } |
| |
| for( def item in dependencyUsage ) { |
| item.value.sort() { it.key() } |
| } |
| |
| problems.sort() { it.class.name + ':' + it.sortKey() } |
| |
| for( def backRefs in versionBackRefsMap ) { |
| for( def backRef in backRefs.value ) { |
| backRef.value.sort() { it.key() } |
| } |
| } |
| |
| missingSource.sort() { it.key() } |
| } |
| |
| void analyzePom( File path ) { |
| def pom = Pom.load( path ) |
| poms << pom |
| |
| log.debug( 'Analyzing {} {}', path, pom.key() ) |
| |
| File pomPath = MavenRepositoryTools.buildPath( repo, pom.key(), 'pom' ).canonicalFile |
| if( path != pomPath ) { |
| problems << new PathProblem( pom, pomPath, path ) |
| } |
| |
| String shortKey = pom.shortKey() |
| Pom other = pomByShortKey[shortKey] |
| if( other ) { |
| def poms = [ pom, other ].sort { it.key() } |
| |
| problems << new ProblemSameKeyDifferentVersion( poms[0], poms[1] ) |
| } |
| |
| String version = pom.version() |
| if( version && version.endsWith( '-SNAPSHOT' ) ) { |
| problems << new ProblemSnaphotVersion( pom ) |
| } |
| |
| pomByShortKey[shortKey] = pom |
| |
| def files = pom.files() |
| if( files && !files.contains( 'sources' ) ) { |
| missingSource << pom |
| } |
| |
| for( def d in pom.dependencies ) { |
| |
| if( 'true' == d.value( Dependency.OPTIONAL ) ) { |
| continue |
| } |
| |
| def depKey = d.shortKey() |
| |
| def list = dependencyUsage.get( depKey, [] ) |
| list << pom |
| |
| version = d.value( Dependency.VERSION ) |
| if( !version ) { |
| problems << new DependencyWithoutVersion( pom, d ) |
| } else if( '[0,)' == version ) { |
| // Ignore |
| } else if( isVersionRange( version ) ) { |
| // This is no longer a problem because the version ranges are overwritten by dependency management |
| // problems << new ProblemVersionRange( pom, d ) |
| } else if( version && version.endsWith( '-SNAPSHOT' ) ) { |
| problems << new ProblemSnaphotVersion( pom, d ) |
| } |
| |
| def set = versions.get( depKey, new HashSet<String>() ) |
| set << version |
| |
| def versionToPoms = versionBackRefsMap.get( depKey, [:] ) |
| def backRefs = versionToPoms.get( version, [] ) |
| backRefs << pom |
| } |
| } |
| |
| boolean isVersionRange( String version ) { |
| return version && ( |
| version.startsWith( '[' ) |
| || version.startsWith( '(' ) |
| ) |
| } |
| } |
| |
| class Problem { |
| Pom pom |
| String message |
| |
| Problem( Pom pom, String message ) { |
| this.pom = pom |
| this.message = message |
| } |
| |
| @Override |
| public String toString() { |
| return "POM ${pom?.key()}: ${message}"; |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| yield( 'POM ', true ) |
| span( 'class': 'pom', pom.key() ) |
| yield( ' ', true ) |
| span( 'class': 'message', message ) |
| } |
| } |
| |
| String key() { |
| return "${getClass().simpleName} ${pom?.key()}" |
| } |
| |
| String sortKey() { |
| return pom.key() |
| } |
| } |
| |
| class ProblemVersionRange extends Problem { |
| |
| Dependency dependency |
| |
| ProblemVersionRange( Pom pom, Dependency dependency ) { |
| super( pom, "The dependency ${dependency.key()} in POM ${pom.key()} uses a version range" ) |
| |
| this.dependency = dependency |
| } |
| |
| @Override |
| public String toString() { |
| return "POM ${pom.key()}: ${message}"; |
| } |
| |
| @Override |
| String key() { |
| return "${super.key()} ${dependency.key()}" |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| yield( 'The dependency ', true ) |
| span( 'class': 'dependency', dependency.key() ) |
| yield( ' in POM ' ) |
| span( 'class': 'pom', pom.key() ) |
| yield( ' uses a version range' ) |
| } |
| } |
| } |
| |
| class MissingSources extends Problem { |
| |
| List<Pom> poms |
| |
| MissingSources( List<Pom> poms ) { |
| super( null, "${poms.size} artifacts are without sources" ) |
| |
| this.poms = poms |
| } |
| |
| @Override |
| public String sortKey() { |
| return "MissingSources"; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder buffer = new StringBuilder() |
| buffer << message |
| buffer << ':\n' |
| |
| poms.each() { |
| buffer << " ${it.key()}\n" |
| } |
| |
| return buffer; |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| p "Missing sources for ${poms.size()} artifacts" |
| ul { |
| for( def pom in poms ) { |
| li { |
| span( 'class': 'ignoreKey', 'MissingSources ' ) |
| span( 'class': 'pom', pom.key() ) |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| class PathProblem extends Problem { |
| |
| File expected |
| File actual |
| |
| PathProblem( Pom pom, File expected, File actual ) { |
| super( pom, "The path for the POM ${pom.key()} should [${expected}] but it is ${actual} " ) |
| |
| this.expected = expected |
| this.actual = actual |
| } |
| |
| @Override |
| public String toString() { |
| return "POM ${pom.key()}: ${message}"; |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| yield( 'The path for the POM ', true ) |
| span( 'class': 'pom', pom.key() ) |
| yield( ' should be' ) |
| span( 'class': 'file', expected ) |
| yield( ' but was ' ) |
| span( 'class': 'file', actual ) |
| } |
| } |
| } |
| |
| class ProblemSnaphotVersion extends Problem { |
| |
| Dependency dependency |
| |
| ProblemSnaphotVersion( Pom pom ) { |
| super( pom, "The POM ${pom.key()} is a snapshot version" ) |
| } |
| |
| ProblemSnaphotVersion( Pom pom, Dependency dependency ) { |
| super( pom, "The dependency ${dependency.key()} in POM ${pom.key()} uses a snapshot version" ) |
| |
| this.dependency = dependency |
| } |
| |
| @Override |
| public String toString() { |
| return "${message}"; |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| if( dependency ) { |
| yield( 'The dependency ', true ) |
| span( 'class': 'dependency', dependency.key() ) |
| yield( ' in POM ' ) |
| span( 'class': 'pom', pom.key() ) |
| yield( ' uses a snapshot version' ) |
| } else { |
| yield( 'The POM ' ) |
| span( 'class': 'pom', pom.key() ) |
| yield( ' uses a snapshot version' ) |
| } |
| } |
| } |
| } |
| |
| class ProblemSameKeyDifferentVersion extends Problem { |
| |
| Pom other |
| List<Pom> usedIn = [] |
| |
| ProblemSameKeyDifferentVersion( Pom pom, Pom other ) { |
| super( pom, 'There is another POM with the same ID but a different version' ) |
| |
| this.other = other |
| } |
| |
| @Override |
| String key() { |
| return "${super.key()} ${other.key()}" |
| } |
| |
| @Override |
| public String toString() { |
| return "POM ${pom.key()}: ${message}: ${other.key()}" |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| div( 'class': 'ignoreKey' ) { |
| yield( key(), true ) |
| } |
| yield( 'There are two POMs with the same ID but different version:', true ) |
| ul { |
| li { |
| span( 'class': 'pom', pom.key() ) |
| } |
| li { |
| span( 'class': 'pom', other.key() ) |
| } |
| } |
| |
| if( usedIn ) { |
| yield( 'These POMs are used in:', true ) |
| ul { |
| for( Pom pom in usedIn ) { |
| li { |
| span( 'class': 'pom', pom.key() ) |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| class DependencyWithoutVersion extends Problem { |
| |
| Dependency dependency |
| |
| DependencyWithoutVersion( Pom pom, Dependency dependency ) { |
| super( pom, 'Missing version in dependency' ) |
| |
| this.dependency = dependency |
| } |
| |
| @Override |
| String key() { |
| return "${super.key()} ${dependency.key()}" |
| } |
| |
| @Override |
| public String toString() { |
| return "POM ${pom.key()}: ${message} ${dependency.key()}"; |
| } |
| |
| void render( MarkupBuilder builder ) { |
| builder.div( 'class': 'problem' ) { |
| yield( 'POM ', true ) |
| span( 'class': 'pom', pom.key() ) |
| yield( ' ', true ) |
| span( 'class': 'message', message ) |
| yield( ' ', true ) |
| span( 'class': 'dependency', dependency.key() ) |
| } |
| } |
| } |
| |
| class ProblemDifferentVersions extends Problem { |
| |
| Map<String, List<Pom>> versionBackRefs |
| String dependency |
| |
| ProblemDifferentVersions( String dependency, Map<String, List<Pom>> versionBackRefs ) { |
| super( null, 'This dependency is referenced with different versions' ) |
| |
| this.dependency = dependency |
| this.versionBackRefs = versionBackRefs |
| } |
| |
| @Override |
| String key() { |
| return "${super.key()} ${dependency}" |
| } |
| |
| @Override |
| String sortKey() { |
| return dependency |
| } |
| |
| @Override |
| public String toString() { |
| def versions = new ArrayList( versionBackRefs.keySet() ) |
| Collections.sort( versions ) |
| |
| StringBuilder buffer = new StringBuilder() |
| buffer.append( "The dependency ${pom.key()} is referenced with ${versions.size()} different versions:\n" ) |
| |
| for( String version in versions ) { |
| buffer.append( " Version ${version} is used in:\n" ) |
| |
| def backRefs = versionBackRefs[version] |
| for( def pom in backRefs ) { |
| buffer.append( " ${pom.key()}" ) |
| } |
| } |
| |
| return buffer |
| } |
| |
| void render( MarkupBuilder builder ) { |
| def versions = new ArrayList( versionBackRefs.keySet() ) |
| Collections.sort( versions ) |
| |
| builder.div( 'class': 'problem' ) { |
| div( 'class': 'ignoreKey' ) { |
| yield( key(), true ) |
| } |
| yield( 'The dependency ', true ) |
| span( 'class': 'dependency', dependency ) |
| yield( " is referenced with ${versions.size()} different versions:", true ) |
| |
| ul() { |
| for( String version in versions ) { |
| li() { |
| yield( 'Version "', true ) |
| span('class': 'version', version) |
| yield( '" is used in:', true ) |
| } |
| |
| def backRefs = versionBackRefs[version] |
| backRefs.sort(true) { |
| it.key() |
| } |
| |
| ul() { |
| for( def pom in backRefs ) { |
| def parts = pom.key().split(':', -1) |
| |
| li { |
| span('class': 'pom', "${parts[0]}:${parts[1]}") |
| yield( ':', true ) |
| span('class': 'version', "${parts[2]}") |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| class MissingDependency extends Problem { |
| |
| String key |
| List<Pom> poms |
| |
| MissingDependency( String key, List<Pom> poms ) { |
| super( poms[0], 'Missing dependencies' ) |
| |
| this.key = key |
| this.poms = poms |
| } |
| |
| @Override |
| String sortKey() { |
| return key |
| } |
| |
| @Override |
| String key() { |
| return "${getClass().simpleName} ${key}" |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder buffer = new StringBuilder() |
| buffer << "The dependency ${key} is used in ${poms.size} POMs but I can't find it in this M2 repo:\n" |
| |
| for( def pom in poms ) { |
| buffer << " ${pom.key()}" |
| } |
| |
| return buffer |
| } |
| |
| void render(MarkupBuilder builder) { |
| builder.div( 'class': 'problem' ) { |
| div( 'class': 'ignoreKey' ) { |
| yield( key(), true ) |
| } |
| yield( 'The dependency ', true ) |
| span( 'class':'dependency', key ) |
| yield( " is used in ${poms.size} POMs:", true ) |
| |
| ul { |
| for( def pom in poms ) { |
| li { |
| span( 'class': 'pom', pom.key() ) |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| enum ProblemType { |
| Problem( 'Generic Problems', null), |
| ProblemSameKeyDifferentVersion( 'POMs with same ID but different version', null), |
| DependencyWithoutVersion( 'Problems With Dependencies', null), |
| ProblemDifferentVersions( 'Dependencies With Different Versions', null), |
| MissingDependency( 'Missing Dependencies', "The following dependencies are used in POMs in the repository but they couldn't be found in it." ), |
| ProblemVersionRange( 'Dependencies With Version Ranges', 'Dependencies should not use version ranges.' ), |
| ProblemSnaphotVersion( 'Snapshot Versions', 'Release Repositories should not contain SNAPSHOTs' ), |
| PathProblem( 'Path Problems', 'These POMs are not where they should be' ), |
| MissingSources( 'Missing Sources', null ) |
| |
| final String title |
| final String description |
| |
| private ProblemType( String title ) { |
| this( title, null ) |
| } |
| |
| private ProblemType( String title, String description ) { |
| this.title = title |
| this.description = description |
| } |
| |
| public static ProblemType byClass( Class type ) { |
| ProblemType result = valueOf( type.simpleName ) |
| if( null == result ) { |
| throw new RuntimeException( "Unknown type ${type.name}" ) |
| } |
| return result |
| } |
| } |