Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add phabricator autoclose script and github action #1

Merged
merged 3 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/phabricator_tasks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Autoclose Finished Phabricator Tasks
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * *'

jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Set Up Python
uses: actions/setup-python@v5
with:
python-version: 3.11.x

- uses: actions/checkout@v4

- name: run script
env:
PHABRICATOR_WRITE_TOKEN: ${{ secrets.PHABRICATOR_WRITE_TOKEN }}
if: env.PHABRICATOR_WRITE_TOKEN != null
run: |
pip3 install -r phabricator_tasks/requirements.txt
python3 phabricator_tasks/tasks.py -t ${{ secrets.PHABRICATOR_WRITE_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
venv
.DS_Store
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# vyos-infrastructure
Various scripts and automations for VyOS infrastructure tasks


## phabricator_tasks
autoclose all finished tasks in https://vyos.dev
2 changes: 2 additions & 0 deletions phabricator_tasks/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
phabricator
requests
186 changes: 186 additions & 0 deletions phabricator_tasks/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
from phabricator import Phabricator as PhabricatorOriginal
from phabricator import parse_interfaces
import argparse


'''
get project wide tasks which are not closed but all in the Finished column

1. get all Workboard columns
- extract workboard phid for the Finished column
- and the project phid and name

2. get all open taks from projects with Finish column
3. get unique taskslists from previous step to get projekts of a task
4. get all transactions for each task and check if the task is in the Finished column per project
5. autoclose if task is in all Finished column

'''

'''
extend of original Phabricator class to add new interface "project.column.search"
this can be delete if PR https://github.com/disqus/python-phabricator/pull/71 is merged in the pip package

'''
import copy
import json
import pkgutil

INTERFACES = json.loads(
pkgutil.get_data('phabricator', 'interfaces.json')
.decode('utf-8'))

INTERFACES['project.column.search'] = {
"description": "Search for Workboard columns.",
"params": {
"ids": "optional list<int>",
"phids": "optional list<phid>",
"projects": "optional list<phid>"
},
"return": "list"
}

class Phabricator(PhabricatorOriginal):
def __init__(self, **kwargs):
kwargs['interface'] = copy.deepcopy(parse_interfaces(INTERFACES))
super(Phabricator, self).__init__(self, **kwargs)

''' end of extend the original Phabricator class'''

def phab_search(method, constraints=dict(), after=None):
results = []
while True:
response = method(
constraints=constraints,
after=after
)
results.extend(response.response['data'])
after = response.response['cursor']['after']
if after is None:
break
return results


def phab_query(method, after=None):
results = []
while True:
response = method(
offset=after
)
results.extend(response.response['data'])
after = response.response['cursor']['after']
if after is None:
break
return results


def close_task(task_id, phab):
try:
response = phab.maniphest.update(
id=task_id,
status='resolved'
)
if response.response['isClosed']:
print(f'T{task_id} closed')
except Exception as e:
print(f'T{task_id} Error: {e}')


parser = argparse.ArgumentParser()
parser.add_argument("-t", "--token", type=str, help="API token", required=True)
args = parser.parse_args()

phab = Phabricator(host='https://vyos.dev/api/', token=args.token)
phab.maniphest.update(id=6053, status='resolved')

workboards = phab_search(phab.project.column.search)
project_hirarchy = {}

# get sub-project hirarchy from proxyPHID in workboards
for workboard in workboards:
if workboard['fields']['proxyPHID']:
proxy_phid = workboard['fields']['proxyPHID']
project_phid = workboard['fields']['project']['phid']

if project_phid not in project_hirarchy.keys():
project_hirarchy[project_phid] = []
project_hirarchy[project_phid].append(proxy_phid)

finished_boards = []


for workboard in workboards:
project_id = workboard['fields']['project']['phid']
if project_id in project_hirarchy.keys():
# skip projects with sub-projects
continue
if workboard['fields']['name'] == 'Finished':
project_tasks = phab_search(phab.maniphest.search, constraints={
'projects': [project_id],
'statuses': ['open'],
})
finished_boards.append({
'project_id': project_id,
'project_name': workboard['fields']['project']['name'],
'project_tasks': project_tasks,
'should_board_id': workboard['phid'],
})

# get unique tasks
# tasks = {
# 9999: {
# 'PHID-PROJ-xxxxx': 'PHID-PCOL-xxxxx',
# 'PHID-PROJ-yyyyy': 'PHID-PCOL-yyyyy'
# }
# }
tasks = {}
for project in finished_boards:
project_id = project['project_id']
board_id = project['should_board_id']
for task in project['project_tasks']:
task_id = task['id']
if task_id not in tasks.keys():
tasks[task_id] = {}
if project_id not in tasks[task_id].keys():
tasks[task_id][project_id] = board_id

tasks = dict(sorted(tasks.items()))

# get transactions for each task and compare if the task is in the Finished column
for task_id, projects in tasks.items():
fisnish_timestamp = 0
project_ids = list(projects.keys())
# don't use own pagination function, because endpoint without pagination
transactions = phab.maniphest.gettasktransactions(ids=[task_id])
transactions = transactions.response[str(task_id)]
finished = False
for transaction in transactions:
if transaction['transactionType'] == 'core:columns':
# test if projectid is in transaction
if transaction['newValue'][0]['boardPHID'] in project_ids:
# remove project_id from project_ids to use only last transaction in this
# project
project_ids.remove(transaction['newValue'][0]['boardPHID'])
# test if boardid is the "Finished" board
if fisnish_timestamp < int(transaction['dateCreated']):
fisnish_timestamp = int(transaction['dateCreated'])
if projects[transaction['newValue'][0]['boardPHID']] == transaction['newValue'][0]['columnPHID']:
finished = True
for project in finished_boards:
if project['project_id'] == transaction['newValue'][0]['boardPHID']:
project_name = project['project_name']
# print(f'T{task_id} is Finished in {project_name}')
if len(project_ids) == 0:
print(f'T{task_id} is Finished in all projects')
close_task(task_id, phab)
break

#if len(project_ids) > 0 and finished:
# collect project names for output
# project_names = []
# for project_id in project_ids:
# for project in finished_boards:
# if project['project_id'] == project_id:
# project_names.append(project['project_name'])
# print(f'T{task_id} is in a different column: {' and '.join(project_names)}')