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
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
@ -102,3 +105,6 @@ venv.bak/
|
||||||
|
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.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