Inledning
Nu för tiden använder nog de flesta Javaprojekt någon sorts dependencyhanterare i sina byggen.
Den tid då man checkade in sina tredjepartsbibliotek i versionshanteringen är förbi. Nu låter man Maven (eller Ivy eller Gradle) läsa en incheckad förteckning, hämta hem jarfilerna ifrån de centrala förråd som finns därute och sätta upp class path.
En sak som dessa verktyg dock inte hjälper till med är att hålla koll på att man inte ligger kvar på gamla versioner. Att köra på gamla versioner kan ses som en sorts teknisk skuld, eftersom det blir värre och värre att uppgradera ju fler versioner man har missat.
Det är dock ett mödosamt och tråkigt manuellt arbete att kolla hur bra man ligger till.
Jag har därför utvecklat en Gradle-task som automatiserar detta!
(Se där, ännu en anledning att byta till Gradle!)
Jag har skrivit tasken i min build.gradle för Varmfront.nu, men med Gradle är det ju lätt att bryta ut kod för återanvändning. (Hur man gör detta kanske blir ämne för ett annat blogginlägg.)
Gradlekoden
Så utan vidare krussiduller, här kommer koden:
task findUpgradableDependencies { description "Find dependencies which have newer versions in Maven central." group "Help" doLast { def checked = [:] allprojects { configurations.each { configuration -> configuration.allDependencies.each { dependency -> if (dependency instanceof ExternalDependency && !checked[dependency]) { def url = """http://search.maven.org/solrsearch/select?q=g:"$dependency.group"+AND+a:"$dependency.name"&rows=1&wt=xml""" try { def response = new XmlSlurper().parseText(url.toURL().text) def latest = response.result.doc.str.grep { it.@name == "latestVersion"}.join() if (dependency.version != latest && latest && !latest.matches('20030203\\.000[0-9]{3}')) { // commons-lang and commons-io have peculiar metadata in Maven Central println "$dependency.group:$dependency.name:$dependency.version -> $latest" } } catch(FileNotFoundException e) { logger.debug "Unable to download $url: " + e.message } catch(org.xml.sax.SAXParseException e) { logger.debug "Unable to parse $url: " + e.message } checked[dependency] = true } } } } } }
Exempel på en körning
Så här kan en körning se ut:
olle@olles-laptop2:~/workspace/git/varmfront$ gradle findUpgradableDependencies :findUpgradableDependencies org.springframework.data:spring-data-commons:1.5.1.RELEASE -> 1.5.2.RELEASE org.springframework.data:spring-data-mongodb:1.2.1.RELEASE -> 1.2.3.RELEASE org.projectlombok:lombok:0.11.8 -> 0.12.0 org.codehaus.groovy:groovy-all:2.1.5 -> 2.2.0-beta-1 de.flapdoodle.embed:de.flapdoodle.embed.mongo:1.33 -> 1.34 org.apache.tomcat.embed:tomcat-embed-core:7.0.41 -> 7.0.42 org.apache.tomcat.embed:tomcat-embed-logging-juli:7.0.41 -> 7.0.42 org.apache.tomcat.embed:tomcat-embed-jasper:7.0.41 -> 7.0.42 BUILD SUCCESSFUL Total time: 14.648 secs olle@olles-laptop2:~/workspace/git/varmfront$
Diskussion
Nu är det ju naturligtvis inte så att man ska uppgradera omedelbart så fort det kommer en ny version. Det kan finnas många legitima skäl till att ligga kvar på gamla versioner.
Men med detta verktyg får man ju lätt fatt på informationen om hur illa man ligger till och kan lättare ta med det i planeringen.
Begränsningar och möjliga förbättringar
Min kod hanterar bara Maven Central via dess REST-gränsnitt.
Man skulle också kunna tänka sig att lägga in en undantagslista, så att man i byggscriptet kan dokumentera beslut om att ligga kvar på vissa gamla versioner.
I maven är det inbyggt:
mvn versions:display-dependency-updates
Gradle vs. Maven: 0-1
😉
Bara att committa till Gradle! Oscar gav ett argument, inte kan det vara sämre än Maven! 🙂
Med Maven måste man använda en plugin för att komma åt interna API:er (som t.ex. dependency-maskineriet). Saknas lämpligt plugin är det en väldigt hög tröskel att klättra över.
Med Gradle kan man använda någon existerande plugin eller helt enkelt hacka till funktionaliteten själv (som jag visade i mitt exempel ovan). Gradle är ju implementerat i Groovy, och du har således direkt tillgång till alla interna API:er.
Detta är i mina ögon en enorm fördel!
PS. Jag har en gång skrivit en Maven-mojo som tog reda på vilket Perforce changeliste-nummer som man hade synkat sin workspace till. En ansenlig mängd accidental complexity, eftersom det gick ut på att slå kommandot
p4 changes -m1 ...#have
och parsa ut ett heltal i första raden i svaret.Med Gradle blev samma sak en Groovy-funktion på c:a 5 rader, direkt i build.gradle. Funktionen är så trivial att det inte är värt att paketera den. Jag copy-pastear när jag behöver den igen i ett annat projekt. DS.
PPS, Ibland tar det längre tid att leta upp en lämplig plugin som fungerar, än att hacka till funktionen själv. DS.
Det fanns faktiskt en plugin för ändamålet: https://github.com/ben-manes/gradle-versions-plugin
Jag kände inte till den när jag skapade min lösning.
Men min poäng kvarstår, det är en enorm fördel att ha en intern DSL!