Skip to content
Closed
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
5 changes: 3 additions & 2 deletions .github/workflows/zulip_emoji_labelling.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
PR_NUMBER: ${{ github.event.number}}
LABEL_STATUS: ${{ github.event.action }}
LABEL_NAME: ${{ github.event.label.name }}
PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
run: |
printf $'Running the python script with pr "%s"\n' "$PR_NUMBER" "$LABEL_STATUS" "$LABEL"
python scripts/zulip_emoji_reactions.py "$ZULIP_API_KEY" "$ZULIP_EMAIL" "$ZULIP_SITE" "$LABEL_STATUS" "$LABEL_NAME" "$PR_NUMBER"
printf $'Running the python script with:\nPR number: "%s"\nlabel status: "%s"\nlabel: "%s"\nPR labels: "%s"\n' "$PR_NUMBER" "$LABEL_STATUS" "$LABEL" "$PR_LABELS"
python scripts/zulip_emoji_reactions.py "$ZULIP_API_KEY" "$ZULIP_EMAIL" "$ZULIP_SITE" "$LABEL_STATUS" "$LABEL_NAME" "$PR_NUMBER" "$PR_LABELS"
60 changes: 44 additions & 16 deletions scripts/zulip_emoji_reactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
import sys
import zulip
import re
import json

# Usage:
# python scripts/zulip_emoji_reactions.py $ZULIP_API_KEY $ZULIP_EMAIL $ZULIP_SITE $ACTION $LABEL_NAME $PR_NUMBER
# python scripts/zulip_emoji_reactions.py $ZULIP_API_KEY $ZULIP_EMAIL $ZULIP_SITE $ACTION $LABEL_NAME $PR_NUMBER $LABELS_TO_KEEP
# The first three variables identify the lean4 Zulip chat and allow the bot to access it
# (see .github/workflows/zulip_emoji_merge_delegate.yaml),
# see the comment below for a description of $ACTION and $LABEL_NAME.
# $LABELS_TO_KEEP is optional, but if present, should be a JSON array of GitHub PR label names
# Emoji reactions that correspond to these labels will not be removed
# (see .github/workflows/zulip_emoji_labelling.yaml)

ZULIP_API_KEY = sys.argv[1]
ZULIP_EMAIL = sys.argv[2]
Expand All @@ -32,6 +36,17 @@
print(f"ACTION: '{ACTION}'")
print(f"LABEL_NAME: '{LABEL_NAME}'")
print(f"PR_NUMBER: '{PR_NUMBER}'")
try:
LABELS_TO_KEEP = sys.argv[7]
print(f"Attempting to parse LABELS_TO_KEEP: '{LABELS_TO_KEEP}")
LABELS_TO_KEEP = json.loads(LABELS_TO_KEEP)
assert isinstance(LABELS_TO_KEEP, list)
assert '' not in LABELS_TO_KEEP
except:
print(f"parsing LABELS_TO_KEEP failed; setting to empty list")
# an empty list is a good default since we remove reactions if the label is `not in LABELS_TO_KEEP`
LABELS_TO_KEEP = []
print(f"LABELS_TO_KEEP: '{LABELS_TO_KEEP}'")

# Initialize Zulip client
client = zulip.Client(
Expand Down Expand Up @@ -101,14 +116,22 @@ def has_reaction(name: str) -> bool:
if match:
print(f"matched: '{message}'")

def remove_reaction(name: str, emoji_name: str, **kwargs) -> None:
print(f'Removing {name}')
result = client.remove_reaction({
"message_id": message['id'],
"emoji_name": emoji_name,
**kwargs
})
print(f"result: '{result}'")
# name: a description of the emoji to be removed
# emoji_name: the emoji name as used on Zulip
# label_name: the name of the corresponding PR label, if present in LABELS_TO_KEEP, we do not remove the reaction
# if there is no corresponding label (e.g. for the "closed-pr" reaction) then this should be an empty string ""
# additional arguments will be passed to the zulip library remove_reaction function
def remove_reaction(name: str, emoji_name: str, label_name: str, **kwargs) -> None:
if label_name not in LABELS_TO_KEEP:
print(f'Removing {name}')
result = client.remove_reaction({
"message_id": message['id'],
"emoji_name": emoji_name,
**kwargs
})
print(f"result: '{result}'")
else:
print(f'The "{label_name}" label is present, so we will not remove the "{name}" reaction.')
def add_reaction(name: str, emoji_name: str) -> None:
print(f'adding {name} emoji')
client.add_reaction({
Expand All @@ -122,25 +145,30 @@ def add_reaction(name: str, emoji_name: str) -> None:
if ACTION == "labeled":
add_reaction('maintainer-merge', 'hammer')
elif ACTION == "unlabeled":
remove_reaction('maintainer-merge', 'hammer')
remove_reaction('maintainer-merge', 'hammer', 'maintainer-merge')
continue

# We should never remove any "this PR was migrated from a fork" reaction.

# Otherwise, remove all previous mutually exclusive emoji reactions.
# Otherwise, remove all previous mutually exclusive emoji reactions (unless
# LABELS_TO_KEEP contains the corresponding label). This is because otherwise this script
# may end up removing reactions that are still relevant. See PR #27570
# Note that the 'merge' and 'closed-pr' reactions do not have a corresponding label,
# so we pass the empty string (which will never be in LABELS_TO_KEEP) so that they are
# always removed.
# If the emoji is a custom emoji, add the fields `emoji_code` and `reaction_type` as well.
print("Removing previous reactions, if present.")
if has_peace_sign:
remove_reaction('delegated', 'peace_sign')
remove_reaction('delegated', 'peace_sign', 'delegated')
if has_bors:
remove_reaction("bors", "bors", emoji_code="22134", reaction_type="realm_emoji")
remove_reaction("bors", "bors", "ready-to-merge", emoji_code="22134", reaction_type="realm_emoji")
if has_merge:
remove_reaction('merge', 'merge')
remove_reaction('merge', 'merge', "")
if has_awaiting_author:
remove_reaction('awaiting-author', 'writing')
remove_reaction('awaiting-author', 'writing', 'awaiting-author')
if has_closed:
# 61282 was the earlier version of the emoji.
remove_reaction('closed-pr', 'closed-pr', emoji_code="61293", reaction_type="realm_emoji")
remove_reaction('closed-pr', 'closed-pr', '', emoji_code="61293", reaction_type="realm_emoji")

# Apply the appropriate emoji reaction.
print("Applying reactions, as appropriate.")
Expand Down