CocosCreator构建自动化

前言

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

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

准备

  • 操作系统

    windows、mac

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

  • 技术栈

    nodejs – 必须

    python – 非必须 如果需要使用到腾讯cos则需要会安装

    jenkins – 如果想自动触发,则理论必须

构建原理

命令行构建

  1. CCC支持命令行构建

参考官方文档 命令行发布项目.

  1. AndroidStudio 提供了gradlew命令行构建脚本

构建步骤

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

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

能用node的原理

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

主要流程

构建项目

我们在项目目录下,新建一个目录,自定义即可,主要是配置一些基础参数,为了保证windows和mac平台都可以使用,我这里用的是json配置,参考如下:

{
    "clientVer": "1.0.0.0",
    "debug": false,
    "appVer": "1.0.0",
    "appCode": 1,
    "channel":["测试"],
	
    "engineVer": "2.3.3",
    "title": "PackTest",
    "packageName": "com.xyzzlky.test.pack",
    "apiLevel": 28,
    "appABIs": ["armeabi-v7a"],
    "xxteaKey": "318927a2-2183-4c",
	
    "appName": "自动化打包测试",
    "appId": "1",
    "appKey": "1",
    "buglyAppId": "1",
    "buglyAppKey": "1",
    "orientation":"portrait"
}
  • clientVer 是配置当前构建的版本
  • debug 是否是测试版本,如果是则不上传到cos
  • appVer 安卓的versionName
  • appCode 安卓的versionCode
  • channel 打包的渠道
  • engineVer 引擎版本
  • title 构建的项目名 安卓打包的项目名
  • packageName 安卓的包名
  • apiLevel 安卓sdk版本
  • appABIs 支持的cpu架构
  • xxteaKey js加密密钥
  • appName 安卓应用名称
  • appId sdk的appid
  • appKey sdk的appkey
  • buglyAppId bugly的appId
  • buglyAppKey bugly的appKey
  • orientation 设备方向

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

    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;'
  1. 最后一行的配置我是写死的,如果有需要,都可以放到配置中。
  2. 因为mac和windows路径是不同的,所以要区分下目录,比如
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. 根据版本配置,也可以通过每次打包备份原版本,然后比较代码,判断是否进行构建,比如
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. 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'));

注意,每个版本的目录形式不一致,需要自行做出一定的修改

之后我们就可以生成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
    }

本代码主要是生成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失败');
        });

热更步骤,相对简单,如上就完成了热更打包,更新以及上传到cos。

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

安卓打包

任务列表

  • [x] 打包构建
  • [x] APK信息修改
  • [ ] 修改icon
  • [ ] sdk自动接入

安卓打包,说简单也简单——调用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();
            }
        });
  1. 如果发现该命令卡住,可以手动到android目录下执行gradlew命令查看原因,大部分情况是在下载gradle

执行

  1. 请确保已经安装jenkins(一步步next即可)

  2. 配置好git/svn/网址触发方式(请自行百度教程)

修改config的版本号,提交git或请求网址即可。

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

9赞

如果我要构建小游戏,或其他平台,是不是要修改config.json

我后期还会继续优化的,如果你需要自己修改,可以在config中添加platform选项,然后下面这个不要写死[quote=“dream_chou93, post:1, topic:98255”]
';template=link;platform=android;
[/quote]

一个项目会接入多个渠道的SDK,在项目构建目录下../frameworks/runtime-src/proj.android-studio.xx 会有多个文件夹。通过上面的上面可以生成不同的渠道包吗

可以的,我们也是多渠道打包

感谢大佬分享

请问 用命令行可以执行场景脚本吗

感谢大佬分享

大佬牛逼,给大佬生猴子!

:no_mouth: 咱们自己不是也有的咩