generator-reddgen
Version:
Reddgen is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
871 lines (781 loc) • 31.1 kB
JavaScript
/**
* Copyright 2013-2022 the original author or authors from the Reddgen project.
*
* This file is part of the Reddgen project, see https://www.reddgen.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.
*/
/* eslint-disable consistent-return */
const crypto = require('crypto');
const _ = require('lodash');
const fs = require('fs');
const ChildProcess = require('child_process');
const util = require('util');
const chalk = require('chalk');
const glob = require('glob');
const BaseBlueprintGenerator = require('../generator-base-blueprint');
const {
INITIALIZING_PRIORITY,
PROMPTING_PRIORITY,
CONFIGURING_PRIORITY,
LOADING_PRIORITY,
DEFAULT_PRIORITY,
WRITING_PRIORITY,
END_PRIORITY,
} = require('../../lib/constants/priorities.cjs').compat;
const statistics = require('../statistics');
const constants = require('../generator-constants');
const cacheProviderOptions = require('../../jdl/reddgen/cache-types');
const { MEMCACHED, REDIS } = require('../../jdl/reddgen/cache-types');
const { OAUTH2 } = require('../../jdl/reddgen/authentication-types');
const { GRADLE, MAVEN } = require('../../jdl/reddgen/build-tool-types');
const { ELASTICSEARCH } = require('../../jdl/reddgen/search-engine-types');
const { GENERATOR_HEROKU } = require('../generator-list');
const { MARIADB, MYSQL, POSTGRESQL } = require('../../jdl/reddgen/database-types');
const { EUREKA } = require('../../jdl/reddgen/service-discovery-types');
const NO_CACHE_PROVIDER = cacheProviderOptions.NO;
const execCmd = util.promisify(ChildProcess.exec);
module.exports = class extends BaseBlueprintGenerator {
constructor(args, options, features) {
super(args, options, features);
this.option('skip-build', {
desc: 'Skips building the application',
type: Boolean,
defaults: false,
});
this.option('skip-deploy', {
desc: 'Skips deployment to Heroku',
type: Boolean,
defaults: false,
});
if (this.options.help) {
return;
}
this.randomPassword = crypto.randomBytes(20).toString('hex');
this.herokuSkipBuild = this.options.skipBuild;
this.herokuSkipDeploy = this.options.skipDeploy || this.options.skipBuild;
}
async _postConstruct() {
if (!this.fromBlueprint) {
await this.composeWithBlueprints(GENERATOR_HEROKU);
}
}
_initializing() {
return {
validateFromCli() {
this.checkInvocationFromCLI();
},
loadCommonConfig() {
this.loadAppConfig();
this.loadServerConfig();
this.loadPlatformConfig();
},
initializing() {
this.log(chalk.bold('Heroku configuration is starting'));
const configuration = this.config;
this.env.options.appPath = configuration.get('appPath') || constants.CLIENT_MAIN_SRC_DIR;
this.cacheProvider = this.cacheProvider || NO_CACHE_PROVIDER;
this.enableHibernateCache = this.enableHibernateCache && ![NO_CACHE_PROVIDER, MEMCACHED].includes(this.cacheProvider);
this.frontendAppName = this.getFrontendAppName();
this.herokuAppName = configuration.get('herokuAppName');
this.dynoSize = 'Free';
this.herokuDeployType = configuration.get('herokuDeployType');
this.herokuJavaVersion = configuration.get('herokuJavaVersion');
this.useOkta = configuration.get('useOkta');
this.oktaAdminLogin = configuration.get('oktaAdminLogin');
this.oktaAdminPassword = configuration.get('oktaAdminPassword');
},
};
}
get [INITIALIZING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._initializing();
}
_prompting() {
return {
askForApp() {
const done = this.async();
if (this.herokuAppName) {
ChildProcess.exec(`heroku apps:info --json ${this.herokuAppName}`, (err, stdout) => {
if (err) {
this.abort = true;
this.log.error(`Could not find application: ${chalk.cyan(this.herokuAppName)}`);
this.log.error('Run the generator again to create a new application.');
this.herokuAppName = null;
} else {
const json = JSON.parse(stdout);
this.herokuAppName = json.app.name;
if (json.dynos.length > 0) {
this.dynoSize = json.dynos[0].size;
}
this.log(`Deploying as existing application: ${chalk.bold(this.herokuAppName)}`);
this.herokuAppExists = true;
this.config.set({
herokuAppName: this.herokuAppName,
herokuDeployType: this.herokuDeployType,
});
}
done();
});
} else {
const prompts = [
{
type: 'input',
name: 'herokuAppName',
message: 'Name to deploy as:',
default: this.baseName,
},
{
type: 'list',
name: 'herokuRegion',
message: 'On which region do you want to deploy ?',
choices: ['us', 'eu'],
default: 0,
},
];
this.prompt(prompts).then(props => {
this.herokuAppName = _.kebabCase(props.herokuAppName);
this.herokuRegion = props.herokuRegion;
this.herokuAppExists = false;
done();
});
}
},
askForHerokuDeployType() {
if (this.abort) return null;
if (this.herokuDeployType) return null;
const prompts = [
{
type: 'list',
name: 'herokuDeployType',
message: 'Which type of deployment do you want ?',
choices: [
{
value: 'git',
name: 'Git (compile on Heroku)',
},
{
value: 'jar',
name: 'JAR (compile locally)',
},
],
default: 0,
},
];
return this.prompt(prompts).then(props => {
this.herokuDeployType = props.herokuDeployType;
});
},
askForHerokuJavaVersion() {
if (this.abort) return null;
if (this.herokuJavaVersion) return null;
const prompts = [
{
type: 'list',
name: 'herokuJavaVersion',
message: 'Which Java version would you like to use to build and run your app ?',
choices: constants.JAVA_COMPATIBLE_VERSIONS.map(version => ({ value: version })),
default: constants.JAVA_VERSION,
},
];
return this.prompt(prompts).then(props => {
this.herokuJavaVersion = props.herokuJavaVersion;
});
},
askForOkta() {
if (this.abort) return null;
if (this.authenticationType !== OAUTH2) return null;
if (this.useOkta) return null;
const prompts = [
{
type: 'list',
name: 'useOkta',
message:
'You are using OAuth 2.0. Do you want to use Okta? When you choose Okta, the automated configuration of users and groups requires cURL and jq.',
choices: [
{
value: true,
name: 'Yes, provision the Okta add-on',
},
{
value: false,
name: 'No, I want to configure my identity provider manually',
},
],
default: 1,
},
{
when: answers => answers.useOkta,
type: 'input',
name: 'oktaAdminLogin',
message: 'Login (valid email) for the Reddgen Admin user:',
validate: input => {
if (!input) {
return 'You must enter a login for the Reddgen admin';
}
return true;
},
},
];
return this.prompt(prompts).then(props => {
this.useOkta = props.useOkta;
if (this.useOkta) {
this.oktaAdminLogin = props.oktaAdminLogin;
this.oktaAdminPassword = this.randomPassword;
}
});
},
};
}
get [PROMPTING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._prompting();
}
_configuring() {
return {
checkInstallation() {
if (this.abort) return;
const done = this.async();
ChildProcess.exec('heroku --version', err => {
if (err) {
this.log.error("You don't have the Heroku CLI installed. Download it from https://cli.heroku.com/");
this.abort = true;
}
done();
});
},
saveConfig() {
this.config.set({
herokuAppName: this.herokuAppName,
herokuDeployType: this.herokuDeployType,
herokuJavaVersion: this.herokuJavaVersion,
useOkta: this.useOkta,
oktaAdminLogin: this.oktaAdminLogin,
});
},
};
}
get [CONFIGURING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._configuring();
}
// Public API method used by the getter and also by Blueprints
_loading() {
return {
loadSharedConfig() {
this.loadAppConfig();
this.loadDerivedAppConfig();
this.loadClientConfig();
this.loadDerivedClientConfig();
this.loadServerConfig();
this.loadTranslationConfig();
this.loadPlatformConfig();
},
};
}
get [LOADING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._loading();
}
_default() {
return {
insight() {
statistics.sendSubGenEvent('generator', GENERATOR_HEROKU);
},
gitInit() {
if (this.abort) return;
const done = this.async();
try {
fs.lstatSync('.git');
this.log(chalk.bold('\nUsing existing Git repository'));
done();
} catch (e) {
// An exception is thrown if the folder doesn't exist
this.log(chalk.bold('\nInitializing Git repository'));
const child = ChildProcess.exec('git init', (err, stdout, stderr) => {
done();
});
child.stdout.on('data', data => {
this.log(data.toString());
});
}
},
installHerokuDeployPlugin() {
if (this.abort) return;
const done = this.async();
const cliPlugin = 'heroku-cli-deploy';
ChildProcess.exec('heroku plugins', (err, stdout) => {
if (_.includes(stdout, cliPlugin)) {
this.log('\nHeroku CLI deployment plugin already installed');
done();
} else {
this.log(chalk.bold('\nInstalling Heroku CLI deployment plugin'));
const child = ChildProcess.exec(`heroku plugins:install ${cliPlugin}`, (err, stdout) => {
if (err) {
this.abort = true;
this.log.error(err);
}
done();
});
child.stdout.on('data', data => {
this.log(data.toString());
});
}
});
},
herokuCreate() {
if (this.abort || this.herokuAppExists) return;
const done = this.async();
const regionParams = this.herokuRegion !== 'us' ? ` --region ${this.herokuRegion}` : '';
this.log(chalk.bold('\nCreating Heroku application and setting up node environment'));
const child = ChildProcess.exec(`heroku create ${this.herokuAppName}${regionParams}`, { timeout: 6000 }, (err, stdout, stderr) => {
if (err) {
if (stderr.includes('is already taken')) {
const prompts = [
{
type: 'list',
name: 'herokuForceName',
message: `The Heroku application "${chalk.cyan(this.herokuAppName)}" already exists! Use it anyways?`,
choices: [
{
value: 'Yes',
name: 'Yes, I have access to it',
},
{
value: 'No',
name: 'No, generate a random name',
},
],
default: 0,
},
];
this.log('');
this.prompt(prompts).then(props => {
if (props.herokuForceName === 'Yes') {
ChildProcess.exec(`heroku git:remote --app ${this.herokuAppName}`, (err, stdout, stderr) => {
if (err) {
this.abort = true;
this.log.error(err);
} else {
this.log(stdout.trim());
this.config.set({
herokuAppName: this.herokuAppName,
herokuDeployType: this.herokuDeployType,
});
}
done();
});
} else {
ChildProcess.exec(`heroku create ${regionParams}`, (err, stdout, stderr) => {
if (err) {
this.abort = true;
this.log.error(err);
} else {
// Extract from "Created random-app-name-1234... done"
this.herokuAppName = stdout.substring(stdout.indexOf('https://') + 8, stdout.indexOf('.herokuapp'));
this.log(stdout.trim());
// ensure that the git remote is the same as the appName
ChildProcess.exec(`heroku git:remote --app ${this.herokuAppName}`, (err, stdout, stderr) => {
if (err) {
this.abort = true;
this.log.error(err);
} else {
this.config.set({
herokuAppName: this.herokuAppName,
herokuDeployType: this.herokuDeployType,
});
}
done();
});
}
});
}
});
} else {
this.abort = true;
if (stderr.includes('Invalid credentials')) {
this.log.error("Error: Not authenticated. Run 'heroku login' to login to your heroku account and try again.");
} else {
this.log.error(err);
}
done();
}
} else {
done();
}
});
child.stdout.on('data', data => {
const output = data.toString();
if (data.search('Heroku credentials') >= 0) {
this.abort = true;
this.log.error("Error: Not authenticated. Run 'heroku login' to login to your heroku account and try again.");
done();
} else {
this.log(output.trim());
}
});
},
herokuAddonsCreate() {
if (this.abort) return;
const done = this.async();
const addonCreateCallback = (addon, err, stdout, stderr) => {
if (err) {
const verifyAccountUrl = 'https://heroku.com/verify';
if (_.includes(err, verifyAccountUrl)) {
this.abort = true;
this.log.error(`Account must be verified to use addons. Please go to: ${verifyAccountUrl}`);
this.log.error(err);
} else {
this.log(`No new ${addon} addon created`);
}
} else {
this.log(`Created ${addon} addon`);
}
};
this.log(chalk.bold('\nProvisioning addons'));
if (this.searchEngine === ELASTICSEARCH) {
this.log(chalk.bold('\nProvisioning bonsai elasticsearch addon'));
ChildProcess.exec(`heroku addons:create bonsai:sandbox-6 --as BONSAI --app ${this.herokuAppName}`, (err, stdout, stderr) => {
addonCreateCallback.bind('Elasticsearch', err, stdout, stderr);
});
}
if (this.useOkta) {
this.log(chalk.bold('\nProvisioning okta addon'));
ChildProcess.exec(`heroku addons:create okta --app ${this.herokuAppName}`, (err, stdout, stderr) => {
addonCreateCallback('Okta', err, stdout, stderr);
});
}
let dbAddOn;
if (this.prodDatabaseType === POSTGRESQL) {
dbAddOn = 'heroku-postgresql --as DATABASE';
} else if (this.prodDatabaseType === MYSQL) {
dbAddOn = 'jawsdb:kitefin --as DATABASE';
} else if (this.prodDatabaseType === MARIADB) {
dbAddOn = 'jawsdb-maria:kitefin --as DATABASE';
}
if (dbAddOn) {
this.log(chalk.bold(`\nProvisioning database addon ${dbAddOn}`));
ChildProcess.exec(`heroku addons:create ${dbAddOn} --app ${this.herokuAppName}`, (err, stdout, stderr) => {
addonCreateCallback('Database', err, stdout, stderr);
});
} else {
this.log(chalk.bold(`\nNo suitable database addon for database ${this.prodDatabaseType} available.`));
}
let cacheAddOn;
if (this.cacheProvider === MEMCACHED) {
cacheAddOn = 'memcachier:dev --as MEMCACHIER';
} else if (this.cacheProvider === REDIS) {
cacheAddOn = 'heroku-redis:hobby-dev --as REDIS';
}
if (cacheAddOn) {
this.log(chalk.bold(`\nProvisioning cache addon ${cacheAddOn}`));
ChildProcess.exec(`heroku addons:create ${cacheAddOn} --app ${this.herokuAppName}`, (err, stdout, stderr) => {
addonCreateCallback('Cache', err, stdout, stderr);
});
} else {
this.log(chalk.bold(`\nNo suitable cache addon for cacheprovider ${this.cacheProvider} available.`));
}
done();
},
configureReddgenRegistry() {
if (this.abort || this.herokuAppExists) return undefined;
if (this.serviceDiscoveryType === EUREKA) {
const prompts = [
{
type: 'input',
name: 'herokuReddgenRegistryApp',
message: 'What is the name of your Reddgen Registry Heroku application?',
default: 'reddgen-registry',
},
{
type: 'input',
name: 'herokuReddgenRegistryUsername',
message: 'What is your Reddgen Registry username?',
default: 'admin',
},
{
type: 'input',
name: 'herokuReddgenRegistryPassword',
message: 'What is your Reddgen Registry password?',
default: 'password',
},
];
this.log('');
return this.prompt(prompts).then(props => {
// Encode username/password to avoid errors caused by spaces
props.herokuReddgenRegistryUsername = encodeURIComponent(props.herokuReddgenRegistryUsername);
props.herokuReddgenRegistryPassword = encodeURIComponent(props.herokuReddgenRegistryPassword);
const herokuReddgenRegistry = `https://${props.herokuReddgenRegistryUsername}:${props.herokuReddgenRegistryPassword}@${props.herokuReddgenRegistryApp}.herokuapp.com`;
const configSetCmd = `heroku config:set REDDGEN_REGISTRY_URL=${herokuReddgenRegistry} --app ${this.herokuAppName}`;
const child = ChildProcess.exec(configSetCmd, (err, stdout, stderr) => {
if (err) {
this.abort = true;
this.log.error(err);
}
});
child.stdout.on('data', data => {
this.log(data.toString());
});
});
}
return undefined;
},
};
}
get [DEFAULT_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._default();
}
_writing() {
return {
copyHerokuFiles() {
if (this.abort) return;
this.log(chalk.bold('\nCreating Heroku deployment files'));
this.template('bootstrap-heroku.yml.ejs', `${constants.SERVER_MAIN_RES_DIR}/config/bootstrap-heroku.yml`);
this.template('application-heroku.yml.ejs', `${constants.SERVER_MAIN_RES_DIR}/config/application-heroku.yml`);
this.template('Procfile.ejs', 'Procfile');
this.template('system.properties.ejs', 'system.properties');
if (this.buildTool === GRADLE) {
this.template('heroku.gradle.ejs', 'gradle/heroku.gradle');
}
if (this.useOkta) {
this.template('provision-okta-addon.sh.ejs', 'provision-okta-addon.sh');
fs.appendFile('.gitignore', 'provision-okta-addon.sh', 'utf8', (err, data) => {
if (err) {
this.log(`${chalk.yellow.bold('WARNING!')} Failed to add 'provision-okta-addon.sh' to .gitignore.'`);
}
});
}
},
addHerokuBuildPlugin() {
if (this.abort) return;
if (this.buildTool !== GRADLE) return;
this.addGradlePlugin('gradle.plugin.com.heroku.sdk', 'heroku-gradle', '1.0.4');
this.applyFromGradleScript('gradle/heroku');
},
addHerokuMavenProfile() {
if (this.abort) return;
if (this.buildTool === MAVEN) {
this.render('pom-profile.xml.ejs', profile => {
this.addMavenProfile('heroku', ` ${profile.toString().trim()}`);
});
}
},
};
}
get [WRITING_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._writing();
}
_end() {
return {
makeScriptExecutable() {
if (this.abort) return;
if (this.useOkta) {
try {
fs.chmodSync('provision-okta-addon.sh', '755');
} catch (err) {
this.log(
`${chalk.yellow.bold(
'WARNING!'
)}Failed to make 'provision-okta-addon.sh' executable, you may need to run 'chmod +x provison-okta-addon.sh'`
);
}
}
},
productionBuild() {
if (this.abort) return;
if (this.herokuSkipBuild || this.herokuDeployType === 'git') {
this.log(chalk.bold('\nSkipping build'));
return;
}
const done = this.async();
this.log(chalk.bold('\nBuilding application'));
const child = this.buildApplication(this.buildTool, 'prod', false, err => {
if (err) {
this.abort = true;
this.log.error(err);
}
done();
});
this.buildCmd = child.buildCmd;
child.stdout.on('data', data => {
process.stdout.write(data.toString());
});
},
async productionDeploy() {
if (this.abort) return;
if (this.herokuSkipDeploy) {
this.log(chalk.bold('\nSkipping deployment'));
return;
}
if (this.herokuDeployType === 'git') {
try {
this.log(chalk.bold('\nUpdating Git repository'));
const gitAddCmd = 'git add .';
this.log(chalk.cyan(gitAddCmd));
const gitAdd = execCmd(gitAddCmd);
gitAdd.child.stdout.on('data', data => {
this.log(data);
});
gitAdd.child.stderr.on('data', data => {
this.log(data);
});
await gitAdd;
const gitCommitCmd = 'git commit -m "Deploy to Heroku" --allow-empty';
this.log(chalk.cyan(gitCommitCmd));
const gitCommit = execCmd(gitCommitCmd);
gitCommit.child.stdout.on('data', data => {
this.log(data);
});
gitCommit.child.stderr.on('data', data => {
this.log(data);
});
await gitCommit;
let buildpack = 'heroku/java';
let configVars = 'MAVEN_CUSTOM_OPTS="-Pprod,heroku -DskipTests" ';
if (this.buildTool === GRADLE) {
buildpack = 'heroku/gradle';
configVars = 'GRADLE_TASK="stage -Pprod -PnodeInstall" ';
}
this.log(chalk.bold('\nConfiguring Heroku'));
await execCmd(`heroku config:set ${configVars}--app ${this.herokuAppName}`);
await execCmd(`heroku buildpacks:add ${buildpack} --app ${this.herokuAppName}`);
this.log(chalk.bold('\nDeploying application'));
const herokuPush = execCmd('git push heroku HEAD:main', { maxBuffer: 1024 * 10000 });
herokuPush.child.stdout.on('data', data => {
this.log(data);
});
herokuPush.child.stderr.on('data', data => {
this.log(data);
});
await herokuPush;
this.log(chalk.green(`\nYour app should now be live. To view it run\n\t${chalk.bold('heroku open')}`));
this.log(chalk.yellow(`And you can view the logs with this command\n\t${chalk.bold('heroku logs --tail')}`));
this.log(chalk.yellow(`After application modification, redeploy it with\n\t${chalk.bold('reddgen heroku')}`));
if (this.useOkta) {
let curlAvailable = false;
let jqAvailable = false;
try {
await execCmd('curl --help');
curlAvailable = true;
} catch (err) {
this.log(
chalk.red('cURL is not available but required. See https://curl.haxx.se/download.html for installation guidance.')
);
this.log(chalk.yellow('After you have installed curl execute ./provision-okta-addon.sh manually.'));
}
try {
await execCmd('jq --help');
jqAvailable = true;
} catch (err) {
this.log(
chalk.red('jq is not available but required. See https://stedolan.github.io/jq/download/ for installation guidance.')
);
this.log(chalk.yellow('After you have installed jq execute ./provision-okta-addon.sh manually.'));
}
if (curlAvailable && jqAvailable) {
this.log(chalk.green('Running ./provision-okta-addon.sh to create all required roles and users for Reddgen.'));
try {
await execCmd('./provision-okta-addon.sh');
this.log(chalk.bold('\nOkta configured successfully!'));
this.log(chalk.green(`\nUse ${chalk.bold(`${this.oktaAdminLogin}/${this.oktaAdminPassword}`)} to login.\n`));
} catch (err) {
this.log(
chalk.red(
'Failed to execute ./provision-okta-addon.sh. Make sure to setup okta according to https://www.reddgen.tech/heroku/.'
)
);
}
}
}
} catch (err) {
this.log.error(err);
}
} else {
this.log(chalk.bold('\nDeploying application'));
let jarFileWildcard = 'target/*.jar';
if (this.buildTool === GRADLE) {
jarFileWildcard = 'build/libs/*.jar';
}
const files = glob.sync(jarFileWildcard, {});
const jarFile = files[0];
const herokuDeployCommand = `heroku deploy:jar ${jarFile} --app ${this.herokuAppName}`;
const herokuSetBuildpackCommand = 'heroku buildpacks:set heroku/jvm';
this.log(
chalk.bold(
`\nUploading your application code.\nThis may take ${chalk.cyan('several minutes')} depending on your connection speed...`
)
);
try {
await execCmd(herokuSetBuildpackCommand);
const herokuDeploy = execCmd(herokuDeployCommand);
herokuDeploy.child.stdout.on('data', data => {
this.log(data);
});
herokuDeploy.child.stderr.on('data', data => {
this.log(data);
});
await herokuDeploy;
this.log(chalk.green(`\nYour app should now be live. To view it run\n\t${chalk.bold('heroku open')}`));
this.log(chalk.yellow(`And you can view the logs with this command\n\t${chalk.bold('heroku logs --tail')}`));
this.log(chalk.yellow(`After application modification, redeploy it with\n\t${chalk.bold('reddgen heroku')}`));
if (this.useOkta) {
let curlAvailable = false;
let jqAvailable = false;
try {
await execCmd('curl --help');
curlAvailable = true;
} catch (err) {
this.log(
chalk.red('cURL is not available but required. See https://curl.haxx.se/download.html for installation guidance.')
);
this.log(chalk.yellow('After you have installed curl execute ./provision-okta-addon.sh manually.'));
}
try {
await execCmd('jq --help');
jqAvailable = true;
} catch (err) {
this.log(
chalk.red('jq is not available but required. See https://stedolan.github.io/jq/download/ for installation guidance.')
);
this.log(chalk.yellow('After you have installed jq execute ./provision-okta-addon.sh manually.'));
}
if (curlAvailable && jqAvailable) {
this.log(chalk.green('Running ./provision-okta-addon.sh to create all required roles and users for Reddgen.'));
try {
await execCmd('./provision-okta-addon.sh');
this.log(chalk.bold('\nOkta configured successfully!'));
this.log(chalk.green(`\nUse ${chalk.bold(`${this.oktaAdminLogin}/${this.oktaAdminPassword}`)} to login.`));
} catch (err) {
this.log(
chalk.red(
'Failed to execute ./provision-okta-addon.sh. Make sure to set up Okta according to https://www.reddgen.tech/heroku/.'
)
);
}
}
}
} catch (err) {
this.log.error(err);
}
}
},
};
}
get [END_PRIORITY]() {
if (this.delegateToBlueprint) return {};
return this._end();
}
};