JB的測試之旅-利用jenkins達到提tag自動打包

最近都好忙,需求太多了,幹不完,加上最近做息時間的調整,都沒時間寫博客了,趁着這兩天稍微有點時間,想擼一發; java

image.png-6.6kB

其實在寫的博客有好幾篇,但都是陸陸續續的,原本想繼續寫以前的,忽然想起半個月前,同窗提出,能不能在開發提tag時自動打包python

以前發佈過一篇項目流程規範,目前的項目都在遵照這個規範,而研發每次有改動過代碼的時候,都會提測,而提測就至關於提tag,這個tag的概念不說了,就是基於當前產品分支從新拉的分支,tag的格式以下圖; git

image.png-26.2kB

而測試拿到這個tag後,就須要去到jenkins打包,要輸入一堆參數,而後等待打包結果; github

image.png-73kB

有時候,研發勤奮點,一天提屢次tag,測試就須要屢次去打包,測試同窗以爲太麻煩了,所以就提出一個想法,能不能在開發提tag時自動打包shell

能,必須能,沒有作不到的事情; json

image.png-99.3kB

分解

作事以前,要先想一想要作什麼?api

  • 搭建jenkins環境及安裝對應插件
  • jenkins打通到gitlab
  • 觸發任務

想了下,大體就這3步,那就來一塊兒作吧;安全

搭建jenkins

爲了測試,jb就用了本身的阿里雲服務器從頭開始搭建一遍,具體搭建過程不說明,以前寫的一篇文章有說起到,直接對照操做就好;bash

安裝jenkins服務器

步驟 說明
安裝Java sudo yum install java
安裝jenkins yum install jenkins
啓動jenkins service jenkins start
安裝插件

注意

修改jenkins端口 jenkins默認端口是8080,可能會跟別的軟件衝突,所以建議修改下端口;

進入jenkins配置文件

vi /etc/sysconfig/jenkins
複製代碼

打開後,找到JENKINS_USERJENKINS_PORT這兩項進行修改便可;

image.png-55.7kB

修改爲root跟具體端口保存退出便可;

這時候,直接輸入主機IP+剛設置的端口就好啦;

阿里雲開放端口權限 上面訪問ip+端口,有可能打不開連接,由於阿里雲對端口作了限制,所以須要開放端口;

登陸阿里雲,首頁右上找到控制檯;

image.png-52.4kB

找到雲服務器ECS

image.png-24.9kB

點擊實例,找到該實例的安裝組配置

image.png-103.5kB

點擊規則說明,新增便可;

image.png-14kB

若是是按照上面的文檔來,安裝完插件,建立完用戶後,就應該進入到首頁,會顯示以下內容,至此,jenkins環境搭建就完啦~!

image.png-11.1kB

對了,jenkins的目錄在這裏:

/var/lib/jenkins
複製代碼

jenkins打通到gitlab

公司的代碼倉庫是用gitlab,因某種緣由,就開放公網啦,那jb就在gitlab新建一個jbtest項目;

image.png-51.5kB

若是有小夥伴說,沒有gitlab怎麼辦?不要緊,能夠用github代替,至於github相關的配置,能夠查看該文章:git介紹及GitHub配置教程

jenkins安裝gitlab

既然肯定是要用jenkins+gitlab,那就先在jenkins上安裝下gitlab插件吧;

點擊jenkins首頁,有個系統管理按鈕;

image.png-48kB

點擊後右側會顯示內容,下滑,點擊插件管理;

image.png-144.7kB

進入到插件管理界面,就看到updates等4個欄目,那點擊Available,右側輸入gitlab,而後找到GitLabGitLab Hook,勾選,點擊底部的install便可;

image.png-42.4kB
image.png-140.1kB

test項目有了,gitlab的插件也安裝了,那咱們就在jenkins新建一個job吧,輸入了項目名稱,就進入到設置項;

image.png-55.9kB

丟棄舊的構建

這裏的丟棄舊的構建,可選,可是習慣選擇7天,,最大保留300個; 緣由是,jenkins每次構建都會生成一個歷史構建記錄及對應的產物,若是公司有100個產品,天天自動打包10次,一天就有1000個產物,服務器磁盤空間是個問題,所以設置個7天,設置個最大數,定時刪除便可;

  • 持構建的天數:根據你所填寫的天數來保存構建記錄
  • 保持構建的最大個數:有幾條構建記錄就保存幾條
  • 發佈包保留天數:發佈的產物保存的天數
  • 發佈包最大保留#個構建:發佈了多少個產物就保存多少個

上面的丟棄舊構建不是要點,繼續;

繼續下滑,會發現Source Code Management,中文是源碼管理,而咱們的代碼是存放到gitlab的,所以就選擇git了,點擊後以下圖展開;

image.png-76.5kB

這裏有小夥伴可能有疑問,既然是放gitlab,爲啥不是選擇gitlab,而是選擇git?

git、github、gitlab

這裏就花點時間說明下git、github、gitlab;

git是一款免費的、開源的分佈式版本管理控制系統(工具);

gitHub是一個面向開源及私有軟件項目的託管平臺,
只支持git做爲惟一的版本庫格式進行託管;

gitlab,是一款基於Git的項目管理軟件,擁有GitHub擁有的一切,但它還具有讓團隊對它們的repositories進行控制的功能;  
複製代碼

但願通過這麼一說,能讓你稍微清楚下;

源碼管理

點擊git,把項目的地址copy到這裏;

image.png-21.6kB
image.png-47.4kB

沒權限是確定的,那咱們就點擊add建立帳戶唄,選擇jenkins;

image.png-37.9kB

輸入gitlab/github的帳號密碼,可是輸入完後發現仍是報錯;

image.png-31.1kB

彆着急,那去服務器上看看,git安裝了嗎?

image.png-13.7kB

沒安裝,那就安裝羅;

yum install git
複製代碼

等待安裝完,再輸入git --versionwhere is git來看版本及安裝位置;

image.png-28.1kB

安裝完後,就順便試試能不能clone項目吧;

image.png-54.8kB

不出所料,果真不行,由於還要配置密鑰啊;

$ ssh-keygen -t rsa -C "your_email@youremail.com"
//注意,雙引號裏面是你的郵箱。填你註冊github的郵箱就好了。按enter執行。
複製代碼

一路回車就行,密碼通常不用設置~

image.png-95.7kB

上圖紅框裏的就是地址,cd過去;

id_rsaid_rsa.pub兩個文件,這兩個就是SSH Key的祕鑰對, id_rsa是私鑰,不能泄露, id_rsa.pub是公鑰,能夠公開;

配置ssh 在git bash 執行 cat id_rsa.pub,把輸出的內容copy出來,而後打開gitlab/github網站,點擊右上角本身的圖標,點擊Setting->ssh key 頁面,點擊add ssh key~

image.png-66.5kB

這裏還有一個特殊的問題: 因開放公網的緣由,每次git clone的下載代碼的時候是鏈接的https://而不是git@git (ssh)的形式,致使每次pull/push等遠程操做的時候,都須要輸入帳號密碼,很是麻煩;

解決辦法:

在項目根目錄輸入:
git config --global credential.helper store
git pull/git push(第一次要再輸入一次,之後就不用)
複製代碼

會在用戶目錄下生成文件.git-credential記錄用戶名密碼的信息;

那回到jenkins,打開測試job的設置,再看是否正常;

image.png-46.1kB

發現已經好了,沒問題,固然也會有小夥伴依然有問題,那就須要這樣作;

  • 打開jenkins首頁
  • 點擊系統管理
  • 全局工具設置
  • 修改git地址

image.png-73.4kB

打開後,滑動到git欄,若是有問題的話,可能會看到錯誤提示:

There's no such executable git in PATH: /sbin, /usr/sbin, /bin, /usr/bin.
複製代碼

image.png-69kB

在出錯的地方填入:

"whereis git"的地址 + "/bin/git" 
(如上面"whereis git"的地址爲"/usr/bin/git",則應該填入 "/usr/bin/git/bin/git") 並保存
複製代碼

而後再回到job的源碼管理中添加git地址,就會好了;

image.png-32kB

既然能夠鏈接到gitlab了,那咱們就直接下滑到build,選擇add build step,由於是服務器,因此直接選擇執行shell,若是是Windows系統,也能夠選擇執行Windows批處理命令

image.png-18.1kB

因jb的項目裏面有個test.py文件,那項目clone下來確定是想執行這個文件啦,所以內容就是輸入python test.py,而後點擊保存;

回到job的首頁,點擊當即構建;

test.py的做用是向釘釘發一條消息,所以成功的話,釘釘就會收到一條信息;

image.png-138.4kB

到這裏,說明jenkins是對接到gitlab是通的了;

觸發任務

看看標題,目的是:提tag自動打包

那先看看,tag是怎麼生成的? 具體看git命令行第11章,這裏有說明;

這裏就不bb了,直接貼:

git tag -a '3.4.0/1811111212' -m 'jbtest tag'
 git push --tags
複製代碼
  • 建立一個含附註類型的標籤很是簡單,用-a指定標籤名字便可;
  • -m 選項則指定了對應的標籤說明;
  • git push --tags就是一次推送全部本地新增的標籤;

而後再輸入git tag查看當前tag信息;

image.png-15.2kB

既然知道tag是怎麼生成的,那就看看有沒有辦法知道tag被建立了?

第一時間固然是想到鉤子-hook啦;

image.png-7.7kB

來看下官方解釋:

鉤子(hooks)是一些在"$GIT-DIR/hooks"目錄的腳本, 在被特定的事件(certain points)觸發後被調用。
當"git init"命令被調用後, 一些很是有用的示例鉤子文件(hooks)被拷到新倉庫的hooks目錄中; 可是在默認狀況下這些鉤子(hooks)是不生效的。 把這些鉤子文件(hooks)的".sample"文件名後綴去掉就可使它們生效了。
複製代碼

簡單地來講有點相似回調,就是特定事情完成後回調執行事件

那這鉤子,在jenkinsgitlab上怎麼搞?gitlab支持嗎?

gitlab鉤子

打開gitlab對應的項目,點擊settings-Integrations(集成)

image.png-34.3kB

打開後,就是hook的內容啦,簡單瞄下,發現有個選項叫Tag push events,看了下描述,好像蠻符合咱們的要求,界面顯示,須要輸入URLSecret Token,這兩玩意怎麼來?

image.png-84.1kB

jenkins獲取URL跟Secret Token

打開jenkins,打開對應的job,點擊設置,查找Build when a change is pushed to GitLab,gitlab上的URL就是該處的連接,以下圖紅框,複製過去便可;

同時點擊右下的Advanced(高級)按鈕;

image.png-107.6kB

而後點擊右下的Generate按鈕;

image.png-89kB

而後會生成一串Secret token,複製到gitlab那;

image.png-11.9kB

那咱們再試試,直接在服務器打個tag且push看釘釘會不會收到信息?

image.png-123.6kB

嘖嘖嘖,沒問題啊,搞定啦,另外看了下hook,支持的內容蠻多的,tag pushpush eventmerge requests等;

image.png-39.5kB

gitlab沒有setting權限

上面說的條條是道,但實際現實是殘忍的,打開實際的項目,會發現本身沒有setting權限:

image.png-34.2kB

遇到這種狀況,不是說不能處理,只能看門檻變高了,有2個方法:

嘗試跟對應的負責人溝通,看可否給一個帶master權限的帳號;

若是有足夠的數據讓老大承認這個功能,就會給一個帶master權限的帳號,就能夠隨心所欲啦;

但若是給的理由不讓人信服或者老大以爲沒卵用,但依然要作這個功能,那隻能換種方式了,但至少要求依然有倉庫的可讀權限; 換個角度,連倉庫權限都沒有,就別想作了。。

image.png-30.5kB

大體的思路就是,利用定時功能,不斷檢測該項目的tag,當發現原tag不是最新tag時,就認爲有最新tag,此時再通知jenkins處理;

若是是這樣,那jenkins上的選項會有點不一樣,構建的時候就要選擇腳本調用,指定TOKEN_NAME,而後能夠經過鏈接JENKINS_URL/job/JOBNAME/build?token=TOKEN_NAME來啓動build

image.png-87.1kB

對了,作這個功能,還有兩種形式:

  • 檢測腳本放到產品項目根目錄下;
  • 檢測腳本單獨一個項目;

第一種方式的好處時,省事,可是通常不容許,由於產品項目通常存放跟該產品相關的內容,而這個腳本典型是通用的,後面若是多個產品想複用就麻煩了

第二種方式的就是獨立項目,可複用,把檢測項目跟產品項目放到同一臺jenkins機器上,這樣它們都在jenkins/workspace目錄下,經過傳參的方式切換不一樣目錄便可;

而jb採用的是第二種方式;

檢測腳本

這個腳本的目的是什麼? 就是定時檢測tag是否是最新的,若是不是,則執行相應操做;

那難度就在於,怎麼判斷tag是否是最新的? Google下,有例子:

git describe --tags `git rev-list --tags --max-count=1`
複製代碼

嘗試下,好像沒問題,那就暫且相信吧;

image.png-21.8kB

好奇問下,這命令究竟是啥意思?

其實這是兩條git命令;

git describe --tags
git rev-list --tags --max-count=1
複製代碼

git describe --tags 顯示當前離當前提交最近的tag,實際發現,帶不帶參數都同樣?

image.png-12.8kB

不加任何參數的狀況下,git describe 只會列出帶有註釋的tag

實際,該命令是能夠帶3個參數的,tagsalwaysdirty,這通常用的比較少,不展開說明;

git rev-list --tags --max-count=1

git-rev-list :按反向時間順序列出提交對象 意思就是把倒序方式展現最新提交內容;

image.png-13.6kB

max-count=1,也很好理解,只獲取1個,若是寫2,則獲取2個;

image.png-20.4kB

那命令實際就變成了這樣:

git describe 9c7246b2026cec052af6a3b6297995e494c7b398
複製代碼

image.png-9.4kB

最終輸出的就是tag,有種奇淫怪招的感受;

image.png-6.9kB

固然,不會用git的話,也有另一種方式達到獲取最終一個tag的效果:

#獲取tag信息,轉list
    content = os.popen("git tag" ).read().split("\n")
    for i in content:
        if ( i != ''):
            tag_list.append(i)

    print(tag_list[-1])
複製代碼

就是輸入git tag,獲取全部tag信息,再從新存在一個list,只拿最後一個,也能夠再作下判斷,只保存最後一條,方式不少,相似,但這種效果很差是,假如一個項目通過多輪迭代,有幾千條tag,就須要時間了,所以仍是推薦第一種;

爲了模擬真實場景,就在跟目錄下新建一個腳本,跟項目同一根目錄;

image.png-12.3kB

腳本的內容以下:

# _*_ coding:utf-8 _*_
import re
import os
import sys
import json
import time
import requests
from apscheduler.schedulers.blocking import BlockingScheduler

tag = ""
scheduler = BlockingScheduler()
base_dir = os.getcwd()

def postDingDing(content):
    dingdingurl = ["https://oapi.dingtalk.com/robot/send?access_token=你的釘釘token"]
    headers = {'Content-Type': 'application/json'}
    String_textMsg = {
        "msgtype": "markdown",
        "markdown": {
            "title" : "有新tag啦",
            "text":   "新tag: "+content
        }
    }
    requests.packages.urllib3.disable_warnings()
    String_textMsg = json.dumps(String_textMsg)
    for i in dingdingurl:
        requests.post(i, data=String_textMsg, headers=headers, verify=False)


def getProjectsName():
    if len(sys.argv) > 1:
        return sys.argv[1]
    else:
        print("沒有傳項目參數,請從新輸入,例子:python check_gittag.py jbtest")

def check_tag(dir):
    os.chdir(base_dir+"/"+dir)
    # 原型:git describe --tags 'git rev-list --tags --max-count=1'
    id = os.popen("git rev-list --tags --max-count=1").read()
    content = os.popen("git describe --tags "+ id).read()
    return content


def start_task():
    global tag
    projects_name = getProjectsName()
    last_tag = check_tag(projects_name)

    if (tag != last_tag):
        print("有新tag啦: "+last_tag)
        postDingDing(last_tag)
        tag = last_tag
    # else:
    # print("沒有新tag,繼續輪訓")

def get_time():
    """ 獲取當前時間 """
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())


if __name__ == "__main__":
    print(get_time() + " 魔法儀式已啓動!")
    scheduler.add_job(start_task, "interval", seconds=6, id="check_tag")
    scheduler.start()
複製代碼

由於是用來測試,所以jb是作了通知釘釘的功能,執行上面的代碼,會發現有一個問題:push了新tag,沒有通知!!!

後來想了下,哦,是沒有,由於本地的代碼仍是舊的,好比研發本地push到倉庫,可是檢測腳本是在服務器A,那確定是不會知道有代碼更新啦;

image.png-14kB

解決方案,立刻想到,那push後就通知啊,結果想起,沒鉤子權限。。

image.png-37.9kB

那沒問題,在上面的腳本加多一個更新命令就好啦;

#更新遠程代碼到本地倉庫
git fetch origin
複製代碼

流程通了,那怎麼對接到jenkins?

再接jenkins

看回jenkins,按照要求試試,輸入如下命令:

curl -i jenkins_url/job/job_name/build?token=yourtoken
複製代碼

image.png-61.4kB

結果出現403.。。

image.png-91.1kB

兩種解決方案:

  • 使用其餘插件代替
  • jenkins全局安全點擊下圖紅框內容,jenkins首頁-系統管理-全局安全設置;

image.png-65.5kB

jb選擇的是第二種方案,勾選後,再次運行上面的命令,沒問題,而且能夠收到啦~

image.png-27.4kB
image.png-62.4kB

再看鉤子

其實故事到上面就完啦,可是呢,雖然知道hook怎麼用,但仍是想了解更多,所以就有了這篇啦;

什麼是鉤子

Git Hooks就是在Git執行特定事件(如commit、push、receive等)後觸發運行的腳本。

按照Git Hooks腳本所在的位置能夠分爲兩類:

  • 本地Hooks,觸發事件如commit、merge等;
  • 服務端Hooks,觸發事件如receive等;

鉤子能作啥

基本上,用上鉤子都是定製化的腳本程序,在實際工做中,基本鉤子是萬能的,如:

  • post-receive:每當有新的提交的時候就通知項目成員;
  • pre-commit:檢查每次的commit message是否符合編碼規範;
  • post-merge:當merge成功後,通知項目成員;

鉤子放哪裏

$project_name/.git/hooks
複製代碼

image.png-81.1kB
.git/hooks/文件夾下。當你init一個倉庫的時候,下邊會有一些鉤子的例子,以 .sample結尾。

固然也能夠在這個目錄下自由定製hooks的功能,當觸發一些git行爲的時候,就會自動觸發執行的hooks功能;

常見鉤子

本地鉤子:

名稱 功能
pre-commit 當執行commit動做時先執行此hook,能夠用此hook作一些檢查,好比代碼風格檢查,或者先跑測試;
prepare-commit-msg 當commit時須要輸入message前會觸發此hook,能夠用此hook來定製本身的default message信息;
commit-msg 當用戶輸入commitmessage後被觸發,能夠用此hook校驗message的信息,好比是否符合規定,有沒有cr等;
post-commit 在整個提交過程完成以後會被調用,能夠用於發送new commit通知;
post-checkout git checkout命令調用,能夠用於爲本身的項目設置合適的工做區,好比自動生成文檔、移動一些大型二進制文件等,也能夠用於檢查版本庫的有效性;
pre-rebase git rebase命令調用,能夠用來拒絕全部的已經push的commits進行rebase操做;
post-merge git merge調用,在merge成功後執行,能夠用於merge後的消息通知;
post-receive 在成功推送後被調用,適合用於發送通知;
pre-receive git push調用,向倉庫推送代碼時會被執行;
update pre-receive以後被調用,分別被每一個推送上來的引用分別調用;
pre-push 當push時,remote refs被更新,可是在全部的objects傳輸前被觸發;

服務端鉤子:

名稱 功能
pre-receive 當收到push動做以前會被執行;
update 收到push動做以前被執行,可是有可能被執行屢次,每一個branch一次;
post-receive 當push動做已經完成的時候會被觸發,能夠用此hook來push notification等,好比發郵件,通知持續構建服務器等;

自定義鉤子

若是想自定義鉤子怎麼辦? 好比如今新增一個jb鉤子,用於發送common通知的;

#!/usr/bin/env python

import smtplib
from email.mime.text import MIMEText
from subprocess import check_output

# 得到新提交的git log --stat輸出
log = check_output(['git', 'log', '-1', '--stat', 'HEAD'])

# 建立一個純文本的郵件內容
msg = MIMEText("Look, I'm actually doing some work:\n\n%s" % log)

msg['Subject'] = 'Git post-commit hook notification'
msg['From'] = 'mary@example.com'
msg['To'] = 'boss@example.com'

# 發送信息
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587

session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
session.login(msg['From'], 'secretPassword')

session.sendmail(msg['From'], msg['To'], msg.as_string())
session.quit()
複製代碼

固然,記得給權限給你鉤子,chmod

小結

好啦,本章就到此接觸啦,基礎內容有點,可是很少,都是環境配置,本章主要講述如下內容:

  • 搭建jenkins
  • git配置
  • jenkins打通git
  • gitlab hook
  • 檢測腳本
  • jenkins經過url觸發任務
  • git tag命令部分講解

大體是這樣,還有些細節,差很少就的啦;

好啦,若是有什麼疑問,歡迎一塊兒溝通;

最後,謝謝你們~

1-140R3154U8.jpg-9kB
相關文章
相關標籤/搜索