Skip to content

Commit

Permalink
ci: migrate github bot from zenhub to github project
Browse files Browse the repository at this point in the history
Signed-off-by: Yang Chiu <[email protected]>
  • Loading branch information
yangchiu committed Aug 15, 2024
1 parent 69ab308 commit e9d2e9f
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 43 deletions.
2 changes: 1 addition & 1 deletion github-bot/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.8.5-alpine3.12
FROM python:3.10.14-alpine

COPY requirements.txt /srv
WORKDIR /srv
Expand Down
117 changes: 78 additions & 39 deletions github-bot/longhorn_github_bot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
FLASK_USERNAME = generate_password_hash(config('flask_username'))
GITHUB_OWNER = config('github_owner')
GITHUB_REPOSITORY = config('github_repository')
ZENHUB_PIPELINE = config('zenhub_pipeline')
ZENHUB_PIPELINE_TRIAGE = "Triage"
ZENHUB_PIPELINE_ICEBOX = "Icebox"
ZENHUB_PIPELINE_BACKLOG = "Backlog"
ZENHUB_WORKSPACE_NAME = "Longhorn Main Board"
PROJECT_PIPELINE = config('project_pipeline')
PROJECT_PIPELINE_TRIAGE = "Triage"
PROJECT_PIPELINE_ICEBOX = "Icebox"
PROJECT_PIPELINE_BACKLOG = "Backlog"
PROJECT_NAME = "Longhorn Sprint"

ADD_TO_SPRINT_PIPELINES = {ZENHUB_PIPELINE_TRIAGE, ZENHUB_PIPELINE_ICEBOX, ZENHUB_PIPELINE_BACKLOG}
REMOVE_FROM_SPRINT_PIPELINES = {ZENHUB_PIPELINE_TRIAGE, ZENHUB_PIPELINE_ICEBOX}
ADD_TO_SPRINT_PIPELINES = {PROJECT_PIPELINE_TRIAGE, PROJECT_PIPELINE_ICEBOX, PROJECT_PIPELINE_BACKLOG}
REMOVE_FROM_SPRINT_PIPELINES = {PROJECT_PIPELINE_TRIAGE, PROJECT_PIPELINE_ICEBOX}

# From https://docs.python.org/3/howto/logging.html#logging-to-a-file
numeric_level = getattr(logging, FLASK_LOGLEVEL.upper(), None)
Expand All @@ -34,49 +34,63 @@

g = Github(config('github_token'))

gql_url = "https://api.zenhub.com/public/graphql"
gql_headers = {"Authorization": f"Bearer {config('zenhub_graphql_token')}"}

gql_url = "https://api.github.com/graphql"
gql_headers = {"Authorization": f"Bearer {config('github_token')}"}

@auth.verify_password
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'])
@app.route('/project', methods=['POST'])
@auth.login_required
def zenhub():
form = request.form
organization = form.get('organization')
repo = form.get('repo')
if organization != GITHUB_OWNER or repo != GITHUB_REPOSITORY:
app.logger.debug("webhook event targets %s/%s, ignoring", organization, repo)
else:
webhook_type = request.form.get('type')
if webhook_type == 'issue_transfer':
issue_transfer(form)
else:
app.logger.debug('unhandled webhook type %s, ignoring', webhook_type)

def project():
json = request.get_json()
print(json)

organization = json['organization']['login']
if organization != GITHUB_OWNER:
app.logger.debug(f"webhook event targets {organization}, ignoring")
return {
'message': 'webhook handled successfully'
}, 200

if 'action' not in json:
app.logger.debug(f"unhandled webhook type, ignoring")
return {
'message': 'webhook handled successfully'
}, 200

webhook_type = json['action']
if webhook_type != 'edited':
app.logger.debug(f"unhandled webhook type {webhook_type}, ignoring")
return {
'message': 'webhook handled successfully'
}, 200

changed_field = json['changes']['field_value']['field_name']
if changed_field != 'Status':
app.logger.debug(f"unhandled changed field {changed_field}, ignoring")
return {
'message': 'webhook handled successfully'
}, 200

issue_transfer(json)
return {
'message': 'webhook handled successfully'
}, 200


def issue_transfer(form):
issue_number = form.get('issue_number')
try:
issue_number = int(issue_number)
except ValueError:
app.logger.warning('could not parse issue_number %s as int', issue_number)
return
def issue_transfer(json):

to_pipeline = form.get('to_pipeline_name')
from_pipeline = form.get('from_pipeline_name')
issue_node_id = json['projects_v2_item']['content_node_id']

if to_pipeline == ZENHUB_PIPELINE:
add_checklist(issue_number)
to_pipeline = json['changes']['field_value']['to']['name']
from_pipeline = json['changes']['field_value']['to']['name']

if to_pipeline == PROJECT_PIPELINE:
add_checklist(issue_node_id)

if from_pipeline in ADD_TO_SPRINT_PIPELINES and to_pipeline not in ADD_TO_SPRINT_PIPELINES:
add_to_sprint(issue_number)
Expand All @@ -87,8 +101,10 @@ def issue_transfer(form):
return


def add_checklist(issue_number):
repo = g.get_repo('{}/{}'.format(GITHUB_OWNER, GITHUB_REPOSITORY))
def add_checklist(issue_node_id):
repo = g.get_repo(f"{GITHUB_OWNER}/{GITHUB_REPOSITORY}")
print(f"get issue with number with node id: {issue_node_id}")
issue_number = query_issue_number(issue_node_id)
issue = repo.get_issue(issue_number)
comments = issue.get_comments()
found = False
Expand All @@ -103,13 +119,13 @@ def add_checklist(issue_number):


def add_to_sprint(issue_number):
active_sprint_id, repository_gh_id = query_active_sprint_id_and_repository_gh_id(ZENHUB_WORKSPACE_NAME)
active_sprint_id, repository_gh_id = query_active_sprint_id_and_repository_gh_id(PROJECT_NAME)
issue_id = query_issue_id(repository_gh_id, issue_number)
add_issue_to_sprint(issue_id, active_sprint_id)


def remove_from_sprint(issue_number):
active_sprint_id, repository_gh_id = query_active_sprint_id_and_repository_gh_id(ZENHUB_WORKSPACE_NAME)
active_sprint_id, repository_gh_id = query_active_sprint_id_and_repository_gh_id(PROJECT_NAME)
issue_id = query_issue_id(repository_gh_id, issue_number)
remove_issue_from_sprint(issue_id, active_sprint_id)

Expand Down Expand Up @@ -149,6 +165,29 @@ def query_active_sprint_id_and_repository_gh_id(workspace_name):
repository_gh_id = response.json()["data"]["viewer"]["searchWorkspaces"]["nodes"][0]["defaultRepository"]["ghId"]
return active_sprint_id, repository_gh_id

def query_issue_number(issue_node_id):
query = """
query($issueId: ID!) {
node(id: $issueId) {
... on Issue {
number
}
}
}
"""
variables = {
"issueId": issue_node_id
}
response = requests.post(url=gql_url,
headers=gql_headers,
json={
"query": query,
"variables": variables
})
app.logger.debug("query_issue_number response %s %s", response.status_code, response.json())
issue_number = response.json()["data"]["node"]["number"]
print(f"issue_number = {issue_number}")
return issue_number

def query_issue_id(repository_gh_id, issue_number):
query = """
Expand Down
3 changes: 1 addition & 2 deletions github-bot/longhorn_github_bot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ class BotConfig(RequiredConfigMixin):
required_config.add_option('github_repository', parser=str, default='longhorn', doc='Set the name of the target '
'GitHub repository.')
required_config.add_option('github_token', parser=str, doc='Set the token of the GitHub machine user.')
required_config.add_option('zenhub_graphql_token', parser=str, doc='Set the token of the Zenhub GraphQL API')
required_config.add_option('zenhub_pipeline', parser=str, default='Review', doc='Set the target ZenHub pipeline to '
required_config.add_option('project_pipeline', parser=str, default='Review', doc='Set the target project pipeline to '
'handle events for.')


Expand Down
2 changes: 1 addition & 1 deletion github-bot/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ werkzeug==2.0.2
itsdangerous==2.0.1
Flask-HTTPAuth==4.1.0
Gunicorn==20.0.4
PyGitHub==1.51
PyGitHub==2.3.0

0 comments on commit e9d2e9f

Please sign in to comment.