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;