Merge branch 'master' into production

production
钟良源 4 weeks ago
commit f1fe33942e

@ -78,6 +78,7 @@
"postcss-less": "^5.0.0",
"prettier": "^2.4.1",
"pretty-quick": "^3.1.2",
"prompts": "^2.4.2",
"stylelint": "^14.1.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-standard": "^24.0.0",

@ -192,6 +192,9 @@ importers:
pretty-quick:
specifier: ^3.1.2
version: 3.3.1(prettier@2.8.8)
prompts:
specifier: ^2.4.2
version: 2.4.2
stylelint:
specifier: ^14.1.0
version: 14.16.1
@ -3312,6 +3315,10 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
kleur@4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
@ -4021,6 +4028,10 @@ packages:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@ -4475,6 +4486,9 @@ packages:
simple-swizzle@0.2.4:
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
size-sensor@1.0.2:
resolution: {integrity: sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==}
@ -9351,6 +9365,8 @@ snapshots:
kind-of@6.0.3: {}
kleur@3.0.3: {}
kleur@4.1.5: {}
klona@2.0.6: {}
@ -10375,6 +10391,11 @@ snapshots:
progress@2.0.3: {}
prompts@2.4.2:
dependencies:
kleur: 3.0.3
sisteransi: 1.0.5
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
@ -10945,6 +10966,8 @@ snapshots:
dependencies:
is-arrayish: 0.3.4
sisteransi@1.0.5: {}
size-sensor@1.0.2: {}
skmeans@0.9.7: {}

@ -1,7 +1,7 @@
#!/usr/bin/env node
const readline = require('readline');
const { spawnSync } = require('child_process');
const prompts = require('prompts');
const COLORS = {
red: '\x1b[31m',
@ -19,6 +19,13 @@ class MergeConflictError extends Error {
}
}
class PromptAbortedError extends Error {
constructor() {
super('操作已取消');
this.name = 'PromptAbortedError';
}
}
function log(message, color = 'reset') {
console.log(`${COLORS[color]}${message}${COLORS.reset}`);
}
@ -53,24 +60,6 @@ function git(args, options = {}) {
return runCommand('git', args, options);
}
function createPrompt() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return {
ask(question) {
return new Promise(resolve => {
rl.question(question, answer => resolve(answer.trim()));
});
},
close() {
rl.close();
}
};
}
function getCurrentBranch() {
return git(['branch', '--show-current'], { silent: true }).trim();
}
@ -178,68 +167,67 @@ function mergeBranch(sourceBranch, targetBranch, state) {
log('合并成功\n', 'green');
}
function printMenu(title, options) {
log(`\n${title}`, 'cyan');
options.forEach((option, index) => {
console.log(` ${index + 1}. ${option.label}`);
});
console.log('');
}
async function selectOption(prompt, title, options) {
while (true) {
printMenu(title, options);
const answer = await prompt.ask('请输入编号并回车: ');
const index = Number(answer);
if (Number.isInteger(index) && index >= 1 && index <= options.length) {
return options[index - 1];
async function promptValue(config) {
const response = await prompts(config, {
onCancel() {
throw new PromptAbortedError();
}
});
log('输入无效,请重新选择。\n', 'red');
}
return response[config.name];
}
async function selectBranch(prompt, title, branches, currentBranch) {
const options = branches.map(branchName => ({
value: branchName,
label: branchName === currentBranch ? `${branchName} (当前分支)` : branchName
}));
const selected = await selectOption(prompt, title, options);
return selected.value;
async function selectOption(title, options) {
log('', 'reset');
return promptValue({
type: 'select',
name: 'value',
message: title,
hint: '使用上下方向键选择,回车确认',
choices: options.map(option => ({
title: option.label,
value: option.value,
description: option.description
}))
});
}
async function confirm(prompt, message) {
while (true) {
const answer = (await prompt.ask(`${message} (y/n): `)).toLowerCase();
if (answer === 'y' || answer === 'yes') {
return true;
}
async function selectBranch(title, branches, currentBranch) {
return selectOption(
title,
branches.map(branchName => ({
value: branchName,
label: branchName === currentBranch ? `${branchName} (当前分支)` : branchName
}))
);
}
if (answer === 'n' || answer === 'no') {
return false;
}
async function confirm(message) {
log('', 'reset');
log('请输入 y 或 n。\n', 'red');
}
return promptValue({
type: 'confirm',
name: 'confirmed',
message,
initial: true
});
}
async function runSingleBranchMerge(prompt, state) {
async function runSingleBranchMerge(state) {
const branches = getLocalBranches();
if (branches.length < 2) {
throw new Error('本地分支数量不足,无法执行单分支合并');
}
const sourceBranch = await selectBranch(prompt, '请选择来源分支', branches, state.currentBranch);
const sourceBranch = await selectBranch('请选择来源分支', branches, state.currentBranch);
const targetCandidates = branches.filter(branchName => branchName !== sourceBranch);
const targetBranch = await selectBranch(prompt, '请选择目标分支', targetCandidates, state.currentBranch);
const targetBranch = await selectBranch('请选择目标分支', targetCandidates, state.currentBranch);
log(`\n将执行: ${sourceBranch} -> ${targetBranch}\n`, 'cyan');
const accepted = await confirm(prompt, '确认开始合并吗');
const accepted = await confirm('确认开始合并吗');
if (!accepted) {
log('已取消操作', 'yellow');
return;
@ -257,14 +245,14 @@ async function runSingleBranchMerge(prompt, state) {
log('========================================\n', 'green');
}
async function runDeployMerge(prompt, state) {
async function runDeployMerge(state) {
if (state.currentBranch === 'production') {
throw new Error('当前分支为 production不能执行部署合并');
}
log(`\n将执行部署合并: ${state.currentBranch} -> master -> production\n`, 'cyan');
const accepted = await confirm(prompt, '确认开始部署合并吗');
const accepted = await confirm('确认开始部署合并吗');
if (!accepted) {
log('已取消操作', 'yellow');
return;
@ -295,7 +283,6 @@ async function runDeployMerge(prompt, state) {
}
async function run() {
const prompt = createPrompt();
const state = {
currentBranch: '',
hasChanges: false,
@ -316,27 +303,27 @@ async function run() {
log(`当前分支: ${state.currentBranch}\n`, 'cyan');
const action = await selectOption(prompt, '请选择操作类型', [
{ label: '单分支合并', value: 'single' },
{ label: '部署合并(当前分支 -> master -> production', value: 'deploy' }
const action = await selectOption('请选择操作类型', [
{ label: '单分支合并', value: 'single', description: '手动选择来源分支和目标分支' },
{ label: '部署合并', value: 'deploy', description: '将当前分支依次合并到 master 和 production' }
]);
if (action.value === 'single') {
await runSingleBranchMerge(prompt, state);
if (action === 'single') {
await runSingleBranchMerge(state);
} else {
await runDeployMerge(prompt, state);
await runDeployMerge(state);
}
} catch (error) {
if (error instanceof MergeConflictError) {
printConflictGuidance(error.branchName);
} else if (error instanceof PromptAbortedError) {
log('\n已取消操作\n', 'yellow');
} else {
log(`\n错误: ${error.message}\n`, 'red');
}
process.exitCode = 1;
process.exitCode = error instanceof PromptAbortedError ? 0 : 1;
} finally {
prompt.close();
if (state.shouldRestoreWorkspace) {
restoreWorkspace(state);
}

Loading…
Cancel
Save