Skip to content

Commit 50c0ec2

Browse files
Saas 1122 new autocomplete (#251)
* basic completion * completion generation on build phase * dynamic completion base * completion command + option/positional completion + completion fixes + user input handling + tests * fix crudFilenameOption * options handling improved * remove yarn install + remove tree * ignore tree.json * virtual mock on tree.json * throw on not existing command path registration * common error handling * "first time pressing tab twice" hack * completions generation/resolution redone + tests added * remove user input eraser * completion command params renamed * version updated
1 parent 8570151 commit 50c0ec2

File tree

14 files changed

+562
-57
lines changed

14 files changed

+562
-57
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ coverage
1010
public
1111
temp
1212
package-lock.json
13+
**/completion/tree.json

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ RUN yarn --prod install
1010

1111
COPY . /cf-cli
1212

13+
RUN yarn generate-completion
14+
1315
RUN ln -s $(pwd)/lib/interface/cli/codefresh /usr/local/bin/codefresh
1416

1517
ENTRYPOINT ["codefresh"]

codefresh-release.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ steps:
3838
tag: latest
3939
registry: dockerhub
4040

41+
generate_comletion:
42+
title: "Generating commands completion tree"
43+
image: codefresh/cli-build
44+
commands:
45+
- "yarn install"
46+
- "yarn generate-completion"
47+
4148
deploy_to_npm:
4249
title: "Publishing To Npm"
4350
image: codefresh/npm-publish:master
@@ -49,7 +56,6 @@ steps:
4956
image: codefresh/cli-build
5057
commands:
5158
- "rm -rf dist"
52-
- "yarn install"
5359
- "yarn pkg"
5460

5561
create_release:

docs/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ const FILES_TO_IGNORE = ['index.js'];
1414
const baseDir = path.resolve(TEMP_DIR, './content');
1515
const ALLOW_BETA_COMMANDS = process.env.ALLOW_BETA_COMMANDS;
1616
const categoriesOrder = {
17-
authentication: 30,
18-
'operate on resources' : 31,
17+
completion: 30,
18+
authentication: 31,
19+
'operate on resources' : 32,
1920
pipelines : 40,
2021
'pipelines v2 (beta)' : 42,
2122
builds: 50,

lib/interface/cli/codefresh

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,74 @@
11
#!/usr/bin/env node
22

3-
const path = require('path');
4-
const _ = require('lodash');
5-
const yargs = require('yargs');
6-
const recursive = require('recursive-readdir');
7-
const CFError = require('cf-errors');
8-
const DEFAULTS = require('./defaults');
9-
const authManager = require('../../logic').auth.manager;
103
const { printError } = require('./helpers/general');
114

12-
process.on('uncaughtException', function (err) {
5+
process.on('uncaughtException', (err) => {
136
printError(err);
147
process.exit(1);
158
});
169

17-
process.on('unhandledRejection', error => {
18-
printError(error);
10+
process.on('unhandledRejection', (err) => {
11+
printError(err);
1912
process.exit(1);
2013
});
2114

15+
if (process.argv.includes('--get-yargs-completions')) {
16+
const initCompletion = require('./completion');
17+
initCompletion().argv;
18+
} else {
19+
const _ = require('lodash');
20+
const yargs = require('yargs');
21+
const path = require('path');
22+
const recursive = require('recursive-readdir');
23+
const DEFAULTS = require('./defaults');
24+
const authManager = require('../../logic').auth.manager;
2225

23-
recursive(path.resolve(__dirname, 'commands'), (err, files) => {
24-
const rootCommands = [];
25-
yargs
26-
.env('')
27-
.options('cfconfig', {
28-
default: DEFAULTS.CFCONFIG,
29-
global: false,
30-
})
31-
.config('cfconfig', 'Custom path for authentication contexts config file', (configFilePath) => {
32-
try {
33-
authManager.loadContexts(process.env.CF_API_KEY, process.env.CF_URL || DEFAULTS.URL , configFilePath);
34-
_.forEach(files, (file) => {
35-
if (file.endsWith('.cmd.js')) {
36-
const command = require(file);
37-
if (command.isRoot()) {
38-
if (command.isBetaCommand()) {
39-
const currentContext = authManager.getCurrentContext();
40-
if (currentContext && currentContext.isBetaFeatEnabled()) {
26+
recursive(path.resolve(__dirname, 'commands'), (err, files) => {
27+
const rootCommands = [];
28+
yargs
29+
.env('')
30+
.options('cfconfig', {
31+
default: DEFAULTS.CFCONFIG,
32+
global: false,
33+
})
34+
.config('cfconfig', 'Custom path for authentication contexts config file', (configFilePath) => {
35+
try {
36+
authManager.loadContexts(process.env.CF_API_KEY, process.env.CF_URL || DEFAULTS.URL, configFilePath);
37+
_.forEach(files, (file) => {
38+
if (file.endsWith('.cmd.js')) {
39+
const command = require(file);
40+
if (command.isRoot()) {
41+
if (command.isBetaCommand()) {
42+
const currentContext = authManager.getCurrentContext();
43+
if (currentContext && currentContext.isBetaFeatEnabled()) {
44+
rootCommands.push(command);
45+
}
46+
} else {
4147
rootCommands.push(command);
4248
}
43-
} else {
44-
rootCommands.push(command);
4549
}
4650
}
47-
}
48-
});
49-
_.forEach(rootCommands, (command) => {
50-
yargs.command(command.toCommand());
51-
});
52-
} catch (err) {
53-
printError(err);
54-
process.exit(1);
55-
}
56-
});
51+
});
52+
_.forEach(rootCommands, (command) => {
53+
yargs.command(command.toCommand());
54+
});
55+
} catch (err) {
56+
printError(err);
57+
process.exit(1);
58+
}
59+
});
5760

5861

59-
yargs // eslint-disable-line
60-
.completion()
61-
.demandCommand(1, 'You need at least one command before moving on')
62-
.wrap(null)
63-
.version(false)
64-
.help('help')
65-
.epilogue('For more information, find our official documentation at http://cli.codefresh.io')
66-
// .option('help', {
67-
// global: false,
68-
// })
69-
.argv;
70-
});
62+
yargs // eslint-disable-line
63+
.demandCommand(1, 'You need at least one command before moving on')
64+
.wrap(null)
65+
.version(false)
66+
.help('help')
67+
.epilogue('For more information, find our official documentation at http://cli.codefresh.io')
68+
// .option('help', {
69+
// global: false,
70+
// })
71+
.argv;
72+
});
73+
}
74+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const yargs = require('yargs');
2+
const Command = require('../../Command');
3+
const path = require('path');
4+
const fs = require('fs');
5+
6+
/**
7+
* yargs script generation file redone
8+
* */
9+
function _generateCompletionScript(appPath, appName, completionCommand) {
10+
let script = fs.readFileSync(
11+
path.resolve(__dirname, 'completion.sh.hbs'),
12+
'utf-8',
13+
);
14+
const name = appName || path.basename(appPath);
15+
16+
script = script.replace(/{{app_name}}/g, name);
17+
script = script.replace(/{{completion_command}}/g, completionCommand);
18+
return script.replace(/{{app_path}}/g, appPath);
19+
}
20+
21+
const command = new Command({
22+
root: true,
23+
command: 'completion',
24+
description: 'Generate codefresh completion',
25+
usage: 'Prints completion script with specified or default path to executable and command alias',
26+
webDocs: {
27+
category: 'Completion',
28+
title: 'Codefresh Completion',
29+
description: 'Generate codefresh completion. See details on [usage](codefresh-completion)',
30+
weight: 30,
31+
},
32+
builder: (yargs) => {
33+
return yargs
34+
.option('executable', {
35+
alias: 'e',
36+
description: 'Name or path to your codefresh executable (default same as alias)',
37+
})
38+
.option('alias', {
39+
alias: 'a',
40+
description: 'Alias used for calling codefresh executable',
41+
default: 'codefresh',
42+
})
43+
.example('codefresh completion', 'Print completion script')
44+
.example('cf completion --alias cf', 'Print completion script for codefresh aliased as "cf"')
45+
.example('/some/path/codefresh completion --e /some/path/codefresh', 'Print completion script with specified path to codefresh executable');
46+
},
47+
handler: async (argv) => {
48+
const { executable, alias: appName } = argv;
49+
const appPath = executable || appName;
50+
const script = _generateCompletionScript(appPath, appName, 'completion');
51+
console.log(script);
52+
},
53+
});
54+
55+
module.exports = command;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
###-begin-{{app_name}}-completions-###
2+
#
3+
# codefresh command completion script
4+
#
5+
# Installation: {{app_path}} {{completion_command}} >> ~/.bashrc
6+
# or {{app_path}} {{completion_command}} >> ~/.bash_profile on OSX.
7+
#
8+
_codefresh_completions()
9+
{
10+
local cur_word args type_list
11+
12+
cur_word="${COMP_WORDS[COMP_CWORD]}"
13+
args=("${COMP_WORDS[@]}")
14+
15+
# ask codefresh to generate completions.
16+
type_list=$({{app_path}} --get-yargs-completions "${args[@]}")
17+
18+
COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )
19+
20+
return 0
21+
}
22+
complete -F _codefresh_completions {{app_name}}
23+
###-end-{{app_name}}-completions-###
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const pipeline = () => require('../../../../logic').api.pipeline;
2+
const { authContextWrapper } = require('../../completion/helpers');
3+
4+
const positionalHandler = async ({word, argv}) => {
5+
const pips = await pipeline().getAll({ limit: 25, offset: 0 });
6+
return pips.map(p => p.name);
7+
};
8+
9+
module.exports = {
10+
positionalHandler: authContextWrapper(positionalHandler),
11+
};
12+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const { handleOptions } = require('../../completion/helpers');
2+
3+
function optionHandler({ word, argv, option }) {
4+
return ['json', 'yaml', 'wide', 'name', 'id'];
5+
}
6+
7+
module.exports = {
8+
optionHandler: handleOptions(['-o', '--output'], optionHandler),
9+
};
10+

0 commit comments

Comments
 (0)