Last active
June 15, 2017 18:17
-
-
Save adam-p/9255324 to your computer and use it in GitHub Desktop.
Export open Github issues for offline use
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
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
''' | |
pip install --upgrade PyGithub | |
TODO: | |
- Configurable output directory | |
- Output more issue info | |
- Better rendering | |
''' | |
import argparse | |
import os | |
import sys | |
import errno | |
from github import Github | |
_OUTPUT_DIR = 'issues' | |
_DIVIDER_LEN = 79 | |
def _makedirs(path): | |
try: | |
os.makedirs(path) | |
except OSError as e: | |
if e.errno == errno.EEXIST and os.path.isdir(path): | |
pass | |
else: | |
raise | |
def _export_issue(issue, export_path): | |
export_path = os.path.join(export_path, '%04d.txt' % (issue.number)) | |
f = open(export_path, 'w') | |
f.write('#%d\n' % (issue.number,)) | |
f.write('%s\n%s\n\n' % (issue.title, '='*len(issue.title.encode('utf-8')))) | |
f.write('%s %s\n\n' % (issue.created_at, issue.user.login)) | |
state = '%s %s' % (issue.state, issue.closed_at if issue.closed_at else '') | |
f.write('%s\n\n' % (state,)) | |
assignee = 'assigned to %s' % (issue.assignee.login,) if issue.assignee else 'unassigned' | |
f.write('%s\n\n' % (assignee,)) | |
labels = ', '.join([l.name for l in issue.labels]) | |
f.write('%s\n\n' % (labels,)) | |
f.write('%s\n\n' % (issue.body.replace('\r\n', '\n').encode('utf-8'))) | |
comment_pager = issue.get_comments() | |
current_page = 0 | |
while True: | |
comments = comment_pager.get_page(current_page) | |
current_page += 1 | |
current_page += 1 | |
if not comments: | |
break | |
for comment in comments: | |
f.write('%s\n\n' %('-'*_DIVIDER_LEN,)) | |
f.write('%s %s\n\n' % (comment.created_at, comment.user.login)) | |
f.write('%s\n\n' % (comment.body.replace('\r\n', '\n').encode('utf-8'))) | |
f.close() | |
def _export_issues_for_project(gh, projname, state, labels, exc_labels, export_path): | |
repo = gh.get_repo(projname) | |
real_labels = None | |
if labels: | |
real_labels = [repo.get_label(label_str) for label_str in labels.split(',')] | |
if exc_labels: | |
exc_labels = exc_labels.split(',') | |
else: | |
exc_labels = () | |
# It seems impossible to get assigned and unassigned issues in a single query, | |
# so we'll do one for each state. | |
for assignee in ['*', 'none']: | |
for issue in repo.get_issues(state=state, labels=real_labels, assignee=assignee): | |
if [l for l in issue.labels if l.name in exc_labels]: | |
continue | |
print '#%d %s' % (issue.number, issue.title) | |
_export_issue(issue, export_path) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(description='Export Github issues to disk') | |
parser.add_argument('projnames', metavar='username/projectname', type=str, nargs='+', | |
help='fully qualified "username/projectname" of projects to export issues from') | |
parser.add_argument('--auth-token', action='store', | |
help='Github auth token to use (optional, but helps prevent request rate denial)') | |
parser.add_argument('--state', action='store', | |
help='Indicates the state of the issues to return. Can be either open, closed, or all. Default: open') | |
parser.add_argument('--labels', action='store', | |
help='A list of comma separated label names. Example: bug,ui,@high') | |
parser.add_argument('--exc_labels', action='store', | |
help='A list of comma separated label names TO EXCLUDE. Example: enhancement,@low') | |
args = parser.parse_args() | |
gh = Github(args.auth_token) | |
for projname in args.projnames: | |
export_path = os.path.normpath(os.path.join(_OUTPUT_DIR, projname)) | |
_makedirs(export_path) | |
_export_issues_for_project( | |
gh, projname, args.state, args.labels, args.exc_labels, export_path) | |
sys.exit(0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment