最近都好忙,需求太多了,干不完,加上最近做息时间的调整,都没时间写博客了,趁着这两天稍微有点时间,想撸一发; java
其实在写的博客有好几篇,但都是陆陆续续的,原本想继续写以前的,忽然想起半个月前,同窗提出,能不能在开发提tag时自动打包?python
以前发布过一篇项目流程规范,目前的项目都在遵照这个规范,而研发每次有改动过代码的时候,都会提测,而提测就至关于提tag,这个tag
的概念不说了,就是基于当前产品分支从新拉的分支,tag的格式以下图; git
而测试拿到这个tag后,就须要去到jenkins打包,要输入一堆参数,而后等待打包结果; github
有时候,研发勤奋点,一天提屡次tag,测试就须要屡次去打包,测试同窗以为太麻烦了,所以就提出一个想法,能不能在开发提tag时自动打包?shell
能,必须能,没有作不到的事情; json
作事以前,要先想一想要作什么?api
想了下,大体就这3步,那就来一块儿作吧;安全
为了测试,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_USER
跟JENKINS_PORT
这两项进行修改便可;
修改为root跟具体端口保存退出便可;
这时候,直接输入主机IP+刚设置的端口就好啦;
阿里云开放端口权限 上面访问ip+端口,有可能打不开连接,由于阿里云对端口作了限制,所以须要开放端口;
登陆阿里云,首页右上找到控制台;
找到云服务器ECS
点击实例,找到该实例的安装组配置
点击规则说明,新增便可;
若是是按照上面的文档来,安装完插件,建立完用户后,就应该进入到首页,会显示以下内容,至此,jenkins环境搭建就完啦~!
对了,jenkins的目录在这里:
/var/lib/jenkins
复制代码
公司的代码仓库是用gitlab,因某种缘由,就开放公网啦,那jb就在gitlab新建一个jbtest项目;
若是有小伙伴说,没有gitlab怎么办?不要紧,能够用github代替,至于github相关的配置,能够查看该文章:git介绍及GitHub配置教程
既然肯定是要用jenkins+gitlab,那就先在jenkins上安装下gitlab插件吧;
点击jenkins首页,有个系统管理按钮;
点击后右侧会显示内容,下滑,点击插件管理;
进入到插件管理界面,就看到updates等4个栏目,那点击Available
,右侧输入gitlab
,而后找到GitLab
跟GitLab Hook
,勾选,点击底部的install便可;
test项目有了,gitlab的插件也安装了,那咱们就在jenkins新建一个job吧,输入了项目名称,就进入到设置项;
这里的丢弃旧的构建,可选,可是习惯选择7天,,最大保留300个; 缘由是,jenkins每次构建都会生成一个历史构建记录及对应的产物,若是公司有100个产品,天天自动打包10次,一天就有1000个产物,服务器磁盘空间是个问题,所以设置个7天,设置个最大数,定时删除便可;
上面的丢弃旧构建不是要点,继续;
继续下滑,会发现Source Code Management
,中文是源码管理,而咱们的代码是存放到gitlab的,所以就选择git了,点击后以下图展开;
这里有小伙伴可能有疑问,既然是放gitlab,为啥不是选择gitlab,而是选择git?
这里就花点时间说明下git、github、gitlab;
git是一款免费的、开源的分布式版本管理控制系统(工具);
gitHub是一个面向开源及私有软件项目的托管平台,
只支持git做为惟一的版本库格式进行托管;
gitlab,是一款基于Git的项目管理软件,拥有GitHub拥有的一切,但它还具有让团队对它们的repositories进行控制的功能;
复制代码
但愿通过这么一说,能让你稍微清楚下;
点击git,把项目的地址copy到这里;
没权限是确定的,那咱们就点击add
建立帐户呗,选择jenkins;
输入gitlab/github的帐号密码,可是输入完后发现仍是报错;
别着急,那去服务器上看看,git安装了吗?
没安装,那就安装罗;
yum install git
复制代码
等待安装完,再输入git --version
跟where is git
来看版本及安装位置;
安装完后,就顺便试试能不能clone项目吧;
不出所料,果真不行,由于还要配置密钥啊;
$ ssh-keygen -t rsa -C "your_email@youremail.com"
//注意,双引号里面是你的邮箱。填你注册github的邮箱就好了。按enter执行。
复制代码
一路回车就行,密码通常不用设置~
上图红框里的就是地址,cd过去;
有id_rsa
和id_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
~
这里还有一个特殊的问题: 因开放公网的缘由,每次git clone
的下载代码的时候是链接的https://
而不是git@git (ssh)
的形式,致使每次pull/push等远程操做的时候,都须要输入帐号密码,很是麻烦;
解决办法:
在项目根目录输入:
git config --global credential.helper store
git pull/git push(第一次要再输入一次,之后就不用)
复制代码
会在用户目录下生成文件.git-credential记录用户名密码的信息;
那回到jenkins,打开测试job的设置,再看是否正常;
发现已经好了,没问题,固然也会有小伙伴依然有问题,那就须要这样作;
打开后,滑动到git栏,若是有问题的话,可能会看到错误提示:
There's no such executable git in PATH: /sbin, /usr/sbin, /bin, /usr/bin.
复制代码
在出错的地方填入:
"whereis git"的地址 + "/bin/git"
(如上面"whereis git"的地址为"/usr/bin/git",则应该填入 "/usr/bin/git/bin/git") 并保存
复制代码
而后再回到job的源码管理中添加git地址,就会好了;
既然能够链接到gitlab了,那咱们就直接下滑到build,选择add build step,由于是服务器,因此直接选择执行shell
,若是是Windows系统,也能够选择执行Windows批处理命令
;
因jb的项目里面有个test.py
文件,那项目clone下来确定是想执行这个文件啦,所以内容就是输入python test.py
,而后点击保存;
回到job的首页,点击当即构建;
因test.py
的做用是向钉钉发一条消息,所以成功的话,钉钉就会收到一条信息;
到这里,说明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信息;
既然知道tag是怎么生成的,那就看看有没有办法知道tag被建立了?
第一时间固然是想到钩子-hook啦;
来看下官方解释:
钩子(hooks)是一些在"$GIT-DIR/hooks"目录的脚本, 在被特定的事件(certain points)触发后被调用。
当"git init"命令被调用后, 一些很是有用的示例钩子文件(hooks)被拷到新仓库的hooks目录中; 可是在默认状况下这些钩子(hooks)是不生效的。 把这些钩子文件(hooks)的".sample"文件名后缀去掉就可使它们生效了。
复制代码
简单地来讲有点相似回调,就是特定事情完成后回调执行事件。
那这钩子,在jenkins
跟gitlab
上怎么搞?gitlab
支持吗?
打开gitlab
对应的项目,点击settings
-Integrations(集成)
打开后,就是hook的内容啦,简单瞄下,发现有个选项叫Tag push events
,看了下描述,好像蛮符合咱们的要求,界面显示,须要输入URL
跟Secret Token
,这两玩意怎么来?
打开jenkins,打开对应的job,点击设置,查找Build when a change is pushed to GitLab
,gitlab上的URL就是该处的连接,以下图红框,复制过去便可;
同时点击右下的Advanced(高级)
按钮;
而后点击右下的Generate按钮;
而后会生成一串Secret token,复制到gitlab那;
那咱们再试试,直接在服务器打个tag且push看钉钉会不会收到信息?
啧啧啧,没问题啊,搞定啦,另外看了下hook,支持的内容蛮多的,tag push
、push event
、merge requests
等;
上面说的条条是道,但实际现实是残忍的,打开实际的项目,会发现本身没有setting
权限:
遇到这种状况,不是说不能处理,只能看门槛变高了,有2个方法:
尝试跟对应的负责人沟通,看可否给一个带master权限的帐号;
若是有足够的数据让老大承认这个功能,就会给一个带master权限的帐号,就能够随心所欲啦;
但若是给的理由不让人信服或者老大以为没卵用,但依然要作这个功能,那只能换种方式了,但至少要求依然有仓库的可读权限
; 换个角度,连仓库权限都没有,就别想作了。。
大体的思路就是,利用定时功能,不断检测该项目的tag,当发现原tag不是最新tag时,就认为有最新tag,此时再通知jenkins处理;
若是是这样,那jenkins上的选项会有点不一样,构建的时候就要选择脚本调用,指定TOKEN_NAME,而后能够经过链接JENKINS_URL/job/JOBNAME/build?token=TOKEN_NAME
来启动build
;
对了,作这个功能,还有两种形式:
第一种方式的好处时,省事,可是通常不容许,由于产品项目通常存放跟该产品相关的内容,而这个脚本典型是通用的,后面若是多个产品想复用就麻烦了
第二种方式的就是独立项目,可复用,把检测项目跟产品项目放到同一台jenkins机器上,这样它们都在jenkins/workspace
目录下,经过传参的方式切换不一样目录便可;
而jb采用的是第二种方式;
这个脚本的目的是什么? 就是定时检测tag是否是最新的,若是不是,则执行相应操做;
那难度就在于,怎么判断tag是否是最新的? Google下,有例子:
git describe --tags `git rev-list --tags --max-count=1`
复制代码
尝试下,好像没问题,那就暂且相信吧;
好奇问下,这命令究竟是啥意思?
其实这是两条git命令;
git describe --tags
git rev-list --tags --max-count=1
复制代码
git describe --tags 显示当前离当前提交最近的tag,实际发现,带不带参数都同样?
不加任何参数的状况下,git describe 只会列出带有注释的tag;
实际,该命令是能够带3个参数的,tags
、always
、dirty
,这通常用的比较少,不展开说明;
git rev-list --tags --max-count=1
git-rev-list :按反向时间顺序列出提交对象 意思就是把倒序方式展现最新提交内容;
max-count=1,也很好理解,只获取1个,若是写2,则获取2个;
那命令实际就变成了这样:
git describe 9c7246b2026cec052af6a3b6297995e494c7b398
复制代码
最终输出的就是tag,有种奇淫怪招的感受;
固然,不会用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,就须要时间了,所以仍是推荐第一种;
为了模拟真实场景,就在跟目录下新建一个脚本,跟项目同一根目录;
脚本的内容以下:
# _*_ 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,那确定是不会知道有代码更新啦;
解决方案,立刻想到,那push后就通知啊,结果想起,没钩子权限。。
那没问题,在上面的脚本加多一个更新命令就好啦;
#更新远程代码到本地仓库
git fetch origin
复制代码
流程通了,那怎么对接到jenkins?
看回jenkins,按照要求试试,输入如下命令:
curl -i jenkins_url/job/job_name/build?token=yourtoken
复制代码
结果出现403.。。
两种解决方案:
jb选择的是第二种方案,勾选后,再次运行上面的命令,没问题,而且能够收到啦~
其实故事到上面就完啦,可是呢,虽然知道hook怎么用,但仍是想了解更多,所以就有了这篇啦;
Git Hooks就是在Git执行特定事件(如commit、push、receive等)后触发运行的脚本。
按照Git Hooks脚本所在的位置能够分为两类:
基本上,用上钩子都是定制化的脚本程序,在实际工做中,基本钩子是万能的,如:
$project_name/.git/hooks
复制代码
.git/hooks/
文件夹下。当你init一个仓库的时候,下边会有一些钩子的例子,以
.sample
结尾。
固然也能够在这个目录下自由定制hooks的功能,当触发一些git行为的时候,就会自动触发执行的hooks功能;
本地钩子:
名称 | 功能 |
---|---|
pre-commit | 当执行commit 动做时先执行此hook ,能够用此hook 作一些检查,好比代码风格检查,或者先跑测试; |
prepare-commit-msg | 当commit时须要输入message 前会触发此hook ,能够用此hook 来定制本身的default message 信息; |
commit-msg | 当用户输入commit 的message 后被触发,能够用此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
;
好啦,本章就到此接触啦,基础内容有点,可是很少,都是环境配置,本章主要讲述如下内容:
大体是这样,还有些细节,差很少就的啦;
好啦,若是有什么疑问,欢迎一块儿沟通;
最后,谢谢你们~