天天看點

python 編寫 git 服務端鈎子hook

官方文檔 https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E9%92%A9%E5%AD%90

建立倉庫

[[email protected] ysb]# git init --bare test.git
Initialized empty Git repository in /work/ysb/test.git/
[[email protected] ysb]# cd test.git/
[[email protected] test.git]# ls
branches  config  description  HEAD  hooks  info  objects  refs
[[email protected] test.git]# cd hooks/
[[email protected] hooks]# ls
applypatch-msg.sample  post-update.sample     pre-commit.sample          pre-push.sample    update.sample
commit-msg.sample      pre-applypatch.sample  prepare-commit-msg.sample  pre-rebase.sample
[[email protected] hooks]# 

           
  • 鈎子都被存儲在 Git 目錄下的 hooks 子目錄中。
  • 當用 git init 初始化一個新版本庫時,Git預設會在這個目錄中放置一些示例腳本。
  • 這些腳本除了本身可以被調用外,它們還透露了被觸發時所傳入的參數。
  • 所有的示例都是 shell腳本,其中一些還混雜了 Perl 代碼,
  • 不過,任何正确命名的可執行腳本都可以正常使用 —— 可以用 Ruby 或 Python,或任何你熟悉的語言編寫它們。
  • 這些示例的名字都是以 .sample 結尾,如果你想啟用它們,得先移除這個字尾。
  • 服務端的鈎子:
    • pre-receive —— 處理來自用戶端的推送操作時,最先被調用的腳本是 pre-receive。 它從标準輸入擷取一系列被推送的引用。如果它以非零值退出,所有的推送内容都不會被接受。 你可以用這個鈎子阻止對引用進行非快進(non-fast-forward)的更新,或者對該推送所修改的所有引用和檔案進行通路控制。
    • post-receive —— 挂鈎在整個過程完結以後運作,可以用來更新其他系統服務或者通知使用者。 它接受與 pre-receive 相同的标準輸入資料。 它的用途包括給某個郵件清單發信,通知持續內建(continous integration)的伺服器, 或者更新問題追蹤系統(ticket-tracking system) —— 甚至可以通過分析送出資訊來決定某個問題(ticket)是否應該被開啟,修改或者關閉。 該腳本無法終止推送程序,不過用戶端在它結束運作之前将保持連接配接狀态, 是以如果你想做其他操作需謹慎使用它,因為它将耗費你很長的一段時間。
    • update —— update 腳本和 pre-receive 腳本十分類似,不同之處在于它會為每一個準備更新的分支各運作一次。 假如推送者同時向多個分支推送内容,pre-receive 隻運作一次,相比之下 update 則會為每一個被推送的分支各運作一次。 它不會從标準輸入讀取内容,而是接受三個參數:引用的名字(分支),推送前的引用指向的内容的 SHA-1 值,以及使用者準備推送的内容的 SHA-1 值。 如果 update 腳本以非零值退出,隻有相應的那一個引用會被拒絕;其餘的依然會被更新。

實作需求

注意: 建立pre-receive、post-receive檔案時, 要賦予執行權限

[[email protected] hooks]# ll
-rw-r--r-- 1 root root    0 Mar 25 20:13 pre-receive
[[email protected] hooks]# chmod a+x pre-receive 
[[email protected] hooks]# ll
-rwxr-xr-x 1 root root    0 Mar 25 20:13 pre-receive
           

保護master分支,設定push權限

# 檔案:pre-receive
#!/usr/bin/python3

import sys
from subprocess import check_output

protect_branch = ['master']
protect_committer = ['username']

line = sys.stdin.readline()

argv = line.split(' ')

oldrev = argv[0].strip()
newrev = argv[1].strip()
refname = argv[2].split('/')[-1].strip()

if len(set(list(oldrev))) == 1:
    print('新分支')
    exit(0)

missed_revs = check_output(['git', 'rev-list', f'{oldrev}..{newrev}']).decode().strip().split('\n')

for missed in missed_revs:
    message = check_output(['git', 'cat-file', 'commit', missed]).decode().strip().split('\n')
    committer = message[3].split(' ')[1].strip()
    commit_message = message[5]
    # 分支保護
    if refname in protect_branch:
        # 送出使用者
        if committer in protect_committer:
            exit(0)
        else:
            print(f'您的賬号(committer)沒有權限push到{refname}分支')
            exit(1)
    else:
        exit(0)
           

push之後企業微信機器人通知

# post-receive
#!/usr/bin/python3

import sys
import requests
import json
from subprocess import check_output

line = sys.stdin.readline()

argv = line.split(' ')

oldrev = argv[0].strip()
newrev = argv[1].strip()
refname = argv[2].split('/')[-1].strip()

uri = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='
key = '企業微信機器人key'


def wx_robot(content, key):
    """
    微信機器人
    :param content:
    :param key:
    :return:
    """
    url = uri + key
    data = {
        "msgtype": "text",
        "text": {
            "content": content,
            "mentioned_list": [],
            "mentioned_mobile_list": []
        }
    }
    requests.post(url, data=json.dumps(data))


if len(set(list(oldrev))) == 1:
    wx_robot('建立了新分支', key=key)
else:
    missed_revs = check_output(['git', 'rev-list', f'{oldrev}..{newrev}']).decode().strip().split('\n')
    content = ''
    for missed in missed_revs:
        message = check_output(['git', 'cat-file', 'commit', missed]).decode()
        content += f'{message}\n'
    wx_robot(content, key=key)

           

如果在用戶端push時報錯 No such file or directory,可能時windows檔案在linux上不能執行的原因

解決方法:remote: error: cannot run hooks/post-receive: No such file or directory