Just think about it, input a command can directly upload the code of the small program, but also do some pre-operation, so that the whole upload process automation, isn’t very cool!

Demand background

Small, native wechat applets usually don’t need to think about this solution, because uploading the code to the wechat server doesn’t require much front-loading.

If it is a large size, or the use of third-party framework development of wechat small program projects, before uploading will need a certain amount of pre-operation.

Such as switching branches, switching environments, compiling code, maintaining logs, etc.

The scene is not limited to one wechat small program project, a set of code can release multiple wechat small program projects, it is very inefficient to switch the wechat developer tools to upload the code one by one.

Doing this manually can cause problems, such as relying on subprojects that forget to update and upload code.

These are fixed steps that require labor costs, and the hope is that a set of tools will free up labor while avoiding problems caused by human error.

plan

Wechat developer tools provides command line tools to do basic operations.

You can upload code via cli Upload.

cd/cli upload --project project address -v project version-d 'Version Description'
Copy the code

Based on the command line tool, you can simply expand the content up.

demand

It is hoped that a simple command can achieve the purpose of uploading.

Suppose the tool name is mpup.

Mpup --config= configuration fileCopy the code

Analyzing configuration files

Analyze according to the most basic version

First, the necessary configuration items:

  • Project pathThat’s the code to upload
  • Path to the command line tool provided by the applets developer toolFor location tools, upload code
  • The version numberEach time the upload code logo, small program official requirements of the mandatory field

Other configuration items:

According to the requirements, there are a few things that need to be done before the code is uploaded. These things are actually commands.

If you switch branches, you run git commands and compile code, probably using Babel tools.

So you need a set of commands.

In addition, some hooks should be exposed at various stages, providing interrupt operations, and so on.

  • The command setAll commands to be executed before uploading
  • Hooks for each stageProvides some additional capabilities, such as logging, interrupt uploads

So a configuration file might look something like this:

module.exports = {
  // Project path
  projectPath: ' '.// Applets tool path
  mpToolPath: ' '.// Code version
  ver: ' '.// Version description, this configuration is passed to the command line tool, wechat to fill in the form before uploading code, but not mandatory
  desc: ' '.// Set of commands
  commends: [
    {
      // The command to execute
      cmd: ' '.// What to do before the command is executed
      async before() {},
      // What you do after executing a command
      async after() {},
      // What to do when the command fails to execute
      async error() {},
    },
  ],

  // Before all hooks are executed
  async beforeExecAllCommends() {},

  // All hooks are executed
  async afterExecAllCommends() {},

  / / before uploading
  async beforeUpload() {},

  / / after upload
  async afterUpload() {},
};
Copy the code

implementation

Obtaining configuration files

The configuration file is passed in after the command, so yargs is used to process the parameters passed in after the command.

const yargs = require('yargs');

const argv = yargs
  .usage('mpup [options]')
  .option('config', {
    describe: 'Upload configuration'.type: 'string',
  })
  .help('help')
  .argv;
Copy the code

With mpup –config=./mpup.config.js, you can retrieve the config argument from argv.

Once you get the parameters, you get the configuration file.

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

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

const mpupConfig = require(configPath);
Copy the code

CWD obtains the path to execute the command and processes it with the config parameter to obtain the configuration file path and require to obtain the configuration.

The interrupt method

As long as you exit the current process, you interrupt the upload, so the interrupt method is very simple.

const abort = (a)= > {
  process.exit();
};
Copy the code

Run all the preceding commands

The exec or execSync methods of the child_process module are used to execute a command in a working directory.

Here we use exec and encapsulate it briefly.

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); }}); };Copy the code

The next step is to get all the commands from mpupConfig and execute them.

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

// The hook before starting command execution
await beforeExecAllCommends({ abort });

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

  await before({ abort, commend });

  let stdout = ' ';
  try {
    stdout = await execCmd(cmd);
  } catch (e) {
    await error({
      e,
      abort, // interrupt function})}await after({ abort, commend, stdout });
}

// End all commands with the hook
await afterExecAllCommends({ abort });
Copy the code

Upload code

This step is to call the tools provided by the wechat team.

// Determine the platform, because the commands executed under Windows and MAC are different
const isMacOS = (a)= > {
  return! (/^win/.test(process.platform));
};

// Get the project path, tool path, version, and version description
const {
  projectPath,
  mpToolPath,
  desc,
  ver,
  beforeUpload,
  afterUpload,
} = mpupConfig;

// Spell out commands according to the environment and configuration
const uploadCmd = `${isMacOS() ? '/' : ' '}cli${isMacOS() ? ' ' : '.bat'} upload --project=${projectPath} --version=${ver} --desc=${desc}`;

// Hook before upload
await beforeUpload({ abort });

// Go to the address of the command line tool to execute the upload command
const stdout = await execCmd(uploadCmd, mpToolPath);

// Upload the hook
await afterUpload({ abort, stdout });
Copy the code

Creating a Soft connection

Because it is ultimately intended to be used as a post-command tool, you need to create a soft connection.

Add the bin property to package.json.

{
  "bin": {
    "mpup-test": "./mpup.js"}}Copy the code

The above code is in./ mppup. Js, where the name is mpP-test to prevent mPUP conflicts already installed locally.

Then execute NPM link in package.json directory to establish soft connection.

The first line of the mput.js file needs to add #! /usr/bin/env node, which uses node to execute files.

results

Let’s put together a code:

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

// It is used to determine the platform, since the commands executed under Windows and MAC are different
const isMacOS = (a)= > {
  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 = (a)= > {
  process.exit();
};

const argv = yargs
  .usage('mpup [options]')
  .option('config', {
    describe: 'Upload configuration'.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;
  
  // The hook before starting command execution
  await beforeExecAllCommends({ abort });

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

    await before({ abort, commend });

    let stdout = ' ';
    try {
      stdout = await execCmd(cmd);
    } catch (e) {
      await error({
        e,
        abort, // interrupt function})}await after({ abort, commend, stdout });
  }

  // End all commands with the hook
  await afterExecAllCommends({ abort });

  // Get the project path, tool path, version, and version description
  const {
    projectPath,
    mpToolPath,
    desc,
    ver,
    beforeUpload,
    afterUpload,
  } = mpupConfig;

  // Spell out commands according to the environment and configuration
  const uploadCmd = `${isMacOS() ? '/' : ' '}cli${isMacOS() ? ' ' : '.bat'} upload --project=${projectPath} --version=${ver} --desc=${desc}`;

  // Hook before upload
  await beforeUpload({ abort });

  // Go to the address of the command line tool to execute the upload command
  const uploadStdout = await execCmd(uploadCmd, mpToolPath);

  // Upload the hook
  await afterUpload({ abort, stdout: uploadStdout }); }) ();Copy the code

You can now install the dependency Yargs in the project directory.

npm i yargs -S
Copy the code

A configuration file is then provided.

// mpup.config.js
module.exports = {
  // Project path
  // Remember to configure this or it will fail
  projectPath: ' '.// Applets tool path
  mpToolPath: '/Applications/wechatwebdevtools.app/Contents/MacOS'.// Code version
  ver: '1.3.1'.// Version description, this configuration is passed to the command line tool, wechat to fill in the form before uploading code, but not mandatory
  desc: 'beta'.// Set of commands
  commends: [
    {
      // The command to execute
      cmd: 'ls -a'.// What to do before the command is executed
      async before() {
        console.log('before ls -a');
      },
      // What you do after executing a command
      async after({ stdout }) {
        console.log('after ls -a');
        console.log(stdout); }},].// Before all hooks are executed
  async beforeExecAllCommends() {
    return new Promise((resolve) = > {
      setTimeout((a)= > {
        console.log('Stop for two seconds before you start! ');
        resolve();
      }, 2000);
    });
  },

  // All hooks are executed
  async afterExecAllCommends() {
    console.log('Executed all hooks');
  },

  / / before uploading
  async beforeUpload() {
    console.log('Start uploading');
  },

  / / after upload
  async afterUpload({ stdout }) {
    console.log(stdout);
    console.log('to end! '); }};Copy the code

The configuration file is called mpup.config.js.

Now it’s run.

perform

There are a few things to do before execution:

  1. Open the service port of the applet (Applet developer Tools => Settings => Security Settings => Open service port)
  2. Using the newer stable developer tools, the old stable, RC, and Nightly Build command-line tools may not work in our tests
  3. Login developer tools, and login account has the project upload code permission

Run 🚀!

mpup-test --config=./mpup.config.js
Copy the code

The result of this configuration is as follows:

extension

What has been implemented so far is basic functionality, and the code has only been written to make the process run, with plenty of room for improvement.

This is the core of the tool, and it’s easy to get away from.

At present, the code is open source and the project name is mPUP. It is still in continuous maintenance iteration. Due to insufficient test scenarios, there may be some bugs.

Project: github.com/hiNISAL/mpu…

A companion server tool is also under development, and since the local upload code has limitations, the ideal situation would be server upload.

Thank you, my lords.