利用工具,解放微信小程序繁琐的代码上传流程

2020-05-21  本文已影响0人  NISAL

想一想,输入一个命令就可以直接上传小程序的代码,还能做一些前置操作,让整个上传流程自动化,岂不是很爽!

需求背景

原生的小体型的微信小程序项目通常不需要思考这方面的解决方案,因为将代码上传到微信服务器不需要太多的前置操作。

如果是大体型的,或是使用第三方框架开发的微信小程序项目,在上传前就需要一定的前置操作。

如切换分支,切换环境,编译代码,维护日志等。

场景也不仅限一个微信小程序项目,一套代码是有肯能发布多个微信小程序项目的,切换微信开发者工具去一个个上传代码效率非常低下。

同时人工的做这些操作是有可能出现问题的,如依赖的子项目忘记更新就上传了代码。

这些需要人工成本的固定步骤,希望有一套工具可以解放劳动力,同时可以避免因为人工失误带来的问题。

方案

微信开发者工具提供了命令行工具来做基础操作。

可以通过 cli upload 来上传代码。

cd 命令行工具所在目录

./cli upload --project 项目地址 -v 项目版本 -d '版本描述'

基于这个命令行工具,向上扩展内容即可。

需求

希望通过简单的命令就可以达到上传的目的。

假设工具名是 mpup

mpup --config=配置文件

分析配置文件

按照最基础版本来分析

首先是必要的配置项:

其他配置项:

根据需求,代码上传前要前置的做一些事情,这些事情其实都是一些命令。

如切换分支,就是执行 git 命令,编译代码,可能会使用 babel 相关工具。

所以需要一个命令集合。

另外应该在各个阶段暴露一些钩子,提供中断操作等。

所以一个配置文件大概会是这样的:

module.exports = {
  // 项目路径
  projectPath: '',

  // 小程序工具路径
  mpToolPath: '',

  // 代码版本
  ver: '',

  // 版本描述,这个配置是传递给命令行工具的,微信上传代码前填写的表单中的一项,但不是必填项
  desc: '',

  // 命令集合
  commends: [
    {
      // 要执行的命令
      cmd: '',
      // 执行命令前做的事
      async before() {},
      // 执行命令后做的事
      async after() {},
      // 执行命令失败的时候做的事
      async error() {},
    },
  ],

  // 所有钩子执行前
  async beforeExecAllCommends() {},

  // 所有钩子执行后
  async afterExecAllCommends() {},

  // 上传前
  async beforeUpload() {},

  // 上传后
  async afterUpload() {},
};

实现

获取配置文件

配置文件是通过命令后传入的,所以使用 yargs 来处理命令后传入的参数。

const yargs = require('yargs');

const argv = yargs
  .usage('mpup [options]')
  .option('config', {
    describe: '上传配置',
    type: 'string',
  })
  .help('help')
  .argv;

使用 mpup --config=./mpup.config.js ,就可以从 argv 里取出 config 参数。

获取到参数后就是取到配置文件。

const path = require('path');
const { config } = argv;

const configPath = path.resolve(process.cwd(), config);

const mpupConfig = require(configPath);

通过 process.cwd 获取到执行命令的路径,然后和 config 参数一起处理,得到配置文件路径,再 require 到就得到了配置。

中断方法

只要退出了当前进程,就是中断了上传,所以中断方法很简单。

const abort = () => {
  process.exit();
};

执行所有前置命令

执行命令需要用到 child_process 模块的 exec 或者 execSync 方法,这两个方法作用是相同的,用于在某个工作目录下执行某个命令。

这里使用 exec 并简单封装一下。

const { exec } = require('child_process');

const execCmd = (cmd, path) => {
  return new Promise((resolve, reject) => {
    try {
      exec(cmd, { cwd: path }, (err, stdout, stderr) => {
        if (stdout) {
          resolve(stdout);
          return;
        }
        reject(err || stderr);
      });
    } catch (e) {
      reject(e);
    }
  });
};

随后就是从 mpupConfig 中获取到所有命令,并执行。

const {
  commends,
  beforeExecAllCommends,
  afterExecAllCommends,
} = mpupConfig;

// 开始执行命令前的钩子
await beforeExecAllCommends({ abort });

for (let i = 0, len = commends.length; i < len; i++) {
  const commend = commends[i];
  const {
    cmd,
    before = () => {},
    after = () => {},
    error = () => {},
  } = commend;

  await before({ abort, commend });

  let stdout = '';
  try {
    stdout = await execCmd(cmd);
  } catch (e) {
    await error({
      e,
      abort, // 中断函数
    })
  }

  await after({ abort, commend, stdout });
}

// 结束所有命令后的钩子
await afterExecAllCommends({ abort });

上传代码

这一步就是调用微信团队提供的工具了。

// 判断平台,因为在windows和mac下执行的命令不同
const isMacOS = () => {
  return !(/^win/.test(process.platform));
};

// 获取到 项目路径 、 工具路径 、 版本 、 版本描述
const {
  projectPath,
  mpToolPath,
  desc,
  ver,
  beforeUpload,
  afterUpload,
} = mpupConfig;

// 根据环境和配置拼出命令
const uploadCmd = `${isMacOS() ? './' : ''}cli${isMacOS() ? '' : '.bat'} upload --project=${projectPath} --version=${ver} --desc=${desc}`;

// 上传前的钩子
await beforeUpload({ abort });

// 到命令行工具的地址执行上传命令
const stdout = await execCmd(uploadCmd, mpToolPath);

// 上传后的钩子
await afterUpload({ abort, stdout });

创建软连接

因为最终是要作为命令后工具使用,所以需要创建一个软连接。

package.json 中加入 bin 属性。

{
  "bin": {
    "mpup-test": "./mpup.js"
  }
}

上述代码都放在 ./mpup.js 中,为了防止本地已经安装好的 mpup 冲突,这里将名字作为了 mpup-test

随后在 package.json 所在的目录执行 npm link,就会建立软连接,碰到权限问题记得加 sudo 前缀。

mpup.js 文件第一行需要加入 #! /usr/bin/env node,表示使用 node 来执行文件。

成果

先汇总一份代码:

#! /usr/bin/env node
const { exec } = require('child_process');
const yargs = require('yargs');
const path = require('path');

// 用于判断平台,因为在windows和mac下执行的命令不同
const isMacOS = () => {
  return !(/^win/.test(process.platform));
};

const execCmd = (cmd, path) => {
  return new Promise((resolve, reject) => {
    try {
      exec(cmd, { cwd: path }, (err, stdout, stderr) => {
        if (stdout) {
          resolve(stdout);
          return;
        }
        reject(err || stderr);
      });
    } catch (e) {
      reject(e);
    }
  });
};

const abort = () => {
  process.exit();
};

const argv = yargs
  .usage('mpup [options]')
  .option('config', {
    describe: '上传配置',
    type: 'string',
  })
  .help('help')
  .argv;

(async () => {

  const { config } = argv;

  const configPath = path.resolve(process.cwd(), config);

  const mpupConfig = require(configPath);

  const {
    commends,
    beforeExecAllCommends,
    afterExecAllCommends,
  } = mpupConfig;
  
  // 开始执行命令前的钩子
  await beforeExecAllCommends({ abort });

  for (let i = 0, len = commends.length; i < len; i++) {
    const commend = commends[i];
    const {
      cmd,
      before = () => {},
      after = () => {},
      error = () => {},
    } = commend;

    await before({ abort, commend });

    let stdout = '';
    try {
      stdout = await execCmd(cmd);
    } catch (e) {
      await error({
        e,
        abort, // 中断函数
      })
    }

    await after({ abort, commend, stdout });
  }

  // 结束所有命令后的钩子
  await afterExecAllCommends({ abort });

  // 获取到 项目路径 、 工具路径 、 版本 、 版本描述
  const {
    projectPath,
    mpToolPath,
    desc,
    ver,
    beforeUpload,
    afterUpload,
  } = mpupConfig;

  // 根据环境和配置拼出命令
  const uploadCmd = `${isMacOS() ? './' : ''}cli${isMacOS() ? '' : '.bat'} upload --project=${projectPath} --version=${ver} --desc=${desc}`;

  // 上传前的钩子
  await beforeUpload({ abort });

  // 到命令行工具的地址执行上传命令
  const uploadStdout = await execCmd(uploadCmd, mpToolPath);

  // 上传后的钩子
  await afterUpload({ abort, stdout: uploadStdout });
})();

现在可以在工程目录下先安装一下依赖 yargs

npm i yargs -S

随后提供一份配置文件。

// mpup.config.js
module.exports = {
  // 项目路径
  // 记得配置这个哦 不然会失败的
  projectPath: '',

  // 小程序工具路径
  mpToolPath: '/Applications/wechatwebdevtools.app/Contents/MacOS',

  // 代码版本
  ver: '1.3.1',

  // 版本描述,这个配置是传递给命令行工具的,微信上传代码前填写的表单中的一项,但不是必填项
  desc: 'beta版本',

  // 命令集合
  commends: [
    {
      // 要执行的命令
      cmd: 'ls -a',
      // 执行命令前做的事
      async before() {
        console.log('before ls -a');
      },
      // 执行命令后做的事
      async after({ stdout }) {
        console.log('after ls -a');
        console.log(stdout);
      },
    },
  ],

  // 所有钩子执行前
  async beforeExecAllCommends() {
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log('开始前先停两秒!');
        resolve();
      }, 2000);
    });
  },

  // 所有钩子执行后
  async afterExecAllCommends() {
    console.log('执行完所有钩子啦');
  },

  // 上传前
  async beforeUpload() {
    console.log('开始上传');
  },

  // 上传后
  async afterUpload({ stdout }) {
    console.log(stdout);
    console.log('结束!');
  },
};

配置文件名为 mpup.config.js

接下来就是运行了。

执行

执行前有一些前置工作:

  1. 打开小程序的服务端口,(小程序开发者工具 => 设置 => 安全设置 => 开启服务端口)
  2. 使用较新稳定版的开发者工具,测试中发现老的稳定版本、 RC 版本和 Nightly Build 版本的命令行工具可能无法运行
  3. 登入开发者工具,且登入的账户拥有项目的上传代码权限

执行一遍 🚀!

mpup-test --config=./mpup.config.js

上面这份配置的执行结果是这样的:

扩展

到目前为止实现的都是基础功能,代码也仅仅是为了可以让流程跑通而写的,有许多可以改进的地方。

这也是整个工具最核心的部分。

目前代码已经开源,项目名也是 mpup,还在持续维护迭代中,因为测试场景不足,可能会有一些bug。

项目:https://github.com/hiNISAL/mpup

其配套的服务端工具也在开发中,因为本地上传代码有局限性,所以理想的情况应该是服务端上传。

谢谢各位观众老爷。

上一篇 下一篇

猜你喜欢

热点阅读