Merge pull request #2732 from strapi/quickstart

Add quickstart mode
This commit is contained in:
Jim LAURIE 2019-02-01 17:26:16 +01:00 committed by GitHub
commit 5ed07cb27a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 232 additions and 192 deletions

View File

@ -10,7 +10,7 @@ Create a new project
```bash ```bash
strapi new <name> strapi new <name>
options: [--dev|--debug|--dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport> --dbname=<dbname> --dbusername=<dbusername> --dbpassword=<dbpassword> --dbssl=<dbssl> --dbauth=<dbauth> --dbforce] options: [--dev|--debug|--quickstart|--dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport> --dbname=<dbname> --dbusername=<dbusername> --dbpassword=<dbpassword> --dbssl=<dbssl> --dbauth=<dbauth> --dbforce]
``` ```
- **strapi new &#60;name&#62;**<br/> - **strapi new &#60;name&#62;**<br/>
@ -18,16 +18,19 @@ options: [--dev|--debug|--dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport
- **strapi new &#60;name&#62; --dev**<br/> - **strapi new &#60;name&#62; --dev**<br/>
Generates a new project called **&#60;name&#62;** and creates symlinks for the `./admin` folder and each plugin inside the `./plugin` folder. It means that the Strapi's development workflow has been set up on the machine earlier. Generates a new project called **&#60;name&#62;** and creates symlinks for the `./admin` folder and each plugin inside the `./plugin` folder. It means that the Strapi's development workflow has been set up on the machine earlier.
- **strapi new &#60;name&#62; --debug**<br/> - **strapi new &#60;name&#62; --debug**<br/>
Will display the full error message if one is fired during the database connection. Will display the full error message if one is fired during the database connection.
- **strapi new &#60;name&#62; --quickstart**<br/>
Use the quickstart system to create your app.
- **strapi new &#60;name&#62; --dbclient=&#60;dbclient&#62; --dbhost=&#60;dbhost&#62; --dbport=&#60;dbport&#62; --dbname=&#60;dbname&#62; --dbusername=&#60;dbusername&#62; --dbpassword=&#60;dbpassword&#62; --dbssl=&#60;dbssl&#62; --dbauth=&#60;dbauth&#62; --dbforce**<br/> - **strapi new &#60;name&#62; --dbclient=&#60;dbclient&#62; --dbhost=&#60;dbhost&#62; --dbport=&#60;dbport&#62; --dbname=&#60;dbname&#62; --dbusername=&#60;dbusername&#62; --dbpassword=&#60;dbpassword&#62; --dbssl=&#60;dbssl&#62; --dbauth=&#60;dbauth&#62; --dbforce**<br/>
Generates a new project called **&#60;name&#62;** and skip the interactive database configuration and initilize with these options. Generates a new project called **&#60;name&#62;** and skip the interactive database configuration and initilize with these options.
- **&#60;dbclient&#62;** can be `mongo`, `postgres`, `mysql`. - **&#60;dbclient&#62;** can be `mongo`, `postgres`, `mysql`.
- **&#60;dbssl&#62;** and **&#60;dbauth&#62;** are available only for `mongo` and are optional. - **&#60;dbssl&#62;** and **&#60;dbauth&#62;** are available only for `mongo` and are optional.
- **--dbforce** Allows you to overwrite content if the provided database is not empty. Only available for `postgres`, `mysql`, and is optional. - **--dbforce** Allows you to overwrite content if the provided database is not empty. Only available for `postgres`, `mysql`, and is optional.
See the [CONTRIBUTING guide](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) for more details. See the [CONTRIBUTING guide](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) for more details.

View File

@ -62,13 +62,11 @@ module.exports = (scope, cb) => {
// ... // ...
} }
console.log('Let\s configurate the connection to your database:');
if (hasDatabaseConfig) { if (hasDatabaseConfig) {
console.log(`Database determined by CLI args: ${scope.database.settings.client}`); console.log(`Database determined by CLI args: ${scope.database.settings.client}`);
} }
const connectionValidation = () => { const connectionValidation = async () => {
const databaseChoices = [ const databaseChoices = [
{ {
name: 'SQLite', name: 'SQLite',
@ -103,10 +101,25 @@ module.exports = (scope, cb) => {
} }
]; ];
inquirer const answers = await inquirer
.prompt([ .prompt([
{ {
when: !hasDatabaseConfig, when: !scope.quick,
type: 'list',
name: 'type',
message: 'Choose your installation type',
choices: [{
name: 'Quickstart (recommended)',
value: 'quick'
}, {
name: 'Custom (manual settings)',
value: 'custom'
}]
},
{
when: (answers) => {
return !hasDatabaseConfig && answers.type === 'custom';
},
type: 'list', type: 'list',
name: 'client', name: 'client',
message: 'Choose your main database:', message: 'Choose your main database:',
@ -117,203 +130,214 @@ module.exports = (scope, cb) => {
} }
} }
} }
]) ]);
.then(answers => {
if (hasDatabaseConfig) { scope.quick = answers.type === 'quick' || scope.quick;
const databaseChoice = _.find(databaseChoices, ['value.database', scope.database.settings.client]); const isQuick = scope.quick;
scope.database.connector = databaseChoice.value.connector;
answers.client = { if (isQuick) {
...databaseChoice.value answers.client = databaseChoices[0].value;
}; }
} else {
_.assign(scope.database, { if (hasDatabaseConfig) {
connector: answers.client.connector, const databaseChoice = _.find(databaseChoices, ['value.database', scope.database.settings.client]);
settings: { scope.database.connector = databaseChoice.value.connector;
client: answers.client.database answers.client = {
...databaseChoice.value
};
} else {
_.assign(scope.database, {
connector: answers.client.connector,
settings: {
client: answers.client.database
},
options: {}
});
}
scope.client = answers.client;
const asyncFn = [
new Promise(async resolve => {
const isMongo = scope.client.database === 'mongo';
const isSQLite = scope.database.settings.client === 'sqlite';
let answers = await inquirer
.prompt([
{
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'database',
message: 'Database name:',
default: _.get(scope.database, 'database', scope.name)
}, },
options: {} {
}); when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'host',
message: 'Host:',
default: _.get(scope.database, 'host', '127.0.0.1')
},
{
when: !hasDatabaseConfig && isMongo,
type: 'boolean',
name: 'srv',
message: '+srv connection:',
default: _.get(scope.database, 'srv', false)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'port',
message: `Port${isMongo ? ' (It will be ignored if you enable +srv)' : ''}:`,
default: (answers) => { // eslint-disable-line no-unused-vars
if (_.get(scope.database, 'port')) {
return scope.database.port;
}
const ports = {
mongo: 27017,
postgres: 5432,
mysql: 3306
};
return ports[scope.client.database];
}
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'username',
message: 'Username:',
default: _.get(scope.database, 'username', undefined)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'password',
name: 'password',
message: 'Password:',
mask: '*',
default: _.get(scope.database, 'password', undefined)
},
{
when: !hasDatabaseConfig && isMongo,
type: 'input',
name: 'authenticationDatabase',
message: 'Authentication database (Maybe "admin" or blank):',
default: _.get(scope.database, 'authenticationDatabase', undefined)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'boolean',
name: 'ssl',
message: 'Enable SSL connection:',
default: _.get(scope.database, 'ssl', false)
},
{
when: !hasDatabaseConfig && isSQLite && !isQuick,
type: 'input',
name: 'filename',
message: 'Filename:',
default: () => '.tmp/data.db'
}
]);
if (isQuick) {
answers.filename = '.tmp/data.db';
} }
scope.client = answers.client;
const asyncFn = [ if (hasDatabaseConfig) {
new Promise(resolve => { answers = _.merge((_.omit(scope.database.settings, ['client'])), scope.database.options);
const isMongo = scope.client.database === 'mongo'; }
const isSQLite = scope.database.settings.client === 'sqlite';
inquirer scope.database.settings.host = answers.host;
.prompt([ scope.database.settings.port = answers.port;
{ scope.database.settings.database = answers.database;
when: !hasDatabaseConfig && !isSQLite, scope.database.settings.username = answers.username;
type: 'input', scope.database.settings.password = answers.password;
name: 'database',
message: 'Database name:',
default: _.get(scope.database, 'database', scope.name)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'host',
message: 'Host:',
default: _.get(scope.database, 'host', '127.0.0.1')
},
{
when: !hasDatabaseConfig && isMongo,
type: 'boolean',
name: 'srv',
message: '+srv connection:',
default: _.get(scope.database, 'srv', false)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'port',
message: `Port${isMongo ? ' (It will be ignored if you enable +srv)' : ''}:`,
default: (answers) => { // eslint-disable-line no-unused-vars
if (_.get(scope.database, 'port')) {
return scope.database.port;
}
const ports = { if (answers.filename) {
mongo: 27017, scope.database.settings.filename = answers.filename;
postgres: 5432, }
mysql: 3306 if (answers.srv) {
}; scope.database.settings.srv = _.toString(answers.srv) === 'true';
}
if (answers.authenticationDatabase) {
scope.database.options.authenticationDatabase = answers.authenticationDatabase;
}
return ports[scope.client.database]; // SQLite requirements.
} if (isSQLite) {
}, // Necessary for SQLite configuration (https://knexjs.org/#Builder-insert).
{ scope.database.options = {
when: !hasDatabaseConfig && !isSQLite, useNullAsDefault: true
type: 'input', };
name: 'username', }
message: 'Username:',
default: _.get(scope.database, 'username', undefined)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'password',
name: 'password',
message: 'Password:',
mask: '*',
default: _.get(scope.database, 'password', undefined)
},
{
when: !hasDatabaseConfig && isMongo,
type: 'input',
name: 'authenticationDatabase',
message: 'Authentication database (Maybe "admin" or blank):',
default: _.get(scope.database, 'authenticationDatabase', undefined)
},
{
when: !hasDatabaseConfig && !isSQLite,
type: 'boolean',
name: 'ssl',
message: 'Enable SSL connection:',
default: _.get(scope.database, 'ssl', false)
},
{
when: !hasDatabaseConfig && isSQLite,
type: 'input',
name: 'filename',
message: 'Filename:',
default: () => '.tmp/data.db'
}
])
.then(answers => {
if (hasDatabaseConfig) {
answers = _.merge((_.omit(scope.database.settings, ['client'])), scope.database.options);
}
scope.database.settings.host = answers.host; if (answers.ssl && scope.client.database === 'mongo') {
scope.database.settings.port = answers.port; scope.database.options.ssl = _.toString(answers.ssl) === 'true';
scope.database.settings.database = answers.database; } else if (answers.ssl) {
scope.database.settings.username = answers.username; scope.database.settings.ssl = _.toString(answers.ssl) === 'true';
scope.database.settings.password = answers.password; }
if (answers.filename) {
scope.database.settings.filename = answers.filename;
}
if (answers.srv) {
scope.database.settings.srv = _.toString(answers.srv) === 'true';
}
if (answers.authenticationDatabase) {
scope.database.options.authenticationDatabase = answers.authenticationDatabase;
}
// SQLite requirements. console.log();
if (isSQLite) { console.log('⏳ Testing database connection...');
// Necessary for SQLite configuration (https://knexjs.org/#Builder-insert).
scope.database.options = {
useNullAsDefault: true
};
}
if (answers.ssl && scope.client.database === 'mongo') { resolve();
scope.database.options.ssl = _.toString(answers.ssl) === 'true'; }),
} else if (answers.ssl) { new Promise(resolve => {
scope.database.settings.ssl = _.toString(answers.ssl) === 'true'; const isStrapiInstalledWithNPM = packageManager.isStrapiInstalledWithNPM();
} let packageCmd = packageManager.commands('install --prefix', scope.tmpPath);
// Manually create the temp directory for yarn
if (!isStrapiInstalledWithNPM) {
shell.exec(`mkdir ${scope.tmpPath}`);
}
console.log(); let cmd = `${packageCmd} ${scope.client.connector}@${scope.strapiPackageJSON.version}`;
console.log('⏳ Testing database connection...'); let linkNodeModulesCommand = `cd ${scope.tmpPath} && npm link ${scope.client.connector}`;
resolve(); if (scope.client.module) {
}); cmd += ` ${scope.client.module}`;
}), }
new Promise(resolve => {
const isStrapiInstalledWithNPM = packageManager.isStrapiInstalledWithNPM(); if (scope.client.connector === 'strapi-hook-bookshelf') {
let packageCmd = packageManager.commands('install --prefix', scope.tmpPath); cmd += ` strapi-hook-knex@${scope.strapiPackageJSON.version}`;
// Manually create the temp directory for yarn linkNodeModulesCommand += ` && npm link strapi-hook-knex`;
if (!isStrapiInstalledWithNPM) {
shell.exec(`mkdir ${scope.tmpPath}`); scope.additionalsDependencies = ['strapi-hook-knex', 'knex'];
}
exec(cmd, () => {
if (scope.client.module) {
const lock = require(path.join(`${scope.tmpPath}`, '/node_modules/', `${scope.client.module}/package.json`));
scope.client.version = lock.version;
if (scope.developerMode === true && scope.client.connector === 'strapi-hook-bookshelf') {
const knexVersion = require(path.join(`${scope.tmpPath}`,`/node_modules/`,`knex/package.json`));
scope.additionalsDependencies[1] = `knex@${knexVersion.version || 'latest'}`;
} }
}
let cmd = `${packageCmd} ${scope.client.connector}@${scope.strapiPackageJSON.version}`; if (scope.developerMode) {
let linkNodeModulesCommand = `cd ${scope.tmpPath} && npm link ${scope.client.connector}`; exec(linkNodeModulesCommand, () => {
resolve();
if (scope.client.module) {
cmd += ` ${scope.client.module}`;
}
if (scope.client.connector === 'strapi-hook-bookshelf') {
cmd += ` strapi-hook-knex@${scope.strapiPackageJSON.version}`;
linkNodeModulesCommand += ` && npm link strapi-hook-knex`;
scope.additionalsDependencies = ['strapi-hook-knex', 'knex'];
}
exec(cmd, () => {
if (scope.client.module) {
const lock = require(path.join(`${scope.tmpPath}`, '/node_modules/', `${scope.client.module}/package.json`));
scope.client.version = lock.version;
if (scope.developerMode === true && scope.client.connector === 'strapi-hook-bookshelf') {
const knexVersion = require(path.join(`${scope.tmpPath}`,`/node_modules/`,`knex/package.json`));
scope.additionalsDependencies[1] = `knex@${knexVersion.version || 'latest'}`;
}
}
if (scope.developerMode) {
exec(linkNodeModulesCommand, () => {
resolve();
});
} else {
resolve();
}
}); });
}) } else {
]; resolve();
}
});
})
];
Promise.all(asyncFn) Promise.all(asyncFn)
.then(() => { .then(() => {
try { try {
require(path.join(`${scope.tmpPath}`, '/node_modules/', `${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation); require(path.join(`${scope.tmpPath}`, '/node_modules/', `${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation);
} catch(err) { } catch(err) {
console.log(err); console.log(err);
shell.rm('-r', scope.tmpPath); shell.rm('-r', scope.tmpPath);
cb.error(); cb.error();
} }
});
}); });
}; };

View File

@ -11,6 +11,7 @@ const path = require('path');
// Public node modules. // Public node modules.
const _ = require('lodash'); const _ = require('lodash');
const shell = require('shelljs');
// Master of ceremonies for generators. // Master of ceremonies for generators.
const generate = require('strapi-generate'); const generate = require('strapi-generate');
@ -43,7 +44,8 @@ module.exports = function (name, cliArguments) {
name, name,
strapiPackageJSON: packageJSON, strapiPackageJSON: packageJSON,
developerMode, developerMode,
debug: cliArguments.debug !== undefined debug: cliArguments.debug !== undefined,
quick: cliArguments.quickstart !== undefined
}; };
const dbArguments = ['dbclient', 'dbhost', 'dbport', 'dbname', 'dbusername', 'dbpassword']; const dbArguments = ['dbclient', 'dbhost', 'dbport', 'dbname', 'dbusername', 'dbpassword'];
@ -83,6 +85,15 @@ module.exports = function (name, cliArguments) {
error: function returnError(err) { error: function returnError(err) {
console.log(err); console.log(err);
process.exit(1); process.exit(1);
},
success: () => {
if (scope.quick) {
shell.cd(scope.rootPath);
shell.exec('strapi start', {
stdio: 'inherit'
});
}
} }
}); });
}; };

View File

@ -54,6 +54,7 @@ program
.command('new') .command('new')
.option('-d, --dev', 'Development mode') .option('-d, --dev', 'Development mode')
.option('--debug', 'Display database connection error') .option('--debug', 'Display database connection error')
.option('--quickstart', 'Quickstart app creation')
.option('--dbclient <dbclient>', 'Database client') .option('--dbclient <dbclient>', 'Database client')
.option('--dbhost <dbhost>', 'Database host') .option('--dbhost <dbhost>', 'Database host')
.option('--dbsrv <dbsrv>', 'Database srv') .option('--dbsrv <dbsrv>', 'Database srv')

View File

@ -59,6 +59,7 @@
"ora": "^3.0.0", "ora": "^3.0.0",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"semver": "^5.4.1", "semver": "^5.4.1",
"shelljs": "^0.8.3",
"stack-trace": "0.0.10", "stack-trace": "0.0.10",
"strapi-generate": "3.0.0-alpha.21", "strapi-generate": "3.0.0-alpha.21",
"strapi-generate-admin": "3.0.0-alpha.21", "strapi-generate-admin": "3.0.0-alpha.21",
@ -99,4 +100,4 @@
}, },
"preferGlobal": true, "preferGlobal": true,
"license": "MIT" "license": "MIT"
} }