@cliz/gpm
Version:
Git Project Manager
293 lines (292 loc) • 10.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageManager = void 0;
const config_1 = require("./config");
const cli_1 = require("@cliz/cli");
const path = require("path");
const semver = require("semver");
const event_1 = require("@zodash/event");
const utils_1 = require("../utils");
class PackageManager {
constructor() {
this.config = new config_1.ConfigManager(cli_1.api.path.cwd('.gpm.yml'));
}
async release() {
// 1. custom release progress with command
const releaseCommand = this.config.get('release');
if (releaseCommand) {
const commands = Array.isArray(releaseCommand)
? releaseCommand
: [releaseCommand];
return new Promise((resolve) => {
const pl = pipeline(commands);
pl.on('error', (e) => process.stderr.write(e));
pl.on('data', (e) => process.stdout.write(e));
pl.on('exit', () => {
resolve();
});
});
}
// 2. release
const projectPath = process.cwd();
// 2.1 Node.js package
const nodejsPath = path.resolve(projectPath, 'package.json');
// 2.2 Go
const goPath = path.resolve(projectPath, 'go.mod');
// 2.3 Zmicro
// const zmicroPath = path.resolve(projectPath, 'mod');
// format: x.y.z, means 1.0.0
// notice: not vx.y.z, not v1.0.0
let newVersion = '';
if (await cli_1.api.fs.exist(nodejsPath)) {
newVersion = await this.releaseNodePackage(nodejsPath);
}
else if (await cli_1.api.fs.exist(goPath)) {
newVersion = await this.releaseGoPackage(goPath);
}
// if found v prefix, maybe should check releaseNodePackage or releaseGoPackage
if (/^v/.test(newVersion)) {
throw new Error(`invalid new version ${newVersion}, should not start with v`);
}
// 3. commit message
await cli_1.api.$ `git commit -m "chore(release): bumped version to v${newVersion}"`;
// 4. create version tag
const tag = `v${newVersion}`;
await cli_1.api.$ `git tag ${tag}`;
// 5. push tag
await (0, utils_1.runInShell)(`git push origin ${tag}`, { cwd: projectPath });
// 6. push master
let current_branch = 'master';
try {
current_branch = await cli_1.api.$ `git rev-parse --abbrev-ref HEAD`;
}
catch (error) {
// nothing
}
await (0, utils_1.runInShell)(`git push origin ${current_branch}`, { cwd: projectPath });
}
// inputNewVersion from origin version to new version
// 1.0.0 -> 1.0.1
async inputNewVersion(originVersion) {
const answers = await cli_1.inquirer.prompt([
{
name: 'newVersion',
type: 'text',
message: `New version (origin: ${originVersion})?`,
default: `v${semver.inc(originVersion, 'patch')}`,
validate: (newVersion) => {
if (!newVersion)
throw new Error(`New version is required`);
if (!/^v/.test(newVersion))
throw new Error(`New version should start with v`);
if (!newVersion.includes('-')) {
if (!semver.gt(newVersion.slice(1), originVersion)) {
throw new Error(`New version should large than ${originVersion}`);
}
}
else {
// custom version
}
return true;
},
},
]);
// 2. change package.json version and write
let newVersion = answers.newVersion;
// fix version
// v1.0.0 -> 1.0.0
// 1.0.0 -> 1.0.0
if (/^v/.test(newVersion)) {
newVersion = newVersion.slice(1);
}
return newVersion;
}
async releaseNodePackage(pkgPath) {
const pkg = await cli_1.api.fs.json.load(pkgPath);
const projectPath = path.dirname(pkgPath);
// 1. get version
const newVersion = pkg.version = await this.inputNewVersion(pkg.version);
// sorted
await cli_1.api.fs.writeFile(pkgPath, JSON.stringify(sortPackageJSON(pkg), null, 2));
// 3. commit change
await cli_1.api.$.cd(projectPath);
await cli_1.api.$ `git add ${pkgPath}`;
return newVersion;
}
async releaseGoPackage(gomodPath) {
const projectPath = path.dirname(gomodPath);
const versionPath = cli_1.api.path.join(projectPath, 'version.go');
let lastVersion = "0.0.0";
if (!await cli_1.api.fs.exist(versionPath)) {
throw new Error(`version.go not found, please create it first, must include format: var Version = "1.0.0"`);
}
const text = await cli_1.api.fs.readFile(versionPath, 'utf8');
const matched = text.match(/var Version = "(.*)"/);
if (!matched || !matched[1]) {
throw new Error(`invalid version.go, should be format like: var Version = "1.0.0"`);
}
lastVersion = matched[1];
// 1. get version
const newVersion = await this.inputNewVersion(lastVersion);
const versionFileText = text.replace(/var Version = "(.*)"/, `var Version = "${newVersion}"`);
// sync to version.go
await cli_1.api.fs.writeFile(versionPath, versionFileText);
// 3. commit change
await cli_1.api.$.cd(projectPath);
await cli_1.api.$ `git add ${versionPath}`;
return newVersion;
}
async prepare() {
await this.config.prepare();
}
}
exports.PackageManager = PackageManager;
// sort-package-json
function sortPackageJSON(pkg) {
const fields = [
{ key: '$schema' },
{ key: 'name' },
/* vscode */ { key: 'displayName' },
{ key: 'version' },
{ key: 'private' },
{ key: 'description' },
/* vscode */ { key: 'categories' },
{ key: 'keywords' },
{ key: 'homepage' },
{ key: 'bugs' },
{ key: 'repository' },
{ key: 'funding' },
{ key: 'license' },
/* vscode */ { key: 'qna' },
{ key: 'author' },
{
key: 'maintainers',
},
{
key: 'contributors',
},
/* vscode */ { key: 'publisher' },
{ key: 'sideEffects' },
{ key: 'type' },
{ key: 'imports' },
{ key: 'exports' },
{ key: 'main' },
{ key: 'umd:main' },
{ key: 'jsdelivr' },
{ key: 'unpkg' },
{ key: 'module' },
{ key: 'source' },
{ key: 'jsnext:main' },
{ key: 'browser' },
{ key: 'types' },
{ key: 'typesVersions' },
{ key: 'typings' },
{ key: 'style' },
{ key: 'example' },
{ key: 'examplestyle' },
{ key: 'assets' },
{ key: 'bin' },
{ key: 'man' },
{ key: 'directories' },
{ key: 'files' },
{ key: 'workspaces' },
// node-pre-gyp https://www.npmjs.com/package/node-pre-gyp#1-add-new-entries-to-your-packagejson
{
key: 'binary',
},
{ key: 'scripts' },
{ key: 'betterScripts' },
/* vscode */ { key: 'contributes' },
/* vscode */ { key: 'activationEvents' },
{ key: 'husky' },
{ key: 'simple-git-hooks' },
{ key: 'pre-commit' },
{ key: 'commitlint' },
{ key: 'lint-staged' },
{ key: 'config' },
{ key: 'nodemonConfig' },
{ key: 'browserify' },
{ key: 'babel' },
{ key: 'browserslist' },
{ key: 'xo' },
{ key: 'prettier' },
{ key: 'eslintConfig' },
{ key: 'eslintIgnore' },
{ key: 'npmpkgjsonlint' },
{ key: 'npmPackageJsonLintConfig' },
{ key: 'npmpackagejsonlint' },
{ key: 'release' },
{ key: 'remarkConfig' },
{ key: 'stylelint' },
{ key: 'ava' },
{ key: 'jest' },
{ key: 'mocha' },
{ key: 'nyc' },
{ key: 'c8' },
{ key: 'tap' },
{ key: 'resolutions' },
{ key: 'dependencies' },
{ key: 'devDependencies' },
{ key: 'dependenciesMeta' },
{ key: 'peerDependencies' },
// TODO: only sort depth = 2
{ key: 'peerDependenciesMeta' },
{ key: 'optionalDependencies' },
{ key: 'bundledDependencies' },
{ key: 'bundleDependencies' },
/* vscode */ { key: 'extensionPack' },
/* vscode */ { key: 'extensionDependencies' },
{ key: 'flat' },
{ key: 'engines' },
{ key: 'engineStrict' },
{ key: 'languageName' },
{ key: 'os' },
{ key: 'cpu' },
{ key: 'preferGlobal' },
{ key: 'publishConfig' },
/* vscode */ { key: 'icon' },
/* vscode */ {
key: 'badges',
},
/* vscode */ { key: 'galleryBanner' },
/* vscode */ { key: 'preview' },
/* vscode */ { key: 'markdown' },
];
const _pkg = {};
for (const field of fields) {
if (pkg[field.key]) {
_pkg[field.key] = pkg[field.key];
delete pkg[field.key];
}
}
// rest
if (Object.keys(pkg).length !== 0) {
for (const key in pkg) {
_pkg[key] = pkg[key];
}
}
return _pkg;
}
function pipeline(commands) {
const emitter = new event_1.Event();
async function run() {
try {
for (const command of commands) {
await new Promise((resolve) => {
const child = cli_1.api.$.spawn(command);
child.on('error', (e) => emitter.emit('data', e));
child.on('data', (e) => emitter.emit('data', e));
child.on('exit', () => {
resolve();
});
});
}
emitter.emit('exit');
}
catch (error) {
emitter.emit('error', error);
}
}
run();
return emitter;
}