前言
分三局部: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.js
exports.pull = function(参数) {return function pull() {// 拉代码}
}
exports.push = function(参数) {return function push() {// 能够把 add commit push 写到一起}
}
// gulpfile.js/file.js
exports.move = function(参数) {return function move() {// 文件从一个目录挪动到另一个目录}
}
exports.del = function(参数) {return function del() {// 删除文件}
}
当然我觉的模块化做的比拟好的中央就是 config 和 task 的拆散,在入口文件组合 config 和 task,组成具体 task。
// 配置 gulpfile.js/config.js
const config = {// 配置}
export.getConfig = function() {// 通过办法取得配置}
export.getCombine = function() {// 通过办法取得动静配置}
// 工作 gulpfile.js/task.js
const {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.js
const {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 用 shell
gulp -- 参数 # 混同压缩
zip - 参数 # 打 zip 包
java -jar -- 参数 # jdbc 批改数据库版本
gulp ftp # ftp 到服务器
# windows 用 batch
gulp -- 参数 # 混同压缩
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"
# 把代码公布到 gerrit
git 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 install
gitlab-runner start
.gitlab-ci.yml:寄存于我的项目仓库的根目录,蕴含了我的项目自动化如何运行的形容语句
stages:
- build
build-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…