UNPKG

gen-jhipster

Version:

VHipster - Spring Boot + Angular/React/Vue in one handy generator

294 lines (293 loc) 13.4 kB
/** * Copyright 2013-2026 the original author or authors from the JHipster project. * * This file is part of the JHipster project, see https://www.jhipster.tech/ * for more information. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { readdir, rm } from 'node:fs/promises'; import { setTimeout } from 'node:timers/promises'; import chalk from 'chalk'; import gitignore from 'parse-gitignore'; import semver from 'semver'; import { ResetMode } from 'simple-git'; import EnvironmentBuilder from "../../cli/environment-builder.js"; import { packageJson } from "../../lib/index.js"; import BaseGenerator from "../base/index.js"; import { SERVER_MAIN_RES_DIR } from "../generator-constants.js"; import { GIT_VERSION_NOT_ALLOW_MERGE_UNRELATED_HISTORIES, UPGRADE_BRANCH } from "./support/index.js"; /* Constants used throughout */ const GENERATOR_JHIPSTER = 'generator-jhipster'; const DEFAULT_CLI_OPTIONS = '--force --skip-install --skip-git --ignore-errors --no-insight --skip-checks'; const DEFAULT_NON_INTERACTIVE_OPTIONS = { skipInstall: true, skipGit: true, skipChecks: true, force: true, ignoreErrors: true, skipPriorities: ['prompting'], }; const DEFAULT_MERGE_OPTIONS = ['--strategy', 'ours']; export default class UpgradeGenerator extends BaseGenerator { requiredPackage = GENERATOR_JHIPSTER; createEnvBuilder; actualApplicationBranch; silent; applyConfig; spawnStdio = 'inherit'; executable; verbose; async beforeQueue() { if (!this.fromBlueprint) { await this.composeWithBlueprints(); } } get initializing() { return this.asInitializingTaskGroup({ displayLogo() { this.log.log(chalk.green('Welcome to the JHipster Upgrade Sub-Generator')); this.log.log(chalk.green(`This will upgrade your current application codebase to JHipster version ${packageJson.version}`)); this.log.log(chalk.green('For advanced options, please use the jhipster-migrate blueprint (https://github.com/jhipster/generator-jhipster-migrate/)')); this.log.log(''); }, initializeOptions() { if (this.silent) { this.spawnStdio = 'ignore'; } this.executable = this.executable ?? this.options.programName ?? 'jhipster'; this.createEnvBuilder = this.options.createEnvBuilder ?? EnvironmentBuilder.createDefaultBuilder; }, assertJHipsterProject() { if (!this.config.existed) { throw new Error("Could not find a valid JHipster application configuration, check if the '.yo-rc.json' file exists and if the 'generator-jhipster' key exists inside it."); } if (!this.jhipsterConfig.baseName) { throw new Error('Current directory does not contain a JHipster project.'); } }, checkoutDependency() { if (this.applyConfig) return; const jhipsterVersion = this.getPackageJsonVersion(); if (!jhipsterVersion) { throw new Error('No generator-jhipster dependency found in your package.json'); } if (this.isV7(jhipsterVersion)) { if (jhipsterVersion !== '7.9.4') { throw new Error('Upgrade the project to JHipster 7.9.4 before upgrading to the latest version.'); } if ((this.jhipsterConfig.blueprints ?? []).length > 0) { const errorMessage = 'Upgrade does not support upgrading a v7 project with blueprints. Please use the jhipster-migrate blueprint.'; if (!this.skipChecks) { throw new Error(errorMessage); } else { this.log.warn(errorMessage); } } } }, async assertGitPresent() { if (!(await this.checkGitVersion(GIT_VERSION_NOT_ALLOW_MERGE_UNRELATED_HISTORIES))) { this.log.warn('git is not found on your computer.\n', ` Install git: ${chalk.yellow('https://git-scm.com/')}`); throw new Error('Exiting the process.'); } }, async assertGitRepository() { const git = this.createGit(); if (!(await git.checkIsRepo())) { this.log.warn('Current directory is not a git repository. Initializing git.'); await git.init().add('.').commit('initial commit', ['--allow-empty', '--no-verify']); } }, async assertNoLocalChanges() { const result = await this.createGit().status(); if (!result.isClean()) { throw new Error(` local changes found.\n\tPlease commit/stash them before upgrading\n\t${result.files .map(file => `${file.index} ${file.path}`) .join('\n\t')}`); } }, async detectCurrentBranch() { this.actualApplicationBranch = await this.createGit().revparse(['--abbrev-ref', 'HEAD']); if (this.actualApplicationBranch === UPGRADE_BRANCH) { throw new Error('You are on the upgrade branch, please switch to another branch before upgrading.'); } }, }); } get [BaseGenerator.INITIALIZING]() { return this.delegateTasksToBlueprint(() => this.initializing); } get default() { return this.asDefaultTaskGroup({ async prepareUpgradeBranch() { const git = this.createGit(); // Checkout upgrade branch try { await git.revparse(['--verify', UPGRADE_BRANCH]); await git.checkout(UPGRADE_BRANCH); } catch { // Branch does not exist await git.checkout(['--orphan', UPGRADE_BRANCH]); } // Cleanup sources await this.cleanUp(); if (this.applyConfig) { // Regenerate sources await this.runNonInteractive(false); } else { // Make sure the node_modules is up to date await this.spawnCommand('npm install', { stdio: this.spawnStdio }); const customCliOptions = []; if (this.getPackageJsonVersion() === '7.9.4') { customCliOptions.push('--with-entities'); } // Regenerate sources this.log.info(`Regenerating sources using ${this.executable} executable`); await this.spawn('npx', ['--no', this.executable, ...customCliOptions, ...DEFAULT_CLI_OPTIONS.split(' ')], { stdio: this.spawnStdio, }); } await this.rmRf(`${SERVER_MAIN_RES_DIR}config/tls/keystore.p12`); // Commit changes await git.add('.').commit(`generated ${UPGRADE_BRANCH} using JHipster ${this.getPackageJsonVersion()}`, ['--no-verify']); this.log.info('reference application created'); // Add 1s between commits for more consistent git log await setTimeout(1000); }, async prepareSourceBranch() { const git = this.createGit(); await git .checkout(this.actualApplicationBranch, ['-f']) .merge([ UPGRADE_BRANCH, ...DEFAULT_MERGE_OPTIONS, '-m', `reference merge of ${UPGRADE_BRANCH} branch into ${this.actualApplicationBranch}`, '--allow-unrelated-histories', ]); this.log.info('reference application merged into source branch'); // Add 1s between commits for more consistent git log await setTimeout(1000); }, async updateUpgradeBranch() { const git = this.createGit(); // Switch back to upgrade branch await git.checkout(UPGRADE_BRANCH); // Cleanup sources await this.cleanUp(); // Regenerate sources await this.runNonInteractive(); await this.rmRf(`${SERVER_MAIN_RES_DIR}config/tls/keystore.p12`); // Commit changes await git.add('.').commit(`generated ${UPGRADE_BRANCH} using JHipster ${packageJson.version}`, ['--no-verify']); this.log.info('upgrade commit created'); // Add 1s between commits for more consistent git log await setTimeout(1000); }, async upgradeSourceBranch() { const git = this.createGit(); await git .checkout(this.actualApplicationBranch, ['-f']) .merge([UPGRADE_BRANCH, '-m', `upgrade merge of ${UPGRADE_BRANCH} branch into ${this.actualApplicationBranch}`]) .reset(ResetMode.HARD); this.log.info('upgrade application merged into source branch'); if (!this.applyConfig) { // Remove packages to allow a clean install await this.rmRf('node_modules'); await this.rmRf('package-lock.json'); this.log.info('node_modules and package-lock.json removed to allow a clean install'); } }, }); } get [BaseGenerator.DEFAULT]() { return this.delegateTasksToBlueprint(() => this.default); } get end() { return this.asEndTaskGroup({ async end() { const diff = await this.createGit().diff(['--name-only', '--diff-filter', 'U']); this.log.ok(chalk.bold('upgraded successfully.')); if (diff) { this.log.warn(`please fix conflicts listed below and commit!\n${diff}`); } }, }); } get [BaseGenerator.END]() { return this.delegateTasksToBlueprint(() => this.end); } async rmRf(file) { const absolutePath = this.destinationPath(file); if (this.verbose) { this.log.verboseInfo(`Removing ${absolutePath}`); } try { await rm(absolutePath, { recursive: true }); } catch { // ignore } } /** * Remove every generated file not related to the generation. */ async cleanUp() { const gitignoreContent = this.readDestination('.gitignore', { defaults: '' }); const ignoredFiles = gitignoreContent ? (gitignore(gitignoreContent).patterns ?? []) : []; const filesToKeep = new Set(['.yo-rc.json', '.jhipster', 'package.json', 'package-lock.json', 'node_modules', '.git', ...ignoredFiles]); for (const file of await readdir(this.destinationPath())) { if (!filesToKeep.has(file)) { await this.rmRf(file); } } this.log.info('cleaned up project directory'); } getPackageJsonVersion() { return this.readResourcesPackageJson(this.destinationPath('package.json'))?.devDependencies?.['generator-jhipster']; } isV7(version) { return version.includes('.') && parseInt(version.split('.', 2)[0], 10) < 8; } async runNonInteractive(inherit = true) { const adapter = this.env.adapter.newAdapter?.(); const sharedFs = inherit ? this.env.sharedFs : undefined; const inheritedOptions = inherit ? this.options : {}; const envOptions = { sharedFs, adapter }; const generatorOptions = { ...inheritedOptions, ...DEFAULT_NON_INTERACTIVE_OPTIONS }; const envBuilder = await this.createEnvBuilder(envOptions); const env = envBuilder.getEnvironment(); await env.run([`jhipster:app`], generatorOptions); } /** * Check git version. */ async checkGitVersion(minVersion) { try { const rawVersion = await this.createGit().raw('--version'); const gitVersion = String(rawVersion.match(/(\d+\.\d+\.\d+)/g)); if (minVersion) { return semver.gte(gitVersion, minVersion); } return true; } catch { return false; } } }