diff --git a/generators/base-workspaces/internal/docker-prompts.mjs b/generators/base-workspaces/internal/docker-prompts.mjs index fe34119b426c..b1bfe49d7d5f 100644 --- a/generators/base-workspaces/internal/docker-prompts.mjs +++ b/generators/base-workspaces/internal/docker-prompts.mjs @@ -17,8 +17,8 @@ * limitations under the License. */ import chalk from 'chalk'; -import shelljs from 'shelljs'; +import { readdirSync, statSync } from 'node:fs'; import { loadConfigs } from './docker-base.mjs'; import { applicationTypes, monitoringTypes, serviceDiscoveryTypes } from '../../../jdl/jhipster/index.mjs'; import { convertSecretToBase64 } from '../../base/support/index.mjs'; @@ -118,15 +118,21 @@ async function askForPath() { name: 'directoryPath', message: messageAskForPath, default: this.directoryPath || '../', - validate: input => { + validate: async input => { const path = this.destinationPath(input); - if (shelljs.test('-d', path)) { - const appsFolders = getAppFolders.call(this, input, deploymentApplicationType); - - if (appsFolders.length === 0) { - return deploymentApplicationType === MONOLITH ? `No monolith found in ${path}` : `No microservice or gateway found in ${path}`; + try { + if (statSync(path).isDirectory) { + const appsFolders = getAppFolders.call(this, input, deploymentApplicationType); + + if (appsFolders.length === 0) { + return deploymentApplicationType === MONOLITH + ? `No monolith found in ${path}` + : `No microservice or gateway found in ${path}`; + } + return true; } - return true; + } catch { + // Ignore error } return `${path} is not a directory or doesn't exist`; }, @@ -366,27 +372,31 @@ async function askForDockerPushCommand() { */ export function getAppFolders(input, deploymentApplicationType) { const destinationPath = this.destinationPath(input); - const files = shelljs.ls('-l', destinationPath); + const files = readdirSync(destinationPath); const appsFolders = []; files.forEach(file => { - if (file.isDirectory()) { - if (shelljs.test('-f', `${destinationPath}/${file.name}/.yo-rc.json`)) { - try { - const fileData = this.fs.readJSON(`${destinationPath}/${file.name}/.yo-rc.json`); - if ( - fileData['generator-jhipster'].baseName !== undefined && - (deploymentApplicationType === undefined || - deploymentApplicationType === fileData['generator-jhipster'].applicationType || - (deploymentApplicationType === MICROSERVICE && fileData['generator-jhipster'].applicationType === GATEWAY)) - ) { - appsFolders.push(file.name.match(/([^/]*)\/*$/)[1]); + try { + if (statSync(this.destinationPath(file)).isDirectory()) { + if (statSync(this.destinationPath(file, '.yo-rc.json')).isFile()) { + try { + const fileData = this.readDestinationJSON(`${file.name}/.yo-rc.json`); + if ( + fileData['generator-jhipster'].baseName !== undefined && + (deploymentApplicationType === undefined || + deploymentApplicationType === fileData['generator-jhipster'].applicationType || + (deploymentApplicationType === MICROSERVICE && fileData['generator-jhipster'].applicationType === GATEWAY)) + ) { + appsFolders.push(file.name.match(/([^/]*)\/*$/)[1]); + } + } catch (err) { + this.log.error(chalk.red(`${file}: this .yo-rc.json can't be read`)); + this.log.debug('Error:', err); } - } catch (err) { - this.log.error(chalk.red(`${file}: this .yo-rc.json can't be read`)); - this.log.debug('Error:', err); } } + } catch { + // Not a file or directory } }); diff --git a/generators/docker-compose/generator.mjs b/generators/docker-compose/generator.mjs index 253ec2523204..987091483a53 100644 --- a/generators/docker-compose/generator.mjs +++ b/generators/docker-compose/generator.mjs @@ -19,10 +19,8 @@ import { existsSync } from 'fs'; import pathjs from 'path'; import chalk from 'chalk'; -import shelljs from 'shelljs'; import jsyaml from 'js-yaml'; import normalize from 'normalize-path'; -import runAsync from 'run-async'; import _ from 'lodash'; import BaseWorkspacesGenerator from '../base-workspaces/index.mjs'; @@ -73,36 +71,31 @@ export default class DockerComposeGenerator extends BaseWorkspacesGenerator { this.parseJHipsterOptions(command.options); }, checkDocker, - checkDockerCompose: runAsync(function () { + async checkDockerCompose() { if (this.skipChecks) return; - const done = this.async(); - - shelljs.exec('docker compose version', { silent: true }, (code, stdout, stderr) => { - if (stderr) { - this.log.error( - chalk.red( - 'Docker Compose 1.6.0 or later is not installed on your computer.\n' + - ' Read https://docs.docker.com/compose/install/\n', - ), - ); - } else { - const composeVersion = stdout.split(' ')[2].replace(/,/g, ''); - const composeVersionMajor = composeVersion.split('.')[0]; - const composeVersionMinor = composeVersion.split('.')[1]; - if (composeVersionMajor < 1 || (composeVersionMajor === 1 && composeVersionMinor < 6)) { - this.log.error( - chalk.red( - `$Docker Compose version 1.6.0 or later is not installed on your computer. + const { stdout, exitCode } = await this.spawnCommand('docker compose version', { reject: false, stdio: 'pipe' }); + if (exitCode !== 0) { + this.log.error( + chalk.red( + 'Docker Compose 1.6.0 or later is not installed on your computer.\n' + + ' Read https://docs.docker.com/compose/install/\n', + ), + ); + } + const composeVersion = stdout.split(' ')[2].replace(/,/g, ''); + const composeVersionMajor = composeVersion.split('.')[0]; + const composeVersionMinor = composeVersion.split('.')[1]; + if (composeVersionMajor < 1 || (composeVersionMajor === 1 && composeVersionMinor < 6)) { + this.log.error( + chalk.red( + `$Docker Compose version 1.6.0 or later is not installed on your computer. Docker Compose version found: ${composeVersion} Read https://docs.docker.com/compose/install`, - ), - ); - } - } - done(); - }); - }), + ), + ); + } + }, }; } diff --git a/generators/docker/support/check-docker.mjs b/generators/docker/support/check-docker.mjs index d04e579221ea..b5ca8740d006 100644 --- a/generators/docker/support/check-docker.mjs +++ b/generators/docker/support/check-docker.mjs @@ -16,46 +16,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import shelljs from 'shelljs'; import chalk from 'chalk'; -import runAsync from 'run-async'; /** * Check that Docker exists. + * @this {import('../../base-core/index.mjs').default} */ -export const checkDocker = runAsync(function () { +export const checkDocker = async function () { if (this.abort || this.skipChecks) return; - const done = this.async(); + const ret = await this.spawnCommand('docker -v', { reject: false, stdio: 'pipe' }); + if (ret.exitCode !== 0) { + this.log.error( + chalk.red( + `Docker version 1.10.0 or later is not installed on your computer. + Read http://docs.docker.com/engine/installation/#installation +`, + ), + ); + throw new Error(); + } - shelljs.exec('docker -v', { silent: true }, (code, stdout, stderr) => { - if (stderr) { - this.log.error( - chalk.red( - 'Docker version 1.10.0 or later is not installed on your computer.\n' + - ' Read http://docs.docker.com/engine/installation/#installation\n', - ), - ); - this.abort = true; - } else { - const dockerVersion = stdout.split(' ')[2].replace(/,/g, ''); - const dockerVersionMajor = dockerVersion.split('.')[0]; - const dockerVersionMinor = dockerVersion.split('.')[1]; - if (dockerVersionMajor < 1 || (dockerVersionMajor === 1 && dockerVersionMinor < 10)) { - this.log.error( - chalk.red( - `Docker version 1.10.0 or later is not installed on your computer. - Docker version found: ${dockerVersion} - Read http://docs.docker.com/engine/installation/#installation`, - ), - ); - this.abort = true; - } else { - this.log.verboseInfo('Docker is installed'); - } - } - done(); - }); -}); + const dockerVersion = ret.stdout.split(' ')[2].replace(/,/g, ''); + const dockerVersionMajor = dockerVersion.split('.')[0]; + const dockerVersionMinor = dockerVersion.split('.')[1]; + if (dockerVersionMajor < 1 || (dockerVersionMajor === 1 && dockerVersionMinor < 10)) { + this.log.error( + chalk.red( + `Docker version 1.10.0 or later is not installed on your computer. + Docker version found: ${dockerVersion} + Read http://docs.docker.com/engine/installation/#installation`, + ), + ); + throw new Error(); + } else { + this.log.verboseInfo('Docker is installed'); + } +}; /** * This is the Generator base class. diff --git a/generators/info/generator.mts b/generators/info/generator.mts index d16dbc46ca42..cf20d6bb706f 100644 --- a/generators/info/generator.mts +++ b/generators/info/generator.mts @@ -47,12 +47,8 @@ export default class InfoGenerator extends BaseApplicationGenerator { }, async checkJHipster() { - try { - const { stdout } = await this.spawnCommand('npm list generator-jhipster', { stdio: 'pipe' }); - console.log(`\n\`\`\`\n${stdout}\`\`\`\n`); - } catch (error) { - console.log(`\n\`\`\`\n${(error as any).stdout}\`\`\`\n`); - } + const { stdout } = await this.spawnCommand('npm list generator-jhipster', { stdio: 'pipe', reject: false }); + console.log(`\n\`\`\`\n${stdout}\`\`\`\n`); }, displayConfiguration() { @@ -121,7 +117,7 @@ export default class InfoGenerator extends BaseApplicationGenerator { async checkCommand(command: string, args: string[], printInfo = ({ stdout }: ExecaReturnValue) => console.log(stdout)) { try { - printInfo(await this.spawnCommand(command, args, { stdio: 'pipe' })); + printInfo(await this.spawn(command, args, { stdio: 'pipe' })); } catch (_error) { console.log(chalk.red(`'${command}' command could not be found`)); } diff --git a/generators/kubernetes-knative/generator.mjs b/generators/kubernetes-knative/generator.mjs index f1fdc1f46e3e..5a23a539c01c 100644 --- a/generators/kubernetes-knative/generator.mjs +++ b/generators/kubernetes-knative/generator.mjs @@ -19,8 +19,6 @@ /* eslint-disable consistent-return */ import fs from 'fs'; import chalk from 'chalk'; -import shelljs from 'shelljs'; -import runAsync from 'run-async'; import BaseWorkspacesGenerator from '../base-workspaces/index.mjs'; @@ -62,7 +60,7 @@ export default class KubernetesKnativeGenerator extends BaseWorkspacesGenerator } get initializing() { - return { + return this.asInitializingTaskGroup({ sayHello() { this.log.log(chalk.white(`${chalk.bold('☸')} Welcome to the JHipster Kubernetes Knative Generator ${chalk.bold('☸')}`)); this.log.log(chalk.white(`Files will be generated in the folder: ${chalk.yellow(this.destinationRoot())}`)); @@ -74,23 +72,19 @@ export default class KubernetesKnativeGenerator extends BaseWorkspacesGenerator checkDocker, checkKubernetes, checkHelm, - checkKnative: runAsync(function () { + async checkKnative() { if (this.skipChecks) return; - const done = this.async(); - shelljs.exec( - 'kubectl get deploy -n knative-serving --label-columns=serving.knative.dev/release | grep -E "v0\\.[8-9]{1,3}\\.[0-9]*', - { silent: true }, - (code, stdout, stderr) => { - if (stderr || code !== 0) { - this.log.warn( - 'Knative 0.8.* or later is not installed on your computer.\n' + - 'Make sure you have Knative and Istio installed. Read https://knative.dev/docs/install/\n', - ); - } - done(); - }, - ); - }), + try { + await this.spawnCommand( + 'kubectl get deploy -n knative-serving --label-columns=serving.knative.dev/release | grep -E "v0\\.[8-9]{1,3}\\.[0-9]*', + ); + } catch (error) { + this.log.warn( + 'Knative 0.8.* or later is not installed on your computer.\n' + + 'Make sure you have Knative and Istio installed. Read https://knative.dev/docs/install/\n', + ); + } + }, loadConfig, localInit() { this.deploymentApplicationType = 'microservice'; @@ -98,7 +92,7 @@ export default class KubernetesKnativeGenerator extends BaseWorkspacesGenerator }, setupKubernetesConstants, setupHelmConstants, - }; + }); } get [BaseWorkspacesGenerator.INITIALIZING]() { diff --git a/generators/kubernetes/kubernetes-base.mjs b/generators/kubernetes/kubernetes-base.mjs index 94368c3c70a4..074b3004fafc 100644 --- a/generators/kubernetes/kubernetes-base.mjs +++ b/generators/kubernetes/kubernetes-base.mjs @@ -18,9 +18,7 @@ */ import crypto from 'crypto'; import _ from 'lodash'; -import runAsync from 'run-async'; -import shelljs from 'shelljs'; import { defaultKubernetesConfig } from './kubernetes-constants.mjs'; import { loadFromYoRc } from '../base-workspaces/internal/docker-base.mjs'; import { @@ -50,39 +48,31 @@ const { INGRESS } = ServiceTypes; const { GKE, NGINX } = IngressTypes; const { K8S, HELM } = GeneratorTypes; -export const checkKubernetes = runAsync(function () { +export const checkKubernetes = async function () { if (this.skipChecks) return; - const done = this.async(); - shelljs.exec('kubectl version', { silent: true }, (code, stdout, stderr) => { - if (stderr) { - this.log.warn( - 'kubectl 1.2 or later is not installed on your computer.\n' + - 'Make sure you have Kubernetes installed. Read https://kubernetes.io/docs/setup/\n', - ); - } - done(); - }); -}); + try { + await this.spawnCommand('kubectl version'); + } catch { + this.log.warn( + 'kubectl 1.2 or later is not installed on your computer.\n' + + 'Make sure you have Kubernetes installed. Read https://kubernetes.io/docs/setup/\n', + ); + } +}; -export const checkHelm = runAsync(function () { +export const checkHelm = async function () { if (this.skipChecks) return; - const done = this.async(); - shelljs.exec( - 'helm version --client | grep -E "(v2\\.1[2-9]{1,2}\\.[0-9]{1,3})|(v3\\.[0-9]{1,2}\\.[0-9]{1,3})"', - { silent: true }, - (code, stdout, stderr) => { - if (stderr || code !== 0) { - this.log.warn( - 'helm 2.12.x or later is not installed on your computer.\n' + - 'Make sure you have helm installed. Read https://github.com/helm/helm/\n', - ); - } - done(); - }, - ); -}); + try { + await this.spawnCommand('helm version --client | grep -E "(v2\\.1[2-9]{1,2}\\.[0-9]{1,3})|(v3\\.[0-9]{1,2}\\.[0-9]{1,3})"'); + } catch { + this.log.warn( + 'helm 2.12.x or later is not installed on your computer.\n' + + 'Make sure you have helm installed. Read https://github.com/helm/helm/\n', + ); + } +}; export function loadConfig() { loadFromYoRc.call(this); diff --git a/generators/languages/generator.mjs b/generators/languages/generator.mjs index 48c7c2a4a6c8..70982ef6b7d9 100644 --- a/generators/languages/generator.mjs +++ b/generators/languages/generator.mjs @@ -33,7 +33,6 @@ import { updateLanguagesTask as updateLanguagesInReact } from '../react/support/ import { updateLanguagesTask as updateLanguagesInVue } from '../vue/support/index.mjs'; import { updateLanguagesTask as updateLanguagesInJava } from '../server/support/index.mjs'; import { SERVER_MAIN_RES_DIR, SERVER_TEST_RES_DIR } from '../generator-constants.mjs'; -import upgradeFilesTask from './upgrade-files-task.mjs'; import command from './command.mjs'; import { QUEUES } from '../base-application/priorities.mjs'; @@ -249,7 +248,6 @@ export default class LanguagesGenerator extends BaseApplicationGenerator { // Public API method used by the getter and also by Blueprints get writing() { return this.asWritingTaskGroup({ - upgradeFilesTask, async writeClientTranslations({ application }) { if (application.skipClient) return; const languagesToApply = application.enableTranslation ? this.languagesToApply : [...new Set([application.nativeLanguage, 'en'])]; diff --git a/generators/languages/index.mts b/generators/languages/index.mts index b98e6972347b..855a438fcc02 100644 --- a/generators/languages/index.mts +++ b/generators/languages/index.mts @@ -18,4 +18,3 @@ */ export { default } from './generator.mjs'; export { default as command } from './command.mjs'; -export { default as upgradeFilesTask } from './upgrade-files-task.mjs'; diff --git a/generators/languages/upgrade-files-task.mts b/generators/languages/upgrade-files-task.mts deleted file mode 100644 index d9f19f8840f6..000000000000 --- a/generators/languages/upgrade-files-task.mts +++ /dev/null @@ -1,52 +0,0 @@ -import shelljs from 'shelljs'; - -import BaseGenerator from '../base/index.mjs'; -import { SERVER_MAIN_RES_DIR } from '../generator-constants.mjs'; -import { languageSnakeCase, languageToJavaLanguage } from './support/index.mjs'; - -/** - * Execute a git mv. - * A git mv operation is required due to file casing change eg: en_us -> en_US at a case insensitive OS. - * - * @param source - * @param dest - * @returns {boolean} true if success; false otherwise - */ -function gitMove(this: BaseGenerator, from: string, to: string) { - const source = this.destinationPath(from); - const dest = this.destinationPath(to); - if (source && dest && shelljs.test('-f', source)) { - this.log.verboseInfo(`Renaming the file - ${source} to ${dest}`); - return !shelljs.exec(`git mv -f ${source} ${dest}`).code; - } - return true; -} - -/** - * Upgrade files. - */ -export default function upgradeFilesTask(this: BaseGenerator) { - let atLeastOneSuccess = false; - if (this.isJhipsterVersionLessThan('6.1.0')) { - const languages = this.config.get('languages'); - if (languages) { - const langNameDiffer = function (lang) { - const langProp = languageSnakeCase(lang); - const langJavaProp = languageToJavaLanguage(lang); - return langProp !== langJavaProp ? [langProp, langJavaProp] : undefined; - }; - languages - .map(langNameDiffer) - .filter(props => props) - .forEach(props => { - const code = gitMove.call( - this, - `${SERVER_MAIN_RES_DIR}i18n/messages_${props![0]}.properties`, - `${SERVER_MAIN_RES_DIR}i18n/messages_${props![1]}.properties`, - ); - atLeastOneSuccess = atLeastOneSuccess || code; - }); - } - } - return atLeastOneSuccess; -} diff --git a/generators/server/internal/build-application.mjs b/generators/server/internal/build-application.mjs deleted file mode 100644 index b1a0e22dd99e..000000000000 --- a/generators/server/internal/build-application.mjs +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2013-2023 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 os from 'os'; -import { exec } from 'child_process'; -import { buildToolTypes } from '../../../jdl/index.js'; - -const { GRADLE } = buildToolTypes; -const isWin32 = os.platform() === 'win32'; - -/** - * build a generated application. - * - * @param {String} buildTool - maven | gradle - * @param {String} profile - dev | prod - * @param {Boolean} buildWar - build a war instead of a jar - * @param {Function} cb - callback when build is complete - * @returns {object} the command line and its result - */ -export const buildApplication = (buildTool, profile, buildWar, cb) => { - let buildCmd = 'mvnw -ntp verify -B'; - - if (buildTool === GRADLE) { - buildCmd = 'gradlew'; - if (buildWar) { - buildCmd += ' bootWar'; - } else { - buildCmd += ' bootJar'; - } - } - if (buildWar) { - buildCmd += ' -Pwar'; - } - - if (!isWin32) { - buildCmd = `./${buildCmd}`; - } - buildCmd += ` -P${profile}`; - return { - stdout: exec(buildCmd, { maxBuffer: 1024 * 10000 }, cb).stdout, - buildCmd, - }; -}; diff --git a/generators/server/internal/index.mts b/generators/server/internal/index.mts deleted file mode 100644 index 561117944595..000000000000 --- a/generators/server/internal/index.mts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright 2013-2023 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. - */ -export * from './build-application.mjs'; diff --git a/generators/upgrade/generator.mjs b/generators/upgrade/generator.mjs index 9b2a0687adfb..f766cd7af0eb 100644 --- a/generators/upgrade/generator.mjs +++ b/generators/upgrade/generator.mjs @@ -17,16 +17,14 @@ * limitations under the License. */ import process from 'node:process'; -import fs from 'fs'; -import path from 'path'; +import fs, { rmSync, readdirSync } from 'node:fs'; +import path from 'node:path'; import chalk from 'chalk'; -import shelljs from 'shelljs'; import semver from 'semver'; import gitignore from 'parse-gitignore'; import latestVersion from 'latest-version'; import BaseGenerator from '../base/index.mjs'; -import { upgradeFilesTask as upgradeLanguagesFilesTask } from '../languages/index.mjs'; import { SERVER_MAIN_RES_DIR } from '../generator-constants.mjs'; import statistics from '../statistics.mjs'; import { parseBluePrints } from '../base/internal/index.mjs'; @@ -40,37 +38,6 @@ const GLOBAL_VERSION = 'global'; const GIT_VERSION_NOT_ALLOW_MERGE_UNRELATED_HISTORIES = '2.9.0'; const FIRST_CLI_SUPPORTED_VERSION = '4.5.1'; // The first version in which CLI support was added -/** - * Executes a Git command using shellJS - * gitExec(args [, options, callback]) - * - * @param {string|array} args - can be an array of arguments or a string command - * @param {object} options[optional] - takes any of child process options - * @param {function} callback[optional] - a callback function to be called once process complete, The call back will receive code, stdout and stderr - * @return {object} when in synchronous mode, this returns a ShellString. Otherwise, this returns the child process object. - */ -function gitExec(logger, args, options = {}, callback) { - if (typeof options === 'function') { - callback = options; - options = {}; - } - - if (options.async === undefined) options.async = callback !== undefined; - if (options.silent === undefined) options.silent = true; - if (options.trace === undefined) options.trace = true; - - if (!Array.isArray(args)) { - args = [args]; - } - const command = `git ${args.join(' ')}`; - - logger.debug(command); - if (callback) { - return shelljs.exec(command, options, callback); - } - return shelljs.exec(command, options); -} - export default class UpgradeGenerator extends BaseGenerator { fromV7; fromV7_9_3App; @@ -122,7 +89,7 @@ export default class UpgradeGenerator extends BaseGenerator { _rmRf(file) { const absolutePath = path.resolve(file); this.log.verboseInfo(`Removing ${absolutePath}`); - shelljs.rm('-rf', absolutePath); + rmSync(absolutePath, { recursive: true, force: true }); } _gitCheckout(branch, options = {}) { @@ -130,23 +97,16 @@ export default class UpgradeGenerator extends BaseGenerator { if (options.force) { args.push('-f'); } - const gitCheckout = this.gitExec(args, { silent: this.silent }); - if (gitCheckout.code !== 0) throw new Error(`Unable to checkout branch ${branch}:\n${gitCheckout.stderr}`); + const gitCheckout = this.spawnSync('git', args, { stdio: 'pipe', reject: false }); + if (gitCheckout.exitCode !== 0) throw new Error(`Unable to checkout branch ${branch}:\n${gitCheckout.stderr}`); this.log.ok(`Checked out branch "${branch}"`); } - _upgradeFiles() { - if (upgradeLanguagesFilesTask.call(this)) { - const gitCommit = this.gitExec(['commit', '-q', '-m', '"Upgrade preparation."', '--no-verify'], { silent: this.silent }); - if (gitCommit.code !== 0) throw new Error(`Unable to prepare upgrade:\n${gitCommit.stderr}`); - this.log.ok('Upgrade preparation'); - } - } - _cleanUp() { const ignoredFiles = gitignore(fs.readFileSync('.gitignore')).patterns || []; const filesToKeep = ['.yo-rc.json', '.jhipster', 'node_modules', '.git', '.idea', '.mvn', ...ignoredFiles]; - shelljs.ls('-A').forEach(file => { + const files = readdirSync(this.destinationPath()); + files.forEach(file => { if (!filesToKeep.includes(file)) { this._rmRf(file); } @@ -163,8 +123,8 @@ export default class UpgradeGenerator extends BaseGenerator { this._rmRf('node_modules'); generatorCommand = 'jhipster'; } else if (semver.gte(jhipsterVersion, FIRST_CLI_SUPPORTED_VERSION)) { - const result = shelljs.exec('npm bin', { silent: this.silent }); - if (result.code === 0) { + const result = this.spawnCommandSync('npm bin', { stdio: 'pipe', reject: false }); + if (result.exitCode === 0) { generatorCommand = `"${result.stdout.replace('\n', '')}/jhipster"`; } else { generatorCommand = 'npm exec --no jhipster --'; @@ -184,13 +144,13 @@ export default class UpgradeGenerator extends BaseGenerator { } _gitCommitAll(commitMsg) { - const gitAdd = this.gitExec(['add', '-A'], { maxBuffer: 1024 * 10000, silent: this.silent }); - if (gitAdd.code !== 0) throw new Error(`Unable to add resources in git:\n${gitAdd.stderr}`); + const gitAdd = this.spawnSync('git', ['add', '-A'], { stdio: 'pipe', reject: false }); + if (gitAdd.exitCode !== 0) throw new Error(`Unable to add resources in git:\n${gitAdd.stderr}`); - const gitCommit = this.gitExec(['commit', '-q', '-m', `"${commitMsg}"`, '-a', '--allow-empty', '--no-verify'], { - silent: this.silent, + const gitCommit = this.spawnSync('git', ['commit', '-q', '-m', commitMsg, '-a', '--allow-empty', '--no-verify'], { + stdio: 'pipe', }); - if (gitCommit.code !== 0) throw new Error(`Unable to commit in git:\n${gitCommit.stderr}`); + if (gitCommit.exitCode !== 0) throw new Error(`Unable to commit in git:\n${gitCommit.stderr}`); this.log.ok(`Committed with message "${commitMsg}"`); } @@ -210,8 +170,8 @@ export default class UpgradeGenerator extends BaseGenerator { const generatorCommand = `${commandPrefix} ${npmPackage}@${version} ${devDependencyParam} ${noPackageLockParam} --ignore-scripts --force`; this.log.verboseInfo(generatorCommand); - const npmIntall = shelljs.exec(generatorCommand, { silent: this.silent }); - if (npmIntall.code === 0) this.log.ok(`Installed ${npmPackage}@${version}`); + const npmIntall = this.spawnCommandSync(generatorCommand, { stdio: 'pipe', reject: false }); + if (npmIntall.exitCode === 0) this.log.ok(`Installed ${npmPackage}@${version}`); else throw new Error(`Something went wrong while installing ${npmPackage}! ${npmIntall.stdout} ${npmIntall.stderr}`); } @@ -299,19 +259,19 @@ export default class UpgradeGenerator extends BaseGenerator { assertGitRepository() { const gitInit = () => { - const gitInit = this.gitExec('init', { silent: this.silent }); - if (gitInit.code !== 0) throw new Error(`Unable to initialize a new Git repository:\n${gitInit.stdout} ${gitInit.stderr}`); + const gitInit = this.spawnSync('git', ['init'], { stdio: 'pipe', reject: false }); + if (gitInit.exitCode !== 0) throw new Error(`Unable to initialize a new Git repository:\n${gitInit.stdout} ${gitInit.stderr}`); this.log.ok('Initialized a new Git repository'); this._gitCommitAll('Initial'); }; - const gitRevParse = this.gitExec(['rev-parse', '-q', '--is-inside-work-tree'], { silent: this.silent }); - if (gitRevParse.code !== 0) gitInit(); + const gitRevParse = this.spawnSync('git', ['rev-parse', '-q', '--is-inside-work-tree'], { stdio: 'pipe', reject: false }); + if (gitRevParse.exitCode !== 0) gitInit(); else this.log.ok('Git repository detected'); }, assertNoLocalChanges() { - const gitStatus = this.gitExec(['status', '--porcelain'], { silent: this.silent }); - if (gitStatus.code !== 0) throw new Error(`Unable to check for local changes:\n${gitStatus.stdout} ${gitStatus.stderr}`); + const gitStatus = this.spawnSync('git', ['status', '--porcelain'], { stdio: 'pipe', reject: false }); + if (gitStatus.exitCode !== 0) throw new Error(`Unable to check for local changes:\n${gitStatus.stdout} ${gitStatus.stderr}`); if (gitStatus.stdout) { this.log.warn(gitStatus.stdout); throw new Error(' local changes found.\n\tPlease commit/stash them before upgrading'); @@ -319,14 +279,15 @@ export default class UpgradeGenerator extends BaseGenerator { }, detectCurrentBranch() { - const gitRevParse = this.gitExec(['rev-parse', '-q', '--abbrev-ref', 'HEAD'], { silent: this.silent }); - if (gitRevParse.code !== 0) throw new Error(`Unable to detect current Git branch:\n${gitRevParse.stdout} ${gitRevParse.stderr}`); + const gitRevParse = this.spawnSync('git', ['rev-parse', '-q', '--abbrev-ref', 'HEAD'], { stdio: 'pipe', reject: false }); + if (gitRevParse.exitCode !== 0) + throw new Error(`Unable to detect current Git branch:\n${gitRevParse.stdout} ${gitRevParse.stderr}`); this.sourceBranch = gitRevParse.stdout.replace('\n', ''); }, async prepareUpgradeBranch() { const getGitVersion = () => { - const gitVersion = this.gitExec(['--version'], { silent: this.silent }); + const gitVersion = this.spawnSync('git', ['--version'], { stdio: 'pipe', reject: false }); return String(gitVersion.stdout.match(/([0-9]+\.[0-9]+\.[0-9]+)/g)); }; @@ -338,8 +299,8 @@ export default class UpgradeGenerator extends BaseGenerator { } else { args = ['merge', '--strategy=ours', '-q', '--no-edit', '--allow-unrelated-histories', UPGRADE_BRANCH]; } - const gitMerge = this.gitExec(args, { silent: this.silent }); - if (gitMerge.code !== 0) { + const gitMerge = this.spawnSync('git', args, { stdio: 'pipe', reject: false }); + if (gitMerge.exitCode !== 0) { throw new Error( `Unable to record current code has been generated with version ${this.currentJhipsterVersion}:\n${gitMerge.stdout} ${gitMerge.stderr}`, ); @@ -373,14 +334,14 @@ export default class UpgradeGenerator extends BaseGenerator { }; const createUpgradeBranch = () => { - const gitCheckout = this.gitExec(['checkout', '--orphan', UPGRADE_BRANCH], { silent: this.silent }); - if (gitCheckout.code !== 0) + const gitCheckout = this.spawnSync('git', ['checkout', '--orphan', UPGRADE_BRANCH], { stdio: 'pipe', reject: false }); + if (gitCheckout.exitCode !== 0) throw new Error(`Unable to create ${UPGRADE_BRANCH} branch:\n${gitCheckout.stdout} ${gitCheckout.stderr}`); this.log.ok(`Created branch ${UPGRADE_BRANCH}`); }; - const gitRevParse = this.gitExec(['rev-parse', '-q', '--verify', UPGRADE_BRANCH], { silent: this.silent }); - if (gitRevParse.code !== 0) { + const gitRevParse = this.spawnSync('git', ['rev-parse', '-q', '--verify', UPGRADE_BRANCH], { stdio: 'pipe', reject: false }); + if (gitRevParse.exitCode !== 0) { // Create and checkout upgrade branch createUpgradeBranch(); // Remove/rename old files @@ -441,10 +402,6 @@ export default class UpgradeGenerator extends BaseGenerator { }); }, - upgradeFiles() { - this._upgradeFiles(); - }, - generateWithTargetVersion() { this._cleanUp(); @@ -464,13 +421,13 @@ export default class UpgradeGenerator extends BaseGenerator { mergeChangesBack() { this.log.verboseInfo(`Merging changes back to ${this.sourceBranch}...`); - this.gitExec(['merge', '-q', UPGRADE_BRANCH], { silent: this.silent }); + this.spawnSync('git', ['merge', '-q', UPGRADE_BRANCH], { stdio: 'pipe', reject: false }); this.log.ok('Merge done!'); }, checkConflictsInPackageJson() { - const gitDiff = this.gitExec(['diff', '--name-only', '--diff-filter=U', 'package.json'], { silent: this.silent }); - if (gitDiff.code !== 0) throw new Error(`Unable to check for conflicts in package.json:\n${gitDiff.stdout} ${gitDiff.stderr}`); + const gitDiff = this.spawnSync('git', ['diff', '--name-only', '--diff-filter=U', 'package.json'], { stdio: 'pipe', reject: false }); + if (gitDiff.exitCode !== 0) throw new Error(`Unable to check for conflicts in package.json:\n${gitDiff.stdout} ${gitDiff.stderr}`); if (gitDiff.stdout) { const installCommand = 'npm install'; this.log.warn(`There are conflicts in package.json, please fix them and then run ${installCommand}`); @@ -481,7 +438,7 @@ export default class UpgradeGenerator extends BaseGenerator { } get [BaseGenerator.INSTALL]() { - return { + return this.asInstallTaskGroup({ install() { if (!this.skipInstall) { this.log.verboseInfo('Installing dependencies, please wait...'); @@ -490,8 +447,8 @@ export default class UpgradeGenerator extends BaseGenerator { const installCommand = 'npm install'; this.log.verboseInfo(installCommand); - const pkgInstall = shelljs.exec(installCommand, { silent: this.silent }); - if (pkgInstall.code !== 0) { + const pkgInstall = this.spawnSync(installCommand, { stdio: 'pipe', reject: false }); + if (pkgInstall.exitCode !== 0) { throw new Error(`${installCommand} failed.`); } } else { @@ -499,14 +456,14 @@ export default class UpgradeGenerator extends BaseGenerator { this.log.ok(logMsg); } }, - }; + }); } get [BaseGenerator.END]() { return { end() { - const gitDiff = this.gitExec(['diff', '--name-only', '--diff-filter=U'], { silent: this.silent }); - if (gitDiff.code !== 0) throw new Error(`Unable to check for conflicts:\n${gitDiff.stdout} ${gitDiff.stderr}`); + const gitDiff = this.spawnSync('git', ['diff', '--name-only', '--diff-filter=U'], { stdio: 'pipe', reject: false }); + if (gitDiff.exitCode !== 0) throw new Error(`Unable to check for conflicts:\n${gitDiff.stdout} ${gitDiff.stderr}`); this.log.ok(chalk.bold('Upgraded successfully.')); if (gitDiff.stdout) { this.log.warn(`Please fix conflicts listed below and commit!\n${gitDiff.stdout}`); @@ -514,18 +471,4 @@ export default class UpgradeGenerator extends BaseGenerator { }, }; } - - /** - * @deprecated - * executes a Git command using shellJS - * gitExec(args [, options] [, callback]) - * - * @param {string|array} args - can be an array of arguments or a string command - * @param {object} options[optional] - takes any of child process options - * @param {function} callback[optional] - a callback function to be called once process complete, The call back will receive code, stdout and stderr - * @return {object} when in synchronous mode, this returns a ShellString. Otherwise, this returns the child process object. - */ - gitExec(args, options, callback) { - return gitExec(this.logger, args, options, callback); - } } diff --git a/generators/upgrade/upgrade.spec.mts b/generators/upgrade/upgrade.spec.mts index 1cc6c04f320b..0ef4e07b32c9 100644 --- a/generators/upgrade/upgrade.spec.mts +++ b/generators/upgrade/upgrade.spec.mts @@ -1,10 +1,10 @@ import path, { dirname, resolve } from 'path'; import { fileURLToPath } from 'url'; -import shelljs from 'shelljs'; import fse from 'fs-extra'; import _ from 'lodash'; import { expect } from 'esmocha'; +import { execaCommandSync } from 'execa'; import { packageJson } from '../../lib/index.mjs'; import { GENERATOR_APP, GENERATOR_UPGRADE } from '../generator-list.mjs'; import { basicHelpers as helpers, getGenerator, result as runResult } from '../../test/support/index.mjs'; @@ -39,7 +39,7 @@ describe('generator - upgrade', function () { }); it('generated git commits to match snapshot', () => { - const commits = shelljs.exec('git log --pretty=format:%s', { silent: false }).stdout; + const commits = execaCommandSync('git log --pretty=format:%s', { stdio: 'pipe', reject: false }).stdout; expect(commits.replace(new RegExp(escapeRegExp(packageJson.version), 'g'), 'VERSION')).toMatchInlineSnapshot(` "Merge branch 'jhipster_upgrade' Generated with JHipster VERSION @@ -50,7 +50,7 @@ Initial version of upgradeTest generated by generator-jhipster@undefined" }); it('generates expected number of commits', () => { - const commitsCount = shelljs.exec('git rev-list --count HEAD', { silent: false }).stdout.replace('\n', ''); + const commitsCount = execaCommandSync('git rev-list --count HEAD', { stdio: 'pipe', reject: false }).stdout.replace('\n', ''); // Expecting 5 commits in history (because we used `force` option): // - master: initial commit // - jhipster_upgrade; initial generation @@ -102,13 +102,13 @@ Initial version of upgradeTest generated by generator-jhipster@undefined" }); it('generated git commits to match snapshot', () => { - const commits = shelljs.exec('git log --pretty=format:%s', { silent: false }).stdout; + const commits = execaCommandSync('git log --pretty=format:%s', { stdio: 'pipe', reject: false }).stdout; expect(commits.replace(new RegExp(escapeRegExp(packageJson.version), 'g'), 'VERSION')).toMatchInlineSnapshot(` `); }); it('generates expected number of commits', () => { - const commitsCount = shelljs.exec('git rev-list --count HEAD', { silent: false }).stdout.replace('\n', ''); + const commitsCount = execaCommandSync('git rev-list --count HEAD', { stdio: 'pipe', reject: false }).stdout.replace('\n', ''); // Expecting 5 commits in history (because we used `force` option): // - master: initial commit // - jhipster_upgrade; initial generation diff --git a/package-lock.json b/package-lock.json index ada55c3975e0..a7d171cd10c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,7 +60,6 @@ "randexp": "0.5.3", "run-async": "3.0.0", "semver": "7.5.4", - "shelljs": "0.8.5", "simple-git": "3.20.0", "type-fest": "4.3.2", "uuid": "9.0.1", @@ -80,7 +79,6 @@ "@types/minimatch": "npm:noist@1.0.0", "@types/mocha": "10.0.2", "@types/node": "18.18.0", - "@types/shelljs": "0.8.13", "@types/sinon-chai": "3.2.10", "chai": "4.3.10", "chokidar": "3.5.3", @@ -3246,32 +3244,6 @@ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==" }, - "node_modules/@types/shelljs": { - "version": "0.8.13", - "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.13.tgz", - "integrity": "sha512-++uMLOQSLlse1kCfEOwhgmHuaABZwinkylmUKCpvcEGZUov3TtM+gJZloSkW/W+9pEAEg/VBOwiSR05oqJsa5A==", - "dev": true, - "dependencies": { - "@types/glob": "~7.2.0", - "@types/node": "*" - } - }, - "node_modules/@types/shelljs/node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/shelljs/node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, "node_modules/@types/sinon": { "version": "10.0.16", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz", @@ -4606,11 +4578,11 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { "node": "*" + }, + "dependencies": { + "get-func-name": "^2.0.2" } }, "node_modules/chevrotain": { @@ -8443,14 +8415,6 @@ "node": ">= 0.4" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/invert-kv": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.1.tgz", @@ -12984,17 +12948,6 @@ "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/redent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", @@ -13616,61 +13569,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/shelljs/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", diff --git a/package.json b/package.json index 527c14feb46a..3bc1927d6a38 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,6 @@ "randexp": "0.5.3", "run-async": "3.0.0", "semver": "7.5.4", - "shelljs": "0.8.5", "simple-git": "3.20.0", "type-fest": "4.3.2", "uuid": "9.0.1", @@ -171,7 +170,6 @@ "@types/minimatch": "npm:noist@1.0.0", "@types/mocha": "10.0.2", "@types/node": "18.18.0", - "@types/shelljs": "0.8.13", "@types/sinon-chai": "3.2.10", "chai": "4.3.10", "chokidar": "3.5.3", @@ -214,13 +212,5 @@ "type": "opencollective", "url": "https://opencollective.com/generator-jhipster", "logo": "https://opencollective.com/opencollective/logo.txt" - }, - "overrides": { - "@types/shelljs": { - "@types/glob": { - "@types/minimatch": "3.0.5", - ".": "7.2.0" - } - } } }