乐趣区

关于前端:前端自动化打包实践

前言

分三局部: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…

退出移动版