node.js - 有没有更好的方法来使用 Node.js 运行 CLI 命令?

标签 node.js command-line-interface

我刚刚编写了一个脚本来发布我正在开发的产品之一的版本。该脚本完成了这项工作,但我不太喜欢代码本身,看起来像意大利面条代码和回调 hell 的组合。

有没有更干净的方法来做到这一点?我希望能够连续运行命令,记录输出( stdout.on('data') )以及任务何时完成。 (更容易进一步调试以及等待任务完成时,放心地了解后台发生的情况)

也许使用 Promises 有助于清理困惑,但我仍然觉得应该有一种更干净的方法来处理多个命令。

<小时/>

有关代码功能的一些解释:

  1. 使用您想要的提交和您想要的标签版本创建一个标签,即:git tag 1.2.5 .
  2. 使用 gulp build 构建发布文件.
  3. 创建文件夹doc/<tag> .
  4. 转换 doc/doc_reader.odtdoc/<tag>/documentation.pdf 。 (打开它并导出为 PDF)
  5. 复制build/reader.jsdoc/changelog.txt在创建的文件夹中。
  6. 压缩 3 个文件。
  7. 使用提交消息提交所有内容:Release 1.2.11 (例如)
  8. 推。
  9. 使用您刚刚推送的提交和相同的标签在 GitHub 上创建新版本。
<小时/>

这里是代码,作为示例。 (ES5, Node 4.6.0+)

var mkdirp = require('mkdirp');
var fs = require('fs-extra');
var path = require('path');
var spawn = require('child_process').spawn;
var zip = new require('node-zip')();

var package = require('../package.json');
var version = package.version;
var releaseDirectory = 'doc'
var gitTagProcess = spawn('git', ['tag', version]);
var gulpBuildProcess = spawn('gulp', ['build']);

console.log(`Running "git tag ${version}"...`);
gitTagProcess.stdout.on('data', function (chunk) {
  console.log(chunk.toString('utf8'));
});

gitTagProcess.on('close', function () {
  console.log('Tag created.')

  console.log('Running "gulp build"...');
  gulpBuildProcess.stdout.on('data', function (chunk) {
    console.log(chunk.toString('utf8'));
  });

  gulpBuildProcess.on('close', function () {
    console.log('"gulp build" done.')

    console.log(`Creating "${releaseDirectory}/${version}" directory.`)
    mkdirp(`${releaseDirectory}/${version}`, function () {
      console.log('Directory created.');
      var docFile = `${releaseDirectory}/doc_reader.md`;
      console.log(`Converting ${docFile} to pdf ...`);
      var docBuildProcess = spawn('npm', ['run', 'build:doc']);

      docBuildProcess.stdout.on('data', function (chunk) {
        console.log(chunk.toString('utf8'));
      });

      docBuildProcess.on('close', function () {
        console.log('Doc created.');

        console.log('Copying changelog.txt ...');
        fs.copySync('doc/changelog.txt', `doc/${version}/changelog.txt`);
        console.log('changelog.txt copied.');

        console.log(`Copying "build/reader.js" to "doc/reader-${version}.js" and "doc/reader.js" ...`);
        fs.copySync('build/reader.js', `doc/${version}/reader.js`);
        fs.copySync('build/reader.js', `doc/${version}/reader-${version}.js`);
        console.log('reader.js copied.');

        console.log('Zipping all files ...');
        zip.file('changelog.txt', fs.readFileSync(`doc/${version}/changelog.txt`));
        zip.file('doc_reader.pdf', fs.readFileSync(`doc/${version}/doc_reader.pdf`));
        zip.file('reader.js', fs.readFileSync(`doc/${version}/reader.js`));
        zip.file(`reader-${version}.js`, fs.readFileSync(`doc/${version}/reader-${version}.js`));

        var data = zip.generate({ base64: false, compression: 'DEFLATE' });
        var zipFilename = `doc/${version}/HTML5Reader_${version}.zip`;
        fs.writeFileSync(zipFilename, data, 'binary'); // it's important to use *binary* encode
        console.log(`${zipFilename} created.`);

        console.log(`\nRelease ${version} done. Please add generated files and commit using:`);
        console.log(`\n\tgit add * && git commit -m "Release ${version}"`);
        console.log(`\n\nDon't forget to push and create a new release on GitHub at https://github.com/$domain/$product/releases/new?tag=${version}`);
      });
    });
  });
});
<小时/>

编辑:

这是使用 async/await 的实现( Node 7.8.0) 我用的是特殊的mkdirpexec模块,允许与 await 一起使用。但我找不到 spawn 的等效项.

const mkdirp = require('async-mkdirp');
const fs = require('fs-extra');
const spawn = require('child-process-promise').spawn;
const exec = require('mz/child_process').exec;
const zip = new require('node-zip')();
const c = require('chalk');

const error = c.bold.red;
const warn = c.yellow;
const info = c.cyan;
const info2 = c.magenta;

const version = require('../package.json').version;
const releaseDirectory = 'doc'

async function git_tag() {
  async function exec_git_tag() {
    return await exec(`git tag ${version}`);
  }

  console.log(info(`Creating git tag ${version}`));
  return exec_git_tag()
    .then(() => {
      console.log(info(`Git tag created for ${version}`))
    })
    .catch((err) => {
      console.log(warn('warn', err));
    })
    // Finally
    .then(() => {
      console.log(info(`"git tag ${version}" - Completed`))
    });
};

async function gulp_build() {
  async function exec_gulp_build() {
    const promise = spawn('gulp', ['build'])
    const childProcess = promise.childProcess;

    childProcess.stdout.on('data', (data) => {
      console.log(info2(data.toString()));
    });
    childProcess.stderr.on('data', (data) => {
      console.log(error(data.toString()));
    });

    return promise
      .catch((err) => {
        console.error(error(err));
      })
      // Finally
      .then(() => {
        console.log(info('"gulp build" - Completed'))
      });
  }

  console.log(info('Running "gulp build"...'))
  return exec_gulp_build()
}

async function create_dir() {
  const dirPath = `${releaseDirectory}/${version}`;
  console.log(info(`Creating "${dirPath}" directory.`))
  await mkdirp(`${dirPath}`);
  console.log(info(`Directory ${dirPath} created.`));
}

async function build_doc() {
  const docFile = `${releaseDirectory}/doc_reader.md`;
  console.log(info(`Converting ${docFile} to pdf ...`));

  async function exec_build_doc() {
    return await exec(`npm run build:doc`);
  }

  return exec_build_doc()
    .catch((err) => {
      console.error(error(err));
    })
    .then(() => {
      console.log(info(`Doc "${docFile}" created.`));
    })
}

function copy_files() {
  console.log(info('Copying changelog.txt ...'));
  fs.copySync('doc/changelog.txt', `doc/${version}/changelog.txt`);
  console.log(info('changelog.txt copied.'));

  console.log(info(`Copying "build/reader.js" to "doc/reader-${version}.js" and "doc/reader.js" ...`));
  fs.copySync('build/reader.js', `doc/${version}/reader.js`);
  fs.copySync('build/reader.js', `doc/${version}/reader-${version}.js`);
  console.log(info('reader.js copied.'));
}

function zip_files() {
  console.log(info('Zipping all files ...'));
  zip.file('changelog.txt', fs.readFileSync(`doc/${version}/changelog.txt`));
  zip.file('doc_reader.pdf', fs.readFileSync(`doc/${version}/doc_reader.pdf`));
  zip.file('reader.js', fs.readFileSync(`doc/${version}/reader.js`));
  zip.file(`reader-${version}.js`, fs.readFileSync(`doc/${version}/reader-${version}.js`));

  const data = zip.generate({ base64: false, compression: 'DEFLATE' });
  const zipFilename = `doc/${version}/HTML5Reader_${version}.zip`;
  fs.writeFileSync(zipFilename, data, 'binary'); // it's important to use *binary* encode
  console.log(info(`${zipFilename} created.`));
}

async function release() {
  await git_tag();
  await gulp_build();
  await create_dir();
  await build_doc();
  copy_files();
  zip_files();

  console.log(`\nRelease ${version} done. Please add generated files and commit using:`);
  console.log(`\n\tgit add . && git commit -m "Release ${version}"`);
}

release();

最佳答案

有一个 mz 模块在这里非常有用。请参阅:

这与async/await相结合将允许您编写如下代码:

let exec = require('mz/child_process').exec;

(async () => {
  let version = await exec('node --version');
  console.log(version);
  let result = await exec('some other command');
  console.log(result);
  // ...
})();

这是一个简单的示例,但您可以通过这种方式使用 child_processfs 和许多其他模块中的所有函数。

这里重要的是,这段代码仍然是异步的并且非阻塞

请注意,您只能在使用 async 关键字创建的函数内部使用 await。欲了解更多信息,请参阅:

有关浏览器的支持,请参阅:

有关 Node 中的支持,请参阅:

在没有对 asyncawait 原生支持的地方,您可以使用 Babel:

或者使用稍微不同的语法,基于生成器的方法,如co或Bluebird协程:

关于node.js - 有没有更好的方法来使用 Node.js 运行 CLI 命令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43187952/

相关文章:

mysql - db URL 在 MySQL Workbench 中有效,但在 node.js 中无效(getaddrinfo ENOTFOUND)

linux - 匹配模式的 mv 文件夹

javascript - Node 8 与 11 时区不同

node.js - 如何使用 Node.js 将对话数据记录到 MS Bot Framework v4 中的本地存储中

bash - 我可以从 CLI 缓存 Linux 上命令的输出吗?

命令行上的 Python 模块

jenkins - 从命令行调用时覆盖 rpmbuild 构建位置

webpack - 如何在命令行中删除文件名中包含特定单词的文件?

node.js - 在 super 账本结构中动态执行交易时出错

javascript - heroku 日志 sh : 1: concurrently: not found