-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from rebortg/main
add phabricator autoclose script and github action
- Loading branch information
Showing
5 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
venv | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
phabricator | ||
requests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)}') | ||
|