Я только что написал script, чтобы выпустить сборку одного из продуктов, над которыми я работаю. script выполняет эту работу, но мне не очень нравится сам код, выглядит как код спагетти и комбинированный аддон.
Есть ли более чистый способ сделать это? Я хотел бы иметь возможность запускать команды последовательно, регистрировать выходы (stdout.on('data')
) и когда задача завершена. (проще для дальнейшего отладки и ожидая выполнения задачи, успокаивая, чтобы узнать, что происходит на заднем плане)
Возможно, использование Promises поможет немного очистить битку, но все же я чувствую, что должен быть более чистый способ борьбы с несколькими командами.
Некоторое объяснение того, что делает код:
- Создайте тег с требуемым фиксацией и нужную версию тега, т.е.
git tag 1.2.5
. - Создайте файл выпуска с
gulp build
. - Создайте папку
doc/<tag>
. - Преобразуйте
doc/doc_reader.odt
вdoc/<tag>/documentation.pdf
. (Откройте его и экспортируйте в формате PDF) - Скопируйте
build/reader.js
иdoc/changelog.txt
в созданную папку. - Замените 3 файла.
- Зафиксировать все с сообщением фиксации:
Release 1.2.11
(например) - Нажмите.
- Создайте новую версию на 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 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}`);
});
});
});
});
Edit:
Вот реализация с использованием async/await (node 7.8.0)
Я использовал специальные модули mkdirp
и exec
, которые позволяют использовать с 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 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();