Skip to content

Commit 377a779

Browse files
old validation refactored + yaml validation command (#246)
* old validation refactored + yaml validation command * docs updated * pull request comments refactoring * version updated
1 parent 5f8ef1a commit 377a779

File tree

6 files changed

+149
-23
lines changed

6 files changed

+149
-23
lines changed

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const Command = require('../../Command');
33
const { crudFilenameOption } = require('../../helpers/general');
44
const { context, pipeline } = require('../../../../logic').api;
55
const yargs = require('yargs');
6-
const { validatePipelineFile } = require('../../helpers/validation');
6+
const { validatePipelineSpec } = require('../../helpers/validation');
77

88
const get = new Command({
99
root: true,
@@ -40,10 +40,9 @@ const get = new Command({
4040
console.log(`Context: ${name} created`);
4141
break;
4242
case 'pipeline':
43-
try {
44-
await validatePipelineFile(data);
45-
} catch (e) {
46-
console.warn(e.message);
43+
const result = await validatePipelineSpec(data);
44+
if (!result.valid) {
45+
console.warn(result.message);
4746
return;
4847
}
4948
await pipeline.createPipeline(data);

lib/interface/cli/commands/root/replace.cmd.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const Command = require('../../Command');
33
const { crudFilenameOption } = require('../../helpers/general');
44
const { context, pipeline } = require('../../../../logic').api;
55
const yargs = require('yargs');
6-
const { validatePipelineFile } = require('../../helpers/validation');
6+
const { validatePipelineSpec } = require('../../helpers/validation');
77

88
const annotate = new Command({
99
root: true,
@@ -39,10 +39,9 @@ const annotate = new Command({
3939
console.log(`Context: ${name} created`);
4040
break;
4141
case 'pipeline':
42-
try {
43-
await validatePipelineFile(data);
44-
} catch (e) {
45-
console.warn(e.message);
42+
const result = await validatePipelineSpec(data);
43+
if (!result.valid) {
44+
console.warn(result.message);
4645
return;
4746
}
4847
await pipeline.replaceByName(name, data);
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
const Command = require('../../Command');
2+
const _ = require('lodash');
3+
const Style = require('../../../../output/style');
4+
const path = require('path');
5+
const { validatePipelineYaml } = require('../../helpers/validation');
6+
const { pathExists, watchFile } = require('../../helpers/general');
7+
8+
const VALID_MESSAGE = Style.green('Yaml is valid!');
9+
10+
function _printResult(result) {
11+
console.log(result.valid ? VALID_MESSAGE : Style.red(result.message), '\n');
12+
}
13+
14+
const validateCmd = new Command({
15+
root: true,
16+
command: 'validate [filenames..]',
17+
description: 'Validate codefresh pipeline yaml config files.',
18+
usage: 'Validate one or many pipeline yaml files or attach validator to one and validate on changes',
19+
webDocs: {
20+
description: 'Validate codefresh pipeline yaml config files',
21+
category: 'Validation',
22+
title: 'Validate pipeline yaml',
23+
weight: 100,
24+
},
25+
builder: (yargs) => {
26+
yargs
27+
.positional('filenames', {
28+
describe: 'Paths to yaml files',
29+
required: true,
30+
})
31+
.option('attach', {
32+
alias: 'a',
33+
describe: 'Attach validator to the file and validate on change',
34+
});
35+
36+
37+
return yargs;
38+
},
39+
handler: async (argv) => {
40+
let { filenames, attach } = argv;
41+
filenames = filenames.map(f => path.resolve(process.cwd(), f));
42+
43+
if (_.isEmpty(filenames)) {
44+
console.log('No filename provided!');
45+
return;
46+
}
47+
48+
const checkPromises = filenames.map((filename) => {
49+
return pathExists(filename)
50+
.then((exists) => {
51+
if (!exists) {
52+
console.log(`File does not exist: ${filename}`);
53+
}
54+
return exists;
55+
});
56+
});
57+
const allExist = (await Promise.all(checkPromises)).reduce((a, b) => a && b);
58+
59+
if (!allExist) {
60+
return;
61+
}
62+
63+
if (filenames.length > 1) {
64+
if (attach) {
65+
console.log('Cannot watch many files!');
66+
return;
67+
}
68+
69+
filenames.forEach(f => validatePipelineYaml(f).then((result) => {
70+
console.log(`Validation result for ${f}:`);
71+
_printResult(result);
72+
}));
73+
return;
74+
}
75+
76+
const filename = filenames[0];
77+
if (attach) {
78+
console.log(`Validator attached to file: ${filename}`);
79+
watchFile(filename, async () => {
80+
console.log('File changed');
81+
const result = await validatePipelineYaml(filename);
82+
_printResult(result);
83+
});
84+
}
85+
86+
// even with --attach option validates file for first time
87+
const result = await validatePipelineYaml(filename);
88+
_printResult(result);
89+
},
90+
});
91+
92+
module.exports = validateCmd;

lib/interface/cli/helpers/general.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,29 @@ const crudFilenameOption = (yargs, options = {}) => {
126126
});
127127
};
128128

129+
function pathExists(p) {
130+
return new Promise(resolve => fs.access(p, resolve))
131+
.then(err => !err);
132+
}
133+
134+
const readFile = Promise.promisify(fs.readFile);
135+
136+
function watchFile(filename, listener) {
137+
fs.watchFile(filename, { interval: 500 }, listener);
138+
const unwatcher = f => () => fs.unwatchFile(f);
139+
['exit', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'SIGTERM'].forEach((eventType) => {
140+
process.on(eventType, unwatcher(filename));
141+
});
142+
}
143+
144+
129145
module.exports = {
130146
printError,
131147
wrapHandler,
132148
prepareKeyValueFromCLIEnvOption,
133149
crudFilenameOption,
134150
prepareKeyValueCompostionFromCLIEnvOption,
151+
pathExists,
152+
readFile,
153+
watchFile,
135154
};
Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
const _ = require('lodash');
22
const yaml = require('js-yaml');
33
const { pipeline } = require('../../../logic').api;
4+
const { readFile } = require('./general');
45

56

6-
async function validatePipelineFile(data) {
7+
function _buildFinalMessage(baseMessage, validationResult) {
8+
if (_.isArray(validationResult.details)) {
9+
const errors = validationResult.details
10+
.map(({ message }) => ` - ${message}`)
11+
.join('\n');
12+
return `${baseMessage}:\n${errors}`;
13+
}
14+
return `${baseMessage}!`;
15+
}
16+
17+
async function validatePipelineSpec(data) {
718
const validatedYaml = yaml.safeDump(Object.assign({ version: data.version }, data.spec));
8-
const validationResult = await pipeline.validateYaml(validatedYaml);
9-
if (!validationResult.valid) {
10-
let finalMessage;
11-
if (_.isArray(validationResult.details)) {
12-
const errors = validationResult.details.map(({ message }) => ` - ${message}`).join('\n');
13-
finalMessage = `Provided spec is not valid:\n${errors}`;
14-
} else {
15-
finalMessage = 'Provided spec is not valid!';
16-
}
17-
throw new Error(finalMessage);
19+
const result = await pipeline.validateYaml(validatedYaml);
20+
let message;
21+
if (!result.valid) {
22+
message = _buildFinalMessage('Provided spec is not valid', result);
23+
}
24+
return { valid: !!result.valid, message };
25+
}
26+
27+
async function validatePipelineYaml(filename) {
28+
const yamlContent = await readFile(filename, 'UTF-8');
29+
const result = await pipeline.validateYaml(yamlContent);
30+
let message;
31+
if (!result.valid) {
32+
message = _buildFinalMessage('Yaml not valid', result);
1833
}
34+
return { valid: !!result.valid, message };
1935
}
2036

2137
module.exports = {
22-
validatePipelineFile,
38+
validatePipelineSpec,
39+
validatePipelineYaml,
2340
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codefresh",
3-
"version": "0.9.1",
3+
"version": "0.9.2",
44
"description": "Codefresh command line utility",
55
"main": "index.js",
66
"preferGlobal": true,

0 commit comments

Comments
 (0)