Skip to content

Commit

Permalink
new plugin publishing mechanism that eliminates the huge amount of ti…
Browse files Browse the repository at this point in the history
…me it takes to publish a plugin and also fixes the problem where the plugin list could get corrupted
  • Loading branch information
graemerocher committed Aug 17, 2009
1 parent f2287c9 commit 2b2ee34
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 12 deletions.
56 changes: 49 additions & 7 deletions grails/scripts/ReleasePlugin.groovy
Expand Up @@ -17,11 +17,15 @@
import org.tmatesoft.svn.core.io.SVNRepositoryFactory
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl
import org.tmatesoft.svn.core.io.*
import org.tmatesoft.svn.core.*
import org.tmatesoft.svn.core.auth.*
import org.tmatesoft.svn.core.wc.*
import org.codehaus.groovy.grails.documentation.MetadataGeneratingMetaClassCreationHandle
import org.codehaus.groovy.grails.plugins.GrailsPluginUtils
import org.codehaus.groovy.grails.plugins.publishing.DefaultPluginPublisher
import org.springframework.core.io.FileSystemResource

/**
* Gant script that handles releasing plugins to a plugin repository.
Expand Down Expand Up @@ -66,7 +70,7 @@ target(processAuth:"Prompts user for login details to create authentication mana
def username = ant.antProject.getProperty(usr)
def password = ant.antProject.getProperty(psw)
authManager = SVNWCUtil.createDefaultAuthenticationManager( username , password )
authManagerMap.put(authKey,authManager)
authManagerMap.put(authKey,authManager)
}

}
Expand All @@ -87,7 +91,10 @@ target(releasePlugin: "The implementation target") {

}
packagePlugin()
docs()
if(argsMap.skipDocs != true) {
docs()
}


if(argsMap.packageOnly) {
return
Expand All @@ -108,10 +115,11 @@ target(releasePlugin: "The implementation target") {

FSRepositoryFactory.setup()
DAVRepositoryFactory.setup()
SVNRepositoryFactoryImpl.setup()

try {
if(argsMap.pluginlist) {
commitNewGlobalPluginList()
modifyOrCreatePluginList()
}
else {
def statusClient = new SVNStatusClient((ISVNAuthenticationManager)authManager,null)
Expand Down Expand Up @@ -161,14 +169,48 @@ a working copy and make your changes there. Alternatively, do you want to procee
}
}

target(modifyOrCreatePluginList:"Updates the remote plugin.xml descriptor or creates a new one in the repo") {
withPluginListUpdate {
ant.delete(file:pluginsListFile)
// get newest version of plugin list
fetchRemoteFile("${pluginSVN}/.plugin-meta/plugins-list.xml", pluginsListFile)

def remoteRevision = "0"
if (shouldUseSVNProtocol(pluginDistURL)) {
withSVNRepo(pluginDistURL) { repo ->
remoteRevision = repo.getLatestRevision().toString()
}
}
else {
new URL(pluginDistURL).withReader { Reader reader ->
def line = reader.readLine()
line.eachMatch(/Revision (.*):/) {
remoteRevision = it[1]
}
}
}

def publisher = new DefaultPluginPublisher(remoteRevision)
def updatedList = publisher.publishRelease(pluginName, new FileSystemResource(pluginsListFile), !skipLatest)
pluginsListFile.withWriter { w ->
publisher.writePluginList(updatedList, w)
}
}
}

target(commitNewGlobalPluginList:"updates the plugins.xml descriptor stored in the repo") {
withPluginListUpdate {
ant.delete(file:pluginsListFile)
println "Building plugin list for commit..."
updatePluginsListManually()
}
}

private withPluginListUpdate(Closure updateLogic) {
if(!commitMessage) {
askForMessage()
}
ant.delete(file:pluginsListFile)
println "Building plugin list for commit..."
updatePluginsListManually()
updateLogic()

def pluginMetaDir = new File("${grailsSettings.grailsWorkDir}/${repositoryName}/.plugin-meta")
def updateClient = new SVNUpdateClient((ISVNAuthenticationManager)authManager, null)
Expand All @@ -189,8 +231,8 @@ target(commitNewGlobalPluginList:"updates the plugins.xml descriptor stored in t
checkoutOrImportPluginMetadata(pluginMetaDir, remotePluginMetadata, updateClient, importClient)
}
}
}

}
private checkoutOrImportPluginMetadata (File pluginMetaDir, String remotePluginMetadata, SVNUpdateClient updateClient, SVNCommitClient importClient) {
def svnURL = SVNURL.parseURIDecoded (remotePluginMetadata)
try {
Expand Down
1 change: 1 addition & 0 deletions grails/scripts/_GrailsCreateProject.groovy
Expand Up @@ -80,6 +80,7 @@ target(createPlugin: "The implementation target") {
include(name: "*GrailsPlugin.groovy")
include(name: "scripts/*")
replacefilter(token: "@plugin.name@", value: pluginName)
replacefilter(token: "@plugin.short.name@", value: GrailsNameUtils.getScriptName(pluginName))
replacefilter(token: "@plugin.version@", value: grailsAppVersion ?: "0.1")
replacefilter(token: "@grails.version@", value: grailsVersion)
}
Expand Down
8 changes: 4 additions & 4 deletions grails/scripts/_PluginDependencies.groovy
Expand Up @@ -681,7 +681,7 @@ target(updatePluginsListManually: "Updates the plugin list by manually reading e
}
}

boolean shouldUseSVNProtocol(pluginDistURL) {
shouldUseSVNProtocol = { pluginDistURL ->
return isSecureUrl(pluginDistURL) || pluginDistURL.startsWith("file://")
}

Expand Down Expand Up @@ -1240,7 +1240,7 @@ def buildPluginInfo(root, pluginName) {
}
}

def fetchRemoteFile(url, destfn) {
fetchRemoteFile = { url, destfn ->
if (shouldUseSVNProtocol(pluginDistURL)) {
// fetch the remote file..
fetchRemote(url) { repo, file ->
Expand All @@ -1259,7 +1259,7 @@ def fetchRemoteFile(url, destfn) {
/**
* Fetch the entire plugin list file.
*/
def fetchPluginListFile(url) {
fetchPluginListFile = { url ->
// attempt to fetch the file using SVN.
if (shouldUseSVNProtocol(pluginDistURL)) {
def rdr = fetchRemote(url) { repo, file ->
Expand Down Expand Up @@ -1304,7 +1304,7 @@ def isSecureUrl(Object url) {
url.startsWith('https://') || url.startsWith('svn://')
}

def withSVNRepo(url, closure) {
withSVNRepo = { url, closure ->
// create a authetication manager using the defaults
ISVNAuthenticationManager authMgr = getAuthFromUrl(url,"discovery")

Expand Down
2 changes: 1 addition & 1 deletion grails/src/grails/templates/plugins/GrailsPlugin.groovy
Expand Up @@ -19,7 +19,7 @@ Brief description of the plugin.
'''

// URL to the plugin's documentation
def documentation = "http://grails.org/@plugin.name@+Plugin"
def documentation = "http://grails.org/plugin/@plugin.short.name@"

def doWithWebDescriptor = { xml ->
// TODO Implement additions to web.xml (optional), this event occurs before
Expand Down
@@ -0,0 +1,140 @@
/*
* Copyright 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.codehaus.groovy.grails.plugins.publishing

import org.codehaus.groovy.grails.plugins.GrailsPluginUtils
import groovy.util.slurpersupport.GPathResult
import groovy.xml.MarkupBuilder
import org.springframework.core.io.Resource
import grails.util.BuildSettingsHolder

/**
* Utility methods for manipulating the plugin-list.xml file used
* when publishing plugins to a Grails plugin repository
*
* @author Graeme Rocher
* @since 1.2
*/

public class DefaultPluginPublisher {

String revision = "0"
DefaultPluginPublisher(String revNumber) {
if(revNumber)
this.revision = revNumber
}

void writePluginList(GPathResult pluginList, Writer targetWriter) {
def stringWriter = new StringWriter()
stringWriter << new groovy.xml.StreamingMarkupBuilder().bind {
mkp.yield pluginList
}
new XmlNodePrinter(new PrintWriter(targetWriter)).print(new XmlParser().parseText(stringWriter.toString()))
}

/**
* Publishes a plugin release to the given plugin list
*
* @param pluginName the name of the plugin
* @param pluginsListFile The plugin list file
* @param makeLatest Whether to make the release the latest release
*
* @return the updated plugin list
*/
GPathResult publishRelease(String pluginName, Resource pluginsList, boolean makeLatest) {
def xml = parsePluginList(pluginsList)

xml.@revision = revision

def releaseMetadata = getPluginMetadata(pluginName)
def pluginVersion = releaseMetadata.@version.toString().trim()
def releaseTag = "RELEASE_${pluginVersion.replaceAll('\\.','_')}"

def props = ['title', 'author', 'authorEmail', 'description', 'documentation']
def releaseInfo = {
release([tag:releaseTag,version:pluginVersion, type:'svn']) {
for(p in props) {
"$p"(releaseMetadata."$p".text())
}
}
}

// build argument, if makeLatest is true make the plugin the latest release
def pluginArgs = [name:pluginName]
if(makeLatest) {
pluginArgs.'latest-release' = pluginVersion
}

def pluginInfo = {
plugin(pluginArgs, releaseInfo)
}


// find plugin
def allPlugins = xml.plugin
if(allPlugins.size()==0) {
// create new plugin list
xml << pluginInfo
}
else {
def existingEntry = xml.plugin.find { it.@name == pluginName }

if(existingEntry.size()==0) {
// plugin doesn't exist, create new entry
def lastPlugin = allPlugins[allPlugins.size()-1]
lastPlugin + pluginInfo
}
else {
// plugin exists, add release info and make latest is appropriate
if(makeLatest) {
existingEntry.'@latest-release' = pluginVersion
}
def existingRelease = existingEntry.release.find { it.@version == pluginVersion }
if(existingRelease.size()==0) {
existingEntry << releaseInfo
}
}
}

return xml

}

protected GPathResult parsePluginList(Resource pluginsListFile) {
if(pluginsListFile.exists()) {
InputStream stream = pluginsListFile.getInputStream()
try {
return new XmlSlurper().parse(stream)
}
finally {
stream?.close()
}
}
else {
return new XmlSlurper().parseText('<?xml version="1.0" encoding="UTF-8"?><plugins revision="0" />')
}
}

GPathResult publishRelease(String pluginName, Resource pluginsList) {
publishRelease(pluginName, pluginsList, true)
}

protected GPathResult getPluginMetadata(String pluginName) {
def basedir = BuildSettingsHolder.settings?.baseDir ?: new File(".")
return new XmlSlurper().parse(new File("${basedir.absolutePath}/plugin.xml"))
}
}

0 comments on commit 2b2ee34

Please sign in to comment.