Skip to content

Commit

Permalink
feat(features): add instructions and help subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
davlgd committed Oct 22, 2024
1 parent f80d87a commit 2de8c68
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 29 deletions.
20 changes: 6 additions & 14 deletions bin/clever.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ 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';
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';
Expand Down Expand Up @@ -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' }),
Expand Down Expand Up @@ -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],
Expand All @@ -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
Expand Down Expand Up @@ -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');

Expand Down
39 changes: 30 additions & 9 deletions src/commands/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`);
}
}
49 changes: 43 additions & 6 deletions src/experimental-features.js
Original file line number Diff line number Diff line change
@@ -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
`,
},
];
};

0 comments on commit 2de8c68

Please sign in to comment.