mirror of
https://gitlab.wikimedia.org/ladsgroup/Phabricator-maintenance-bot
synced 2024-11-28 06:52:38 +01:00
Initial commit
This commit is contained in:
parent
e06b410188
commit
73098f3ff1
4 changed files with 271 additions and 0 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
|||
# My credentials
|
||||
creds.json
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
@ -102,3 +105,6 @@ venv.bak/
|
|||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
|
|
72
column_mover.py
Normal file
72
column_mover.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
from lib import Client
|
||||
|
||||
|
||||
class Checker():
|
||||
def __init__(self, work, client):
|
||||
self.work = work
|
||||
self.client = client
|
||||
|
||||
def phid_check(self, phid):
|
||||
if self.work.get('projects'):
|
||||
return self.phid_check_project(
|
||||
phid, [
|
||||
self.client.lookupPhid(
|
||||
'#' + i) for i in self.work['projects']])
|
||||
if self.work.get('status'):
|
||||
return self.phid_check_status(phid, self.work['status'])
|
||||
return False
|
||||
|
||||
def phid_check_project(self, phid, project_phids):
|
||||
taskDetails = self.client.taskDetails(phid)
|
||||
for project_phid in project_phids:
|
||||
if project_phid in taskDetails['projectPHIDs']:
|
||||
return True
|
||||
return False
|
||||
|
||||
def phid_check_status(self, phid, statuses):
|
||||
taskDetails = self.client.taskDetails(phid)
|
||||
return taskDetails['statusName'] in statuses
|
||||
|
||||
|
||||
client = Client.newFromCreds()
|
||||
|
||||
work = [{'from': ['incoming'],
|
||||
'project': 'Wikidata',
|
||||
'to': 'in progress',
|
||||
'projects': ['wikidata-campsite-iteration-∞',
|
||||
'RL_Module_Terminators_Trailblazing',
|
||||
'wikidata-bridge-sprint-8']},
|
||||
{'from': ['Incoming'],
|
||||
'project': 'User-Ladsgroup',
|
||||
'to': 'In progress',
|
||||
'projects': ['wikidata-campsite-iteration-∞',
|
||||
'RL_Module_Terminators_Trailblazing']},
|
||||
{'from': ['In progress',
|
||||
'Incoming'],
|
||||
'project': 'User-Ladsgroup',
|
||||
'to': 'Done',
|
||||
'status': ['Resolved']},
|
||||
{'from': ['Unsorted'],
|
||||
'project': 'user-dannys712',
|
||||
'to': 'Global Watchlist',
|
||||
'projects': ['DannyS712-Global_watchlist.js']},
|
||||
]
|
||||
for case in work:
|
||||
gen = client.getTasksWithProject(client.lookupPhid('#' + case['project']))
|
||||
checker = Checker(case, client)
|
||||
columns = client.getColumns(client.lookupPhid('#' + case['project']))
|
||||
mapping = {}
|
||||
for column in columns['data']:
|
||||
mapping[column['fields']['name']] = column['phid']
|
||||
for phid in gen:
|
||||
if checker.phid_check(phid):
|
||||
project_phid = client.lookupPhid('#' + case['project'])
|
||||
currentColumnName = client.getTaskColumns(
|
||||
phid)['boards'][project_phid]['columns'][0]['name']
|
||||
if currentColumnName not in case['from']:
|
||||
continue
|
||||
try:
|
||||
print(phid)
|
||||
client.moveColumns(phid, mapping[case['to']])
|
||||
except KeyboardInterrupt:
|
||||
continue
|
123
lib.py
Normal file
123
lib.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
import json
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class Client(object):
|
||||
"""Phabricator client"""
|
||||
|
||||
def __init__(self, url, username, key):
|
||||
self.url = url
|
||||
self.username = username
|
||||
self.column_cache = {}
|
||||
self.phid_cache = {}
|
||||
self.session = {
|
||||
'token': key,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def newFromCreds(cls):
|
||||
with open('creds.json', 'r') as f:
|
||||
creds = json.loads(f.read())
|
||||
return cls(*creds)
|
||||
|
||||
def post(self, path, data):
|
||||
data['__conduit__'] = self.session
|
||||
r = requests.post('%s/api/%s' % (self.url, path), data={
|
||||
'params': json.dumps(data),
|
||||
'output': 'json',
|
||||
})
|
||||
resp = r.json()
|
||||
if resp['error_code'] is not None:
|
||||
raise Exception(resp['error_info'])
|
||||
return resp['result']
|
||||
|
||||
def lookupPhid(self, label):
|
||||
"""Lookup information on a Phab object by name."""
|
||||
if not self.phid_cache.get(label):
|
||||
r = self.post('phid.lookup', {'names': [label]})
|
||||
if label in r and 'phid' in r[label]:
|
||||
obj = r[label]['phid']
|
||||
self.phid_cache[label] = obj
|
||||
else:
|
||||
raise Exception('No object found for %s' % label)
|
||||
return self.phid_cache[label]
|
||||
|
||||
def getColumns(self, project_phid):
|
||||
if not self.column_cache.get(project_phid):
|
||||
self.column_cache[project_phid] = self.post(
|
||||
'project.column.search', {
|
||||
"constraints": {
|
||||
"projects": [project_phid]}})
|
||||
return self.column_cache[project_phid]
|
||||
|
||||
def moveColumns(self, task_phid, to_column):
|
||||
self.post('maniphest.edit', {
|
||||
'objectIdentifier': task_phid,
|
||||
'transactions': [{
|
||||
'type': 'column',
|
||||
'value': [to_column],
|
||||
}]
|
||||
})
|
||||
|
||||
def taskDetails(self, phid):
|
||||
"""Lookup details of a Maniphest task."""
|
||||
r = self.post('maniphest.query', {'phids': [phid]})
|
||||
if phid in r:
|
||||
return r[phid]
|
||||
raise Exception('No task found for phid %s' % phid)
|
||||
|
||||
def getTransactions(self, phid):
|
||||
r = self.post('transaction.search', {'objectIdentifier': phid})
|
||||
if 'data' in r:
|
||||
return r['data']
|
||||
raise Exception('No transaction found for phid %s' % phid)
|
||||
|
||||
def removeProject(self, project_phid, task):
|
||||
return self.removeProjectByPhid(project_phid, self.lookupPhid(task))
|
||||
|
||||
def removeProjectByPhid(self, project_phid, task_phid):
|
||||
self.post('maniphest.edit', {
|
||||
'objectIdentifier': task_phid,
|
||||
'transactions': [{
|
||||
'type': 'projects.remove',
|
||||
'value': [project_phid],
|
||||
}]
|
||||
})
|
||||
|
||||
def getTasksWithProject(self, project_phid, continue_=None):
|
||||
r = self._getTasksWithProjectContinue(project_phid, continue_)
|
||||
cursor = r['cursor']
|
||||
for case in r['data']:
|
||||
if case['type'] != 'TASK':
|
||||
continue
|
||||
yield case['phid']
|
||||
if cursor.get('after'):
|
||||
for case in self.getTasksWithProject(
|
||||
project_phid, cursor['after']):
|
||||
yield case
|
||||
|
||||
def _getTasksWithProjectContinue(self, project_phid, continue_=None):
|
||||
params = {
|
||||
'limit': 100,
|
||||
'constraints': {
|
||||
'projects': [project_phid],
|
||||
"modifiedStart": int(time.time() - 3600)
|
||||
}
|
||||
}
|
||||
if continue_:
|
||||
params['after'] = continue_
|
||||
return self.post('maniphest.search', params)
|
||||
|
||||
def getTaskColumns(self, phid):
|
||||
params = {
|
||||
"attachments": {
|
||||
"columns": {"boards": {"columns": True}}
|
||||
},
|
||||
"constraints": {
|
||||
"phids": [phid]
|
||||
}
|
||||
}
|
||||
return self.post('maniphest.search', params)[
|
||||
'data'][0]['attachments']['columns']
|
70
patchforreview_remover.py
Normal file
70
patchforreview_remover.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
import re
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from lib import Client
|
||||
|
||||
|
||||
class Checker():
|
||||
def __init__(self, gerrit_bot_phid, project_patch_for_review_phid, client):
|
||||
self.gerrit_bot_phid = gerrit_bot_phid
|
||||
self.project_patch_for_review_phid = project_patch_for_review_phid
|
||||
self.client = client
|
||||
|
||||
def check(self, t_id):
|
||||
phid = self.client.lookupPhid(t_id)
|
||||
return self.phid_check(phid)
|
||||
|
||||
def phid_check(self, phid):
|
||||
gerrit_bot_actions = []
|
||||
for transaction in self.client.getTransactions(phid):
|
||||
if 'https://github.com/' in str(transaction):
|
||||
return False
|
||||
if transaction['authorPHID'] == self.gerrit_bot_phid:
|
||||
gerrit_bot_actions.append(transaction)
|
||||
else:
|
||||
if transaction['type'] == 'projects':
|
||||
check = self.project_patch_for_review_phid in str(
|
||||
transaction['fields'])
|
||||
add_check = "'add'" in str(transaction['fields'])
|
||||
if check and add_check:
|
||||
return False
|
||||
|
||||
gerrit_patch_status = defaultdict(list)
|
||||
for case in gerrit_bot_actions:
|
||||
if case['type'] != 'comment':
|
||||
continue
|
||||
|
||||
if len(case['comments']) != 1:
|
||||
return False
|
||||
raw_comment = case['comments'][0]['content']['raw']
|
||||
gerrit_patch_id = re.findall(
|
||||
r'https\:\/\/gerrit\.wikimedia\.org\/r\/(\d+)', raw_comment)[0]
|
||||
merged = re.findall(
|
||||
r'Change \d+ (?:\*\*merged\*\*|abandoned) by ',
|
||||
raw_comment)
|
||||
|
||||
gerrit_patch_status[gerrit_patch_id].append(not(bool(merged)))
|
||||
|
||||
for patch in gerrit_patch_status:
|
||||
if gerrit_patch_status[patch] != [False, True]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
client = Client.newFromCreds()
|
||||
|
||||
project_patch_for_review_phid = 'PHID-PROJ-onnxucoedheq3jevknyr'
|
||||
checker = Checker(
|
||||
'PHID-USER-idceizaw6elwiwm5xshb',
|
||||
project_patch_for_review_phid,
|
||||
client)
|
||||
gen = client.getTasksWithProject(project_patch_for_review_phid)
|
||||
for phid in gen:
|
||||
if checker.phid_check(phid):
|
||||
print(client.taskDetails(phid)['id'])
|
||||
try:
|
||||
client.removeProjectByPhid(project_patch_for_review_phid, phid)
|
||||
except BaseException:
|
||||
continue
|
||||
time.sleep(10)
|
Loading…
Reference in a new issue