CocosCreator自动化构建打包

前言

我已经在mac上搭建了一套自动化打包流程,但是是用的sh,首先无法跨平台,其次成本比较高(穷~),借鉴python等工具,构思了一下从自己熟悉的js语言重构这套流程,目前已基本实现打包流程的自动化.

听闻官方也准备有个集成的热更方案,也许此教程不久就没什么价值了~

准备

  • 操作系统: windows/mac

因为cocos官方没有支持linus构建,所以如果想用linux需要先在windows或mac上先构建,然后继续下面的步骤(为什么要多此一举,同时希望官方能尽快支持linux构建)

  • 技术栈

    • nodejs – 必须
    • python – 非必须 如果需要使用到腾讯cos则需要会安装
    • jenkins – 如果想自动触发,则理论必须

构建原理

命令行构建

  1. CCC支持命令行构建:参考官方文档 命令行发布项目
  2. AndroidStudio 提供了gradlew命令行构建脚本

构建步骤

如果只是构建web平台,那么看完文档安装好jenkins理论上整个流程就可以完成,我这边主要是原生工程的构建,主要是以下几个步骤:

  1. 构建项目
  2. 制作更新包
  3. 打包原生工程

Node命令

  1. nodejs可以通过require("child_process").exec命令执行sh或cmd命令
  2. cos支持cmd上传,SDK文档

主要流程

构建项目

我们在项目目录下,新建一个目录,自定义即可,主要是配置一些基础参数,第一版考虑的JSON配置,但是不能加注释,比较麻烦,所以第二版,改成了yaml文件,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 每次打包可能都需要配置的位置
// 是否是测试版本,如果是则不上传到cos
debug: false
// 是配置当前构建的版本
clientVer: 1.0.0.0
// 安卓的versionName
appVer: 1.0.0
// 安卓的versionCode
appCode: 1
// 打包的渠道
channel: [测试]
// 创建项目才需要配置的位置
// 引擎版本
engineVer: 2.3.3
// 构建的项目名 安卓打包的项目名
title: PackTest
// 安卓应用名称
appName: 自动化打包测试
// 安卓的包名
packageName: com.xyzzlky.test.pack
// 设备方向
orientation: portrait
// 安卓sdk版本
apiLevel: 28
// 支持的cpu架构
appABIs: [armeabi-v7a]
// js加密密钥
xxteaKey: 318927a2-2183-4c
// SDK的appid
appId: 1
// SDK的appkey
appKey: 1
// bugly的appId
buglyAppId: 1
// bugly的appKey
buglyAppKey: 1

据此,先写出构建的cmd命令

1
2
3
4
5
6
7
8
9
let cmd = 
config.enginePath +
' --path ' + config.path +
' --build title=' + config.title +
';packageName=' + config.packageName +
';apiLevel=' + config.apiLevel +
';appABIs=' + appABIs +
';xxteaKey=' + config.xxteaKey +
';template=link;platform=android;debug=false;inlineSpriteFrame=false;mergeStartScene=false;optimizeHotUpdate=false;useDebugKeystore=true;md5Cache=false;encryptJs=true;zipCompressJs=true;'

最后一行的配置我是写死的,如果有需要,都可以放到配置中。

因为mac和windows路径是不同的,所以要区分下目录,比如

1
2
3
4
5
if (utils.isMac()) {
enginePath = '/Applications/CocosCreator/Creator/' + config.engineVer + '/CocosCreator.app/Contents/MacOS/CocosCreator';
} else {
enginePath = 'C:/CocosDashboard/resources/.editors/Creator/' + config.engineVer + '/CocosCreator.exe';
}

根据版本配置,也可以通过每次打包备份原版本,然后比较代码,判断是否进行构建,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (utils.compareVersion(config.clientVer, backConfig.clientVer || '0')) {
console.log('Coocs开始构建');
config.path = projectPath;
ccc.pack(config).then(() => {
console.log('Cocos构建成功');
hotupdate();
}).catch(() => {
console.log('Cocos构建失败');
});
} else {
pack();
}

然后执行该命令,理论上包就打好了。

utils.runSh(cmd);

1
2
3
4
5
6
7
8
9
10
11
12

### 热更配置

如上,我们已经构建了CCC的原生工程,现在我们将制作一个热更包,供线上用户使用

官方文档也有相关的说明,这里我们根据自己的需求,做出一些修改,主要有以下几个方面的不同

1. manifest 不纳入版本管理 所以我们项目中不能出现manifest后缀的文件(这里是为了方便大版本升级时,能判断热更版本号和包中版本号的大小关系,是否清空缓存)
2. 考虑cdn缓存问题,我们热更每次的目录不同,所以链接是不同的(同时这样做,可以后台指定用户的热更版本,逻辑代码可以指定是否允许回退)
3. 构建的代码需要打包,包里面的project需要保持最新,网上用的方法是二次打包机制,因为我们有第一步的保障,项目内只有一个manifest,可以一次打包,然后替换即可。

因为有目录关系,首先,我们根据版本创建目录,并把构建目录中的代码复制过来
    // 复制文件到当前文件夹
    const src = mfs.join(buildPath, 'src');
    const res = mfs.join(buildPath, 'res');
    const subpackages = mfs.join(buildPath, 'subpackages');
    const dirPath = mfs.join(packPath, dirName);
    mfs.cp(src, mfs.join(dirPath, 'src'));
    mfs.cp(res, mfs.join(dirPath, 'res'));
    mfs.cp(subpackages, mfs.join(dirPath, 'subpackages'));
1
2
3
4
注意,每个版本的目录形式不一致,需要自行做出一定的修改

之后我们就可以生成manifest文件,如下

let manifest = {};
const dirInfo = mfs.parse(config.dirPath);
manifest.packageUrl = url + '/' + dirInfo.base + '/';
manifest.remoteManifestUrl = manifest.packageUrl + 'project.manifest';
manifest.remoteVersionUrl = manifest.packageUrl + 'version.manifest';
manifest.version = config.version;

const destVersion = mfs.join(config.dirPath, 'version.manifest');
mfs.writeFile(destVersion, JSON.stringify(manifest, null, 4));// 写入基本信息到version.manifest

manifest.assets = {};
const paths = mfs.readDir(config.dirPath);

let manifestPath = '';
paths.forEach(filePath => {
    const relative = encodeURI(mfs.relative(config.dirPath, filePath).replace(/\\/g, '/'));
    if (mfs.extname(filePath) === '.manifest') { // manifest 不进行更新
        manifestPath = relative;
        return;
    }
    manifest.assets[relative] = {
        size: mfs.size(filePath),
        md5: utils.md5(mfs.readFile(filePath, 'binary'))
    };
    const compressed = mfs.extname(filePath).toLowerCase() === '.zip';
    if (compressed) {
        manifest.assets[relative].compressed = true;
    }
});
const destManifest = mfs.join(config.dirPath, 'project.manifest');
mfs.writeFile(destManifest, JSON.stringify(manifest, null, 0));
return {
    manifest: manifest,
    path: manifestPath
}
1
2

本代码主要是生成md5以及找出manifest的目录,当manifest生成完毕后,我们就可以将该文件更新到build目录下面manifest, 并上传到cos
    const destManifest = mfs.join(buildPath, info.path);
    mfs.writeFile(destManifest, JSON.stringify(info.manifest, null, 0));
    const cmd = 'coscmd upload -r ' + dirPath + ' ' + cosPath;
    utils.runSh(cmd).then(() => {
        console.log('上传成功');
        backConfig.clientVer = config.clientVer;
        pack();
    }).catch(() => {
        console.log('上传cos失败');
    });
1
2
3
4
5
6
7
8
9
10
热更步骤,相对简单,如上就完成了热更打包,更新以及上传到cos。

完成以上工作后,我们将要编译一个安卓包出来,这样整个流程就完毕了。

## 安卓打包

安卓打包,说简单也简单——调用gradlew命令即可,说复杂也复杂,特别是我们如果接入了一些sdk的话,比如接入bugly就需要修改build.gradle,正则修改是一个方式,但太过于繁琐,所以——我直接重写了整个文件,文件过于冗杂,就不贴了,主要是修改一些参数,其中的运用了多渠道,各位可以下载我的工程查看。另外,示例工程中,我们的icon什么的都没有换,接入SDK还有一些文件需要替换,自己需要定义这一块内容,当然我完善后,也会同步更新到仓库中。

打包的话,直接调用gradlew即可(其实百度了好久,才知道方式) 请保证这个工程能正常运行,gradle版本什么已经下载下来了,不然这一步会卡到世界末日,你也看不到进度

    utils.execFile('gradlew.bat', [':' + config.title + ':assembleRelease'], { cwd: config.androidPath }, (error, stdout, stderr) => {
        if (error) {
            console.log('error', error);
            reject();
        } else {
            console.log('执行成功');
            resolve();
        }
    });

- 如果发现该命令卡住,可以手动到android目录下执行gradlew命令查看原因,大部分情况是在下载gradle

## 执行

1. 请确保已经安装jenkins(一步步next即可)
2. 配置好git/svn/网址触发方式(请自行百度教程)
3. 修改config的版本号,提交git或请求网址即可。

一些命令都被我封装了,包含但不仅仅包含:文件处理相关、执行cmd等,完整代码,请关注公众号【xyzzlky】回复AutoPack 获取。