最近在幫忙處理 Team 的雜事,寫好 Script 請 project 的 Infra Team 幫忙在 Jenkins 上 trigger 任務
我選擇用 Python 去寫,最後會需要跑一些跟 Git & Gerrit 有關的命令,在寫的時候查一下資料改一改也還好,但碰到一個卡有點久的問題是,我要如何取得 Push 到 Gerrit 上的 change link,單純取 subprocess
的 stdout 無法達成這件事
Gerrit
Gerrit 就是一個 Git server,跟 GitHub 是差不多的,只是工具跟介面的差異,比較顯為人知的是他是 Google 放 Android repo 主要採用的 server,適合管理比較大型的專案,詳情可以參考官網
Subprocess
subprocess 如其名會開啟一個新的 process 來執行其他命令,使用 run()
就能夠執行(included in python 3.5)
想要檢查命令是不是成功,也有 check_call
能用(python 2.7),等於是 subprocess.run(..., check=True)
需要注意的是 python2 & python3 能用的 API 稍微有些不一樣,最好是能 cover python2 也能使用的,如果確定只會執行在 python3 的環境,就可以盡量用高階一點的 API
Communicate
在使用 subprocess
時,如果需要跟 Process 有互動,也就是需要知道他的 output 或是需要輸入 input,可以用 communicate()
與 Popen()
這兩個較為低階的操作才有辦法取得
communicate() returns a tuple (stdout_data, stderr_data). The data will be strings if streams were opened in text mode; otherwise, bytes.
1 | def execute_command(command): |
執行成功的話 process 的 returncode 通常會是 0,可以藉由這個來判斷,也可以用 communicate()
回傳的 stderr
來看是什麼錯誤
如果只是很單純的想檢查這個指令有沒有輸出可以用 subprocess.check_output
,但通常遇到的實際情況都比較複雜一點
這裡我的 stderr
指給的是 STDOUT 而不是 PIPE 的原因是如果是處理類似 npm installing 的指令,會有一些 packages 安裝失敗,就會出現在 stderr stream 中,但在這個指令中的錯誤只是一個安裝的過程並不代表真的失敗
Universal Newline
Push change 到 Gerrit,執行過程中的輸出如下
1 | ... |
如果把 stdout 印出來,不會看到以上的結果,後來我先試了 process.stdout.readline()
跑迴圈一行一行把 output 讀出來的方式才有辦法得到,在這過程中還需要 decode(),也就是我們得到的 stdout 是 byte 形式,也許是因為這樣才沒辦法抓到想要的輸出,所以另外找到了更簡潔的方法
在 Popen 加入 universal_newlines=True
,會把 stdout, stderr 都轉成 string 的形式,
1 | process = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) |
Search URL
接著可以使用 regular expression 來檢查得到的每一行中有沒有我們要的 change url
如果 re.search
有找到的話,回傳 reg.group()
,就是回傳對應的 reg line
1 | import re |
我是回傳空字串來代表沒有找到對應的 url,條件怎麼寫看個人