dockerfilelint
Advanced tools
Comparing version 1.0.0 to 1.1.1
121
lib/index.js
'use strict'; | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var yaml = require('js-yaml'); | ||
var checks = require('./checks'); | ||
var run_checks = require('./run_checks'); | ||
var command_parser = require('./command_parser'); | ||
var apk = require('./apk'); | ||
var apt = require('./apt'); | ||
var messages = require('./messages'); | ||
module.exports.run = function(content) { | ||
module.exports.run = function(configPath, content) { | ||
// Parse the file into an array of instructions | ||
@@ -50,3 +55,4 @@ var instructions = {}; | ||
cmdFound: false, | ||
items: [] | ||
items: [], | ||
rules: loadRules(configPath) | ||
} | ||
@@ -70,2 +76,25 @@ | ||
function loadRules(configPath) { | ||
// Find any .dockerfilelintrc | ||
var configFileContents = tryLoadFile(path.join(configPath, '.dockerfilelintrc')) | ||
if (!configFileContents) { | ||
return {}; | ||
} | ||
var rc = yaml.safeLoad(configFileContents); | ||
return rc.rules; | ||
} | ||
function tryLoadFile(filename) { | ||
try { | ||
var stats = fs.lstatSync(filename); | ||
if (stats.isFile()) { | ||
return fs.readFileSync(filename, 'UTF-8'); | ||
} | ||
return null; | ||
} catch (e) { | ||
return null; | ||
} | ||
} | ||
function runLine(state, instructions, idx) { | ||
@@ -79,3 +108,3 @@ // return items in an object with the line number as key, value is array of items for this line | ||
if (instruction.trim().match(/\S+/g).length === 1) { | ||
items.push(messages.build('required_params', line)); | ||
items.push(messages.build(state.rules, 'required_params', line)); | ||
} | ||
@@ -88,3 +117,3 @@ | ||
if (instruction.trim().match(/\S+/g)[0] !== cmd.toUpperCase()) { | ||
items.push(messages.build('uppercase_commands', line)); | ||
items.push(messages.build(state.rules, 'uppercase_commands', line)); | ||
} | ||
@@ -95,3 +124,3 @@ | ||
if ((state.instructionsProcessed === 0 && cmd !== 'from') || (state.instructionsProcessed !== 0 && cmd === 'from')) { | ||
items.push(messages.build('from_first', line)); | ||
items.push(messages.build(state.rules, 'from_first', line)); | ||
} | ||
@@ -103,3 +132,3 @@ | ||
if (!args) { | ||
items.push(messages.build('invalid_line', line)); | ||
items.push(messages.build(state.rules, 'invalid_line', line)); | ||
} | ||
@@ -111,3 +140,3 @@ | ||
if (arg.trim().toLowerCase() === 'sudo') { | ||
items.push(messages.build('sudo_usage', line)); | ||
items.push(messages.build(state.rules, 'sudo_usage', line)); | ||
} | ||
@@ -120,3 +149,3 @@ }.bind(this)); | ||
checks.base_image_tag(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -126,3 +155,3 @@ break; | ||
checks.valid_maintainer(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -132,9 +161,11 @@ break; | ||
var aptgetSubcommands = []; | ||
var commands = run_checks.split_commands(args); | ||
run_checks.aptget_commands(commands).forEach(function(aptget_command, index) { | ||
var subcommand = run_checks.aptget_subcommand(aptget_command); | ||
var commands = command_parser.split_commands(args); | ||
// parse apt commands | ||
apt.aptget_commands(commands).forEach(function(aptget_command, index) { | ||
var subcommand = apt.aptget_subcommand(aptget_command); | ||
aptgetSubcommands.push(subcommand); | ||
if (["install", "remove", "upgrade"].indexOf(subcommand) > -1) { | ||
if (!run_checks.aptget_hasyes(aptget_command)) { | ||
items.push(messages.build('apt-get_missing_param', line)); | ||
if (!apt.aptget_hasyes(aptget_command)) { | ||
items.push(messages.build(state.rules, 'apt-get_missing_param', line)); | ||
} | ||
@@ -144,26 +175,43 @@ } | ||
if (subcommand === 'install') { | ||
if (!run_checks.aptget_hasnorecommends(aptget_command)) { | ||
items.push(messages.build('apt-get_recommends', line)); | ||
if (!apt.aptget_hasnorecommends(aptget_command)) { | ||
items.push(messages.build(state.rules, 'apt-get_recommends', line)); | ||
} | ||
} else if (subcommand === 'update') { | ||
if (!run_checks.follows_rmaptlists(commands, index)) { | ||
items.push(messages.build('apt-get_missing_rm', line)); | ||
if (!apt.follows_rmaptlists(commands, index)) { | ||
items.push(messages.build(state.rules, 'apt-get_missing_rm', line)); | ||
} | ||
} else if (subcommand === 'upgrade') { | ||
items.push(messages.build('apt-get-upgrade', line)); | ||
items.push(messages.build(state.rules, 'apt-get-upgrade', line)); | ||
} else if (subcommand === 'dist-upgrade') { | ||
items.push(messages.build('apt-get-dist-upgrade', line)); | ||
items.push(messages.build(state.rules, 'apt-get-dist-upgrade', line)); | ||
} | ||
}); | ||
if (aptgetSubcommands.indexOf('update') > -1 && aptgetSubcommands.indexOf('install') === -1) { | ||
items.push(messages.build('apt-get-update_require_install', line)); | ||
items.push(messages.build(state.rules, 'apt-get-update_require_install', line)); | ||
} | ||
run_checks.apk_commands(commands).forEach(function(apk_command, index) { | ||
var subcommand = run_checks.apk_subcommand(apk_command); | ||
// parse apk commands | ||
var num_apkdel = 0; | ||
apk.apk_commands(commands).forEach(function(apk_command) { | ||
var subcommand = apk.apk_subcommand(apk_command); | ||
if (subcommand === 'del') { | ||
num_apkdel++; | ||
} | ||
}); | ||
apk.apk_commands(commands).forEach(function(apk_command, index) { | ||
var subcommand = apk.apk_subcommand(apk_command); | ||
if (subcommand === 'add') { | ||
if (!run_checks.apkadd_hasnocache(apk_command)) { | ||
if (!run_checks.apkadd_hasupdate(apk_command) || !run_checks.follows_rmapkcache(commands, index)) { | ||
items.push(messages.build('apkadd-missing_nocache_or_updaterm', line)); | ||
// If there is more than 1 package we are adding and we are also del'ing, we should suggest --virtual | ||
if (apk.apkadd_numpackages(apk_command) > 1) { | ||
if (num_apkdel > 0) { | ||
if (!apk.apkadd_hasvirtual(apk_command)) { | ||
items.push(messages.build(state.rules, 'apkadd-missing-virtual', line)); | ||
} | ||
} | ||
} | ||
if (!apk.apkadd_hasnocache(apk_command)) { | ||
if (!apk.apkadd_hasupdate(apk_command) || !apk.follows_rmapkcache(commands, index)) { | ||
items.push(messages.build(state.rules, 'apkadd-missing_nocache_or_updaterm', line)); | ||
} | ||
} | ||
} | ||
@@ -176,3 +224,3 @@ }); | ||
checks.label_format(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -182,3 +230,3 @@ break; | ||
checks.expose_container_port_only(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -188,3 +236,3 @@ args.match(/\S+/g).forEach(function(port) { | ||
if (!port.includes(':')) { // Just eliminate a double message here | ||
items.push(messages.build('invalid_port', line)); | ||
items.push(messages.build(state.rules, 'invalid_port', line)); | ||
} | ||
@@ -196,3 +244,3 @@ } | ||
checks.is_valid_env(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -202,3 +250,3 @@ break; | ||
checks.is_valid_add(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -214,3 +262,3 @@ break; | ||
checks.valid_user(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -220,3 +268,3 @@ break; | ||
checks.is_valid_workdir(args).forEach(function(item) { | ||
items.push(messages.build(item, line)); | ||
items.push(messages.build(state.rules, item, line)); | ||
}); | ||
@@ -231,3 +279,3 @@ break; | ||
default: | ||
items.push(messages.build('invalid_command', line)); | ||
items.push(messages.build(state.rules, 'invalid_command', line)); | ||
break; | ||
@@ -237,2 +285,5 @@ } | ||
// Remove any null items from the result | ||
items = items.filter(function(n){ return n != null }); | ||
return { | ||
@@ -239,0 +290,0 @@ command: cmd, |
@@ -5,3 +5,17 @@ | ||
var messages = module.exports = { | ||
build: function(name, line) { | ||
parseBool: function(s) { | ||
s = s.toLowerCase(); | ||
if (s === 'off' || s === 'false' || s === '0' || s === 'n') { | ||
return false; | ||
} | ||
return true; | ||
}, | ||
build: function(rules, name, line) { | ||
if (name in rules) { | ||
if (!this.parseBool(rules[name])) { | ||
return null; | ||
} | ||
} | ||
var message = reference[name]; | ||
@@ -16,4 +30,5 @@ if (!message) { | ||
message.line = line; | ||
message.rule = name; | ||
return message; | ||
}, | ||
}; |
@@ -90,2 +90,8 @@ var reference = module.exports = { | ||
}, | ||
'apkadd-missing-virtual': { | ||
'title': 'Consider `--virtual` when using apk add and del together in a command.', | ||
'description': `Consider using a \`--virtual\` or \`-t\` switch to group multiple packages for easy cleanup. This will help ensure future authors will continue to clean up build dependencies and other temporary packages. For | ||
more information, see [this link](https://github.com/gliderlabs/docker-alpine/blob/master/docs/usage.md#virtual-packages)`, | ||
'category': 'Optimization' | ||
}, | ||
'invalid_port': { | ||
@@ -92,0 +98,0 @@ 'title': 'Invalid Port Exposed', |
{ | ||
"name": "dockerfilelint", | ||
"version": "1.0.0", | ||
"version": "1.1.1", | ||
"description": "A linter for Dockerfiles to find bugs and encourage best practices", | ||
@@ -31,2 +31,3 @@ "main": "./lib/index.js", | ||
"cliui": "^3.1.0", | ||
"js-yaml": "^3.6.0", | ||
"lodash": "^4.3.0", | ||
@@ -33,0 +34,0 @@ "yargs": "^3.32.0" |
@@ -12,3 +12,3 @@ # Linter and validator for Dockerfile | ||
## Running | ||
From the command line: | ||
#### From the command line: | ||
```shell | ||
@@ -18,4 +18,49 @@ ./bin/dockerfilelint <path/to/Dockerfile> | ||
If you don't want to install this locally you can try it out [here](https://www.fromlatest.io/#/). | ||
#### Configuring | ||
You can configure the linter by creating a `.dockerfilelintrc` with the following syntax: | ||
```yaml | ||
rules: | ||
uppercase_commands: off | ||
``` | ||
The keys for the rules can be any file in the /lib/reference.js file. At this time, it's only possible to disable rules. They are all enabled by default. | ||
The following rules are supported: | ||
``` | ||
required_params | ||
uppercase_commands | ||
from_first | ||
invalid_line | ||
sudo_usage | ||
apt-get_missing_param | ||
apt-get_recommends | ||
apt-get-upgrade | ||
apt-get-dist-upgrade | ||
apt-get-update_require_install | ||
apkadd-missing_nocache_or_updaterm | ||
apkadd-missing-virtual | ||
invalid_port | ||
invalid_command | ||
expose_host_port | ||
label_invalid | ||
missing_tag | ||
latest_tag | ||
extra_args | ||
missing_args | ||
add_src_invalid | ||
add_dest_invalid | ||
invalid_workdir | ||
invalid_format | ||
apt-get_missing_rm | ||
``` | ||
#### From a Docker container | ||
(Replace the ``pwd``/Dockerfile with the path to your local Dockerfile) | ||
```shell | ||
sudo docker run -v `pwd`/Dockerfile:/Dockerfile dockerfilelint /Dockerfile | ||
``` | ||
#### Online | ||
If you don't want to install this locally you can try it out on [https://fromlatest.io](https://www.fromlatest.io/#/). | ||
## Checks performed | ||
@@ -41,3 +86,3 @@ ### `FROM` | ||
- [x] apk add commands should include a `--no-cache` flag or be paired with an `--update` flag with `rm -rf /var/cache/apk/*` in the same layer | ||
- [ ] apk add support for --virtual flag | ||
- [x] apk add support for --virtual flag | ||
- [ ] handle best practices for yum operations and cleanup | ||
@@ -44,0 +89,0 @@ |
@@ -101,3 +101,3 @@ 'use strict'; | ||
expect(fileReport.uniqueIssues).to.equal(3); | ||
expect(fileReport.contentArray).to.have.length(33); | ||
expect(fileReport.contentArray).to.have.length(34); | ||
expect(fileReport.itemsByLine).to.deep.equal({ | ||
@@ -130,3 +130,3 @@ '5': [ items[0] ], | ||
expect(fileReport.uniqueIssues).to.equal(1); | ||
expect(fileReport.contentArray).to.have.length(33); | ||
expect(fileReport.contentArray).to.have.length(34); | ||
expect(fileReport.itemsByLine).to.deep.equal({ | ||
@@ -176,3 +176,3 @@ '6': [ items[0] ] | ||
expect(file2Report.uniqueIssues).to.equal(2); | ||
expect(file2Report.contentArray).to.have.length(33); | ||
expect(file2Report.contentArray).to.have.length(34); | ||
expect(file2Report.itemsByLine).to.deep.equal({ | ||
@@ -250,3 +250,3 @@ '5': [ file2Items[0] ], | ||
category: 'Deprecation', | ||
line: 20 | ||
line: 21 | ||
} | ||
@@ -276,3 +276,3 @@ ]; | ||
'', | ||
'Line 20: ' + chalk.magenta('EXPOSE 80:80'), | ||
'Line 21: ' + chalk.magenta('EXPOSE 80:80'), | ||
'Issue Category Title Description', | ||
@@ -279,0 +279,0 @@ ' ' + chalk.red('4') + ' ' + chalk.red.inverse('Deprecation') + ' ' + chalk.red('Expose Only') + ' ' + chalk.gray('Using `EXPOSE` to specify a host port is not allowed.'), |
@@ -9,3 +9,3 @@ var expect = require('chai').expect | ||
it("validates the busybox Dockerfile has no issues", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.busybox', 'UTF-8'))).to.be.empty; | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.busybox', 'UTF-8'))).to.be.empty; | ||
}); | ||
@@ -16,3 +16,3 @@ }); | ||
it("validates the debian Dockerfile has no issues", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.debian', 'UTF-8'))).to.be.empty; | ||
expect(dockerfilelint.run('./test/invalid', fs.readFileSync('./test/examples/Dockerfile.debian', 'UTF-8'))).to.be.empty; | ||
}); | ||
@@ -23,3 +23,3 @@ }); | ||
it("validates the mongo Dockerfile has 1 issue (known issue)", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.mongo', 'UTF-8'))).to.have.length(1); | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.mongo', 'UTF-8'))).to.have.length(1); | ||
}); | ||
@@ -30,3 +30,3 @@ }); | ||
it("validates the mysql Dockerfile has 1 issue (known issue)", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.mysql', 'UTF-8'))).to.have.length(1); | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.mysql', 'UTF-8'))).to.have.length(1); | ||
}); | ||
@@ -37,3 +37,3 @@ }); | ||
it("validates the nginx Dockerfile has 1 issue (known issue)", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.nginx', 'UTF-8'))).to.have.length(1); | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.nginx', 'UTF-8'))).to.have.length(1); | ||
}); | ||
@@ -44,3 +44,3 @@ }); | ||
it("validates the node Dockerfile has no issues", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.node', 'UTF-8'))).to.be.empty; | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.node', 'UTF-8'))).to.be.empty; | ||
}); | ||
@@ -51,3 +51,3 @@ }); | ||
it("validates the redis Dockerfile has no issues", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.redis', 'UTF-8'))).to.be.empty; | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.redis', 'UTF-8'))).to.be.empty; | ||
}); | ||
@@ -58,3 +58,3 @@ }); | ||
it("validates the mysql Dockerfile has 1 issue (known issue)", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.registry', 'UTF-8'))).to.have.length(1); | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.registry', 'UTF-8'))).to.have.length(1); | ||
}); | ||
@@ -65,3 +65,3 @@ }); | ||
it("validates the swarm Dockerfile has no issues", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.swarm', 'UTF-8'))).to.be.empty; | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.swarm', 'UTF-8'))).to.be.empty; | ||
}); | ||
@@ -72,3 +72,3 @@ }); | ||
it("validates the ubuntu Dockerfile has no issues", function(){ | ||
expect(dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.ubuntu', 'UTF-8'))).to.be.empty; | ||
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.ubuntu', 'UTF-8'))).to.be.empty; | ||
}); | ||
@@ -81,60 +81,91 @@ }); | ||
{ title: 'First Command Must Be FROM', | ||
rule: 'from_first', | ||
line: 6 }, | ||
{ title: 'First Command Must Be FROM', | ||
rule: 'from_first', | ||
line: 6 }, | ||
{ title: 'Base Image Missing Tag', | ||
rule: 'missing_tag', | ||
line: 5 }, | ||
{ title: 'First Command Must Be FROM', | ||
rule: 'from_first', | ||
line: 6 }, | ||
{ title: 'Base Image Latest Tag', | ||
rule: 'latest_tag', | ||
line: 6 }, | ||
{ title: 'Capitalize Dockerfile Instructions', | ||
rule: 'uppercase_commands', | ||
line: 7 }, | ||
{ title: 'Invalid Command', | ||
rule: 'invalid_command', | ||
line: 7 }, | ||
{ title: 'Missing Arguments', | ||
rule: 'missing_args', | ||
line: 9 }, | ||
{ title: 'Missing Required Arguments', | ||
rule: 'required_params', | ||
line: 11 }, | ||
{ title: 'Invalid Line', | ||
rule: 'invalid_line', | ||
line: 11 }, | ||
{ title: 'Use Of sudo Is Not Allowed', | ||
rule: 'sudo_usage', | ||
line: 12 }, | ||
{ title: 'Missing parameter for `apt-get`', | ||
rule: 'apt-get_missing_param', | ||
line: 14 }, | ||
{ title: '`apt-get upgrade` Is Not Allowed', | ||
rule: 'apt-get-upgrade', | ||
line: 13 }, | ||
{ title: 'Missing parameter for `apt-get`', | ||
rule: 'apt-get_missing_param', | ||
line: 14 }, | ||
{ title: 'Consider `--no-install-recommends`', | ||
rule: 'apt-get_recommends', | ||
line: 14 }, | ||
{ title: 'apt-get dist-upgrade Is Not Allowed', | ||
rule: 'apt-get-dist-upgrade', | ||
line: 15 }, | ||
{ title: 'apt-get update with matching cache rm', | ||
rule: 'apt-get_missing_rm', | ||
line: 16 }, | ||
{ title: 'apt-get update without matching apt-get install', | ||
rule: 'apt-get-update_require_install', | ||
line: 16 }, | ||
{ title: 'Consider `--no-cache or --update with rm -rf /var/cache/apk/*`', | ||
line: 17 }, | ||
{ title: 'Invalid Port Exposed', | ||
line: 19 }, | ||
{ title: 'Expose Only Container Port', | ||
line: 20 }, | ||
{ title: 'Invalid Argument Format', | ||
line: 22 }, | ||
{ title: 'Label Is Invalid', | ||
line: 23 }, | ||
{ title: 'Label Is Invalid', | ||
line: 23 }, | ||
{ title: 'Extra Arguments', | ||
line: 25 }, | ||
{ title: 'Invalid WORKDIR', | ||
line: 26 }, | ||
{ title: 'Invalid ADD Source', | ||
line: 30 }, | ||
{ title: 'Invalid ADD Destination', | ||
line: 30 } | ||
{ title: 'Consider `--no-cache or --update with rm -rf /var/cache/apk/*`', | ||
rule: 'apkadd-missing_nocache_or_updaterm', | ||
line: 17 }, | ||
{ title: 'Consider `--virtual` when using apk add and del together in a command.', | ||
rule: 'apkadd-missing-virtual', | ||
line: 18 }, | ||
{ title: 'Invalid Port Exposed', | ||
rule: 'invalid_port', | ||
line: 20 }, | ||
{ title: 'Expose Only Container Port', | ||
rule: 'expose_host_port', | ||
line: 21 }, | ||
{ title: 'Invalid Argument Format', | ||
rule: 'invalid_format', | ||
line: 23 }, | ||
{ title: 'Label Is Invalid', | ||
rule: 'label_invalid', | ||
line: 24 }, | ||
{ title: 'Label Is Invalid', | ||
rule: 'label_invalid', | ||
line: 24 }, | ||
{ title: 'Extra Arguments', | ||
rule: 'extra_args', | ||
line: 26 }, | ||
{ title: 'Invalid WORKDIR', | ||
rule: 'invalid_workdir', | ||
line: 27 }, | ||
{ title: 'Invalid ADD Source', | ||
rule: 'add_src_invalid', | ||
line: 31 }, | ||
{ title: 'Invalid ADD Destination', | ||
rule: 'add_dest_invalid', | ||
line: 31 } | ||
]; | ||
var result = dockerfilelint.run(fs.readFileSync('./test/examples/Dockerfile.misc', 'UTF-8')); | ||
var result = dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.misc', 'UTF-8')); | ||
expect(result).to.have.length(expected.length); | ||
@@ -141,0 +172,0 @@ |
@@ -5,8 +5,15 @@ var expect = require('chai').expect | ||
describe("messages", function(){ | ||
describe("#ignore_works", function(){ | ||
it("validates that disabled commands are ignored", function(){ | ||
expect(messages.build({latest_tag: 'off'}, 'latest_tag', 1)).to.be.null; | ||
expect(messages.build({latest_tag: 'TRUE'}, 'latest_tag', 1)).to.have.property('line', 1); | ||
}); | ||
}), | ||
describe("#build_a_message", function(){ | ||
it("validates that line numbers are added to messages", function(){ | ||
expect(messages.build('latest_tag', 1)).to.have.property('line', 1); | ||
expect(messages.build('', 1)).to.have.property('line', 1); | ||
expect(messages.build({}, 'latest_tag', 1)).to.have.property('line', 1); | ||
expect(messages.build({}, '', 1)).to.have.property('line', 1); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
82103
38
1487
146
5
3
+ Addedjs-yaml@^3.6.0
+ Addedargparse@1.0.10(transitive)
+ Addedesprima@4.0.1(transitive)
+ Addedjs-yaml@3.14.1(transitive)
+ Addedsprintf-js@1.0.3(transitive)