Skip to content

Commit d03f763

Browse files
Saas 965 configuring cli (#239)
* cli-config output formatting * constants extracted * cli config management * profiles + schema validation + output changes + get command changes + docs * packages + docs * fix docs order * removed constants * errors refactored * set null support * help for properties + null is now displayed * config validation * model defaults for objects in schema * manager config "lazy load" * advanced formatting + tests * output formatting config on get command + fallback mode * version updated * add current config to fallback mode
1 parent 3161b8b commit d03f763

22 files changed

+872
-16
lines changed

docs/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const categoriesOrder = {
2626
compositions : 110 ,
2727
'helm repos' : 111 ,
2828
'predefined pipelines': 120,
29+
'cli config': 121,
2930
teams: 130,
3031
more : 150,
3132
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const Command = require('../../Command');
2+
const yargs = require('yargs');
3+
4+
const Manager = require('../../../../logic/cli-config/Manager');
5+
const { outputCliConfig } = require('../../helpers/cli-config');
6+
7+
const cliConfig = new Command({
8+
root: true,
9+
command: 'cli-config',
10+
description: 'Codefresh CLI configuration. Uses profiles',
11+
usage: 'CLI configuration is used for user-specific properties, for example pretty-print',
12+
category: 'cli config',
13+
webDocs: {
14+
description: 'Codefresh CLI configuration. Uses profiles',
15+
category: 'CLI Config',
16+
title: 'Show Config',
17+
weight: 100,
18+
},
19+
builder: (yargs) => {
20+
return yargs
21+
.option('output', {
22+
alias: 'o',
23+
describe: 'Output format',
24+
options: ['json', 'yaml'],
25+
})
26+
.help(false)
27+
.example('codefresh cli-config', 'Print configuration for current profile');
28+
},
29+
handler: async (argv) => {
30+
if (argv.help) {
31+
yargs.showHelp();
32+
return;
33+
}
34+
const config = Manager.config();
35+
console.log(`Current profile: | ${Manager.currentProfile()} |\n`);
36+
outputCliConfig(argv.output, config);
37+
},
38+
});
39+
40+
module.exports = cliConfig;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const Command = require('../../../Command');
2+
const cliCommand = require('../cli-config.cmd');
3+
const yargs = require('yargs');
4+
5+
const Manager = require('../../../../../logic/cli-config/Manager');
6+
const { printProperties, outputCliConfig, propertyErrorHandler } = require('../../../helpers/cli-config');
7+
8+
const getCommand = new Command({
9+
command: 'get [name]',
10+
parent: cliCommand,
11+
description: 'For current profile get all properties containing provided "name"',
12+
usage: 'Used when you may need to know some exact properties values',
13+
webDocs: {
14+
description: 'For current profile get all properties containing provided "name"',
15+
category: 'CLI Config',
16+
title: 'Get Config Properties',
17+
weight: 110,
18+
},
19+
builder: (yargs) => {
20+
return yargs
21+
.positional('name', {
22+
describe: 'Property name',
23+
})
24+
.example('codefresh cli-config get', 'Print available property names')
25+
.example('codefresh cli-config get output', 'Print properties, containing "output" word')
26+
.example('codefresh cli-config get output.pretty', 'Print properties, containing "output.pretty" path');
27+
},
28+
handler: async (argv) => {
29+
if (argv.help) {
30+
yargs.showHelp();
31+
return;
32+
}
33+
const propertyName = argv.name;
34+
if (!propertyName) {
35+
console.log('Available properties:\n');
36+
printProperties(Manager.availableProperties());
37+
return;
38+
}
39+
40+
let properties;
41+
try {
42+
properties = Manager.get(propertyName);
43+
} catch (e) {
44+
propertyErrorHandler(e);
45+
return;
46+
}
47+
console.log(`Current profile: | ${Manager.currentProfile()} |\n`);
48+
outputCliConfig(argv.output, properties);
49+
},
50+
});
51+
52+
module.exports = getCommand;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const Command = require('../../../Command');
2+
const cliCommand = require('../cli-config.cmd');
3+
const yargs = require('yargs');
4+
5+
const Manager = require('../../../../../logic/cli-config/Manager');
6+
const { outputCliMeta } = require('../../../helpers/cli-config');
7+
8+
const helpCommand = new Command({
9+
command: 'help [name]',
10+
parent: cliCommand,
11+
description: 'Show help for properties',
12+
usage: 'Used when you may need to know some properties types, defaults, description etc.',
13+
webDocs: {
14+
description: 'Show help for properties. Used when you may need to know some properties types, defaults, description etc.',
15+
category: 'CLI Config',
16+
title: 'Describe Config Properties',
17+
weight: 130,
18+
},
19+
builder: (yargs) => {
20+
return yargs
21+
.positional('name', {
22+
describe: 'Property name',
23+
})
24+
.example('codefresh cli-config help', 'Print help for all properties')
25+
.example('codefresh cli-config help output', 'Print help for properties, containing "output" word')
26+
},
27+
handler: async (argv) => {
28+
if (argv.help) {
29+
yargs.showHelp();
30+
return;
31+
}
32+
outputCliMeta(Manager.meta(argv.name));
33+
},
34+
});
35+
36+
module.exports = helpCommand;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const Command = require('../../../Command');
2+
const cliCommand = require('../cli-config.cmd');
3+
const yargs = require('yargs');
4+
5+
const CliConfigManager = require('../../../../../logic/cli-config/Manager');
6+
7+
const setCommand = new Command({
8+
command: 'profile [name]',
9+
parent: cliCommand,
10+
description: 'Change cli-config profile',
11+
usage: 'Using profiles gives an ability to switch fast between the different configuration sets',
12+
webDocs: {
13+
description: 'Change cli-config profile',
14+
category: 'CLI Config',
15+
title: 'Profiles',
16+
weight: 120,
17+
},
18+
builder: (yargs) => {
19+
return yargs
20+
.positional('name', {
21+
describe: 'Profile name',
22+
})
23+
.example('codefresh cli-config profile myProfile', 'Use or create profile with name "myProfile"');
24+
},
25+
handler: async (argv) => {
26+
if (argv.help) {
27+
yargs.showHelp();
28+
return;
29+
}
30+
const { name } = argv;
31+
32+
if (!name) {
33+
console.log(`Current profile: | ${CliConfigManager.currentProfile()} |\n`);
34+
console.log('Available profiles:');
35+
CliConfigManager.profiles().forEach((profile) => {
36+
console.log(profile);
37+
});
38+
return;
39+
}
40+
41+
const created = CliConfigManager.useProfile(name);
42+
CliConfigManager.persistConfig();
43+
44+
const message = created ? `Profile "${name}" was created` : `Using profile "${name}"`;
45+
console.log(message);
46+
},
47+
});
48+
49+
module.exports = setCommand;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const Command = require('../../../Command');
2+
const cliCommand = require('../cli-config.cmd');
3+
const yargs = require('yargs');
4+
5+
const Manager = require('../../../../../logic/cli-config/Manager');
6+
const { outputCliConfig, propertyErrorHandler, printProperties } = require('../../../helpers/cli-config');
7+
8+
// todo : fix descriptions for docs
9+
const setCommand = new Command({
10+
command: 'set [name] [value]',
11+
parent: cliCommand,
12+
description: 'For current profile set a property with "name"',
13+
webDocs: {
14+
description: 'For current profile set an available property',
15+
category: 'CLI Config',
16+
title: 'Set Config Property',
17+
weight: 110,
18+
},
19+
builder: (yargs) => {
20+
return yargs
21+
.positional('name', {
22+
describe: 'Property name',
23+
})
24+
.positional('value', {
25+
describe: 'Property value',
26+
})
27+
.example('codefresh cli-config set', 'Print available property names')
28+
.example('codefresh cli-config set output.pretty false', 'Set "output.pretty" property');
29+
},
30+
handler: async (argv) => {
31+
if (argv.help) {
32+
yargs.showHelp();
33+
return;
34+
}
35+
let { name, value } = argv;
36+
if (!name) {
37+
console.log('Available properties:\n');
38+
printProperties(Manager.availableProperties());
39+
return;
40+
}
41+
if (value === undefined) {
42+
console.log(`No value provided for property "${name}"`);
43+
return;
44+
}
45+
if (value === 'null') {
46+
value = null;
47+
}
48+
49+
let props;
50+
try {
51+
Manager.set(name, value);
52+
Manager.persistConfig();
53+
props = Manager.get(name);
54+
} catch (e) {
55+
propertyErrorHandler(e);
56+
return;
57+
}
58+
console.log(`Property set on profile: | ${Manager.currentProfile()} |\n`);
59+
outputCliConfig(argv.output, props);
60+
},
61+
});
62+
63+
module.exports = setCommand;

lib/interface/cli/commands/runtimeEnvironments/create.cmd.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@ const _ = require('lodash');
33
const Command = require('../../Command');
44
const fs = require('fs');
55
const { spawn } = require('child_process');
6-
const { homedir } = require('os');
76
const rp = require('request-promise');
87
const createRoot = require('../root/create.cmd');
98
const authManager = require('../../../../logic/auth').manager; // eslint-disable-line
109
const { cluster } = require('../../../../logic').api;
1110
const CFError = require('cf-errors');
1211

12+
const { CODEFRESH_PATH } = require('../../defaults');
1313
const scriptUrl = 'https://raw.githubusercontent.com/codefresh-io/k8s-dind-config/master/codefresh-k8s-configure.sh';
14-
let filePath = `${homedir()}/.Codefresh/runtime/codefresh-k8s-configure.sh`;
15-
const dirPath = `${homedir()}/.Codefresh/runtime`;
16-
const codefreshPath = `${homedir()}/.Codefresh`;
14+
let filePath = `${CODEFRESH_PATH}/runtime/codefresh-k8s-configure.sh`;
15+
const dirPath = `${CODEFRESH_PATH}/runtime`;
1716

1817

1918
const callToScript = (k8sScript) => {
@@ -74,8 +73,8 @@ const command = new Command({
7473
});
7574
if (validCluster) {
7675
if (!process.env.LOCAL) {
77-
if (!fs.existsSync(codefreshPath)) {
78-
fs.mkdirSync(codefreshPath);
76+
if (!fs.existsSync(CODEFRESH_PATH)) {
77+
fs.mkdirSync(CODEFRESH_PATH);
7978
fs.mkdirSync(dirPath);
8079
} else if (!fs.existsSync(dirPath)) {
8180
fs.mkdirSync(dirPath);

lib/interface/cli/defaults.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
const { homedir } = require('os');
2+
const path = require('path');
3+
14
const DEFAULTS = {
25
URL: 'https://g.codefresh.io',
36
CFCONFIG: `${process.env.HOME || process.env.USERPROFILE}/.cfconfig`,
@@ -7,6 +10,7 @@ const DEFAULTS = {
710
CODEFRESH_REGISTRIES: ['r.cfcr.io', '192.168.99.100:5000'],
811
WATCH_INTERVAL_MS: 3000,
912
MAX_CONSECUTIVE_ERRORS_LIMIT: 10,
13+
CODEFRESH_PATH: path.resolve(homedir(), '.Codefresh'),
1014
};
1115

1216
module.exports = DEFAULTS;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const _ = require('lodash');
2+
const flatten = require('flat');
3+
const columnify = require('columnify');
4+
const yaml = require('js-yaml');
5+
const Style = require('../../../output/style');
6+
const { NoPropertyError, MultiplePropertiesError, NotFullPropertyError, SchemaValidationError } = require('../../../logic/cli-config/errors');
7+
8+
const _jsonFormatter = data => JSON.stringify(data, null, 4);
9+
const COLUMNIFY_OPTS = { columnSplitter: ' ', headingTransform: Style.bold.uppercase };
10+
11+
const NO_PROPERTIES_MESSAGE = 'no properties';
12+
13+
function _valueFormatter(value) {
14+
if (value === null) {
15+
return Style.gray('<null>');
16+
}
17+
if (typeof value === 'string') {
18+
return Style.green(`'${value}'`);
19+
}
20+
if (typeof value === 'boolean') {
21+
return Style.yellow(value);
22+
}
23+
return value;
24+
}
25+
26+
function _defaultOutput(data) {
27+
const flat = flatten(data);
28+
const columns = _.keys(flat)
29+
.sort()
30+
.map((key) => {
31+
const value = _valueFormatter(_.get(data, key));
32+
return { key, value };
33+
});
34+
return columnify(columns, COLUMNIFY_OPTS);
35+
}
36+
37+
function _formatter(format) {
38+
switch (format) {
39+
case 'yaml':
40+
return yaml.safeDump;
41+
case 'json':
42+
return _jsonFormatter;
43+
default:
44+
return _defaultOutput;
45+
}
46+
}
47+
48+
function outputCliConfig(format, data) {
49+
const formatter = _formatter(format);
50+
console.log(formatter(data));
51+
}
52+
53+
function outputCliMeta(props) {
54+
const columns = props.map(meta => {
55+
meta.default = _valueFormatter(meta.default);
56+
return meta;
57+
});
58+
if (columns.length) {
59+
console.log(columnify(columns, COLUMNIFY_OPTS));
60+
} else {
61+
console.log(NO_PROPERTIES_MESSAGE);
62+
}
63+
}
64+
65+
function printProperties(properties) {
66+
properties.sort().forEach(prop => console.log(prop));
67+
}
68+
69+
function propertyErrorHandler(e) {
70+
if (e instanceof NoPropertyError) {
71+
console.log(`Property "${e.property}" is not supported`);
72+
return;
73+
}
74+
if (e instanceof MultiplePropertiesError) {
75+
console.log('Choose one of the following properties:\n');
76+
printProperties(e.properties);
77+
return;
78+
}
79+
if (e instanceof NotFullPropertyError) {
80+
console.log(`Did you mean property: ${e.property}?`);
81+
return;
82+
}
83+
if (e instanceof SchemaValidationError) {
84+
e.printErrors();
85+
return;
86+
}
87+
throw e;
88+
}
89+
90+
module.exports = {
91+
outputCliConfig,
92+
printProperties,
93+
propertyErrorHandler,
94+
outputCliMeta,
95+
};

0 commit comments

Comments
 (0)