eslint-teamcity
Advanced tools
Comparing version 1.4.0 to 2.0.0
29
index.js
#!/usr/bin/env node | ||
'use strict'; | ||
var fs = require('fs-extra'), | ||
procArg = process.argv; | ||
const fs = require('fs-extra'); | ||
const formatter = require('./src/formatter'); | ||
const procArg = process.argv; | ||
/** | ||
* The entry point for this project | ||
* @param {string} input The output generated by running ESLint. | ||
* @param {object} [teamcityPropNames] Any user defined config variables. | ||
* @returns {string} The concatenated output of all messages to display in TeamCity | ||
*/ | ||
function main(input, teamcityPropNames) { | ||
return formatter(input, teamcityPropNames); | ||
} | ||
if (require.main === module) { | ||
process.stdout.write(main(fs.readJSONSync(procArg[2]))) | ||
process.stdout.write(main(fs.readJSONSync(procArg[2]))); | ||
} else { | ||
module.exports = main; | ||
} | ||
/** | ||
* main function | ||
* @param {String} input - input data stringify json | ||
* @param {Object} [teamcityPropNames] - names of the vars | ||
* @returns {String} format result | ||
*/ | ||
function main(input, teamcityPropNames) { | ||
return require('./src/formatter')(input, teamcityPropNames); | ||
} |
{ | ||
"name": "eslint-teamcity", | ||
"version": "1.4.0", | ||
"version": "2.0.0", | ||
"description": "An ESLint formatter plugin for TeamCity", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "./node_modules/mocha/bin/mocha ./test/*.spec.js", | ||
"test-travis": "./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- -R spec ./test/*.spec.js" | ||
"lint": "./node_modules/.bin/eslint .", | ||
"precommit": "lint-staged", | ||
"prettier": "./node_modules/.bin/prettier --write", | ||
"test": "./node_modules/mocha/bin/mocha \"./test/**/*.spec.js\"", | ||
"test-travis": "./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- -R spec \"./test/**/*.spec.js\"" | ||
}, | ||
@@ -25,14 +28,26 @@ "repository": { | ||
}, | ||
"dependencies": { | ||
"fs-extra": "^5.0.0" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"coveralls": "^2.12.0", | ||
"eslint": "^3.18.0", | ||
"chai": "^4.1.0", | ||
"coveralls": "^3.0.0", | ||
"eslint": "^4.12.1", | ||
"eslint-config-airbnb-base": "^12.1.0", | ||
"eslint-plugin-import": "^2.8.0", | ||
"husky": "^0.14.3", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^3.2.0", | ||
"shelljs": "^0.7.8", | ||
"sinon": "^2.1.0" | ||
"lint-staged": "^6.0.0", | ||
"mocha": "^4.1.0", | ||
"prettier": "^1.9.1", | ||
"shelljs": "^0.8.0", | ||
"sinon": "^4.1.5" | ||
}, | ||
"dependencies": { | ||
"fs-extra": "^3.0.1" | ||
"lint-staged": { | ||
"*.js": [ | ||
"npm run prettier", | ||
"npm run lint", | ||
"git add" | ||
] | ||
} | ||
} |
114
README.md
@@ -14,2 +14,4 @@ # eslint-teamcity | ||
**As of v2.0, Node v6+ is required. If you use an older version of Node, please stick with v1.x** | ||
Prerequisite: You must have either [npm](https://docs.npmjs.com/cli/install) or [Yarn](https://yarnpkg.com/en/docs/install) installed. | ||
@@ -22,9 +24,8 @@ | ||
## Usage | ||
There are currently 3 ways to use eslint-teamcity: | ||
##### As a regular ESLint formatter plugin: | ||
There are 3 ways to use eslint-teamcity: | ||
##### 1. As a regular ESLint formatter plugin: | ||
```sh | ||
eslint --format ./node_modules/eslint-teamcity/index.js myfiletolint.js | ||
``` | ||
##### Running against a generated ESLint JSON report: | ||
##### 2. Running against a generated ESLint JSON report: | ||
Generate an ESLint JSON report: | ||
@@ -34,18 +35,69 @@ ```sh | ||
``` | ||
Run eslint-teamcity against the new report: | ||
Run eslint-teamcity against your new report: | ||
```sh | ||
node ./node_modules/eslint-teamcity/index.js result.json | ||
``` | ||
##### 3. Requiring and running directly from inside your JavaScript code: | ||
```javascript | ||
const eslintTeamcity = require('eslint-teamcity'); | ||
console.log(eslintTeamcity(eslintOutput)); | ||
``` | ||
##### Requiring and running directly from inside your JavaScript code: | ||
## Configuration | ||
As of version 2.0, there are two different formatters you can use to report with. They have no material | ||
impact on the output - they're just different ways of viewing the same data. The "Code Inspection" tab will only | ||
appear if you have configured eslint-teamcity to use the inspections reporter. | ||
Errors (default) | Inspections | ||
:-------------------------:|:-------------------------: | ||
![Example Errors Report](https://i.imgur.com/3AzQeMy.png) | ![Example Inspections Report](https://i.imgur.com/JXzBuaV.png) | ||
There are several ways that you can configure eslint-teamcity. **You don't have to configure anything by default**, you just have the option to if you would like. | ||
Settings are looked for in the following priority: | ||
##### 1. As a second argument | ||
If you run eslint-teamcity by requiring it in JavaScript, you can pass a second argument to the function: | ||
```javascript | ||
var eslintTeamcity = require('eslint-teamcity'); | ||
console.log(eslintTeamcity(result)); | ||
const eslintTeamcity = require('eslint-teamcity'); | ||
const options = { | ||
reporter: 'inspections', // default: 'errors' | ||
reportName: 'My ESLint Violations', // default: 'ESLint Violations' | ||
errorStatisticsName: 'My ESLint Error Count', // default: 'ESLint Error Count' | ||
warningStatisticsName: 'My ESLint Warning Count', // default: 'ESLint Warning Count' | ||
}; | ||
console.log(eslintTeamcity(eslintOutput, options)); | ||
``` | ||
##### 2. From your package.json | ||
If you have a package.json file in the **current directory**, you can add an extra "eslint-teamcity" property to it: | ||
```json | ||
..., | ||
"eslint-teamcity": { | ||
"reporter": "inspections", | ||
"report-name": "My ESLint Violations", | ||
"error-statistics-name": "My ESLint Error Count", | ||
"warning-statistics-name": "My ESLint Warning Count" | ||
}, | ||
... | ||
``` | ||
##### 3. ENV variables | ||
```bash | ||
export ESLINT_TEAMCITY_REPORTER=inspections | ||
export ESLINT_TEAMCITY_REPORT_NAME="My Formatting Problems" | ||
export ESLINT_TEAMCITY_ERROR_STATISTICS_NAME="My Error Count" | ||
export ESLINT_TEAMCITY_WARNING_STATISTICS_NAME="My Warning Count" | ||
``` | ||
You can also output your current settings to the log if you set: | ||
```bash | ||
export ESLINT_TEAMCITY_DISPLAY_CONFIG=true | ||
``` | ||
## [gulp-eslint](https://github.com/adametry/gulp-eslint) integration | ||
```javascript | ||
var gulp = require('gulp'); | ||
var eslint = require('gulp-eslint'); | ||
var teamcity = require('eslint-teamcity'); | ||
const gulp = require('gulp'); | ||
const eslint = require('gulp-eslint'); | ||
const teamcity = require('eslint-teamcity'); | ||
@@ -63,26 +115,44 @@ gulp.task('lint', function () { | ||
## TeamCity usage (with [gulp-eslint](http://github.com/adametry/gulp-eslint)) | ||
Add a gulp task to run ESLint (see above) | ||
## TeamCity Usage | ||
The simplest way to run eslint-teamcity is from an npm script in a build step. You could setup a script similar to this: | ||
```json | ||
"scripts": { | ||
"lint:teamcity": "node node_modules/.bin/eslint app/src -f './node_modules/eslint-teamcity/index.js'" | ||
} | ||
``` | ||
Setup a TeamCity build step, similar to the below screenshot: | ||
You could also run it as a gulp task (if you use [gulp](https://github.com/gulpjs/gulp) and [gulp-eslint](https://github.com/adametry/gulp-eslint)): | ||
![Example TeamCity Setup](https://i.imgur.com/j7qMSYg.jpg) | ||
![Example TeamCity Setup](https://i.imgur.com/R3ypYXu.png) | ||
Kick off a new build, by deploying again, and you should see your build errors - assuming you have any! | ||
#### Screenshot with TeamCity | ||
![Example TeamCity Output](https://i.imgur.com/DkwEPEN.jpg) | ||
## Extras | ||
eslint-teamcity will also output statistic values which you can use in TeamCity to track your progress in resolving errors! | ||
Graphs can be setup from the Builds -> Statistics tab. | ||
Graphs can be setup from the Build -> Statistics tab. | ||
![Example Statistics Output](http://i.imgur.com/oHbiuZE.png) | ||
## Development | ||
The quickest way to get a TeamCity server setup is to use Docker: | ||
```shell | ||
docker run -itd --name teamcity-server \ | ||
-v <path to data directory>:/data/teamcity_server/datadir \ | ||
-v <path to logs directory>:/opt/teamcity/logs \ | ||
-p 8111:8111 \ | ||
jetbrains/teamcity-server | ||
docker run -itd -e SERVER_URL="<your ip4 address>:8111" \ | ||
-v <path to agent config folder>:/data/teamcity_agent/conf \ | ||
jetbrains/teamcity-agent | ||
``` | ||
If you fork the repo and are testing on your local TeamCity instance, it may help to run `rm -rf node_modules` in a | ||
build step as TeamCity seems to cache versions between commits. | ||
## Issues | ||
I will try keep this project up to date, but please log any issues | ||
[here](https://github.com/andreogle/eslint-teamcity/issues). | ||
Any pull requests are also welcome! |
@@ -1,99 +0,78 @@ | ||
/** | ||
* @fileoverview TeamCity report formatter plugin for ESLint | ||
* @author Andre Ogle | ||
*/ | ||
const utils = require('./utils'); | ||
const formatErrors = require('./formatters/errors'); | ||
const formatInspections = require('./formatters/inspections'); | ||
'use strict'; | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
/** | ||
* Escape special characters with the respective TeamCity escaping. | ||
* See below link for list of special characters: | ||
* https://confluence.jetbrains.com/display/TCD9/Build+Script+Interaction+with+TeamCity | ||
* @param {string} str An error message to display in TeamCity. | ||
* @returns {string} An error message formatted for display in TeamCity | ||
* Determines the config to be used by the respective formatter | ||
* Config is selected based on the following priority: | ||
* 1. Any user defined props when running eslint-teamcity | ||
* 2. package.json settings | ||
* 3. ENV variables | ||
* 4. Default value | ||
* @param {object} propNames Optional config variables that will override all other config settings | ||
* @returns {object} The final config settings to be used | ||
*/ | ||
function escapeTeamCityString(str) { | ||
if (!str) { | ||
return ''; | ||
} | ||
function getUserConfig(propNames) { | ||
// Attempt to load package.json from current directory | ||
const config = JSON.parse(utils.loadPackageJson())['eslint-teamcity'] || {}; | ||
return str.replace(/\|/g, '||') | ||
.replace(/\'/g, '|\'') | ||
.replace(/\n/g, '|n') | ||
.replace(/\r/g, '|r') | ||
.replace(/\u0085/g, '|x') // TeamCity 6 | ||
.replace(/\u2028/g, '|l') // TeamCity 6 | ||
.replace(/\u2029/g, '|p') // TeamCity 6 | ||
.replace(/\[/g, '|[') | ||
.replace(/\]/g, '|]'); | ||
} | ||
const reporter = | ||
propNames.reporter || config.reporter || process.env.ESLINT_TEAMCITY_REPORTER || 'errors'; | ||
//------------------------------------------------------------------------------ | ||
// Public Interface | ||
//------------------------------------------------------------------------------ | ||
module.exports = function(results, teamcityPropNames) { | ||
var output = ''; | ||
var errorCount = 0; | ||
var warningCount = 0; | ||
var reportName; | ||
var errorCountName; | ||
var warningCountName; | ||
var varNames = teamcityPropNames || {}; | ||
const reportName = | ||
propNames.reportName || | ||
config['report-name'] || | ||
process.env.ESLINT_TEAMCITY_REPORT_NAME || | ||
'ESLint Violations'; | ||
reportName = varNames.reportName || 'ESLint Violations'; | ||
errorCountName = varNames.errorCountName || 'ESLintErrorCount'; | ||
warningCountName = varNames.warningCountName || 'ESLintWarningCount'; | ||
const errorStatisticsName = | ||
propNames.errorStatisticsName || | ||
config['error-statistics-name'] || | ||
process.env.ESLINT_TEAMCITY_ERROR_STATISTICS_NAME || | ||
'ESLint Error Count'; | ||
output += '##teamcity[testSuiteStarted name=\'' + reportName + '\']\n'; | ||
const warningStatisticsName = | ||
propNames.warningStatisticsName || | ||
config['warning-statistics-name'] || | ||
process.env.ESLINT_TEAMCITY_WARNING_STATISTICS_NAME || | ||
'ESLint Warning Count'; | ||
results.forEach(function(result) { | ||
var messages = result.messages; | ||
return { | ||
reporter, | ||
reportName: utils.escapeTeamCityString(reportName), | ||
errorStatisticsName: utils.escapeTeamCityString(errorStatisticsName), | ||
warningStatisticsName: utils.escapeTeamCityString(warningStatisticsName) | ||
}; | ||
} | ||
if (messages.length === 0) { | ||
return; | ||
} | ||
/** | ||
* Determines the formatter to use and any config variables to use | ||
* @param {array} results The output generated by running ESLint. | ||
* @param {object} [propNames] Optional config variables that will override all other | ||
* config settings | ||
* @returns {string} The concatenated output of all messages to display in TeamCity | ||
*/ | ||
function getTeamCityOutput(results, propNames) { | ||
const config = getUserConfig(propNames || {}); | ||
output += '##teamcity[testStarted name=\'' + reportName + ': ' + | ||
escapeTeamCityString(result.filePath) + '\']\n'; | ||
if (process.env.ESLINT_TEAMCITY_DISPLAY_CONFIG) { | ||
console.info(`Running ESLint Teamcity with config: ${JSON.stringify(config, null, 4)}`); | ||
} | ||
var errorsList = []; | ||
var warningsList = []; | ||
messages.forEach(function(message) { | ||
var userMessage = 'line ' + (message.line || 0) + | ||
', col ' + (message.column || 0) + | ||
', ' + message.message + (message.ruleId ? ' (' + message.ruleId + ')' : ''); | ||
if (message.fatal || message.severity === 2) { | ||
errorsList.push(userMessage); | ||
errorCount++; | ||
} else { | ||
warningsList.push(userMessage); | ||
warningCount++; | ||
} | ||
}); | ||
if (errorsList.length) { | ||
output += '##teamcity[testFailed name=\'' + reportName + ': ' + | ||
escapeTeamCityString(result.filePath) + '\' message=\'' + | ||
escapeTeamCityString(errorsList.join('\n')) + '\']\n'; | ||
} else if (warningsList.length) { | ||
output += '##teamcity[testStdOut name=\'' + reportName + ': ' + | ||
escapeTeamCityString(result.filePath) + '\' out=\'warning: ' + | ||
escapeTeamCityString(warningsList.join('\n')) + '\']\n'; | ||
let outputMessages = []; | ||
switch (config.reporter.toLowerCase()) { | ||
case 'inspections': { | ||
outputMessages = formatInspections(results, config); | ||
break; | ||
} | ||
output += '##teamcity[testFinished name=\'' + reportName + ': ' + | ||
escapeTeamCityString(result.filePath) + '\']\n'; | ||
}); | ||
case 'errors': | ||
default: { | ||
outputMessages = formatErrors(results, config); | ||
break; | ||
} | ||
} | ||
output += '##teamcity[testSuiteFinished name=\'' + reportName + '\']\n'; | ||
return outputMessages.join('\n'); | ||
} | ||
output += '##teamcity[buildStatisticValue key=\'' + errorCountName + '\' value=\'' + errorCount +'\' ]\n'; | ||
output += '##teamcity[buildStatisticValue key=\'' + warningCountName + '\' value=\'' + warningCount +'\' ]\n'; | ||
return output; | ||
}; | ||
module.exports = getTeamCityOutput; |
@@ -1,52 +0,61 @@ | ||
var expect = require('chai').expect; | ||
var sinon = require('sinon'); | ||
var sh = require('shelljs'); | ||
var path = require('path'); | ||
var basePath = path.resolve(__dirname, '..'); | ||
var eslintResultGenerator = require('./eslintResultGenerator'); | ||
var fs = require('fs-extra'); | ||
var pathToTestJson = path.resolve(__dirname, 'result.json'); | ||
var pathToIndex = path.resolve(__dirname, '..', 'index.js'); | ||
var input = []; | ||
var result; | ||
describe('support interface',function() { | ||
/* global it, describe, beforeEach, afterEach */ | ||
before(function() { | ||
input.push(eslintResultGenerator.createDummyError()); | ||
}); | ||
const { expect } = require('chai'); | ||
const sh = require('shelljs'); | ||
const path = require('path'); | ||
const fs = require('fs-extra'); | ||
const { createDummyError } = require('./helpers/eslint-factory'); | ||
describe('cmd',function() { | ||
it('as eslint formatter', function() { | ||
result = sh.exec('eslint --format ' + '\'' + pathToIndex + '\' ' + pathToIndex); | ||
expect(result.stdout).to.contain('##teamcity'); | ||
}); | ||
const basePath = path.resolve(__dirname, '..'); | ||
const pathToTestJson = path.resolve(__dirname, 'result.json'); | ||
const pathToIndex = path.resolve(__dirname, '..', 'index.js'); | ||
it('as standalone',function() { | ||
fs.writeJSONSync(pathToTestJson, input); | ||
result = sh.exec('cd ' + basePath + '; node ' + 'index.js ' + pathToTestJson); | ||
expect(result.stdout).to.contain('##teamcity'); | ||
expect(result.stderr).to.equal(''); | ||
describe('smoke tests', function() { | ||
describe('support interface', function() { | ||
let esLintOutput = []; | ||
sh.rm(pathToTestJson); | ||
beforeEach(function() { | ||
esLintOutput.push(createDummyError()); | ||
}); | ||
}); | ||
describe('requirejs',function() { | ||
it('basic', function() { | ||
result = require(pathToIndex)(input); | ||
expect(result).to.contain('##teamcity'); | ||
afterEach(function() { | ||
esLintOutput = []; | ||
}); | ||
it('with parameters', function() { | ||
var teamcityPropNames = { | ||
errorCountName: 'EslintInspectionStatsE', | ||
warningCountName: 'EslintInspectionStatsW' | ||
}; | ||
describe('cmd', function() { | ||
it('as eslint formatter plugin', function() { | ||
this.timeout(8000); | ||
const result = sh.exec(`eslint --format '${pathToIndex}' ${pathToIndex}`); | ||
expect(result.stdout).to.contain('##teamcity'); | ||
}); | ||
result = require(pathToIndex)(input, teamcityPropNames); | ||
expect(result).to.contain('ESLint Violations'); | ||
expect(result).to.contain('EslintInspectionStatsE'); | ||
expect(result).to.contain('EslintInspectionStatsW'); | ||
it('as standalone', function() { | ||
fs.writeJSONSync(pathToTestJson, esLintOutput); | ||
const result = sh.exec(`cd ${basePath}; node index.js ${pathToTestJson}`); | ||
expect(result.stdout).to.contain('##teamcity'); | ||
expect(result.stderr).to.equal(''); | ||
sh.rm(pathToTestJson); | ||
}); | ||
}); | ||
describe('requirejs', function() { | ||
it('basic', function() { | ||
const result = require(pathToIndex)(esLintOutput); | ||
expect(result).to.contain('##teamcity'); | ||
}); | ||
it('with parameters', function() { | ||
const teamcityPropNames = { | ||
errorStatisticsName: 'EslintInspectionStatsE', | ||
warningStatisticsName: 'EslintInspectionStatsW' | ||
}; | ||
const result = require(pathToIndex)(esLintOutput, teamcityPropNames); | ||
expect(result).to.contain('ESLint Violations'); | ||
expect(result).to.contain('EslintInspectionStatsE'); | ||
expect(result).to.contain('EslintInspectionStatsW'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
36610
18
726
155
12
19
+ Addedfs-extra@5.0.0(transitive)
+ Addedjsonfile@4.0.0(transitive)
- Removedfs-extra@3.0.1(transitive)
- Removedjsonfile@3.0.1(transitive)
Updatedfs-extra@^5.0.0