最近一年都在捣腾
Electron
,从0.X版本,到现在最新Stable的3.X版本。一路上踩的坑很多,Electron的版本号也飞快的。项目设立的初衷是一份代码,即可构建出Windows
和Mac
,并且部分功能模块可以与Web
版公用,因此我们项目名为:三体
。
由于程序比较复杂,使用Electron程度也较深,因此对比一般的桌面程序,构建流程比较繁琐,因此项目开始之初,就使用
Jenkins
作为我们项目的自动构建的工具。随着时间的推移,Jenkins的构建任务也一步步的智能化,力求智能,省心,省力。
Jenkins的安装和部署这些就不多说了,读者自行了解。
Jenkins单独部署
项目开始之初,采用的Jenkins的方式就是:windows构建主机安装一个Jenkins,Mac主机安装一个Jenkins。当需要构建时候,手动点下运行任务。
这估计也是最多人使用Jenkins的方式,0门槛的入门。
优点:部署简单,入门快
弱点:每次执行任务,需要访问两个Jenkins(这个时候,我们就只有两个主机构建,如果多个主机,则需要访问多个Jenkins),然后再执行任务。
Jenkins多节点部署
到了某个时候,发现实在太麻烦了,就使用单个Jenkins,然后通过节点管理执行Jenkins任务。
由于我们桌面端程序,构建过程,还需要把其他团队的项目进行构建,放入到我们程序的指定目录(JsBundle),这时候Jenkins的Config Files
就大显身手,把构建其他JsBundle的命令放入Config Files
(如果包没变更,则跳过构建,获取缓存的包),这样就没必要每个构建任务重复录入相同的流程,如图feature
, master
等。
这时候幸福满满的,所有任务都可以通过Jenkins执行了,而且构建权限开放给所有开发,随时构建包。
优点:充分利用不同机器资源,组建为构建集群。
Jenkins多配置
如上图所示,我们有很多构建任务,有C++的,有第三方Node(预构建为二进制包,例如Napajs),但是核心构建我们的安装包也有不同的任务,例如开发构建的是feature
,测试构建的是master
,灰度和发布则还有其他。但其实他们的构建流程都几乎一致的。
上述的Jenkins使用方式,大概持续了大半年多,一直也挺满意,觉得挺美好的。这时候,公司大力发展混合云,桌面端程序也需要按企业资源构建(简单理解,就是类似手机的各种渠道包吧)
但是混合云的构建,由别的团队负责,因此搭了另外一套Jenkins,我们团队提供构建步骤,并且额外还有替换企业资源包的步骤。好景不长,这样的方式,进行了一两个月,问题逐渐暴露出来,每当我们需要修改构建步骤,都需要通知其他团队一并进行修改。
因此思考,有没方式可以改善这种方式呢?恰逢把Jenkins升级了一番,发现以下这
根据描述,好像可以解决我们一些问题,例如上面图里面的全平台构建
,不需要再搞两个任务了,起码能干掉一半的任务,每次修改脚本,可以省掉一半修改量了。
但是还是没根本解决问题,只是优化了一点。这时候,发现了Jenkins Pipeline
,赶紧了解一下,眼前一亮,好像很美好。
Jenkins Pipeline
赶紧尝试一下,由于我们项目过于复杂,没办法很省心省力的用起来。试了几回,都打算放弃了几回,只是不甘心,还是念念不忘。真正的从入门到“放弃”。
由于Jenkins Pipeline
对于大部分人来说,过于高深,因此一直担心脚本太复杂,对其他维护的同事来说是个负担。(作为Lead,还是得考虑维护成本的,否则自个Hight不行),因此寻求简单易懂,但又能组织形成复杂的构建步骤的平衡点。
Jenkins Pipeline的脚本形式分为:声明式
和脚本式
,初次采用了声明式
,因此易懂,类似json的结构。但是尝试了写了下来,发现嵌套层数很多,有点累赘。最后还是采用了脚本式
的,结合现学的groovy
语法把一些复杂的逻辑,以前通过Config Files
编写的逻辑独立为单独的文件,保持主文件的清爽。
最结合之前的Multi-configuration
,完美...
废话不说,直接上脚本代码
properties([parameters([
string(name: 'runNode', defaultValue: 'mac', description: '节点'),
string(name: 'cloudhubX_branch', defaultValue: 'master', description: '三体分支'),
string(name: 'xx_branch', defaultValue: 'release', description: 'XX分支'),
])])
node ("${params.runNode}") {
checkout scm;
def noCacheToBuild = load("${WORKSPACE}/.jenkins/noCacheToBuild.groovy")
def check = load("${WORKSPACE}/.jenkins/check.groovy")
stage('pre') {
// 检查任务
check()
sh 'rm -rf node_modules build dist bundle'
}
ansiColor('xterm') {
stage('XX') {
dir(".resource/xx") {
def git = git(branch: env.xx_branch,credentialsId: 'git_ssh', changelog:true, url: '.............')
noCacheToBuild(git, 'JsBundle的目录名', 'dist') { // 如果缓存找不到,则构建,否则返回缓存
sh """
yarn install
yarn run build
"""
}
}
}
// .............
stage('cloudhub') {
sh """
yarn install
yarn build
yarn run publish $OS
"""
// 推送到nginx
sshPublisher(publishers: [sshPublisherDesc(configName: 'master', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: "/$cloudhubx_branch/${OS}", remoteDirectorySDF: false, removePrefix: 'install', sourceFiles: 'install/*.*')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
发展到这,项目都可以复用一个构建任务,如下图,由上游项目传不同的参数触发
下一步计划
这次的调整,可以把公有云的产品构建都统一为一个了。把Jenkinsfile
放入到工程,以后修改都可以直接修改,不用担心构建任务与项目代码不匹配了。
- 干掉混合云的Jenkins,都收拢在一个构建平台
- 尝试把构建任务不同stage,由不同机器执行。例如:C++的由指定平台的编译机器,JsBundle由服务器Docker构建,Electron本身由另外的机器打包