From 8eba35acf16d30eca37827bafd052dad3657b290 Mon Sep 17 00:00:00 2001 From: fuchunhui Date: Fri, 24 Sep 2021 18:21:53 +0800 Subject: [PATCH] feat: add release --- package.json | 12 ++++ scripts/release.js | 149 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 scripts/release.js diff --git a/package.json b/package.json index bccae91..a6e5f49 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Proxy tool based on nodejs for front-end development", "main": "index.js", "scripts": { + "release": "node scripts/release.js", "test": "node ./test/index.js" }, "keywords": [ @@ -11,6 +12,10 @@ "auto", "login" ], + "files": [ + "libs", + "index.js" + ], "author": "gismanli", "license": "MIT", "engines": { @@ -24,5 +29,12 @@ "bird-auth": "^2.1.0", "express": "^4.14.0", "raw-body": "^2.4.1" + }, + "devDependencies": { + "chalk": "^4.1.2", + "enquirer": "^2.3.6", + "execa": "^5.1.1", + "minimist": "^1.2.5", + "semver": "^7.3.5" } } diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000..bba507e --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,149 @@ +console.log('release template.') + +const execa = require('execa'); +const path = require('path'); +const fs = require('fs'); +const args = require('minimist')(process.argv.slice(2)); +const semver = require('semver'); +const chalk = require('chalk'); +const { prompt } = require('enquirer'); + +const pkgDir = process.cwd(); +const pkgPath = path.resolve(pkgDir, 'package.json'); +/** + * @type {{ name: string, version: string }} + */ +const pkg = require(pkgPath); +const pkgName = pkg.name; +const currentVersion = pkg.version; + +/** + * @type {import('semver').ReleaseType[]} + */ +const versionIncrements = ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease']; + +/** + * @param {import('semver').ReleaseType} i + */ +const inc = (i) => semver.inc(currentVersion, i); + +/** + * @param {string} bin + * @param {string[]} args + * @param {object} opts + */ +const run = (bin, args, opts = {}) => execa(bin, args, { stdio: 'inherit', ...opts }); + +/** + * @param {string} msg + */ +const step = (msg) => console.log(chalk.cyan(msg)); + +async function main() { + let targetVersion = args._[0]; + + if (!targetVersion) { + /** + * @type {{ release: string }} + */ + const { release } = await prompt({ + type: 'select', + name: 'release', + message: 'Select release type', + choices: versionIncrements.map((i) => `${i} (${inc(i)})`).concat(['custom']) + }); + + if (release === 'custom') { + /** + * @type {{ version: string }} + */ + const customVersion = await prompt({ + type: 'input', + name: 'version', + message: 'Input custom version', + initial: currentVersion + }); + targetVersion = customVersion.version; + } else { + targetVersion = release.match(/\((.*)\)/)[1]; + } + } + + if (!semver.valid(targetVersion)) { + throw new Error(`invalid target version: ${targetVersion}`); + } + + const tag = `v${targetVersion}`; + + /** + * @type {{ yes: boolean }} + */ + const { yes } = await prompt({ + type: 'confirm', + name: 'yes', + message: `Releasing ${tag}. Confirm?`, + initial: 'true' + }); + + if (!yes) { + return; + } + + step('\nUpdating package version...'); + updateVersion(targetVersion); + + const { stdout } = await run('git', ['diff'], { stdio: 'pipe' }); + if (stdout) { + step('\nCommitting changes...'); + await run('git', ['add', '-A']); + await run('git', ['commit', '-m', `release: ${tag}`]); + } else { + console.log('No changes to commit.'); + } + + step('\nPublishing package...'); + await publishPackage(targetVersion, run); + + step('\nPushing to GitHub...'); + await run('git', ['tag', tag]); + await run('git', ['push', 'origin', `refs/tags/${tag}`]); + await run('git', ['push']); + + console.log(); +} + +/** + * @param {string} version + */ +function updateVersion(version) { + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + pkg.version = version; + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); +} + +/** + * @param {string} version + * @param {Function} run + */ +async function publishPackage(version, run) { + const publicArgs = ['publish', '--access', 'public']; + if (args.tag) { + publicArgs.push(`--tag`, args.tag); + } + try { + await run('npm', publicArgs, { + stdio: 'pipe' + }); + console.log(chalk.green(`Successfully published ${pkgName}@${version}`)); + } catch (e) { + if (e.stderr.match(/previously published/)) { + console.log(chalk.red(`Skipping already published: ${pkgName}`)); + } else { + throw e; + } + } +} + +main().catch((err) => { + console.error(err); +});