簡化工作流之程式碼審查回覆訊息生成(Python實現)

freephp發表於2021-01-31

在一個規範化的研發流程中,一般遵循如下流程:

  1. 開發階段:研發功能或者修復bug,在本地自測。
  2. 程式碼稽核階段:提交程式碼,並請求團隊內人員做code review。
  3. 測試環境測試階段:部署到測試環境並請求測試。
  4. 釋出線上待測階段:測試環境通過測試釋出到線上進行測試。
  5. 驗收完成任務:線上驗證成功,關閉這個任務。

實際上這只是一種最理想化的過程,因為我們預設每次狀態流轉都是順利的,開發沒有毛病,測試一次就通過,現實中的研發流程的情況更復雜,如圖1所示。
圖1 研發流程圖
圖1

整個過程一氣呵成,環環相扣。而其中可以被自動化的正好是第二步:請求他人進行code review的時候的反饋訊息。

根據實踐的經驗,比較好的內容格式如下(包含Markdown格式,因為跟蹤任務的系統支援這種格式):

**Changes  has been committed to feature/xxx-xxx**

- https://git.xxx.com/xxxx/ddaf18f9be4613c31363d4c92b8bafc3sdfdsf

**Details**
Remove invalid logic for admin pannel

由於每次走到Code Review的步驟的時候都需要寫類似的回覆在任務管理系統中,所以考慮使用Python指令碼去自動生成這段文字,簡化工作。

根據樣例回覆進行分析,需要獲取專案的分支名(任務目標分支),專案最後一次提交的commit id去組裝第二行的git commit的連結,然後Details的內容可以從git log中的提交資訊裡面提取。
第一步:獲取分支名稱。

為了簡化過程,預設專案的當前分支就是我們需要的分支,那麼問題簡化為獲取當前分支名。可以利用git的相關命令實現,如下:

git branch | sed -n '/\* /s///p'

第二步:獲取commit id。
而獲取commit id也非常簡單,只需要如下命令:

git rev-parse HEAD

第三步:獲取提交資訊。
還需要獲取提交資訊,利用git log的命令進行過濾也能得到:

git log --pretty=format:"%s" -1

git log --pretty=format命令很強大,除了獲得提交資訊外,還有如下引數可以使用。

%H 提交物件(commit)的完整雜湊字串 
%h 提交物件的簡短雜湊字串 
%T 樹物件(tree)的完整雜湊字串 
%t 樹物件的簡短雜湊字串 
%P 父物件(parent)的完整雜湊字串 
%p 父物件的簡短雜湊字串 
%an 作者(author)的名字 
%ae 作者的電子郵件地址 
%ad 作者修訂日期(可以用 -date= 選項定製格式) 
%ar 作者修訂日期,按多久以前的方式顯示 
%cn 提交者(committer)的名字 
%ce 提交者的電子郵件地址 
%cd 提交日期 
%cr 提交日期,按多久以前的方式顯示 
%s 提交說明

所以第二步也可以使用git log命令實現,如下所示:

git log --pretty=format:"%H" -1

當然還需要在後面加一點人性化的感謝的話,畢竟是麻煩其他人來對你程式碼進行稽核,說一些感謝的話吧,這裡我就用一個list來裝一些感謝的話,然後隨機獲取一段貼到最後。
如果是以程式導向的方式去編寫,那麼可以編寫如下程式碼:

#coding=utf-8
#!/usr/bin/python

import os, subprocess
import random

# use subprocess to get the current branch name from output
def get_branch_name(cd_path):
    os.chdir(cd_path)
    status, branch_name = subprocess.getstatusoutput("git branch | sed -n '/\* /s///p'")
    # print(output)
    # exit(0)
    return branch_name

def get_latest_git_log(cd_path):
    """
    docstring
    """
    os.chdir(cd_path)
    status, log_info = subprocess.getstatusoutput("git log --pretty=format:\"%s\" -1")
    return log_info

def get_latest_commit_id(cd_path):
    os.chdir(cd_path)
    status, commit_id = subprocess.getstatusoutput("git rev-parse HEAD")
    return commit_id

def get_reviewer_by_random(reviewers):
    return random.choice(reviewers)

def get_thanks_words_by_random(thanks_words):
    return random.choice(thanks_words)

def create_comment(reviewers, branch_name, log_info, commit_id, thanks_words):
    print(get_reviewer_by_random(reviewers))
    print("*Changes made has been committed to " + branch_name + "*")
    print("- https://git.xxxxx.com/someproject/subname/-/commit/" + commit_id)
    print("*Details*")
    print("-" + log_info)
    print(get_thanks_words_by_random(thanks_words))

branch_name = get_branch_name('/Users/tony/www/autoWork')
log_info = get_latest_git_log('/Users/tony/www/autoWork')
commit_id = get_latest_commit_id('/Users/tony/www/autoWork')

reviewers = [
    '[~Harry]',
    '[~Tom]'
]

random_thanks_words = [
    'Review it please, thanks.',
    'Actually, I am glad to see you have time to review it, thanks a lot.',
    'Please check it if you have free time, thanks.',
    'Check it please.'
    'Waiting for your code review, thank you.'
]

create_comment(reviewers, branch_name, log_info, commit_id, random_thanks_words)

由於Python指令碼和專案沒有放在一個目錄下面,所以每次在執行git相關命令之前都需要先cd到目標專案目錄下。而分別執行git命令的時候使用subprocess.getstatusoutput()來執行,方便獲取標準化輸出的結果。這裡之所以不使用os.system來執行命令,是因為os.system執行命令的返回值裡面包括兩個部分,第一部分是命令的結果輸出,第二部分是結果是否成功的識別符號。

例如執行os.system("git branch | sed -n '/* /s///p'")會返回如下內容:

feature/ST-247
0

第一行是我們獲取到的分支名,第二行是成功的識別符號,0表示命令沒有任何問題。

所以我考慮使用subprocess.getstatusoutput來執行命令,這個函式會分別返回結果標識和輸出,方便得到想要的執行輸出結果。

雖然程式碼還可以進一步優化,但是已經能滿足我的需求了,執行這個指令碼就能得到如下的輸出結果:

[~Harry]
*Changes made has been committed to feature/ST-247*
- https://git.xxxxx.com/someproject/subname/-/commit/d21033057677e6d49d9cea07c64c49e35529545dx
*Details*
- Remove some invalid logic
Please check it if you have free time, thanks.

如果改寫成物件導向的方式會更好,呼叫更簡單,傳遞引數也更少,採用Python3語法編寫的程式碼如下所示:

#coding=utf-8
#!/usr/bin/python
import os
import subprocess
import random

class CommitComment:
    def __init__(self, project_path: str, reviewers: list, thanks_words: list):
        self.project_path = project_path
        self.reviewers = reviewers
        self.thanks_words = thanks_words
    # use subprocess to get the current branch name from output
    def get_branch_name(self) -> str:
        os.chdir(self.project_path)
        status, branch_name = subprocess.getstatusoutput("git branch | sed -n '/\* /s///p'")
        return branch_name
    # use subprocess to get the latest commit message from git log 
    def get_latest_git_log(self) -> str:
        os.chdir(self.project_path)
        status, log_info = subprocess.getstatusoutput("git log --pretty=format:\"%s\" -1")
        return log_info

    # use subprocess to get the latest commit id from git log
    def get_latest_commit_id(self) -> str:
        os.chdir(self.project_path)
        status, commit_id = subprocess.getstatusoutput("git rev-parse HEAD")
        return commit_id

    def get_reviewer_by_random(self) -> str:
        return random.choice(self.reviewers)

    def get_thanks_words_by_random(self) -> str:
        return random.choice(self.thanks_words)

    def create_comment(self):
        print(self.get_reviewer_by_random())
        print("*Changes  has been committed to " + self.get_branch_name() + "*")
        print("- https://git.xxxx.com/MyProject/ProjectName/-/commit/" + self.get_latest_commit_id())
        print("*Details*")
        print("-" + self.get_latest_git_log())
        print(self.get_thanks_words_by_random())


thanks_words = [
        'Review it please, thanks.',
        'Actually, I am glad to see you have time to review it, thanks a lot.',
        'Please check it if you have free time, thanks.',
        'Check it please.'
        'Waiting for your code review, thank you.'
    ]
reviewers = [
'[~Harry]',
'[~Tom]'
]

comment = CommitComment('/Users/tony/www/autoWork', reviewers, thanks_words)

comment.create_comment() # will print out the complete comment

thanks_words列表可以在增加多一點,這樣隨機獲取之下重複的概率會更少。當然最後一段也可以自己每次diy,畢竟感謝要發自內心的最好。

這種簡化工作流的指令碼本質是減少重複性勞動,特別是一天完成了很多個任務的時候。但是反思本身是無法被簡化的,不做工作的奴隸,而是工作的主人。
拋磚引玉,希望對自己和未來的自己也是一個還原映象。
Todo:
1.可以每天定時執行這個指令碼去生成回覆訊息。
2.通過指令碼傳參來動態選擇需要被處理的專案目錄。在這個案例程式碼中是hard code的,預設是選擇了autoWork這個專案。
3.還可以考慮接入語料庫(thanks words),這樣感謝的話永不重複,還能學點新單詞。:)

相關文章