34f5c4ef02
Without universal_newlines=True or text=True, subprocess.check_output returns bytes, not str, so it makes no sense to compare its return to "true". But upstream Git’s behavior only depends on the filename, not whether the repository is bare; emulate this more closely. Signed-off-by: Anders Kaseorg <anders@zulip.com>
103 lines
3.3 KiB
Python
Executable file
103 lines
3.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# Zulip notification post-receive hook.
|
|
#
|
|
# The "post-receive" script is run after receive-pack has accepted a pack
|
|
# and the repository has been updated. It is passed arguments in through
|
|
# stdin in the form
|
|
# <oldrev> <newrev> <refname>
|
|
# For example:
|
|
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/main
|
|
|
|
import os
|
|
import os.path
|
|
import subprocess
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
import zulip_git_config as config
|
|
|
|
VERSION = "0.9"
|
|
|
|
if config.ZULIP_API_PATH is not None:
|
|
sys.path.append(config.ZULIP_API_PATH)
|
|
|
|
import zulip
|
|
|
|
client = zulip.Client(
|
|
email=config.ZULIP_USER,
|
|
site=config.ZULIP_SITE,
|
|
api_key=config.ZULIP_API_KEY,
|
|
client="ZulipGit/" + VERSION,
|
|
)
|
|
|
|
|
|
def git_repository_name() -> str:
|
|
path, name = os.path.split(os.getcwd())
|
|
if name == ".git":
|
|
name = os.path.basename(path)
|
|
return name[: -len(".git")] if name.endswith(".git") else name
|
|
|
|
|
|
def git_commit_range(oldrev: str, newrev: str) -> str:
|
|
log_cmd = ["git", "log", "--reverse", "--pretty=%aE %H %s", f"{oldrev}..{newrev}"]
|
|
commits = ""
|
|
for ln in subprocess.check_output(log_cmd, universal_newlines=True).splitlines():
|
|
author_email, commit_id, subject = ln.split(None, 2)
|
|
if hasattr(config, "format_commit_message"):
|
|
commits += config.format_commit_message(author_email, subject, commit_id)
|
|
else:
|
|
commits += f"!avatar({author_email}) {subject}\n"
|
|
return commits
|
|
|
|
|
|
def send_bot_message(oldrev: str, newrev: str, refname: str) -> None:
|
|
repo_name = git_repository_name()
|
|
branch = refname.replace("refs/heads/", "")
|
|
destination = config.commit_notice_destination(repo_name, branch, newrev)
|
|
if destination is None:
|
|
# Don't forward the notice anywhere
|
|
return
|
|
|
|
new_head = newrev[:12]
|
|
old_head = oldrev[:12]
|
|
|
|
if (
|
|
oldrev == "0000000000000000000000000000000000000000"
|
|
or newrev == "0000000000000000000000000000000000000000"
|
|
):
|
|
# New branch pushed or old branch removed
|
|
added = ""
|
|
removed = ""
|
|
else:
|
|
added = git_commit_range(oldrev, newrev)
|
|
removed = git_commit_range(newrev, oldrev)
|
|
|
|
if oldrev == "0000000000000000000000000000000000000000":
|
|
message = f"`{new_head}` was pushed to new branch `{branch}`"
|
|
elif newrev == "0000000000000000000000000000000000000000":
|
|
message = f"branch `{branch}` was removed (was `{old_head}`)"
|
|
elif removed:
|
|
message = f"`{new_head}` was pushed to `{branch}`, **REMOVING**:\n\n{removed}"
|
|
if added:
|
|
message += "\n**and adding**:\n\n" + added
|
|
message += "\n**A HISTORY REWRITE HAS OCCURRED!**"
|
|
message += "\n@everyone: Please check your local branches to deal with this."
|
|
elif added:
|
|
message = f"`{new_head}` was deployed to `{branch}` with:\n\n{added}"
|
|
else:
|
|
message = f"`{new_head}` was pushed to `{branch}`... but nothing changed?"
|
|
|
|
message_data = {
|
|
"type": "stream",
|
|
"to": destination["stream"],
|
|
"subject": destination["subject"],
|
|
"content": message,
|
|
}
|
|
client.send_message(message_data)
|
|
|
|
|
|
for ln in sys.stdin:
|
|
oldrev, newrev, refname = ln.strip().split()
|
|
send_bot_message(oldrev, newrev, refname)
|