commit
e1a9e016e9
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const cli = require('../server/cli');
|
||||||
|
|
||||||
|
cli();
|
|
@ -1,5 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
require('../server/cli')(process.argv);
|
|
12
package.json
12
package.json
|
@ -10,9 +10,8 @@
|
||||||
"required": false,
|
"required": false,
|
||||||
"kind": "plugin"
|
"kind": "plugin"
|
||||||
},
|
},
|
||||||
"main": "server/cli.js",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"config-sync": "./bin/config-sync.js"
|
"config-sync": "./bin/config-sync"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"eslint": "eslint --max-warnings=0 './**/*.{js,jsx}'",
|
"eslint": "eslint --max-warnings=0 './**/*.{js,jsx}'",
|
||||||
|
@ -48,10 +47,11 @@
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fortawesome/react-fontawesome": "^0.1.16",
|
"@fortawesome/react-fontawesome": "^0.1.16",
|
||||||
"@strapi/design-system": "0.0.1-alpha.51",
|
"@strapi/design-system": "0.0.1-alpha.64",
|
||||||
"@strapi/helper-plugin": "4.0.0-beta.6",
|
"@strapi/helper-plugin": "4.0.0-beta.12",
|
||||||
"@strapi/icons": "0.0.1-alpha.51",
|
"@strapi/icons": "0.0.1-alpha.64",
|
||||||
"@strapi/utils": "4.0.0-beta.6",
|
"@strapi/strapi": "^4.0.0-beta.12",
|
||||||
|
"@strapi/utils": "4.0.0-beta.12",
|
||||||
"babel-eslint": "9.0.0",
|
"babel-eslint": "9.0.0",
|
||||||
"codecov": "^3.8.3",
|
"codecov": "^3.8.3",
|
||||||
"eslint": "^5.16.0",
|
"eslint": "^5.16.0",
|
||||||
|
|
188
server/cli.js
188
server/cli.js
|
@ -4,14 +4,17 @@ const { Command } = require('commander');
|
||||||
const Table = require('cli-table');
|
const Table = require('cli-table');
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const inquirer = require('inquirer');
|
const inquirer = require('inquirer');
|
||||||
|
const { isEmpty } = require('lodash');
|
||||||
|
const strapi = require('@strapi/strapi');
|
||||||
|
|
||||||
const packageJSON = require('../package.json');
|
const packageJSON = require('../package.json');
|
||||||
|
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
|
|
||||||
const initTable = () => {
|
const initTable = (head) => {
|
||||||
return new Table({
|
return new Table({
|
||||||
head: [chalk.green('Name'), chalk.green('State')],
|
head: [chalk.green('Name'), chalk.green(head || 'State')],
|
||||||
|
colWidths: [50, 15],
|
||||||
chars: { top: '═',
|
chars: { top: '═',
|
||||||
'top-mid': '╤',
|
'top-mid': '╤',
|
||||||
'top-left': '╔',
|
'top-left': '╔',
|
||||||
|
@ -31,6 +34,93 @@ const initTable = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getConfigState = (diff, configName, syncType) => {
|
||||||
|
if (
|
||||||
|
diff.fileConfig[configName]
|
||||||
|
&& diff.databaseConfig[configName]
|
||||||
|
) {
|
||||||
|
return chalk.yellow(syncType ? 'Update' : 'Different');
|
||||||
|
} else if (
|
||||||
|
diff.fileConfig[configName]
|
||||||
|
&& !diff.databaseConfig[configName]
|
||||||
|
) {
|
||||||
|
if (syncType === 'import') {
|
||||||
|
return chalk.green('Create');
|
||||||
|
} else if (syncType === 'export') {
|
||||||
|
return chalk.red('Delete');
|
||||||
|
} else {
|
||||||
|
return chalk.red('Only in sync dir');
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
!diff.fileConfig[configName]
|
||||||
|
&& diff.databaseConfig[configName]
|
||||||
|
) {
|
||||||
|
if (syncType === 'import') {
|
||||||
|
return chalk.red('Delete');
|
||||||
|
} else if (syncType === 'export') {
|
||||||
|
return chalk.green('Create');
|
||||||
|
} else {
|
||||||
|
return chalk.green('Only in DB');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAction = async (type, skipConfirm) => {
|
||||||
|
const app = await strapi().load();
|
||||||
|
const diff = await app.plugin('config-sync').service('main').getFormattedDiff();
|
||||||
|
|
||||||
|
// No changes.
|
||||||
|
if (isEmpty(diff.diff)) {
|
||||||
|
console.log(`${chalk.cyan('[notice]')} There are no changes to ${type}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init table.
|
||||||
|
const table = initTable('Action');
|
||||||
|
|
||||||
|
// Add diff to table.
|
||||||
|
Object.keys(diff.diff).map((configName) => {
|
||||||
|
table.push([configName, getConfigState(diff, configName, type)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Print table.
|
||||||
|
console.log(table.toString(), '\n');
|
||||||
|
|
||||||
|
// Prompt to confirm.
|
||||||
|
let answer = {};
|
||||||
|
if (!skipConfirm) {
|
||||||
|
answer = await inquirer.prompt([{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirm',
|
||||||
|
message: `Are you sure you want to ${type} the config changes?`,
|
||||||
|
}]);
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preform the action.
|
||||||
|
if (skipConfirm || answer.confirm) {
|
||||||
|
if (type === 'import') {
|
||||||
|
const onSuccess = (name) => console.log(`${chalk.green('[success]')} Imported ${name}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await app.plugin('config-sync').service('main').importAllConfig(null, onSuccess);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`${chalk.red('[error]')} Something went wrong during the import. ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'export') {
|
||||||
|
try {
|
||||||
|
await app.plugin('config-sync').service('main').exportAllConfig();
|
||||||
|
console.log(`${chalk.green('[success]')} Exported config`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`${chalk.red('[error]')} Something went wrong during the export. ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
};
|
||||||
|
|
||||||
// Initial program setup
|
// Initial program setup
|
||||||
program.storeOptionsAsProperties(false).allowUnknownOption(true);
|
program.storeOptionsAsProperties(false).allowUnknownOption(true);
|
||||||
|
|
||||||
|
@ -50,88 +140,44 @@ program
|
||||||
// `$ config-sync import`
|
// `$ config-sync import`
|
||||||
program
|
program
|
||||||
.command('import')
|
.command('import')
|
||||||
.option('-t, --type <type>', 'The type of config')
|
// .option('-t, --type <type>', 'The type of config') // TODO: partial import
|
||||||
|
.option('-y', 'Skip the confirm prompt')
|
||||||
.description('Import the config')
|
.description('Import the config')
|
||||||
.action(async (args) => {
|
.action(async ({ y, type }) => {
|
||||||
console.log('import', args);
|
return handleAction('import', y, type);
|
||||||
|
|
||||||
// Init table.
|
|
||||||
const table = initTable();
|
|
||||||
|
|
||||||
// Fill table.
|
|
||||||
table.push(
|
|
||||||
['admin-role.author', chalk.yellow('different')],
|
|
||||||
['core-store.plugin_i18n_default_locale', chalk.green('Only in DB')],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Print table.
|
|
||||||
console.log(table.toString());
|
|
||||||
|
|
||||||
// Prompt to confirm.
|
|
||||||
const answer = await inquirer.prompt([{
|
|
||||||
type: 'confirm',
|
|
||||||
name: 'confirm',
|
|
||||||
message: 'Are you sure you want to import the config changes?',
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Make the import.
|
|
||||||
if (answer.confirm) {
|
|
||||||
console.log(chalk.magenta('IMPORT!!'));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// `$ config-sync export`
|
// `$ config-sync export`
|
||||||
program
|
program
|
||||||
.command('export')
|
.command('export')
|
||||||
.option('-t, --type <type>', 'The type of config')
|
// .option('-t, --type <type>', 'The type of config') // TODO: partial export
|
||||||
|
.option('-y', 'Skip the confirm prompt')
|
||||||
.description('Export the config')
|
.description('Export the config')
|
||||||
.action(async (args) => {
|
.action(async ({ y, type }) => {
|
||||||
console.log('export', args);
|
return handleAction('export', y, type);
|
||||||
|
|
||||||
// Init table.
|
|
||||||
const table = initTable();
|
|
||||||
|
|
||||||
// Fill table.
|
|
||||||
table.push(
|
|
||||||
['admin-role.author', chalk.yellow('different')],
|
|
||||||
['core-store.plugin_i18n_default_locale', chalk.green('Only in DB')],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Print table.
|
|
||||||
console.log(table.toString());
|
|
||||||
|
|
||||||
// Prompt to confirm.
|
|
||||||
const answer = await inquirer.prompt([{
|
|
||||||
type: 'confirm',
|
|
||||||
name: 'confirm',
|
|
||||||
message: 'Are you sure you want to export the config changes?',
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Make the export.
|
|
||||||
if (answer.confirm) {
|
|
||||||
console.log(chalk.magenta('EXPORT!!'));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// `$ config-sync diff`
|
// `$ config-sync diff`
|
||||||
program
|
program
|
||||||
.command('diff')
|
.command('diff')
|
||||||
.option('-t, --type <type>', 'The type of config')
|
// .option('-t, --type <type>', 'The type of config') // TODO: partial diff
|
||||||
.description('The config diff')
|
.description('The config diff')
|
||||||
.action(async (args) => {
|
.action(async ({ type }) => {
|
||||||
console.log('diff', args);
|
const app = await strapi().load();
|
||||||
|
const diff = await app.plugin('config-sync').service('main').getFormattedDiff();
|
||||||
|
|
||||||
// Init table.
|
// Init table.
|
||||||
const table = initTable();
|
const table = initTable();
|
||||||
|
|
||||||
// Fill table.
|
// Add diff to table.
|
||||||
table.push(
|
Object.keys(diff.diff).map((configName) => {
|
||||||
['admin-role.author', chalk.yellow('different')],
|
table.push([configName, getConfigState(diff, configName)]);
|
||||||
['core-store.plugin_i18n_default_locale', chalk.green('Only in DB')],
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Print table.
|
// Print table.
|
||||||
console.log(table.toString());
|
console.log(table.toString());
|
||||||
});
|
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
program.parseAsync(process.argv);
|
program.parseAsync(process.argv);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const difference = require('../utils/getObjectDiff');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main controllers for config import/export.
|
* Main controllers for config import/export.
|
||||||
|
@ -64,24 +63,6 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedDiff = {
|
return strapi.plugin('config-sync').service('main').getFormattedDiff();
|
||||||
fileConfig: {},
|
|
||||||
databaseConfig: {},
|
|
||||||
diff: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles();
|
|
||||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase();
|
|
||||||
|
|
||||||
const diff = difference(databaseConfig, fileConfig);
|
|
||||||
|
|
||||||
formattedDiff.diff = diff;
|
|
||||||
|
|
||||||
Object.keys(diff).map((changedConfigName) => {
|
|
||||||
formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName];
|
|
||||||
formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName];
|
|
||||||
});
|
|
||||||
|
|
||||||
return formattedDiff;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -144,9 +144,10 @@ module.exports = () => ({
|
||||||
* Import all config files into the db.
|
* Import all config files into the db.
|
||||||
*
|
*
|
||||||
* @param {string} configType - Type of config to impor. Leave empty to import all config.
|
* @param {string} configType - Type of config to impor. Leave empty to import all config.
|
||||||
|
* @param {object} onSuccess - Success callback to run on each single successfull import.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
importAllConfig: async (configType = null) => {
|
importAllConfig: async (configType = null, onSuccess) => {
|
||||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles();
|
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles();
|
||||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase();
|
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase();
|
||||||
|
|
||||||
|
@ -160,7 +161,14 @@ module.exports = () => ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await strapi.plugin('config-sync').service('main').importSingleConfig(type, name);
|
try {
|
||||||
|
await strapi.plugin('config-sync').service('main').importSingleConfig(type, name);
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess(`${type}.${name}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -207,4 +215,33 @@ module.exports = () => ({
|
||||||
exportSingleConfig: async (configType, configName) => {
|
exportSingleConfig: async (configType, configName) => {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the formatted diff.
|
||||||
|
*
|
||||||
|
* @param {string} configType - Type of config to get the diff of. Leave empty to get the diff of all config.
|
||||||
|
*
|
||||||
|
* @returns {object} - the formatted diff.
|
||||||
|
*/
|
||||||
|
getFormattedDiff: async (configType = null) => {
|
||||||
|
const formattedDiff = {
|
||||||
|
fileConfig: {},
|
||||||
|
databaseConfig: {},
|
||||||
|
diff: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles(configType);
|
||||||
|
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase(configType);
|
||||||
|
|
||||||
|
const diff = difference(databaseConfig, fileConfig);
|
||||||
|
|
||||||
|
formattedDiff.diff = diff;
|
||||||
|
|
||||||
|
Object.keys(diff).map((changedConfigName) => {
|
||||||
|
formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName];
|
||||||
|
formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName];
|
||||||
|
});
|
||||||
|
|
||||||
|
return formattedDiff;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const { logMessage, sanitizeConfig, dynamicSort } = require('../utils');
|
const { logMessage, sanitizeConfig, dynamicSort } = require('../utils');
|
||||||
const difference = require('../utils/getObjectDiff');
|
const difference = require('../utils/getArrayDiff');
|
||||||
const arrayDifference = require('../utils/getArrayDiff');
|
|
||||||
|
|
||||||
|
|
||||||
const ConfigType = class ConfigType {
|
const ConfigType = class ConfigType {
|
||||||
constructor(queryString, configPrefix, uid, jsonFields, relations) {
|
constructor(queryString, configPrefix, uid, jsonFields, relations) {
|
||||||
|
@ -21,24 +19,9 @@ const ConfigType = class ConfigType {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
exportAll = async () => {
|
exportAll = async () => {
|
||||||
const formattedDiff = {
|
const formattedDiff = await strapi.plugin('config-sync').service('main').getFormattedDiff(this.configPrefix);
|
||||||
fileConfig: {},
|
|
||||||
databaseConfig: {},
|
|
||||||
diff: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles(this.configPrefix);
|
await Promise.all(Object.entries(formattedDiff.diff).map(async ([configName, config]) => {
|
||||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase(this.configPrefix);
|
|
||||||
const diff = difference(databaseConfig, fileConfig);
|
|
||||||
|
|
||||||
formattedDiff.diff = diff;
|
|
||||||
|
|
||||||
Object.keys(diff).map((changedConfigName) => {
|
|
||||||
formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName];
|
|
||||||
formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName];
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(Object.entries(diff).map(async ([configName, config]) => {
|
|
||||||
// Check if the config should be excluded.
|
// Check if the config should be excluded.
|
||||||
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${configName}`);
|
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${configName}`);
|
||||||
if (shouldExclude) return;
|
if (shouldExclude) return;
|
||||||
|
@ -132,29 +115,29 @@ const ConfigType = class ConfigType {
|
||||||
const entity = await queryAPI.update({ where: { [this.uid]: configName }, data: query });
|
const entity = await queryAPI.update({ where: { [this.uid]: configName }, data: query });
|
||||||
|
|
||||||
// Delete/create relations.
|
// Delete/create relations.
|
||||||
this.relations.map(async ({ queryString, relationName, parentName, relationSortField }) => {
|
await Promise.all(this.relations.map(async ({ queryString, relationName, parentName, relationSortField }) => {
|
||||||
const relationQueryApi = strapi.query(queryString);
|
const relationQueryApi = strapi.query(queryString);
|
||||||
existingConfig = sanitizeConfig(existingConfig, relationName, relationSortField);
|
existingConfig = sanitizeConfig(existingConfig, relationName, relationSortField);
|
||||||
configContent = sanitizeConfig(configContent, relationName, relationSortField);
|
configContent = sanitizeConfig(configContent, relationName, relationSortField);
|
||||||
|
|
||||||
const configToAdd = arrayDifference(configContent[relationName], existingConfig[relationName], relationSortField);
|
const configToAdd = difference(configContent[relationName], existingConfig[relationName], relationSortField);
|
||||||
const configToDelete = arrayDifference(existingConfig[relationName], configContent[relationName], relationSortField);
|
const configToDelete = difference(existingConfig[relationName], configContent[relationName], relationSortField);
|
||||||
|
|
||||||
configToDelete.map(async (config) => {
|
await Promise.all(configToDelete.map(async (config) => {
|
||||||
await relationQueryApi.delete({
|
await relationQueryApi.delete({
|
||||||
where: {
|
where: {
|
||||||
[relationSortField]: config[relationSortField],
|
[relationSortField]: config[relationSortField],
|
||||||
[parentName]: entity.id,
|
[parentName]: entity.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
|
|
||||||
configToAdd.map(async (config) => {
|
await Promise.all(configToAdd.map(async (config) => {
|
||||||
await relationQueryApi.create({
|
await relationQueryApi.create({
|
||||||
data: { ...config, [parentName]: entity.id },
|
data: { ...config, [parentName]: entity.id },
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue