mirror of https://github.com/zcash/developers.git
243 lines
7.7 KiB
Python
243 lines
7.7 KiB
Python
from sgqlc.endpoint.http import HTTPEndpoint
|
|
from sgqlc.operation import Operation
|
|
|
|
from github_schema import github_schema as schema
|
|
from helpers.repos import (
|
|
CORE_REPOS,
|
|
HALO2_REPOS,
|
|
TFL_REPOS,
|
|
WALLET_REPOS,
|
|
IOS_REPOS,
|
|
ANDROID_REPOS,
|
|
ECC_REPOS,
|
|
ZF_REPOS,
|
|
ZF_FROST_REPOS,
|
|
ZCASHD_DEPRECATION_REPOS,
|
|
POOL_DEPRECATION_REPOS,
|
|
POOL_DEPRECATION_REPOS,
|
|
)
|
|
|
|
REPO_SETS = {
|
|
'core': CORE_REPOS,
|
|
'halo2': HALO2_REPOS,
|
|
'tfl': TFL_REPOS,
|
|
'wallet': WALLET_REPOS,
|
|
'wallet-ios': IOS_REPOS,
|
|
'wallet-android': ANDROID_REPOS,
|
|
'ecc': ECC_REPOS,
|
|
'zf': ZF_REPOS,
|
|
'zf-frost': ZF_FROST_REPOS,
|
|
'zf-devops': ZF_REPOS + ZF_FROST_REPOS,
|
|
'zcashd-deprecation': ZCASHD_DEPRECATION_REPOS,
|
|
'sprout-deprecation': POOL_DEPRECATION_REPOS,
|
|
'transparent-deprecation': POOL_DEPRECATION_REPOS,
|
|
}
|
|
|
|
|
|
def api(token):
|
|
return HTTPEndpoint(
|
|
'https://api.github.com/graphql',
|
|
{'Authorization': 'bearer %s' % token},
|
|
)
|
|
|
|
|
|
class GitHubIssue:
|
|
def __init__(self, repo, issue_number, data, REPOS):
|
|
self.repo = repo
|
|
self.issue_number = issue_number
|
|
self.milestone = None
|
|
self._REPOS = REPOS
|
|
|
|
if data is not None:
|
|
labels = [label['name'] for label in data['labels']['nodes']]
|
|
self.title = data['title']
|
|
self.labels = labels
|
|
self.is_release = 'C-release' in labels
|
|
self.is_target = 'C-target' in labels
|
|
self.is_pr = 'merged' in data
|
|
self.is_committed = 'S-committed' in labels
|
|
self.is_in_progress = 'S-in-progress' in labels
|
|
self.waiting_on_review = 'S-waiting-on-review' in labels
|
|
self.url = data['url']
|
|
self.state = 'closed' if data['state'] in ['CLOSED', 'MERGED'] else 'open'
|
|
if 'milestone' in data and data['milestone']:
|
|
self.milestone = data['milestone']['title']
|
|
else:
|
|
# If we can't fetch issue data, assume we don't care.
|
|
self.title = ''
|
|
self.labels = []
|
|
self.url = None
|
|
self.is_release = False
|
|
self.is_target = False
|
|
self.is_pr = False
|
|
self.is_committed = False
|
|
self.is_in_progress = False
|
|
self.waiting_on_review = False
|
|
self.state = 'closed'
|
|
|
|
def __repr__(self):
|
|
if self.repo in self._REPOS:
|
|
return '%s#%d' % (self.repo, self.issue_number)
|
|
else:
|
|
return 'Unknown'
|
|
|
|
def __eq__(self, other):
|
|
return (self.repo, self.issue_number) == (other.repo, other.issue_number)
|
|
|
|
def __hash__(self):
|
|
return hash((self.repo, self.issue_number))
|
|
|
|
def any_cat(self, categories):
|
|
release_cat = self.is_release if 'releases' in categories else False
|
|
targets_cat = self.is_target if 'targets' in categories else False
|
|
return release_cat or targets_cat
|
|
|
|
|
|
def fetch_issues(op, issues):
|
|
repos = set([repo for (repo, _) in issues])
|
|
repos = {repo: [issue for (r, issue) in issues if r == repo] for repo in repos}
|
|
|
|
for repo, issues in repos.items():
|
|
conn = op.repository(
|
|
owner=repo.name[0],
|
|
name=repo.name[1],
|
|
__alias__='repo%d' % repo.gh_id,
|
|
)
|
|
|
|
for issue in issues:
|
|
res = conn.issue_or_pull_request(number=issue, __alias__='issue%d' % issue)
|
|
for typ in [schema.Issue, schema.PullRequest]:
|
|
node = res.__as__(typ)
|
|
node.labels(first=50).nodes().name()
|
|
node.state()
|
|
node.milestone().title()
|
|
node.title()
|
|
node.url()
|
|
if typ == schema.PullRequest:
|
|
node.merged()
|
|
|
|
|
|
# `nodes` is a list of `(Repo, issue_number)` tuples.
|
|
def download_issues(endpoint, nodes, REPOS):
|
|
issues = [(repo, issue) for (repo, issue) in nodes if repo in REPOS]
|
|
|
|
ret = {}
|
|
|
|
# Ensure that any graph nodes from ZenHub that are not in the repos we care about have
|
|
# default entries, to simplify subsequent graph manipulation code.
|
|
for repo, issue in [(repo, issue) for (repo, issue) in nodes if repo not in REPOS]:
|
|
ret[(repo, issue)] = GitHubIssue(repo, issue, None, REPOS)
|
|
|
|
def chunks(lst, n):
|
|
for i in range(0, len(lst), n):
|
|
yield lst[i : i + n]
|
|
|
|
for issues in chunks(issues, 50):
|
|
op = Operation(schema.Query)
|
|
fetch_issues(op, issues)
|
|
|
|
d = endpoint(op)
|
|
data = op + d
|
|
|
|
for repo, issue in issues:
|
|
repo_data = data['repo%d' % repo.gh_id]
|
|
issue_key = 'issue%d' % issue
|
|
# If GITHUB_TOKEN doesn't have permission to read from a particular private
|
|
# repository in REPOS, GitHub returns an empty repo_data section.
|
|
issue_data = repo_data[issue_key] if issue_key in repo_data else None
|
|
ret[(repo, issue)] = GitHubIssue(repo, issue, issue_data, REPOS)
|
|
|
|
return ret
|
|
|
|
|
|
def fetch_issues_with_labels(op, labels, repos):
|
|
for (repo, (issue_cursor, pr_cursor)) in repos:
|
|
conn = op.repository(
|
|
owner=repo.name[0],
|
|
name=repo.name[1],
|
|
__alias__='repo%d' % repo.gh_id,
|
|
)
|
|
|
|
if issue_cursor != -1:
|
|
issues = conn.issues(
|
|
labels=labels,
|
|
first=50,
|
|
after=issue_cursor,
|
|
)
|
|
issues.nodes.number()
|
|
issues.nodes.labels(first=50).nodes().name()
|
|
issues.nodes.state()
|
|
issues.nodes.milestone().title()
|
|
issues.nodes.title()
|
|
issues.nodes.url()
|
|
issues.page_info.has_next_page()
|
|
issues.page_info.end_cursor()
|
|
|
|
if pr_cursor != -1:
|
|
prs = conn.pull_requests(
|
|
labels=labels,
|
|
first=50,
|
|
after=pr_cursor,
|
|
)
|
|
prs.nodes.number()
|
|
prs.nodes.labels(first=50).nodes().name()
|
|
prs.nodes.state()
|
|
prs.nodes.milestone().title()
|
|
prs.nodes.title()
|
|
prs.nodes.url()
|
|
prs.nodes.merged()
|
|
prs.page_info.has_next_page()
|
|
prs.page_info.end_cursor()
|
|
|
|
|
|
def download_issues_with_labels(endpoint, labels, REPOS):
|
|
ret = {}
|
|
repos = {repo: (None, None) for repo in REPOS}
|
|
|
|
while True:
|
|
op = Operation(schema.Query)
|
|
fetch_issues_with_labels(op, labels, repos.items())
|
|
|
|
d = endpoint(op)
|
|
data = op + d
|
|
|
|
done = []
|
|
for (repo, (_, _)) in repos.items():
|
|
repo_data = data['repo%d' % repo.gh_id]
|
|
|
|
if hasattr(repo_data, 'issues'):
|
|
for issue in repo_data.issues.nodes:
|
|
ret[(repo, issue.number)] = GitHubIssue(repo, issue.number, issue, REPOS)
|
|
if repo_data.issues.page_info.has_next_page:
|
|
issue_cursor = repo_data.issues.page_info.end_cursor
|
|
else:
|
|
issue_cursor = -1
|
|
else:
|
|
issue_cursor = -1
|
|
|
|
if hasattr(repo_data, 'pull_requests'):
|
|
for pr in repo_data.pull_requests.nodes:
|
|
ret[(repo, pr.number)] = GitHubIssue(repo, pr.number, pr, REPOS)
|
|
if repo_data.pull_requests.page_info.has_next_page:
|
|
pr_cursor = repo_data.pull_requests.page_info.end_cursor
|
|
else:
|
|
pr_cursor = -1
|
|
else:
|
|
pr_cursor = -1
|
|
|
|
if issue_cursor == -1 and pr_cursor == -1:
|
|
done.append(repo)
|
|
else:
|
|
repos[repo] = (issue_cursor, pr_cursor)
|
|
|
|
for repo in done:
|
|
del repos[repo]
|
|
|
|
if len(repos) > 0:
|
|
print('.', end='', flush=True)
|
|
else:
|
|
print()
|
|
break
|
|
|
|
return ret
|