| 1 | import org.tmatesoft.svn.core.wc.* | 
|---|
| 2 |  | 
|---|
| 3 | //includeTargets << grailsScript("Init") | 
|---|
| 4 |  | 
|---|
| 5 | /** | 
|---|
| 6 | * Check and update the app.vcsRevision property in application.properties file and metadata. | 
|---|
| 7 | * May be run directly by `grails update-rev` and normally run by _Events.groovy and eventCompileStart. | 
|---|
| 8 | * The revision in the properties file is checked against the version control system (VCS) revision | 
|---|
| 9 | * and updated if required. | 
|---|
| 10 | * The VCS revision is written to the properties file so that it is available in the compiled war. | 
|---|
| 11 | * The compile is intentionally allowed to go ahead if a handled exception occurs. | 
|---|
| 12 | * VCS currently supported: subversion. | 
|---|
| 13 | */ | 
|---|
| 14 | target(updateVcsRevision: "Check and update the app.vcsRevision property in application.properties file and metadata.") { | 
|---|
| 15 |  | 
|---|
| 16 | def result = [:] | 
|---|
| 17 |  | 
|---|
| 18 | // Properties file. | 
|---|
| 19 | def url = basedir + "/application.properties" | 
|---|
| 20 | def propertiesFile = new File(url) | 
|---|
| 21 |  | 
|---|
| 22 | def fail = { Map m -> | 
|---|
| 23 | updateInMemoryMetadata('Unknown') | 
|---|
| 24 | def writeResult = writePropertiesFileRevision(propertiesFile, 'Unknown') | 
|---|
| 25 | if(writeResult.error) { | 
|---|
| 26 | m.code= writeResult.error.code | 
|---|
| 27 | m.args= writeResult.error.args | 
|---|
| 28 | } | 
|---|
| 29 |  | 
|---|
| 30 | result.error = [ code: m.code, args: m.args ] | 
|---|
| 31 | println "Error: UpdateRev script: " + result.error | 
|---|
| 32 | return result | 
|---|
| 33 | } | 
|---|
| 34 |  | 
|---|
| 35 | // Get propertiesFile revision. | 
|---|
| 36 | def properitesFileResult = getPropertiesFileRevision(propertiesFile) | 
|---|
| 37 | if(properitesFileResult.error) | 
|---|
| 38 | return fail(code: properitesFileResult.error.code, args: properitesFileResult.error.args) | 
|---|
| 39 |  | 
|---|
| 40 | // Get VCS revision. | 
|---|
| 41 | def vcsResult = getVcsRevision() | 
|---|
| 42 | if(vcsResult.error) | 
|---|
| 43 | return fail(code: vcsResult.error.code, args: vcsResult.error.args) | 
|---|
| 44 |  | 
|---|
| 45 | // Compare and update. | 
|---|
| 46 | if(properitesFileResult.revision != vcsResult.revision) { | 
|---|
| 47 |  | 
|---|
| 48 | println "app.vcsRevision = "+properitesFileResult.revision +', VCS Revision = '+vcsResult.revision | 
|---|
| 49 |  | 
|---|
| 50 | updateInMemoryMetadata(vcsResult.revision) | 
|---|
| 51 |  | 
|---|
| 52 | // Update application.properties file. | 
|---|
| 53 | def writeResult = writePropertiesFileRevision(propertiesFile, vcsResult.revision) | 
|---|
| 54 | if(writeResult.error) | 
|---|
| 55 | return fail(code: writeResult.error.code, args: writeResult.error.args) | 
|---|
| 56 |  | 
|---|
| 57 | } // if(rev != rev) | 
|---|
| 58 | else { | 
|---|
| 59 | println "VCS Revisions match: app.vcsRevision = ${properitesFileResult.revision}, VCS Revision = ${vcsResult.revision}." | 
|---|
| 60 | } | 
|---|
| 61 |  | 
|---|
| 62 | // Success. | 
|---|
| 63 | return result | 
|---|
| 64 |  | 
|---|
| 65 | } // updateVcsRevision() | 
|---|
| 66 |  | 
|---|
| 67 | /** | 
|---|
| 68 | * Get the app.vcsRevision property from properties file. | 
|---|
| 69 | * @retuns A map containing revision and lineNumber otherwise an error map. | 
|---|
| 70 | */ | 
|---|
| 71 | def getPropertiesFileRevision(propertiesFile) { | 
|---|
| 72 | def result = [:] | 
|---|
| 73 |  | 
|---|
| 74 | def fail = { Map m -> | 
|---|
| 75 | result.error = [ code: m.code, args: m.args ] | 
|---|
| 76 | return result | 
|---|
| 77 | } | 
|---|
| 78 |  | 
|---|
| 79 | if(!propertiesFile.isFile()) | 
|---|
| 80 | return fail(code:"application.properties.file.not.found", args:[propertiesFile.getAbsoluteFile()]) | 
|---|
| 81 |  | 
|---|
| 82 | propertiesFile.eachLine { line, lineNumber -> | 
|---|
| 83 | // app.vcsRevision=$Rev: NUM $ | 
|---|
| 84 | if ( line =~ '^app.vcsRevision.' ) { | 
|---|
| 85 | if(line.size() > 23) { | 
|---|
| 86 | result.revision = line[22..-3] | 
|---|
| 87 | result.lineNumber = lineNumber | 
|---|
| 88 | } | 
|---|
| 89 | } | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 | if(!result.revision || !result.lineNumber) | 
|---|
| 93 | return fail(code:"app.vcsRevision.not.found") | 
|---|
| 94 |  | 
|---|
| 95 | // Success. | 
|---|
| 96 | return result | 
|---|
| 97 |  | 
|---|
| 98 | } // getAppRevision() | 
|---|
| 99 |  | 
|---|
| 100 | /** | 
|---|
| 101 | * Get the working copy's base revision from SVN. | 
|---|
| 102 | * @retuns A map containing revision otherwise an error map. | 
|---|
| 103 | */ | 
|---|
| 104 | def getVcsRevision() { | 
|---|
| 105 | def result = [:] | 
|---|
| 106 |  | 
|---|
| 107 | def fail = { Map m -> | 
|---|
| 108 | result.error = [ code: m.code, args: m.args ] | 
|---|
| 109 | return result | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | def wc = new File(basedir) | 
|---|
| 113 | if(!wc.isDirectory()) | 
|---|
| 114 | return fail(code:"vcs.working.copy.not.found", args:[basedir]) | 
|---|
| 115 |  | 
|---|
| 116 | // Use svnkit to get the base revision. | 
|---|
| 117 | def clientManager = SVNClientManager.newInstance() | 
|---|
| 118 | def wcClient = clientManager.getWCClient() | 
|---|
| 119 | try { | 
|---|
| 120 | result.revision = wcClient.doInfo(wc, SVNRevision.BASE).getRevision().toString() | 
|---|
| 121 | } | 
|---|
| 122 | catch(org.tmatesoft.svn.core.SVNException e) { | 
|---|
| 123 | fail(code:"vcs.exception", args:[e]) | 
|---|
| 124 | } | 
|---|
| 125 |  | 
|---|
| 126 | // Success. | 
|---|
| 127 | return result | 
|---|
| 128 | } // getVcsRevision() | 
|---|
| 129 |  | 
|---|
| 130 | /** | 
|---|
| 131 | * Update the in memory metadata if already loaded. | 
|---|
| 132 | * Available vars: binding.variables.each { println it.key } | 
|---|
| 133 | */ | 
|---|
| 134 | def updateInMemoryMetadata(revision) { | 
|---|
| 135 | if(binding.variables.containsKey('metadata')) { | 
|---|
| 136 | def metadata = binding.variables['metadata'] | 
|---|
| 137 | metadata['app.vcsRevision'] = '$Rev: '+revision+' $' | 
|---|
| 138 | } | 
|---|
| 139 | } // updateInMemoryMetadata() | 
|---|
| 140 |  | 
|---|
| 141 | /** | 
|---|
| 142 | * Write revision to properties file. | 
|---|
| 143 | * @retuns An error map if any errors. | 
|---|
| 144 | */ | 
|---|
| 145 | def writePropertiesFileRevision(propertiesFile, revision) { | 
|---|
| 146 | def result = [:] | 
|---|
| 147 |  | 
|---|
| 148 | def fail = { Map m -> | 
|---|
| 149 | result.error = [ code: m.code, args: m.args ] | 
|---|
| 150 | return result | 
|---|
| 151 | } | 
|---|
| 152 |  | 
|---|
| 153 | if(!propertiesFile.isFile()) | 
|---|
| 154 | return fail(code:"application.properties.file.not.found", args:[propertiesFile.getAbsoluteFile()]) | 
|---|
| 155 |  | 
|---|
| 156 | def revisionString = 'app.vcsRevision=\\$Rev: '+revision+' \\$' | 
|---|
| 157 | println "Updating application.properties file with: ${revisionString}" | 
|---|
| 158 |  | 
|---|
| 159 | def processFileInplace = { file, Closure processText -> | 
|---|
| 160 | def text = file.text | 
|---|
| 161 | file.write(processText(text)) | 
|---|
| 162 | } | 
|---|
| 163 |  | 
|---|
| 164 | processFileInplace(propertiesFile) { text -> | 
|---|
| 165 | text.replaceAll('app.vcsRevision.*', revisionString) | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | // Success. | 
|---|
| 169 | return result | 
|---|
| 170 |  | 
|---|
| 171 | } // writePropertiesFileRevision() | 
|---|