zy-gbs-cmd/src/push.js

193 lines
6.3 KiB
JavaScript
Raw Normal View History

2026-08-25 17:17:36 +08:00
const childProcess = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const readline = require('readline/promises');
const { ensureYarnAvailable, runYarn } = require('./package-manager');
const DEFAULT_BRANCH = 'master';
const CONFIG_FILE_NAME = 'jjb.config.js';
const JAVA_TEMPLATE_PATH = path.join('start', 'src', 'main', 'resources', 'templates');
function run(command, args, cwd) {
console.log(`执行: ${command} ${args.join(' ')}`);
childProcess.execFileSync(command, args, { cwd, stdio: 'inherit' });
}
function readProjectConfig(projectRoot) {
const configPath = path.join(projectRoot, CONFIG_FILE_NAME);
if (!fs.existsSync(configPath)) {
throw new Error(`未找到 ${CONFIG_FILE_NAME}`);
}
delete require.cache[require.resolve(configPath)];
const importedConfig = require(configPath);
const config = importedConfig.default || importedConfig;
const requiredFields = ['javaGit', 'javaGitName', 'appIdentifier'];
const missingFields = requiredFields.filter(field => !config[field]);
if (missingFields.length) {
throw new Error(`${CONFIG_FILE_NAME} 缺少字段: ${missingFields.join(', ')}`);
}
return config;
}
function readPackageJson(projectRoot) {
const packagePath = path.join(projectRoot, 'package.json');
if (!fs.existsSync(packagePath)) {
throw new Error('未找到 package.json');
}
return JSON.parse(fs.readFileSync(packagePath, 'utf8'));
}
async function selectOption(input, message, options) {
if (options.length === 1) {
return options[0];
}
console.log(message);
options.forEach((option, index) => console.log(` ${index + 1}. ${option}`));
while (true) {
const answer = await input.question('请输入序号: ');
const selected = options[Number(answer) - 1];
if (selected) {
return selected;
}
console.log('请输入有效序号。');
}
}
async function resolveEnvironment(input, config, environmentArgument) {
const environments = config.environment && typeof config.environment === 'object'
? Object.keys(config.environment)
: [];
if (!environments.length) {
return null;
}
if (environmentArgument) {
if (!environments.includes(environmentArgument)) {
throw new Error(`未找到环境 ${environmentArgument}`);
}
return environmentArgument;
}
return selectOption(input, '请选择环境:', environments);
}
function resolveBuildScript(buildScripts, environment) {
const buildScript = `build:${environment}`;
if (!buildScripts.includes(buildScript)) {
throw new Error(`package.json 中没有构建脚本 ${buildScript}`);
}
return buildScript;
}
function ensureDependencies(projectRoot) {
if (fs.existsSync(path.join(projectRoot, 'node_modules'))) {
return;
}
console.log('未检测到 node_modules正在安装依赖。');
runYarn(['install'], projectRoot);
}
function copyBuildOutput(projectRoot, javaRepository, appIdentifier) {
const distPath = path.join(projectRoot, 'dist');
const indexPath = path.join(distPath, 'index.html');
const applicationPath = path.join(distPath, appIdentifier);
if (!fs.existsSync(indexPath)) {
throw new Error('构建产物缺少 dist/index.html');
}
if (!fs.existsSync(applicationPath)) {
throw new Error(`构建产物缺少 dist/${appIdentifier}`);
}
const templateRoot = path.join(javaRepository, JAVA_TEMPLATE_PATH);
const targetApplicationPath = path.join(templateRoot, appIdentifier);
fs.mkdirSync(templateRoot, { recursive: true });
fs.rmSync(targetApplicationPath, { recursive: true, force: true });
fs.cpSync(applicationPath, targetApplicationPath, { recursive: true });
fs.copyFileSync(indexPath, path.join(templateRoot, `${appIdentifier}.html`));
}
async function pushJava({ environment: environmentArgument } = {}) {
const projectRoot = process.cwd();
const packageJson = readPackageJson(projectRoot);
const config = readProjectConfig(projectRoot);
const buildScripts = Object.keys(packageJson.scripts || {}).filter(name =>
name.toLowerCase().includes('build')
);
if (!buildScripts.length) {
throw new Error('package.json 中没有名称包含 build 的脚本');
}
const input = readline.createInterface({ input: process.stdin, output: process.stdout });
let temporaryRoot;
try {
let buildScript;
let environment;
if (environmentArgument) {
environment = await resolveEnvironment(input, config, environmentArgument);
buildScript = resolveBuildScript(buildScripts, environment);
} else {
buildScript = await selectOption(input, '请选择构建命令:', buildScripts);
environment = await resolveEnvironment(input, config);
}
const environmentConfig = environment ? config.environment[environment] || {} : {};
const branch = environmentConfig.javaGitBranch || config.javaGitBranch || DEFAULT_BRANCH;
ensureYarnAvailable();
temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'zy-gbs-cmd-java-'));
const javaRepository = path.join(temporaryRoot, config.javaGitName);
console.log(`正在克隆 Java 仓库: ${config.javaGit}`);
run('git', ['clone', config.javaGit, javaRepository], temporaryRoot);
run('git', ['checkout', branch], javaRepository);
run('git', ['pull'], javaRepository);
ensureDependencies(projectRoot);
runYarn(['run', buildScript], projectRoot);
copyBuildOutput(projectRoot, javaRepository, config.appIdentifier);
run('git', ['add', '.'], javaRepository);
const changedFiles = childProcess.execFileSync('git', ['status', '--porcelain'], {
cwd: javaRepository,
encoding: 'utf8',
}).trim();
if (!changedFiles) {
console.log('构建产物没有变化,无需提交。');
return;
}
const defaultMessage = `zy-gbs-cmd: update ${config.appIdentifier}`;
const message = (await input.question(`请输入提交信息 (${defaultMessage}): `)).trim()
|| defaultMessage;
run('git', ['commit', '-m', message, '--no-verify'], javaRepository);
run('git', ['push'], javaRepository);
console.log('代码推送完成。');
} finally {
input.close();
if (temporaryRoot) {
try {
fs.rmSync(temporaryRoot, { recursive: true, force: true });
} catch (error) {
console.warn(`临时目录清理失败: ${error.message}`);
}
}
}
}
module.exports = pushJava;
module.exports.resolveBuildScript = resolveBuildScript;