diff options
Diffstat (limited to '.gitlab-ci/run-eslint')
-rwxr-xr-x | .gitlab-ci/run-eslint | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/.gitlab-ci/run-eslint b/.gitlab-ci/run-eslint new file mode 100755 index 0000000..2a8f60d --- /dev/null +++ b/.gitlab-ci/run-eslint @@ -0,0 +1,128 @@ +#!/usr/bin/env node + +const { ESLint } = require('eslint'); +const fs = require('fs'); +const path = require('path'); +const { spawn } = require('child_process'); + +function createConfig(config) { + const options = { + cache: true, + cacheLocation: `.eslintcache-${config}`, + }; + + if (config === 'legacy') + options.overrideConfigFile='lint/eslintrc-legacy.yml'; + + return new ESLint(options); +} + +function git(...args) { + const git = spawn('git', args, { stdio: ['ignore', null, 'ignore'] }); + git.stdout.setEncoding('utf8'); + + return new Promise(resolve => { + let out = ''; + git.stdout.on('data', chunk => out += chunk); + git.stdout.on('end', () => resolve(out.trim())); + }); +} + +function createCommon(report1, report2, ignoreColumn=false) { + return report1.map(result => { + const { filePath, messages } = result; + const match = + report2.find(r => r.filePath === filePath) || { messages: [] }; + + const filteredMessages = messages.filter( + msg => match.messages.some( + m => m.line === msg.line && (ignoreColumn || m.column === msg.column))); + + const [errorCount, warningCount] = filteredMessages.reduce( + ([e, w], msg) => { + return [ + e + Number(msg.severity === 2), + w + Number(msg.severity === 1)]; + }, [0, 0]); + + return { + filePath, + messages: filteredMessages, + errorCount, + warningCount, + }; + }); +} + +async function getMergeRequestChanges(remote, branch) { + await git('fetch', remote, branch); + const branchPoint = await git('merge-base', 'HEAD', 'FETCH_HEAD'); + const diff = await git('diff', '-U0', `${branchPoint}...HEAD`); + + const report = []; + let messages = null; + for (const line of diff.split('\n')) { + if (line.startsWith('+++ b/')) { + const filePath = path.resolve(line.substring(6)); + messages = filePath.endsWith('.js') ? [] : null; + if (messages) + report.push({ filePath, messages }); + } else if (messages && line.startsWith('@@ ')) { + [, , changes] = line.split(' '); + [start, count] = `${changes},1`.split(',').map(i => parseInt(i)); + for (let i = start; i < start + count; i++) + messages.push({ line: i }); + } + } + + return report; +} + +function getOption(...names) { + const optIndex = + process.argv.findIndex(arg => names.includes(arg)) + 1; + + if (optIndex === 0) + return undefined; + + return process.argv[optIndex]; +} + +(async function main() { + const outputOption = getOption('--output-file', '-o'); + const outputPath = outputOption ? path.resolve(outputOption) : null; + + const sourceDir = path.dirname(process.argv[1]); + process.chdir(path.resolve(sourceDir, '..')); + + const remote = getOption('--remote') || 'origin'; + const branch = getOption('--branch', '-b'); + + const sources = ['js', 'subprojects/extensions-app/js']; + const regular = createConfig('regular'); + + const ops = []; + ops.push(regular.lintFiles(sources)); + if (branch) + ops.push(getMergeRequestChanges(remote, branch)); + else + ops.push(createConfig('legacy').lintFiles(sources)); + + const results = await Promise.all(ops); + const commonResults = createCommon(...results, branch !== undefined); + + const formatter = await regular.loadFormatter(getOption('--format', '-f')); + const resultText = formatter.format(commonResults); + + if (outputPath) { + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, resultText); + } else { + console.log(resultText); + } + + process.exitCode = commonResults.some(r => r.errorCount > 0) ? 1 : 0; +})().catch((error) => { + process.exitCode = 1; + console.error(error); +}); |