diff --git a/bin/clever.js b/bin/clever.js index 775c311..c9a5d01 100755 --- a/bin/clever.js +++ b/bin/clever.js @@ -5,7 +5,6 @@ import '../src/initial-setup.js'; import cliparse from 'cliparse'; import cliparseCommands from 'cliparse/src/command.js'; -import colors from 'colors/safe.js'; import _sortBy from 'lodash/sortBy.js'; import { getPackageJson } from '../src/load-package-json.cjs'; @@ -13,8 +12,6 @@ import * as git from '../src/models/git.js'; import * as Parsers from '../src/parsers.js'; import { handleCommandPromise } from '../src/command-promise-handler.js'; import { AVAILABLE_ZONES } from '../src/models/application.js'; -import { EXPERIMENTAL_FEATURES } from '../src/experimental-features.js'; -import { loadFeaturesConf } from '../src/models/configuration.js'; import { getOutputFormatOption, getSameCommitPolicyOption, getExitOnOption } from '../src/command-options.js'; import * as Addon from '../src/models/addon.js'; @@ -110,6 +107,7 @@ async function run () { description: 'Comma-separated list of experimental features to manage', parser: Parsers.commaSeparated, }), + featureId: cliparse.argument('feature', { description: 'Experimental feature to manage' }), notificationName: cliparse.argument('name', { description: 'Notification name' }), notificationId: cliparse.argument('notification-id', { description: 'Notification ID' }), webhookUrl: cliparse.argument('url', { description: 'Webhook URL' }), @@ -671,6 +669,10 @@ async function run () { description: 'List available experimental features', options: [opts.humanJsonOutputFormat], }, features.list); + const helpFeaturesCommand = cliparse.command('help', { + description: 'Display help about an experimental feature', + args: [args.featureId], + }, features.help); const enableFeatureCommand = cliparse.command('enable', { description: 'Enable an experimental feature', args: [args.features], @@ -681,7 +683,7 @@ async function run () { }, features.disable); const featuresCommands = cliparse.command('features', { description: 'Manage Clever Tools experimental features', - commands: [listFeaturesCommand, enableFeatureCommand, disableFeatureCommand], + commands: [enableFeatureCommand, disableFeatureCommand, listFeaturesCommand, helpFeaturesCommand], }, features.list); // LINK COMMAND @@ -939,16 +941,6 @@ async function run () { webhooksCommand, ]; - // Add experimental features only if they are enabled through the configuration file - const featuresFromConf = await loadFeaturesConf(); - // Here we add the commands for the enabled features - // if (featuresFromConf.kv) { - // commands.push(kvCommand); - // } - // if (featuresFromConf.ng) { - // commands.push(ngCommand); - // } - // We sort the commands by name commands = _sortBy(commands, 'name'); diff --git a/src/commands/features.js b/src/commands/features.js index 92a955a..fdd34b9 100644 --- a/src/commands/features.js +++ b/src/commands/features.js @@ -11,9 +11,10 @@ export async function list (params) { const { format } = params.options; const features_conf = await getFeatures(); - const features = EXPERIMENTAL_FEATURES.map((feature) => { - const enabled = features_conf[feature.id] === true; - return { ...feature, enabled }; + // Add status from configuration file and remove instructions + const features = Object.entries(EXPERIMENTAL_FEATURES).map(([id, feature]) => { + const enabled = features_conf[id] === true; + return { ...feature, id, enabled, instructions: undefined }; }); // For each feature, print the object with the id, status, description and enabled @@ -48,30 +49,50 @@ export async function list (params) { } } +export async function help (params) { + const { feature } = params.namedArgs; + const availableFeatures = Object.keys(EXPERIMENTAL_FEATURES); + + if (!availableFeatures.includes(feature)) { + Logger.printErrorLine(`Feature '${feature}' is not available`); + } + else { + Logger.println(EXPERIMENTAL_FEATURES[feature].instructions); + } +} + export async function enable (params) { const { features } = params.namedArgs; - const availableFeatures = EXPERIMENTAL_FEATURES.map((feature) => feature.id); + const availableFeatures = Object.keys(EXPERIMENTAL_FEATURES); + const canEnableFeatures = features.filter((feature) => availableFeatures.includes(feature)); for (const featureName of features) { if (!availableFeatures.includes(featureName)) { - Logger.printErrorLine(`- Feature '${featureName}' is not available`); + Logger.printErrorLine(`Feature '${featureName}' is not available`); continue; } await setFeature(featureName, true); - Logger.println(`- Experimental feature '${featureName}' enabled`); + Logger.println(`Experimental feature '${featureName}' enabled`); + + if (canEnableFeatures.length === 1) Logger.println(EXPERIMENTAL_FEATURES[featureName].instructions); + } + + if (canEnableFeatures.length > 1) { + Logger.println(); + Logger.println("To learn more about these experimental features, use 'clever features help FEATURE_NAME'"); } } export async function disable (params) { const { features } = params.namedArgs; - const availableFeatures = EXPERIMENTAL_FEATURES.map((feature) => feature.id); + const availableFeatures = Object.keys(EXPERIMENTAL_FEATURES); for (const featureName of features) { if (!availableFeatures.includes(featureName)) { - Logger.printErrorLine(`- Feature '${featureName}' is not available`); + Logger.printErrorLine(`Feature '${featureName}' is not available`); continue; } await setFeature(featureName, false); - Logger.println(`- Experimental feature '${featureName}' disabled`); + Logger.println(`Experimental feature '${featureName}' disabled`); } } diff --git a/src/experimental-features.js b/src/experimental-features.js index 3447f45..685c867 100644 --- a/src/experimental-features.js +++ b/src/experimental-features.js @@ -1,12 +1,49 @@ -export const EXPERIMENTAL_FEATURES = [ - { - id: 'kv', +export const EXPERIMENTAL_FEATURES = { + kv: { status: 'alpha', description: 'Send commands to Materia KV directly from Clever Tools, without other dependencies', + instructions: ` +To use Materia KV from Clever Tools, you need the 'KV_TOKEN' environment variable set. + +Then you can directly send any supported command to Materia KV: + + clever kv ping + clever kv set myKey myValue + clever kv set myTempKey myTempValue ex 120 + clever kv get myKey + clever kv ttl myTempKey + +You can also use the 'clever kv getJson' command to query a value from key containing a JSON object: + + clever kv set simpleJson '{"key": "value"}' + clever kv getJson simpleJson key + clever kv set jsonKey '[{"key": "value"}, {"bigKey": {"subKey1": "subValue1","subKey2": "subValue2"}}]' + clever kv getjson jsonKey bigKey.subKey2 + clever kv getjson jsonKey '' + +Learn more about Materia KV: https://developers.clever-cloud.com/doc/addons/materia-kv/ +`, }, - { - id: 'ng', + ng: { status: 'beta', description: 'Manage Network Groups to link applications, add-ons, external peers in a Wireguard® network', + instructions: ` +- Create a Network Group: + clever ng create myNG +- Create a Network Group with members (application, add-on, external): + clever ng create myNG --members-ids appId1,appId2 +- Add an application to an existing Network Group: + clever ng add-app myNG myApp +- List Network Groups: + clever ng list +- List Network Groups members: + clever ng members list myNG +- List Network Groups peers (instances of a member): + clever ng peers list myNG +- Delete a Network Group: + clever ng delete myNG + +Learn more about Network Groups: https://github.com/CleverCloud/clever-tools/tree/master/docs/ng.md +`, }, -]; +};