前言
分三局部:gulp介绍自动化打包脚本编写;gerrit介绍工作流;gitlab-ci介绍平台搭建。每局部都会联合做过的两个我的项目进行总结,心愿对大家有所帮忙。
gulp
第一个我的项目用gulp是因为在过后它还是比拟“新”的,用一下尝尝鲜。第二个我的项目还用它,是因为那段时间在学习函数式编程,接触了Monadic,想到能与这个概念匹配的例子就gulp,所以想用它的同时深刻理解一下这个概念,当然最次要的起因是用起来相熟棘手。
Monadic编程概念有点形象,但gulp的编程范式却能直观的展示。
const { src } = require('gulp')src('glob格局').pipe(func1).pipe(func2).pipe(func3)
这个“.pipe(func1).pipe(func2).pipe(func3)”就算是Monadic了,其实和promise的then是一个情理。
Promise.resolve().then(func1).then(func2).then(func3)
他们之所以能链式调用,就是为func1、func2、fnc3...这些函数按规定返回了雷同的接口(对象)。
// gulp 大抵这样var through = require('through2');function func1() { return through.obj(function(file, enc, cb) { // through.obj返回的对象有pipe办法 // 调用cd就示意实现了 })}// promise 大抵这样Promise.prototype.then = function(func) { let result = func() if(result.then && typeof result.then == 'function') { return result } else { return Promise.resolve(result) }}
第一个我的项目编写打包步骤时大抵是下边这样的,步骤多了靠看代码很难缕清步骤之间的关系。
var gulp = require('gulp')gulp.task('步骤1', function() { // 步骤1})gulp.task('步骤2', function() { // 步骤2})gulp.task('default', ['步骤1', '步骤2'], function() { // 步骤1和步骤2是并行运行的,步骤1步骤2都完结之后进行这个步骤})
第二个我的项目用了gulp新的版本,在API层面上解决了这个问题,并行用parallel,串行用series。
const { parallel, series } = require('gulp')function 步骤1() { // 步骤1}function 步骤2() { // 步骤2}function 步骤3() { // 步骤3}exports.default = series(parallel(步骤1, 步骤2), 步骤3) // 步骤1和步骤2是并行运行的,步骤1步骤2都完结之后进行步骤3
如果步骤1和步骤2有雷同的局部,只是某些参数不同,咋办?学的函数式又派上用场了,高阶函数用上。
const { parallel, series } = require('gulp')exports.步骤 = function(参数) { return function 步骤() { // 步骤1 步骤2 雷同的局部,用参数来辨别不同 }}const 步骤1 = 步骤(参数1)const 步骤2 = 步骤(参数2)function 步骤3() { // 步骤3}exports.default = series(parallel(步骤1, 步骤2), 步骤3) // 步骤1和步骤2是并行运行的,步骤1步骤2都完结之后进行步骤3
用高阶函数能够把相似步骤封装起来,而且很天然的就想到模块化了。
所以第二个我的项目就做了模块化:比方须要操作两个git库,就波及git的pull和push;比方不同阶段都须要操作文件,就波及文件的挪动和删除。
gulp模块化须要把gulpfile.js文件改成目录名,入口文件是这个目录下的index.js,其余模块则放到这个目录下随便命名了。
// gulpfile.js/git.jsexports.pull = function(参数) { return function pull() { // 拉代码 }}exports.push = function(参数) { return function push() { // 能够把 add commit push 写到一起 }}// gulpfile.js/file.jsexports.move = function(参数) { return function move() { // 文件从一个目录挪动到另一个目录 }}exports.del = function(参数) { return function del() { // 删除文件 }}
当然我觉的模块化做的比拟好的中央就是config和task的拆散,在入口文件组合config和task,组成具体task。
// 配置 gulpfile.js/config.jsconst config = { // 配置}export.getConfig = function() { // 通过办法取得配置}export.getCombine = function() { // 通过办法取得动静配置}// 工作 gulpfile.js/task.jsconst { parallel, series } = require('gulp')exports.task = function(name, config, combine) { const arr1 = [...] // 依据 config, combine 生成工作1 if(name == '工作1') { return series(...arr1) } const arr2 = [...] // 依据 config, combine 生成工作2后续的工作 if(name == '工作2') { return series(...arr1, ...arr2) } return series(...) // 依据 config, combine 任意组合的工作}// 入口 gulpfile.js/index.jsconst { getConfig, getCombine } = require('./config')const { task } = require('./task')const config = getConfig()const combine = getCombine(参数)// 依据不同工作放开相应正文// exports.default = task('工作1', config, combine) // 简略的工作1// exports.default = task('工作2', config, combine) // 简单的工作2
第一个我的项目遇到了一个问题,就是有的步骤gulp插件做不到,本人也没有能力实现插件的开发,因而gulp不能串起来所有步骤,最初是用命令行脚本串起来的。
# mac或linux用shellgulp --参数 # 混同压缩zip -参数 # 打zip包java -jar --参数 # jdbc 批改数据库版本gulp ftp # ftp到服务器# windows用batchgulp --参数 # 混同压缩haozip.exe -参数 # 打zip包java -jar --参数 # jdbc 批改数据库版本gulp ftp # ftp到服务器
第二个我的项目也同样面临这样的问题,然而这次却找到了解决办法。
首先须要在命令行运行的步骤能够在nodejs里运行,原来nodejs本人自身就反对,不须要npm别的包。
const { execSync, exec } = require('child_process')execSync('命令', 配置)exec('命令', 配置)
其次是这次用的gulp版本对异步工作的反对,只有工作返回stream、promise、event emitter、child process或observable就能够了,如果是上述命令行工作,依照他们接口的范式编写代码就晓得这工作啥时候执行结束,好执行下个工作。
gerrit
在多人开发的状况下,还须要工作流来治理代码的合并。
第一个我的项目的工作流大抵是这样的:一个需要一个分支,批改bug也算需要,每个测试环境一个分支,测试环境分支是需要分支merge在一起的。之所以采纳这样治理形式,是因为有的需要在开发,然而不晓得什么时候上线,或者有的需要遇到非凡状况延期上线,这样每次上线只有确定了上线的内容,merge一下对应分支就行了,准生产分支这样做简直没问题。然而平时的测试分支一边开发一边merge,有抵触时有的人在测试分支解决了,然而在本人分支上没有批改记录,再重新组合时还得解决抵触,而且随着工夫的推移,测试分支会越来越简单,甚至呈现每个人本人分支运行没问题,只有测试分支有问题,把测试分支删了,从新merge就没问题了,这个问题始终没有解决,每次都是删了重新组合。
第二个我的项目的工作流大抵是这样的:每个月份上线内容在一个个性分支上开发,拉他人的代码的形式是衍和(rebase),这样缩小了分支的关系的复杂度,不会呈现第一我的项目那样须要删了重新组合的状况,而且push代码是受gerrit限度的,这就是为啥用它来做题目了,具体来说本地代码不能间接push到近程分支,而是公布到gerrit,公布的代码是须要通过审核通过,能力提交到近程分支。
第二个我的项目刚开始是用工具操作衍和和公布的,然而主动打包也要把这些步骤写到脚本里,gulp那局部介绍的“child process”尽管就几行代码,我可是破费了我不少心血的,同样下边的脚本尽管也是短短几行,但也是破费了我不少心血的。
# 衍和git pull --rebase --progress "origin"# 把代码公布到 gerritgit push --progress origin HEAD:refs/for/refs/heads/分支
这个“HEAD:refs/for/refs/heads/”是默认的,也能够在gerrit里配置(没实际操作过,我猜的)。
这两个工作流都有各自特点,第二个工作流尽管分支的复杂度小,然而搭建复杂度较高(须要搭建+1的主动审核)和治理消耗的人力较多(多了一步+2的人工审核)。第一个工作流尽管有时须要删了重新组合,然而这个问题呈现概率小,适宜3-5人的小团队。我集体认为这两种的联合的工作流比拟不错,对于存量性能用第二种,而对于增量性能还是用第一种比拟好,新性能危险高,如果不能按期实现交付,能够先不合到个性分支,等达到可交付了再合。而且增量性能分支有了,也能够尽早将公共的模块代码提交分享进来,而不必放心会影响个性分支的交付。
gitlab
自动化打包脚本有了,工作流有了,接下啦就是最要害的了,怎么主动打包?
其实原理就是近程仓库增加git-hooks,近程仓库某些分支标签的某些动作如push就会触发git-hooks关联的自动化平台,自动化平台收到信号,开始执行对应的自动化脚本。当然了不止这一种触发形式,还有定时工作等等,这些不在这篇文章探讨范畴。
第一个我的项目用的是 gitblit + genkins
首先要下载jar包:http://mirrors.jenkins.io/war...
而后启动:java -jar jenkins.war --httpPort=8080
在浏览器输出:http://localhost:8080
配置明码:依照页面指引找到initialAdminPassword文件,复制出明码
首次配置插件:首次能够先跳过插件装置,进入jenkins后再配置
配置插件:在Manage Jenkins/Manage Plugins/Advanced/Update Site里填写
http://mirrors.tuna.tsinghua....
装置插件:在Manage Jenkins/Manage Plugins/Available里找即可
主动打包必不可少的插件必须装置:git、nodejs,如果须要写流水线脚本,须要装置pipeline相干插件(这个我的项目没有用)
那么近程仓库和jenkins怎么触发主动打包?这个就是git-hooks
点击New Item填入项目名称,点击ok
在Source Code Management里抉择Git输出近程仓库url
点击“增加“后输出用户名明码
抉择输出的用户名明码
在Build Triggers设置触发器,这个在设置gitblit的时候会用到(令牌随便输就行)
在Build里填写要触发的脚本
点击保留
进入gitblit的groovy目录
编辑jenkins.groovy文件,在jenkinsUrl输出Build Triggers设置触发器,生成的地址
保留之后重启gitblit
进入gitblit治理页面,点击编辑,选中Receive
选中jenkins.groovy,挪动到Selected,点击保留
这样gitblit push代码后就会触发Build里填写的脚本
能够参考:
https://www.jianshu.com/p/9a3...
第二个我的项目用的是 gitlab
gitlab runner下载:https://docs.gitlab.com/runne...
gitlab装置:在下载好的目录下运行
gitlab-runner installgitlab-runner start
.gitlab-ci.yml:寄存于我的项目仓库的根目录,蕴含了我的项目自动化如何运行的形容语句
stages: - buildbuild-gulp: tags: - 标签 stage: build script: - gulp only: - web
pipeline:就是一个分成不同stage的job的汇合;
stage:是job的一个逻辑上的划分,如“stage: build”;
job:就是Runner要执行的指令汇合,必须蕴含 script,“build-gulp:...” 就是 job,能够自定义多个;
stages:定义了作业执行的程序,默认蕴含build、test和deploy三个stage;
script:是一段由Runner执行的shell脚本;
only:web:在gitlab页面上按run pipline的时候执行;
tags:须要执行的Runner标签,须要在“首页=>setting=>CI/CD=>Runners=>Specific Runners”增加;
能够参考:
https://zhuanlan.zhihu.com/p/...
https://juejin.cn/post/701814...