Skip to content

Commit

Permalink
feat: use projectv2_item webhook
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Yu <[email protected]>
  • Loading branch information
Yu-Jack committed Aug 7, 2024
1 parent f0db028 commit f519a27
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export ZENHUB_TOKEN=""
export GITHUB_TOKEN=""
export GITHUB_OWNER=""
export GITHUB_REPOSITORY=
export ZENHUB_PIPELINE="New Issues, Product Backlog, Icebox" # example
export ZENHUB_PIPELINE="New Issues,Product Backlog,Icebox" # example
export FLASK_USERNAME="" # Use basic auth here, such as http://username:passowrd@localhost:8080
export FLASK_PASSWORD=""
export GITHUB_REPOSITORY_TEST=""
Expand Down
5 changes: 2 additions & 3 deletions github-bot/harvester_github_bot/action.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import abc

class ActionRequest:
def __init__(self):
pass
def setAction(self, action):
def __init__(self, action, event_type):
self.action = action
self.event_type = event_type


class Action(abc.ABC):
Expand Down
2 changes: 2 additions & 0 deletions github-bot/harvester_github_bot/action_label.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def __init__(self):
pass

def isMatched(self, actionRequest):
if actionRequest.event_type not in ['issue']:
return False
if actionRequest.action not in ['labeled']:
return False
return True
Expand Down
101 changes: 101 additions & 0 deletions github-bot/harvester_github_bot/action_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import re

from flask import render_template

from harvester_github_bot.label_action.create_gui_issue import CreateGUIIssue
from harvester_github_bot.label_action.create_backport import CreateBackport
from harvester_github_bot.action import Action
from harvester_github_bot import app, gtihub_project_manager, repo, repo_test, \
ZENHUB_PIPELINE, GITHUB_OWNER, GITHUB_REPOSITORY, ZENHUB_PIPELINE, GITHUB_REPOSITORY_TEST

template_re = re.compile('---\n.*?---\n', re.DOTALL)


class ActionProject(Action):
def __init__(self):
self.__event_type_key = "projects_v2_item"

def isMatched(self, actionRequest):
if actionRequest.event_type not in [self.__event_type_key]:
return False
if actionRequest.action not in ['edited']:
return False
return True

def action(self, request):
if request[self.__event_type_key]["content_type"] != "Issue":
return

project_node_id = request[self.__event_type_key]['project_node_id']
if gtihub_project_manager.project()["id"] != project_node_id:
app.logger.error("project is not matched")
return

target_column = request['changes']['field_value']['to']
if target_column["name"] not in ZENHUB_PIPELINE.split(","):
app.logger.debug('target_column is {}, ignoring'.format(target_column["name"]))
return

issue_node_id = request[self.__event_type_key]['content_node_id']
issue = gtihub_project_manager.get_global_issue(issue_node_id)

if issue["number"] is None:
app.logger.error("issue number is None")
return

it = IssueTransfer(issue["number"])
it.create_comment_if_not_exist()
it.create_e2e_issue()


class IssueTransfer:
def __init__(
self,
issue_number
):
self.__comments = None
self.__issue = repo.get_issue(issue_number)

def create_comment_if_not_exist(self):
self.__comments = self.__issue.get_comments()
found = False
for comment in self.__comments:
if comment.body.strip().startswith('## Pre Ready-For-Testing Checklist'):
app.logger.debug('pre-merged checklist already exists, not creating a new one')
found = True
break
if not found:
app.logger.debug('pre-merge checklist does not exist, creating a new one')
self.__issue.create_comment(render_template('pre-merge.md'))

def create_e2e_issue(self):
require_e2e = True
labels = self.__issue.get_labels()
for label in labels:
if label.name == 'not-require/test-plan':
require_e2e = False
break
if require_e2e:
found = False
for comment in self.__comments:
if comment.body.startswith('Automation e2e test issue:'):
app.logger.debug('Automation e2e test issue already exists, not creating a new one')
found = True
break
if not found:
app.logger.debug('Automation e2e test issue does not exist, creating a new one')

issue_link = '{}/{}#{}'.format(GITHUB_OWNER, GITHUB_REPOSITORY, self.__issue.number)
issue_test_title = '[e2e] {}'.format(self.__issue.title)
issue_test_template_content = repo_test.get_contents(
".github/ISSUE_TEMPLATE/test.md").decoded_content.decode()
issue_test_body = template_re.sub("\n", issue_test_template_content, count=1)
issue_test_body += '\nrelated issue: {}'.format(issue_link)
issue_test = repo_test.create_issue(title=issue_test_title, body=issue_test_body)

issue_test_link = '{}/{}#{}'.format(GITHUB_OWNER, GITHUB_REPOSITORY_TEST, issue_test.number)

# link test issue in Harvester issue
self.__issue.create_comment('Automation e2e test issue: {}'.format(issue_test_link))
else:
app.logger.debug('label require/automation-e2e does not exists, not creating test issue')
28 changes: 14 additions & 14 deletions github-bot/harvester_github_bot/github_graphql/manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import requests
from harvester_github_bot.github_graphql.ql_queries import GET_ISSUE_QUERY, GET_ORGANIZATION_PROJECT_QUERY, GET_USER_PROJECT_QUERY
from harvester_github_bot.github_graphql.ql_queries import GET_ISSUE_QUERY, GET_GLOBAL_ISSUE_QUERY, GET_ORGANIZATION_PROJECT_QUERY, GET_USER_PROJECT_QUERY
from harvester_github_bot.github_graphql.ql_mutation import ADD_ISSUE_TO_PROJECT_MUTATION

class GitHubProjectManager:
Expand All @@ -9,7 +9,9 @@ def __init__(self, organization, repository, project_number, headers):
self.headers = headers
self.url = "https://api.github.com/graphql"
self.__project = self.__get_orgnization_project(project_number)
# self.__project = self.__get_user_project(project_number) # used in testing

def project(self):
return self.__project

def get_issue(self, issue_number):
variables = {
Expand All @@ -22,6 +24,16 @@ def get_issue(self, issue_number):
return response.json()['data']['repository']['issue']
else:
raise Exception(f"Query failed to run by returning code of {response.status_code}. {response.json()}")

def get_global_issue(self, issue_node_id):
variables = {
'issue_node_id': issue_node_id
}
response = requests.post(self.url, headers=self.headers, json={'query': GET_GLOBAL_ISSUE_QUERY, 'variables': variables})
if response.status_code == 200:
return response.json()['data']['node']
else:
raise Exception(f"Query failed to run by returning code of {response.status_code}. {response.json})")

def add_issue_to_project(self, issue_id):
variables = {
Expand All @@ -42,17 +54,5 @@ def __get_orgnization_project(self, project_number):
response = requests.post(self.url, headers=self.headers, json={'query': GET_ORGANIZATION_PROJECT_QUERY, 'variables': variables})
if response.status_code == 200:
return response.json()['data']['organization']['projectV2']
else:
raise Exception(f"Query failed to run by returning code of {response.status_code}. {response.json()}")


def __get_user_project(self, project_number):
variables = {
'organization': self.organization,
'project_number': project_number
}
response = requests.post(self.url, headers=self.headers, json={'query': GET_USER_PROJECT_QUERY, 'variables': variables})
if response.status_code == 200:
return response.json()['data']['user']['projectV2']
else:
raise Exception(f"Query failed to run by returning code of {response.status_code}. {response.json()}")
12 changes: 12 additions & 0 deletions github-bot/harvester_github_bot/github_graphql/ql_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,16 @@
}
}
}
"""

GET_GLOBAL_ISSUE_QUERY = """
query Organization($issue_node_id: ID!) {
node(id: $issue_node_id) {
... on Issue {
title
id
number
}
}
}
"""
20 changes: 14 additions & 6 deletions github-bot/harvester_github_bot/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from harvester_github_bot.issue_transfer import issue_transfer
from harvester_github_bot.action import ActionRequest
from harvester_github_bot.action_label import ActionLabel
from harvester_github_bot.action_project import ActionProject

auth = HTTPBasicAuth()

Expand All @@ -17,7 +18,6 @@ def verify_password(username, password):
if check_password_hash(FLASK_USERNAME, username) and check_password_hash(FLASK_PASSWORD, password):
return username


@app.route('/zenhub', methods=['POST'])
@auth.login_required
def zenhub():
Expand All @@ -36,25 +36,33 @@ def zenhub():
return {
'message': 'webhook handled successfully'
}, http.HTTPStatus.OK


SUPPORTED_ACTIONS = [
ActionLabel(),
ActionProject(),
]

SUPPORTED_EVENT = [
"projects_v2_item",
"issue"
]

@app.route('/github', methods=['POST'])
@auth.login_required
def gh():
req = request.get_json()
msg = "Skip action"
event_type = ""

for event in SUPPORTED_EVENT:
if req.get(event) is not None:
event_type = event

if req.get('issue') is None:
if event_type == "":
return {
'message': msg
}, http.HTTPStatus.OK

action_request = ActionRequest()
action_request.setAction(req.get('action'))
action_request = ActionRequest(req.get('action'), event_type)

for action in SUPPORTED_ACTIONS:
if action.isMatched(action_request):
Expand Down

0 comments on commit f519a27

Please sign in to comment.