-
-
Save apangeajwrubel/5150287 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
module.exports = { | |
pivotal: { | |
TOKEN: 'TOKEN' | |
PID: 'PID', | |
}, | |
sprintly: { | |
USER: "USER_EMAIL", | |
ID: 'PRODUCT_ID', | |
KEY: 'API_KEY' | |
}, | |
} |
This file contains hidden or 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
// change the value of env to match the full name of the file (minus the .js part) | |
// If you change the filename, change this value too | |
var http = require("http"), | |
https = require("https"), | |
parser = require("xml2json"), | |
fs = require("fs"), | |
qs = require("querystring"), | |
env = require(__dirname+"/env.sample"); | |
// Added a filter here to only bring over stories in process | |
// depending on your requirements you might remove that or | |
// also pull over backlog/icebox stories | |
var pivotalOptions = { | |
host: 'www.pivotaltracker.com', | |
port: 443, | |
path: "/services/v3/projects/"+env.pivotal.PID+"/stories?filter=state:unstarted,finished,delivered," | |
headers: { "X-TrackerToken": env.pivotal.TOKEN} | |
}; | |
var sprintlyOptions = { | |
hostname: 'sprint.ly', | |
path: '/api/products/'+env.sprintly.ID+'/items.json', | |
auth: env.sprintly.USER+':'+env.sprintly.KEY, | |
method: 'POST' | |
} | |
var getPivotalStories = function(options) { | |
if (options.local) { | |
fs.readFile("pivotalData.xml", "utf-8",function(err,response) { | |
handleStories(parser.toJson(response, {object:true})); | |
}); | |
} | |
else { | |
var response = ''; | |
https.get(pivotalOptions, function(res) { | |
res.on('data', function (chunk) { | |
response += chunk; | |
}); | |
res.on('end', function() { | |
fs.writeFileSync("pivotalData.xml", response); | |
handleStories(parser.toJson(response, {object:true})); | |
}); | |
}).on('error', function(e) { | |
console.log("Got error: " + e.message); | |
}); | |
} | |
}; | |
var addToSprintly = function(story) { | |
var options = sprintlyOptions; | |
options['headers'] = { | |
"Content-Type": 'application/x-www-form-urlencoded', | |
"Content-Length": story.length | |
}; | |
request = https.request(options, function(res) { | |
var response = ''; | |
res.on('data', function(chunk) { | |
response += chunk; | |
}); | |
res.on('end', function() { | |
console.log("Story Added!", response); | |
}); | |
}); | |
request.write(story); | |
request.end(); | |
} | |
var handleStories = function(data) { | |
var stories = data.stories.story; | |
stories = parseStories(stories); | |
var j = 0; | |
var poll = setInterval(function() { | |
if ( j < stories.length) { | |
addToSprintly(qs.stringify(stories[j])); | |
j++; | |
} | |
else { | |
clearInterval(poll); | |
} | |
},250); | |
} | |
var parseStories = function(stories) { | |
var whatSearch = new RegExp(/I want (to [\w\s\.\'\"]+)/i); | |
var whySearch = new RegExp(/In order to ([\w\s\.\'\"]+)/i); | |
var whoSearch = new RegExp(/As an? (\w+)/i); | |
var addStory = function(feature) { | |
// we didn't use the strict "as an I want..." format | |
// so the import script choked on many stories | |
// this sets them all to "As an 'imported feature from Pivotal' | |
// I want " + the Pivotal story name. | |
return { | |
type: "story", | |
who: "imported feature from Pivotal", | |
what: feature.name, | |
why: "I can "+translate(feature.description, whySearch), | |
score: parseScore(feature.estimate), | |
tags: feature.labels, | |
description: feature.description.toString(), | |
status: mapStatus(feature.current_state) | |
}; | |
} | |
var addDefect = function(bug) { | |
return { | |
type: "defect", | |
title: bug.name, | |
tags: bug.labels, | |
status: mapStatus(bug.current_state) | |
} | |
} | |
var addTask = function(pivotalTask) { | |
return { | |
type: "task" , | |
title: pivotalTask.name, | |
tags: pivotalTask.labels, | |
status: mapStatus(pivotalTask.current_state) | |
}; | |
} | |
var translate = function(description, re) { | |
var result = re.exec(description); | |
if (result && result.length > 0) { | |
return result[1] | |
} | |
else { | |
return "unknown"; | |
} | |
}; | |
var whatTranslate = function(description, re,title) { | |
var result = re.exec(description); | |
if (result && result.length > 0) { | |
return result[1] | |
} | |
else { | |
return title; | |
} | |
}; | |
var mapStatus = function(pivotal_state) { | |
var result = 'backlog'; | |
if(pivotal_state === 'unstarted') { result = 'backlog'} | |
else if(pivotal_state === 'unscheduled') { result = 'backlog'} | |
else if(pivotal_state === 'started') { result = 'in-progress'} | |
else if(pivotal_state === 'delivered') { result = 'completed'} | |
else if(pivotal_state === 'accepted') { result = 'accepted'} | |
else { | |
console.log("I have no idea what to do with a pivotal state of "+pivotal_state); | |
} | |
return result; | |
} | |
var parseScore = function(score) { | |
var result = +score['$t'] | |
if (!result || result == -1) result = "~"; | |
// these should match your pivotal story point assignments | |
else if (result==0) result = "S" | |
else if (result==1) result = "M" | |
else if (result==2) result = "L" | |
else if (result==3) result = "XL" | |
return result; | |
} | |
return stories.map (function(story) { | |
switch(story.story_type) { | |
case 'feature': | |
return addStory(story); | |
case 'bug': | |
return addDefect(story); | |
case 'chore': | |
return addTask(story); | |
} | |
}); | |
} | |
// Setting this to true will cause the script to hit the APIs for both products | |
// false will try to use a local file called pivotalData.xml for the import | |
getPivotalStories({local:false}); |
This file contains hidden or 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
{ | |
"author": "Justin Reidy <[email protected]> (http://rzrsharp.net)", | |
"name": "pivotal-to-sprintly", | |
"description": "Convert Pivotal Tracker stories to Sprint.ly stories", | |
"version": "0.0.1", | |
"repository": { | |
"url": "" | |
}, | |
"engines": { | |
"node": "~0.6.0" | |
}, | |
"dependencies": { | |
"xml2json" : "0.2.x", | |
"querystring": "0.1.x" | |
}, | |
"devDependencies": {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment