Created
March 2, 2013 21:43
-
-
Save rbramley/5073413 to your computer and use it in GitHub Desktop.
Script to migrate GoogleCode issues
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copying and distribution of this file, with or without modification, | |
* are permitted in any medium without royalty provided the copyright | |
* notice and this notice are preserved. This file is offered as-is, | |
* without any warranty. | |
* | |
* Migrate issues from one Googlecode project to another (e.g. if you need to rename). | |
* Note (line 39): fetch of issues from source project currently limited to 50 | |
* | |
* See: http://code.google.com/p/support/wiki/IssueTrackerAPI for API details | |
* | |
* @author Robin Bramley, Ixxus Limited (c) 2011 | |
*/ | |
@Grab(group='commons-logging', module='commons-logging', version='1.1.1') | |
@Grab(group='commons-codec', module='commons-codec', version='1.4') | |
@Grab(group='org.apache.httpcomponents', module='httpclient', version='4.1.2') | |
import groovy.xml.MarkupBuilder | |
import org.apache.http.* | |
import org.apache.http.client.* | |
import org.apache.http.client.entity.UrlEncodedFormEntity | |
import org.apache.http.client.methods.* | |
import org.apache.http.entity.StringEntity | |
import org.apache.http.impl.client.DefaultHttpClient | |
import org.apache.http.message.BasicNameValuePair | |
import org.apache.http.util.EntityUtils | |
// ------------------ | |
// Set these 5 items | |
def sourceProjectName = 'source-project' | |
def targetProjectName = 'target-project' | |
def emailAddress = '[email protected]' | |
def password = 'foobar' | |
def sourceScript = 'acme-groovyTest-0.1' // first portion is meant to be a company name | |
// ------------------ | |
def googleLoginUrl = 'https://www.google.com/accounts/ClientLogin' | |
def issuesListUrl = "https://code.google.com/feeds/issues/p/${sourceProjectName}/issues/full?max-results=50" | |
def issuePostUrl = "https://code.google.com/feeds/issues/p/${targetProjectName}/issues/full" | |
def issuesXmlns = 'http://schemas.google.com/projecthosting/issues/2009' | |
def atomXmlns = 'http://www.w3.org/2005/Atom' | |
/** Build an atom feed for an issue */ | |
def buildIssue(entry, issuesXmlns, atomXmlns) { | |
def writer = new StringWriter() | |
def xml = new MarkupBuilder(writer) | |
xml.'atom:entry'('xmlns:atom':atomXmlns,'xmlns:issues':issuesXmlns) { | |
'atom:title'(entry.title) | |
'atom:content'(type:'html', entry.content) | |
'atom:author' { | |
'atom:name'(entry.author.name) | |
} | |
entry.'issues:label'.each { | |
'issues:label'(it) | |
} | |
'issues:owner' { | |
'issues:username'(entry.'issues:owner'.'issues:username') | |
} | |
'issues:state'(entry.'issues:state') | |
'issues:status'(entry.'issues:status') | |
} | |
return writer.toString() | |
} | |
/** Build an atom feed for a comment */ | |
def buildComment(entry, issuesXmlns, atomXmlns) { | |
def writer = new StringWriter() | |
def xml = new MarkupBuilder(writer) | |
xml.'atom:entry'('xmlns:atom':atomXmlns,'xmlns:issues':issuesXmlns) { | |
'atom:content'(type:'html', entry.content) | |
'atom:author' { | |
'atom:name'(entry.author.name) | |
} | |
'issues:updates' { | |
entry.'issues:updates'.'issues:label'.each { | |
'issues:label'(it) | |
} | |
'issues:ownerUpdate'(entry.'issues:updates'.'issues:owner'.'issues:username') | |
'issues:state'(entry.'issues:updates'.'issues:state') | |
'issues:status'(entry.'issues:updates'.'issues:status') | |
} | |
} | |
return writer.toString() | |
} | |
// set up login parameters | |
NameValuePair accountType = new BasicNameValuePair('accountType', 'GOOGLE') | |
NameValuePair email = new BasicNameValuePair('Email', emailAddress) | |
NameValuePair passwd = new BasicNameValuePair('Passwd', password) | |
NameValuePair service = new BasicNameValuePair('service', 'code') | |
NameValuePair source = new BasicNameValuePair('source', sourceScript) | |
List<NameValuePair> params = new ArrayList<NameValuePair>(5) | |
params.addAll([accountType, email, passwd, service, source]) | |
UrlEncodedFormEntity form = new UrlEncodedFormEntity(params) | |
HttpPost post = new HttpPost(googleLoginUrl) | |
post.setEntity(form) | |
HttpClient httpclient = new DefaultHttpClient() | |
HttpResponse response = httpclient.execute(post) | |
// check whether 200 or 403 | |
if (response.getStatusLine().getStatusCode() == 403) { | |
println response.getStatusLine().getReasonPhrase() | |
return | |
} | |
HttpEntity entity = response.getEntity() | |
def strings = EntityUtils.toString(entity).split('\n') | |
// fetch the 'Auth' token from the response | |
def authToken | |
strings.each { | |
if (it.startsWith('Auth=')) { | |
authToken = it.substring(5) | |
} | |
} | |
// get the Atom feed | |
HttpGet get = new HttpGet(issuesListUrl) | |
// Set the header => Authorization: GoogleLogin auth=<b>yourAuthToken</b> | |
get.setHeader('Authorization', "GoogleLogin auth=${authToken}") | |
response = httpclient.execute(get) | |
//TODO: check response.getStatusLine().getStatusCode() | |
entity = response.getEntity() | |
def atom = EntityUtils.toString(entity) | |
// Parse and process the atom feed | |
feed = new XmlSlurper().parseText(atom).declareNamespace([issues:issuesXmlns]) | |
feed.entry.each { entry -> | |
issueId = entry.'issues:id' | |
println "Issue ${issueId} - ${entry.title}" | |
issueCreationXml = buildIssue(entry, issuesXmlns, atomXmlns) | |
// post the issue | |
post = new HttpPost(issuePostUrl) | |
post.setHeader('Content-type', 'application/atom+xml') | |
post.setHeader('Authorization', "GoogleLogin auth=${authToken}") | |
post.setEntity(new StringEntity(issueCreationXml)) | |
response = httpclient.execute(post) | |
// check for bad request | |
println "${response.getStatusLine().getStatusCode()} - ${response.getStatusLine().getReasonPhrase()}" | |
if (response.statusLine.statusCode == 400) { | |
println EntityUtils.toString(response.entity) | |
return | |
} | |
EntityUtils.consume(response.entity) | |
/* | |
* Now get the comments and iterate | |
*/ | |
def issuesCommentsListUrl = "https://code.google.com/feeds/issues/p/${sourceProjectName}/issues/${issueId}/comments/full" | |
def issuesCommentsPostUrl = "https://code.google.com/feeds/issues/p/${targetProjectName}/issues/${issueId}/comments/full" | |
// get the Atom feed | |
get = new HttpGet(issuesCommentsListUrl) | |
get.setHeader('Authorization', "GoogleLogin auth=${authToken}") | |
response = httpclient.execute(get) | |
println "${response.getStatusLine().getStatusCode()} - ${response.getStatusLine().getReasonPhrase()}" | |
entity = response.getEntity() | |
commentsAtom = EntityUtils.toString(entity) | |
commentsFeed = new XmlSlurper().parseText(commentsAtom).declareNamespace([issues:issuesXmlns]) | |
commentsFeed.entry.each { commEntry -> | |
commentCreationXml = buildComment(commEntry, issuesXmlns, atomXmlns) | |
// post the comment | |
post = new HttpPost(issuesCommentsPostUrl) | |
post.setHeader('Authorization', "GoogleLogin auth=${authToken}") | |
post.setHeader('Content-type', 'application/atom+xml') | |
post.setEntity(new StringEntity(commentCreationXml)) | |
response = httpclient.execute(post) | |
// check for bad request | |
println "${response.getStatusLine().getStatusCode()} - ${response.getStatusLine().getReasonPhrase()}" | |
if (response.statusLine.statusCode == 400) { | |
println EntityUtils.toString(response.entity) | |
return | |
} | |
EntityUtils.consume(response.entity) | |
} | |
} | |
return // with no noise in GroovyConsole! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment