@adinsure-ops/ops-cli
Version:
Operations CLI for working with AdInsure
200 lines (199 loc) • 8.75 kB
JavaScript
import { __asyncValues, __awaiter } from "tslib";
import { Flags, ux } from '@oclif/core';
import chalk from 'chalk';
import { spawn } from 'child_process';
import CommandBase from '../../command.base.js';
import NpmContext from '../../lib/npm.js';
class NpmPublish extends CommandBase {
publishPackage(pkg_1) {
return __awaiter(this, arguments, void 0, function* (pkg, cmd = ['yarn', 'publish']) {
var _a, e_1, _b, _c, _d, e_2, _e, _f;
let sh;
let args;
// cross platform compatibility
if (process.platform === 'win32') {
sh = 'cmd';
args = ['/c', ...cmd];
}
else {
[sh, ...args] = cmd;
}
try {
this.cp = spawn(sh, args, {
cwd: pkg.path,
env: Object.assign(Object.assign({}, process.env), process.stdout.isTTY ? { FORCE_COLOR: process.env.FORCE_COLOR || '1' } : {}),
stdio: 'pipe',
});
let data = '';
try {
for (var _g = true, _h = __asyncValues(this.cp.stdout), _j; _j = yield _h.next(), _a = _j.done, !_a; _g = true) {
_c = _j.value;
_g = false;
const chunk = _c;
data += chunk;
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_g && !_a && (_b = _h.return)) yield _b.call(_h);
}
finally { if (e_1) throw e_1.error; }
}
let error = '';
try {
for (var _k = true, _l = __asyncValues(this.cp.stderr), _m; _m = yield _l.next(), _d = _m.done, !_d; _k = true) {
_f = _m.value;
_k = false;
const chunk = _f;
error += chunk;
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (!_k && !_d && (_e = _l.return)) yield _e.call(_l);
}
finally { if (e_2) throw e_2.error; }
}
const exitCode = yield new Promise((resolve) => {
this.cp.on('close', resolve);
});
if (exitCode) {
throw new Error(`subprocess error exit ${exitCode}, ${error}`);
}
return { data, error };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (error) {
throw new Error(error.message);
}
});
}
checkPackageCanPublish(pkg, allPackages, version, retries) {
return __awaiter(this, void 0, void 0, function* () {
this.log('');
ux.action.start(`${pkg.name}@${pkg.version}`, undefined, { stdout: true });
const matchedPackage = allPackages.value.find(azurePackage => azurePackage.name === pkg.name);
if (pkg.version !== version) {
this.warn(`${pkg.name}@${pkg.version} doesn't match desired publish version!`);
ux.action.stop(chalk.red(`version doesn't match version ${version}`));
throw new Error(`Version in package.json doesn't match desired published version`);
}
if (!matchedPackage) {
ux.action.stop(`${chalk.green('new package')}`);
ux.action.start(`publish new package ${pkg.name}@${pkg.version} from ${pkg.jsonPath}`, undefined, { stdout: true });
try {
yield this.publishPackage(pkg);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (error) {
if (error instanceof Error && error.message.includes('subprocess error exit')) {
// Check if the error message contains 'subprocess error exit'
// Extract the HTTP status code from the error message
const statusCodeMatch = /\d{3}/.exec(error.message);
const statusCode = statusCodeMatch ? Number.parseInt(statusCodeMatch[0], 10) : null;
if (statusCode && (statusCode >= 400 && statusCode < 600)) {
for (let i = 0; i < retries - 1; i++) {
yield this.publishPackage(pkg);
}
}
}
}
ux.action.stop(`${chalk.green('done')}`);
return true;
}
const matchVersion = matchedPackage.versions.find(azurePackageVersion => azurePackageVersion.version === pkg.version);
if (!matchVersion) {
ux.action.stop(`${chalk.yellow('version missing')}`);
ux.action.start(`publish new version ${pkg.name}@${pkg.version} from ${pkg.jsonPath}`, undefined, { stdout: true });
yield this.publishPackage(pkg);
ux.action.stop(`${chalk.green('done')}`);
return true;
}
this.warn(`${pkg.name}@${pkg.version} already exists`);
ux.action.stop(`${chalk.blue('skipping')}`);
return false;
});
}
publishPackages(packages, allPackages, version, retries) {
return __awaiter(this, void 0, void 0, function* () {
let errorCount = 0;
this.log(`publishing ${packages.length} packages:`);
this.log(packages.map(pkg => pkg.name).toString());
for (const pkg of packages) {
try {
yield this.checkPackageCanPublish(pkg, allPackages, version, retries);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (error) {
this.warn(error.message);
errorCount++;
continue;
}
}
if (errorCount > 0) {
throw new Error(`${errorCount} with Errors!`);
}
});
}
run() {
const _super = Object.create(null, {
run: { get: () => super.run }
});
return __awaiter(this, void 0, void 0, function* () {
_super.run.call(this);
try {
const { flags } = yield this.parse(NpmPublish);
const npmContext = new NpmContext();
ux.action.start('Parsing packages to check', undefined, { stdout: true });
const packages = yield npmContext.packageList(this, flags.package, flags.exclude);
ux.action.stop(`${chalk.green('done')}`);
ux.action.start('Getting list of published packages', undefined, { stdout: true });
const allPackages = (yield npmContext.getAllNpmPackages(flags.pat)).data;
ux.action.stop(`${chalk.green('done')}`);
yield this.publishPackages(packages, allPackages, flags.version, flags.retry);
ux.action.stop('success');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (error) {
ux.action.stop('error');
this.error(`${error.message}`);
}
});
}
}
NpmPublish.description = 'Publish npm packages';
NpmPublish.usage = 'npm:publish [options]';
NpmPublish.examples = [
'$ ops npm:publish -v 15.0.0',
'$ ops npm:publish -v 15.0.0',
'$ ops npm:publish -p @adinsure-tooling/* -v 15.0.0',
'$ ops npm:publish -p @adinsure-tooling/* -x @adisure-tooling/studio-services -v 6.0.0',
];
NpmPublish.flags = {
package: Flags.string({
char: 'p',
description: 'package name filter you want to check',
multiple: true,
}),
exclude: Flags.string({
char: 'x',
description: 'exclude packages from filter',
multiple: true,
}),
version: Flags.string({
char: 'v',
description: 'version we want to check if published',
required: true,
}),
pat: Flags.string({
description: 'PAT token or AZURE_DEVOPS_EXT_PAT environment variable',
env: 'AZURE_DEVOPS_EXT_PAT',
}),
retry: Flags.integer({
description: 'Number of npm publishing retries in case of 4xx or 5xx http errors',
default: 3,
}),
};
export default NpmPublish;