Skip to content

Instantly share code, notes, and snippets.

@non
Created January 9, 2020 18:15
Show Gist options
  • Save non/01fdb04a34625bb94dc8fe3035521223 to your computer and use it in GitHub Desktop.
Save non/01fdb04a34625bb94dc8fe3035521223 to your computer and use it in GitHub Desktop.
Script to delete all issues for a repo. This is different than just closing all of them. It uses the v4 GraphQL Github API.
#!/usr/bin/env python
#
# by Erik Osheim
#
# STEPS TO USE
#
# 0. install deps
# - pip install requests
#
# 1. generate personal access token
# - log into github
# - go to https://github.com/settings/tokens
# - the account used must have 'admin' permission to delete issues for the repo
# - include at least the 'public_repo' permission for the access token
# - save the token string (something like 'd33e2d1d3fb978eafded0c9817e8bce9cca04472')
#
# 2. update user, tok, owner, and repo variables in this script
# - user: the account that generated the token
# - tok: the generated token string
# - owner: the owner of the repo whose issues are to be deleted
# - repo: the name of the repo whose issues are to be deleted
#
# 3. python massdel.py
#
# The script will delete the issues 20 at a time. It will display the
# remaining API calls available and when the calls refresh. For repos
# with many thousands of issues the script may need to be run multiple
# times (e.g. delete the first 5K issues, wait until the API calls are
# refreshed, delete the next 5K, etc.). The script doesn't do this
# backoff itself, and contains no rate limiting of any kind.
#
# FUTURE WORK
#
# * delete only issues with a matching label
# * delete only issues opened by a particular account
# * delete a set of issues given by issue number
import datetime
import json
import requests
user = 'github-username'
tok = 'abcdef0123456789abcdef0123456789abcdef01'
owner = "repo-owner"
repo = "repo-name"
# get info about how many API requests remain
def info(r0):
remaining = int(r0.headers['X-RateLimit-Remaining'])
limit = int(r0.headers['X-RateLimit-Limit'])
return (remaining, limit)
# get into about when API requests are refreshed
def retryat(r0):
t = r0.headers['X-RateLimit-Reset']
d = datetime.datetime.fromtimestamp(int(t))
return (d, t)
# make a graphql request to github
def req(q):
dat = json.dumps({"query": q})
return requests.post('https://api.github.com/graphql', auth=(user, tok), data=dat)
# request all the issues for the repo
def getissues():
q = """
query {
repository(owner:"%s", name:"%s") {
issues(last:20, states:[CLOSED,OPEN]) {
edges {
node { id number }
}
}
}
}
""" % (owner, repo)
return req(q)
# delete a particular issues by its internal issue ID (not issue number)
def delete(iid):
m = """
mutation {
deleteIssue(input:{issueId:"%s"}) {
clientMutationId
}
}
""" % iid
return req(m)
# the actual script body
def main():
while True:
r0 = getissues()
if r0.status_code != 200:
print('failed to get issues')
print(r0.json())
return
rem, lim = info(r0)
dt, _ = retryat(r0)
print('%d of %d API calls remaining until %s' % (rem, lim, dt))
lst = r0.json()['data']['repository']['issues']['edges']
if not lst:
print('no issues to delete')
return
print('got next %d issues' % len(lst))
for item in lst:
iid = item['node']['id']
num = item['node']['number']
r1 = delete(iid)
if r1.status_code != 200:
print('failed to delete #%s (%s)' % (num, iid))
d, t = retryat(r1)
print('retry at %s (%s)' % (d, t))
print(r1.json())
return None
rem, lim = info(r1)
print(' %04d/%04d: deleted issue #%s (%s)' % (rem, lim, num, iid))
if __name__ == "__main__":
# run the script!
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment