Skip to content

Commit

Permalink
Merge pull request #13 from jakebiesinger/master
Browse files Browse the repository at this point in the history
Use new library (PyGithub) which uses Github API v3
  • Loading branch information
joelpt committed Oct 1, 2012
2 parents a04232c + 4a29846 commit 150d630
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 19 deletions.
7 changes: 4 additions & 3 deletions README.md
Expand Up @@ -7,17 +7,18 @@ This script can be run repeatedly and will just pull in new issues and new comme
Required Python libraries: Required Python libraries:


* [gdata](http://code.google.com/p/gdata-python-client/) -- `pip install gdata` * [gdata](http://code.google.com/p/gdata-python-client/) -- `pip install gdata`
* [python github](http://github.com/ask/python-github2) -- `pip install github2` * [PyGithub](https://github.com/jacquev6/PyGithub/) -- `pip install PyGithub`


Usage: Usage:


migrate-issues.py [options] <google_project_name> <github_api_token> <github_user_name> <github_project> migrate-issues.py [options] <google_project_name> <github_user_name> <github_project>


google_project_name The project name (from the URL) from google code google_project_name The project name (from the URL) from google code
github_api_token Your Github api token
github_user_name The Github username github_user_name The Github username
github_project The Github project name, e.g. username/project github_project The Github project name, e.g. username/project


Options: Options:
-h, --help show this help message and exit -h, --help show this help message and exit
-d, --dry-run don't modify anything on Github -d, --dry-run don't modify anything on Github

You will be prompted for your github password.
61 changes: 45 additions & 16 deletions migrateissues.py
Expand Up @@ -5,16 +5,17 @@
import re import re
import logging import logging
import dateutil.parser import dateutil.parser
import getpass


from github2.client import Github from github import Github, GithubException


import gdata.projecthosting.client import gdata.projecthosting.client
import gdata.projecthosting.data import gdata.projecthosting.data
import gdata.gauth import gdata.gauth
import gdata.client import gdata.client
import gdata.data import gdata.data


GITHUB_REQUESTS_PER_SECOND = 0.5 #GITHUB_REQUESTS_PER_SECOND = 0.5
GOOGLE_MAX_RESULTS = 25 GOOGLE_MAX_RESULTS = 25


logging.basicConfig(level=logging.ERROR) logging.basicConfig(level=logging.ERROR)
Expand All @@ -33,26 +34,51 @@ def add_issue_to_github(issue):
id = parse_gcode_id(issue.id.text) id = parse_gcode_id(issue.id.text)
title = issue.title.text title = issue.title.text
link = issue.link[1].href link = issue.link[1].href
author = issue.author[0].name.text
content = issue.content.text content = issue.content.text
date = dateutil.parser.parse(issue.published.text).strftime('%B %d, %Y %H:%M:%S') date = dateutil.parser.parse(issue.published.text).strftime('%B %d, %Y %H:%M:%S')
body = '%s\n\n\n_Original issue: %s (%s)_' % (content, link, date) header = '_Original author: %s (%s)_' % (author, date)
body = '%s\n\n%s\n\n\n_Original issue: %s_' % (header, content, link)

# Github takes issue with % in the title or body.
title = title.replace('%', '&#37;')
body = body.replace('%', '&#37;')


output('Adding issue %s' % (id)) output('Adding issue %s' % (id))


github_issue = None github_issue = None


if not options.dry_run: if not options.dry_run:
github_issue = github.issues.open(github_project, title=title, body=body.encode('utf-8')) github_issue = github_repo.create_issue(title, body=body.encode('utf-8'))
github.issues.add_label(github_project, github_issue.number, "imported") github_issue.edit(state=issue.state.text)
if issue.status.text.lower() in "invalid closed fixed wontfix verified done duplicate".lower(): try:
github.issues.close(github_project, github_issue.number) import_label = github_repo.get_label('imported')
except GithubException:
import_label = github_repo.create_label('imported', 'FFFFFF')
github_issue.add_to_labels(import_label)

#if issue.status.text.lower() in "invalid closed fixed wontfix verified done duplicate".lower():
# github_issue.edit(state='closed')
else:
# don't actually open an issue during a dry run...
class blank:
def get_comments(self):
return []
github_issue = blank()


# Add any labels # Add any labels
label_mapping = {'Type-Defect' : 'bug', 'Type-Enhancement' : 'enhancement'}
if len(issue.label) > 0: if len(issue.label) > 0:
output(', adding labels') output(', adding labels')
for label in issue.label: for label in issue.label:
# get github equivalent if it exists
label_text = label_mapping.get(label.text, label.text)
if not options.dry_run: if not options.dry_run:
github.issues.add_label(github_project, github_issue.number, label.text.encode('utf-8')) try:
github_label = github_repo.get_label(label_text)
except GithubException:
github_label = github_repo.create_label(label_text, 'FFFFFF')
github_issue.add_to_labels(github_label)
output('.') output('.')


return github_issue return github_issue
Expand All @@ -65,7 +91,7 @@ def add_comments_to_issue(github_issue, gcode_issue_id):
while True: while True:
comments_feed = google.get_comments(google_project_name, gcode_issue_id, query=gdata.projecthosting.client.Query(start_index=start_index, max_results=max_results)) comments_feed = google.get_comments(google_project_name, gcode_issue_id, query=gdata.projecthosting.client.Query(start_index=start_index, max_results=max_results))
comments = filter(lambda c: c.content.text is not None, comments_feed.entry) # exclude empty comments comments = filter(lambda c: c.content.text is not None, comments_feed.entry) # exclude empty comments
existing_comments = github.issues.comments(github_project, github_issue.number) existing_comments = github_issue.get_comments()
existing_comments = filter(lambda c: c.body[0:5] == '_From', existing_comments) # only look at existing github comments that seem to have been imported existing_comments = filter(lambda c: c.body[0:5] == '_From', existing_comments) # only look at existing github comments that seem to have been imported
existing_comments = map(lambda c: re.sub(r'^_From.+_\n', '', c.body), existing_comments) # get the existing comments' bodies as they appear in gcode existing_comments = map(lambda c: re.sub(r'^_From.+_\n', '', c.body), existing_comments) # get the existing comments' bodies as they appear in gcode
comments = filter(lambda c: c.content.text not in existing_comments, comments) # exclude gcode comments that already exist in github comments = filter(lambda c: c.content.text not in existing_comments, comments) # exclude gcode comments that already exist in github
Expand All @@ -85,12 +111,13 @@ def add_comment_to_github(comment, github_issue):
author = comment.author[0].name.text author = comment.author[0].name.text
date = dateutil.parser.parse(comment.published.text).strftime('%B %d, %Y %H:%M:%S') date = dateutil.parser.parse(comment.published.text).strftime('%B %d, %Y %H:%M:%S')
content = comment.content.text content = comment.content.text
content = content.replace('%', '&#37;') # Github chokes on % in the payload
body = '_From %s on %s:_\n%s' % (author, date, content) body = '_From %s on %s:_\n%s' % (author, date, content)


logging.info('Adding comment %s', id) logging.info('Adding comment %s', id)


if not options.dry_run: if not options.dry_run:
github.issues.comment(github_project, github_issue.number, body.encode('utf-8')) github_issue.create_comment(body.encode('utf-8'))




def process_gcode_issues(existing_issues): def process_gcode_issues(existing_issues):
Expand All @@ -113,29 +140,31 @@ def process_gcode_issues(existing_issues):


def get_existing_github_issues(): def get_existing_github_issues():
try: try:
existing_issues = github.issues.list(github_project, 'open') + github.issues.list(github_project, 'closed') existing_issues = list(github_repo.get_issues(state='open')) + list(github_repo.get_issues(state='closed'))
existing_issues = filter(lambda i: 'imported' in i.labels, existing_issues) existing_issues = filter(lambda i: 'imported' in [l.name for l in i.get_labels()], existing_issues)
existing_issues = dict(zip([str(i.title) for i in existing_issues], existing_issues)) existing_issues = dict(zip([str(i.title) for i in existing_issues], existing_issues))
except: except:
existing_issues = {} existing_issues = {}
return existing_issues return existing_issues




if __name__ == "__main__": if __name__ == "__main__":
usage = "usage: %prog [options] <google_project_name> <github_api_token> <github_user_name> <github_project>" usage = "usage: %prog [options] <google_project_name> <github_user_name> <github_project>"
description = "Migrate all issues from a Google Code project to a Github project." description = "Migrate all issues from a Google Code project to a Github project."
parser = optparse.OptionParser(usage=usage, description=description) parser = optparse.OptionParser(usage=usage, description=description)
parser.add_option('-d', '--dry-run', action="store_true", dest="dry_run", help="Don't modify anything on Github", default=False) parser.add_option('-d', '--dry-run', action="store_true", dest="dry_run", help="Don't modify anything on Github", default=False)
options, args = parser.parse_args() options, args = parser.parse_args()


if len(args) != 4: if len(args) != 3:
parser.print_help() parser.print_help()
sys.exit() sys.exit()


google_project_name, github_api_token, github_user_name, github_project = args google_project_name, github_user_name, github_project = args
github_password = getpass.getpass('Github password: ')


google = gdata.projecthosting.client.ProjectHostingClient() google = gdata.projecthosting.client.ProjectHostingClient()
github = Github(username=github_user_name, api_token=github_api_token, requests_per_second=GITHUB_REQUESTS_PER_SECOND) github = Github(github_user_name, github_password)
github_repo = github.get_user().get_repo(github_project)


try: try:
existing_issues = get_existing_github_issues() existing_issues = get_existing_github_issues()
Expand Down

0 comments on commit 150d630

Please sign in to comment.