Skip to content
Open
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
107 changes: 0 additions & 107 deletions .github/workflows/pr_bot.yml

This file was deleted.

35 changes: 0 additions & 35 deletions .github/workflows/pr_review_trigger.yml

This file was deleted.

136 changes: 1 addition & 135 deletions dev/archery/archery/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
# specific language governing permissions and limitations
# under the License.

import enum
import os
import shlex
from pathlib import Path
from functools import lru_cache, partial
from functools import partial
import tempfile

import click
Expand All @@ -30,10 +29,6 @@
from .crossbow import Repo, Queue, Config, Target, Job, CommentReport


def cached_property(fn):
return property(lru_cache(maxsize=1)(fn))


class EventError(Exception):
pass

Expand Down Expand Up @@ -87,135 +82,6 @@ def parse_args(self, ctx, args):
group = partial(click.group, cls=Group)


LABEL_PREFIX = "awaiting"


@enum.unique
class PullRequestState(enum.Enum):
"""State of a pull request."""

review = f"{LABEL_PREFIX} review"
committer_review = f"{LABEL_PREFIX} committer review"
changes = f"{LABEL_PREFIX} changes"
change_review = f"{LABEL_PREFIX} change review"
merge = f"{LABEL_PREFIX} merge"


COMMITTER_ROLES = {'OWNER', 'MEMBER'}


class PullRequestWorkflowBot:

def __init__(self, event_name, event_payload, token=None, committers=None):
kwargs = {}
if token is not None:
kwargs["auth"] = github.Auth.Token(token)
self.github = github.Github(**kwargs)
self.event_name = event_name
self.event_payload = event_payload
self.committers = committers

@cached_property
def pull(self):
"""
Returns a github.PullRequest object associated with the event.
"""
return self.repo.get_pull(self.event_payload['pull_request']['number'])

@cached_property
def repo(self):
return self.github.get_repo(self.event_payload['repository']['id'], lazy=True)

def is_committer(self, action):
"""
Returns whether the author of the action is a committer or not.
If the list of committer usernames is not available it will use the
author_association as a fallback mechanism.
"""
if self.committers:
return (self.event_payload[action]['user']['login'] in
self.committers)
return (self.event_payload[action]['author_association'] in
COMMITTER_ROLES)

def handle(self):
current_state = None
try:
current_state = self.get_current_state()
except EventError:
# In case of error (more than one state) we clear state labels
# only possible if a label has been manually added.
self.clear_current_state()
next_state = self.compute_next_state(current_state)
if not current_state or current_state != next_state:
if current_state:
self.clear_current_state()
self.set_state(next_state)

def get_current_state(self):
"""
Returns a PullRequestState with the current PR state label
based on label starting with LABEL_PREFIX.
If more than one label is found raises EventError.
If no label is found returns None.
"""
states = [label.name for label in self.pull.get_labels()
if label.name.startswith(LABEL_PREFIX)]
if len(states) > 1:
raise EventError(f"PR cannot be on more than one states - {states}")
elif states:
return PullRequestState(states[0])

def clear_current_state(self):
"""
Removes all existing labels starting with LABEL_PREFIX
"""
for label in self.pull.get_labels():
if label.name.startswith(LABEL_PREFIX):
self.pull.remove_from_labels(label)

def compute_next_state(self, current_state):
"""
Returns the expected next state based on the event and
the current state.
"""
if (self.event_name == "pull_request_target" and
self.event_payload['action'] == 'opened'):
if self.is_committer('pull_request'):
return PullRequestState.committer_review
else:
return PullRequestState.review
elif (self.event_name == "pull_request_review" and
self.event_payload["action"] == "submitted"):
review_state = self.event_payload["review"]["state"].lower()
if not self.is_committer('review'):
# Non-committer reviews cannot change state once committer has already
# reviewed, requested changes or approved
if current_state in (
PullRequestState.change_review,
PullRequestState.changes,
PullRequestState.merge):
return current_state
else:
return PullRequestState.committer_review
if review_state == 'approved':
return PullRequestState.merge
else:
return PullRequestState.changes
elif (self.event_name == "pull_request_target" and
self.event_payload['action'] == 'synchronize' and
current_state == PullRequestState.changes):
return PullRequestState.change_review
# Default already opened PRs to Review state.
if current_state is None:
current_state = PullRequestState.review
return current_state

def set_state(self, state):
"""Sets the State label to the PR."""
self.pull.add_to_labels(state.value)


class CommentBot:

def __init__(self, name, handler, token=None):
Expand Down
22 changes: 7 additions & 15 deletions dev/archery/archery/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,26 +866,18 @@ def integration(with_all=False, random_seed=12345, write_generated_json="",
@archery.command()
@click.option('--arrow-token', envvar=['GH_TOKEN', 'ARROW_GITHUB_TOKEN'],
help='OAuth token for responding comment in the arrow repo')
@click.option('--committers-file', '-c', type=click.File('r', encoding='utf8'))
@click.option('--event-name', '-n', required=True)
@click.option('--event-payload', '-p', type=click.File('r', encoding='utf8'),
default='-', required=True)
def trigger_bot(arrow_token, committers_file, event_name, event_payload):
from .bot import CommentBot, PullRequestWorkflowBot, actions
from ruamel.yaml import YAML
def trigger_bot(arrow_token, event_name, event_payload):
from .bot import CommentBot, actions

if 'comment' not in event_name:
raise click.UsageError(
f"Unsupported event {event_name!r}, only comment events are handled")
event_payload = json.loads(event_payload.read())
if 'comment' in event_name:
bot = CommentBot(name='github-actions', handler=actions, token=arrow_token)
bot.handle(event_name, event_payload)
else:
committers = None
if committers_file:
committers = [committer['alias']
for committer in YAML().load(committers_file)]
bot = PullRequestWorkflowBot(event_name, event_payload, token=arrow_token,
committers=committers)
bot.handle()
bot = CommentBot(name='github-actions', handler=actions, token=arrow_token)
bot.handle(event_name, event_payload)


@archery.group("linking")
Expand Down
Loading
Loading