gen-jhipster
Version:
VHipster - Spring Boot + Angular/React/Vue in one handy generator
392 lines (391 loc) • 16 kB
JavaScript
/**
* 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 { readFileSync, readdirSync, statSync } from 'node:fs';
import { join } from 'node:path';
import chalk from 'chalk';
import { APPLICATION_TYPE_GATEWAY, APPLICATION_TYPE_MICROSERVICE, APPLICATION_TYPE_MONOLITH } from "../../../lib/core/application-types.js";
import { monitoringTypes, serviceDiscoveryTypes } from "../../../lib/jhipster/index.js";
import { YO_RC_CONFIG_KEY } from "../../../lib/utils/yo-rc.js";
import { asPromptingTask } from "../../base-application/support/index.js";
import { asPromptingWorkspacesTask } from "../support/task-type-inference.js";
const { PROMETHEUS } = monitoringTypes;
const monitoring = monitoringTypes;
const NO_MONITORING = monitoring.NO;
const { CONSUL, EUREKA, NO: NO_SERVICE_DISCOVERY } = serviceDiscoveryTypes;
/**
* Ask For Application Type
*/
export const askForApplicationType = asPromptingTask(async function askForApplicationType({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
await this.prompt([
{
type: 'select',
name: 'deploymentApplicationType',
message: 'Which *type* of application would you like to deploy?',
choices: [
{
value: APPLICATION_TYPE_MONOLITH,
name: 'Monolithic application',
},
{
value: APPLICATION_TYPE_MICROSERVICE,
name: 'Microservice application',
},
],
default: APPLICATION_TYPE_MONOLITH,
},
], this.config);
});
/**
* Ask For Path
*/
export const askForPath = asPromptingTask(async function askForPath({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
const deploymentApplicationType = this.jhipsterConfigWithDefaults.deploymentApplicationType;
let messageAskForPath;
if (deploymentApplicationType === APPLICATION_TYPE_MONOLITH) {
messageAskForPath = 'Enter the root directory where your applications are located';
}
else {
messageAskForPath = 'Enter the root directory where your gateway(s) and microservices are located';
}
await this.prompt([
{
type: 'input',
name: 'directoryPath',
message: messageAskForPath,
default: '../',
validate: async (input) => {
const path = this.destinationPath(input);
try {
if (statSync(path).isDirectory()) {
const appsFolders = getAppFolders.call(this, path, deploymentApplicationType);
if (appsFolders.length === 0) {
return deploymentApplicationType === APPLICATION_TYPE_MONOLITH
? `No monolith found in ${path}`
: `No microservice or gateway found in ${path}`;
}
return true;
}
}
catch {
// Ignore error
}
return `${path} is not a directory or doesn't exist`;
},
},
], this.config);
});
/**
* Ask For Apps
*/
export const askForApps = asPromptingTask(async function askForApps({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
const workspacesRoot = this.destinationPath(this.jhipsterConfig.directoryPath);
const appsFolders = getAppFolders
.call(this, workspacesRoot, this.jhipsterConfigWithDefaults.deploymentApplicationType)
.filter(appFolder => appFolder !== 'jhipster-registry' && appFolder !== 'registry');
this.log.log(chalk.green(`${appsFolders.length} applications found at ${workspacesRoot}\n`));
const messageAskForApps = 'Which applications do you want to include in your configuration?';
const answers = await this.prompt([
{
type: 'checkbox',
name: 'chosenApps',
message: messageAskForApps,
choices: appsFolders ?? [],
default: this.jhipsterConfig.appsFolders,
validate: input => (input.length === 0 ? 'Please choose at least one application' : true),
},
]);
this.jhipsterConfig.appsFolders = answers.chosenApps;
});
/**
* Ask For Clusters Mode
*/
export const askForClustersMode = asPromptingWorkspacesTask(async function askForClustersMode({ control, applications }) {
if (!this.shouldAskForPrompts({ control }))
return;
const clusteredDbApps = applications.filter(app => app.databaseTypeMongodb || app.databaseTypeCouchbase).map(app => app.appFolder);
if (clusteredDbApps.length === 0)
return;
await this.prompt([
{
type: 'checkbox',
name: 'clusteredDbApps',
message: 'Which applications do you want to use with clustered databases (only available with MongoDB and Couchbase)?',
choices: clusteredDbApps,
},
], this.config);
});
/**
* Ask For Monitoring
*/
export const askForMonitoring = asPromptingTask(async function askForMonitoring({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
await this.prompt([
{
type: 'select',
name: 'monitoring',
message: 'Do you want to setup monitoring for your applications ?',
choices: [
{
value: NO_MONITORING,
name: 'No',
},
{
value: PROMETHEUS,
name: 'Yes, for metrics only with Prometheus',
},
],
default: NO_MONITORING,
},
], this.config);
});
/**
* Ask For APISIX Gateway
*/
export const askForApisixGateway = asPromptingWorkspacesTask(async function askForApisixGateway({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
await this.prompt([
{
type: 'confirm',
name: 'apisixGateway',
message: 'Do you want to use APISIX as gateway (Support dynamic routing, recommend to use)?',
default: true,
},
], this.config);
});
/**
* Ask For Service Discovery
*/
export const askForServiceDiscovery = asPromptingWorkspacesTask(async function askForServiceDiscovery({ control, applications }) {
if (!this.shouldAskForPrompts({ control }))
return;
const serviceDiscoveryEnabledApps = applications
.filter(app => app.serviceDiscoveryAny)
.map(app => ({
baseName: app.baseName,
serviceDiscoveryType: app.serviceDiscoveryType,
}));
if (serviceDiscoveryEnabledApps.length === 0) {
this.jhipsterConfig.serviceDiscoveryType = NO_SERVICE_DISCOVERY;
return;
}
if (serviceDiscoveryEnabledApps.every(app => app.serviceDiscoveryType === CONSUL)) {
this.jhipsterConfig.serviceDiscoveryType = CONSUL;
this.log.log(chalk.green('Consul detected as the service discovery and configuration provider used by your apps'));
}
else if (serviceDiscoveryEnabledApps.every(app => app.serviceDiscoveryType === EUREKA)) {
this.jhipsterConfig.serviceDiscoveryType = EUREKA;
this.log.log(chalk.green('JHipster registry detected as the service discovery and configuration provider used by your apps'));
}
else {
this.log.warn(chalk.yellow('Unable to determine the service discovery and configuration provider to use from your apps configuration.'));
this.log.verboseInfo('Your service discovery enabled apps:');
serviceDiscoveryEnabledApps.forEach(app => {
this.log.verboseInfo(` -${app.baseName} (${app.serviceDiscoveryType})`);
});
await this.prompt([
{
type: 'select',
name: 'serviceDiscoveryType',
message: 'Which Service Discovery registry and Configuration server would you like to use ?',
choices: [
{
value: CONSUL,
name: 'Consul',
},
{
value: EUREKA,
name: 'JHipster Registry',
},
{
value: NO_SERVICE_DISCOVERY,
name: 'No Service Discovery and Configuration',
},
],
default: CONSUL,
},
], this.config);
}
});
export const askForClustersModeWorkspace = asPromptingWorkspacesTask(async function askForClustersMode({ control, applications }) {
if (!this.shouldAskForPrompts({ control }))
return;
const clusteredDbApps = applications.filter(app => app.databaseTypeMongodb || app.databaseTypeCouchbase).map(app => app.appFolder);
if (clusteredDbApps.length === 0)
return;
await this.prompt([
{
type: 'checkbox',
name: 'clusteredDbApps',
message: 'Which applications do you want to use with clustered databases (only available with MongoDB and Couchbase)?',
choices: clusteredDbApps,
default: clusteredDbApps,
},
], this.config);
});
export const askForServiceDiscoveryWorkspace = asPromptingWorkspacesTask(async function askForServiceDiscovery({ control, applications }) {
if (!this.shouldAskForPrompts({ control }))
return;
const serviceDiscoveryEnabledApps = applications.filter(app => app.serviceDiscoveryAny);
if (serviceDiscoveryEnabledApps.length === 0) {
this.jhipsterConfig.serviceDiscoveryType = NO_SERVICE_DISCOVERY;
return;
}
if (serviceDiscoveryEnabledApps.every(app => app.serviceDiscoveryConsul)) {
this.jhipsterConfig.serviceDiscoveryType = CONSUL;
this.log.log(chalk.green('Consul detected as the service discovery and configuration provider used by your apps'));
}
else if (serviceDiscoveryEnabledApps.every(app => app.serviceDiscoveryTypeEureka)) {
this.jhipsterConfig.serviceDiscoveryType = EUREKA;
this.log.log(chalk.green('JHipster registry detected as the service discovery and configuration provider used by your apps'));
}
else {
this.log.warn(chalk.yellow('Unable to determine the service discovery and configuration provider to use from your apps configuration.'));
this.log.verboseInfo('Your service discovery enabled apps:');
serviceDiscoveryEnabledApps.forEach(app => {
this.log.verboseInfo(` -${app.baseName} (${app.serviceDiscoveryType})`);
});
await this.prompt([
{
type: 'select',
name: 'serviceDiscoveryType',
message: 'Which Service Discovery registry and Configuration server would you like to use ?',
choices: [
{
value: CONSUL,
name: 'Consul',
},
{
value: EUREKA,
name: 'JHipster Registry',
},
{
value: NO_SERVICE_DISCOVERY,
name: 'No Service Discovery and Configuration',
},
],
default: CONSUL,
},
], this.config);
}
if (this.jhipsterConfig.serviceDiscoveryType === EUREKA) {
await this.prompt([
{
type: 'input',
name: 'adminPassword',
message: 'Enter the admin password used to secure the JHipster Registry',
default: 'admin',
validate: input => (input.length < 5 ? 'The password must have at least 5 characters' : true),
},
], this.config);
}
});
/**
* Ask For Admin Password
*/
export const askForAdminPassword = asPromptingTask(async function askForAdminPassword({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
if (this.jhipsterConfigWithDefaults.serviceDiscoveryType !== EUREKA)
return;
await this.prompt([
{
type: 'input',
name: 'adminPassword',
message: 'Enter the admin password used to secure the JHipster Registry',
default: 'admin',
validate: input => (input.length < 5 ? 'The password must have at least 5 characters' : true),
},
], this.config);
});
/**
* Ask For Docker Repository Name
*/
export const askForDockerRepositoryName = asPromptingTask(async function askForDockerRepositoryName({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
await this.prompt([
{
type: 'input',
name: 'dockerRepositoryName',
message: 'What should we use for the base Docker repository name?',
default: this.jhipsterConfigWithDefaults.dockerRepositoryName,
},
], this.config);
});
/**
* Ask For Docker Push Command
*/
export const askForDockerPushCommand = asPromptingTask(async function askForDockerPushCommand({ control }) {
if (!this.shouldAskForPrompts({ control }))
return;
await this.prompt([
{
type: 'input',
name: 'dockerPushCommand',
message: 'What command should we use for push Docker image to repository?',
default: this.jhipsterConfigWithDefaults.dockerPushCommand,
},
], this.config);
});
/**
* Get App Folders
* @param directory path to join to destination path
* @param deploymentApplicationType type of application being composed
* @returns {Array} array of string representing app folders
*/
export function getAppFolders(directory, deploymentApplicationType) {
const files = readdirSync(directory);
const appsFolders = [];
files.forEach(file => {
try {
if (statSync(join(directory, file)).isDirectory()) {
const yoRcFile = join(directory, file, '.yo-rc.json');
if (statSync(yoRcFile).isFile()) {
try {
const fileData = JSON.parse(readFileSync(yoRcFile).toString());
const yoRcConfig = fileData?.[YO_RC_CONFIG_KEY];
if (yoRcConfig?.baseName !== undefined &&
(deploymentApplicationType === undefined ||
deploymentApplicationType === (yoRcConfig.applicationType ?? APPLICATION_TYPE_MONOLITH) ||
(deploymentApplicationType === APPLICATION_TYPE_MICROSERVICE &&
yoRcConfig.applicationType === APPLICATION_TYPE_GATEWAY))) {
appsFolders.push(/([^/]*)\/*$/.exec(file)[1]);
}
}
catch (err) {
this.log.error(chalk.red(`${yoRcFile}: this .yo-rc.json can't be read`));
this.log.debug('Error:', err);
}
}
}
}
catch {
// Not a file or directory
}
});
return appsFolders;
}