Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Add configuration file support and exclude option #213

Merged
merged 2 commits into from
Jun 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const config = {
moreInfoTitle: 'For more information about the issues follow the links: \n',
tslintProjectWebsite: 'https://github.com/webschik/tslint-config-security',
numFilesPerPage: 30,
numAnnotationsPerUpdate: 50
numAnnotationsPerUpdate: 50,
configFilePath: '.precaution.yaml'
}

const annotationsLevels = {
Expand Down
48 changes: 48 additions & 0 deletions filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2019 VMware, Inc.
// SPDX-License-Identifier: BSD-2-Clause

const { config } = require('./config')

const minimatch = require('minimatch')
const path = require('path')

/**
* Checks whether a file should be excluded from processing
* @param {String} filePath path of file to check
* @param {String[]} excludes array of exclusion rules to compare to
*/
MVrachev marked this conversation as resolved.
Show resolved Hide resolved
function isExcluded (filePath, excludes) {
let ok = false
for (let excludeRule of excludes) {
excludeRule = path.normalize(excludeRule)
if (minimatch(filePath, excludeRule, { matchBase: true })) {
ok = true
break
}
}
return ok
}

/**
* Filters the listed files and returns only the files relevant to us
* @param {*} rawData the raw output from list files API call
* @param {String[]} excludes array of exclusion rules in glob format provided by the user
* @return {Promise[]<any>} the filtered GitHub response
*/
function filterData (rawData, excludes) {
return rawData.data
.filter(file =>
config.fileExtensions.reduce(
(acc, ext) => acc || file.filename.endsWith(ext),
false
)
)
.filter(fileJSON => fileJSON.status !== 'removed')
.map(fileInfo => {
return fileInfo.filename
})
.filter(filePath => !isExcluded(filePath, excludes))
}

module.exports.filterData = filterData
module.exports.isExcluded = isExcluded
joshuagl marked this conversation as resolved.
Show resolved Hide resolved
42 changes: 31 additions & 11 deletions github_api_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: BSD-2-Clause

const { config } = require('./config')
const { filterData } = require('./filter')

const rawMediaType = 'application/vnd.github.v3.raw'

Expand All @@ -27,24 +28,42 @@ function inProgressAPIresponse (context, headSha) {
}

/**
* Filters the listed files and returns only the files relevant to us
* @param {*} rawData the raw output from list files API call
* @return {Promise[]<any>} the filtered GitHub response
* Get Precaution config file contents as raw data,
* if that file exists.
* @param {import('probot').Context} context Probot context
* @param {String} customConfigPath Optional (default value config.configFilePath)
joshuagl marked this conversation as resolved.
Show resolved Hide resolved
* @return {Promise<any>} GitHub response
* See https://developer.github.com/v3/repos/contents/#get-contents
*/
function filterData (rawData) {
return rawData.data.filter(file => config.fileExtensions.reduce((acc, ext) => acc || file.filename.endsWith(ext), false))
.filter(fileJSON => fileJSON.status !== 'removed').map(fileInfo => {
return fileInfo.filename
async function getConfigFile (context, customConfigPath = config.configFilePath) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange that we introduce a new function in commit one of this PR then immediately change it in commit two of the same PR. That's a perfectly natural way for code to evolve during development but not necessary to expose in the revision control history.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a change was introduced when we added our unit tests and that's why I change it in the commit where I added the unit tests.

let { owner, repo } = context.repo()
// GitHub doesn't provide "checkIfFileExists" API endpoint.
// That's why we should try to download the file and if we have
// 404 error code the file doesn't exist in the repository.
try {
return await context.github.repos.getContents({
owner,
repo,
path: customConfigPath,
headers: { accept: rawMediaType }
})
} catch (error) {
if (error.code === 404) {
return null
MVrachev marked this conversation as resolved.
Show resolved Hide resolved
} else {
throw new Error(error)
}
}
}

/**
* Get list of files modified by a pull request
* @param {import('probot').Context} context Probot context
* @param {number} number the pull request number inside the repository
* @param {Object} configObj configuration object populated by a config file
* @returns {String[]} paths to the diff files from the pull request relevant to us
*/
async function getPRFiles (context, number) {
async function getPRFiles (context, number, configObj) {
const { owner, repo } = context.repo()

// See https://developer.github.com/v3/pulls/#list-pull-requests-files
Expand All @@ -54,11 +73,11 @@ async function getPRFiles (context, number) {
number: number,
per_page: config.numFilesPerPage
})

let data = filterData(response)
let excludes = configObj && configObj.exclude ? configObj.exclude : []
let data = filterData(response, excludes)
while (context.github.hasNextPage(response)) {
response = await context.github.getNextPage(response)
data = data.concat(filterData(response))
data = data.concat(filterData(response, excludes))
}
return Promise.all(data)
}
Expand Down Expand Up @@ -197,3 +216,4 @@ module.exports.getPRFiles = getPRFiles
module.exports.getContents = getRawFileContents
module.exports.sendResults = sendResults
module.exports.getConclusion = getConclusion
module.exports.getConfigFile = getConfigFile
16 changes: 11 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const cache = require('./cache')
const { config } = require('./config')
const apiHelper = require('./github_api_helper')
const { runLinters } = require('./runner')
const yaml = require('js-yaml')

/**
* @param {import('probot').Application} app - Probot's Application class.
Expand Down Expand Up @@ -56,12 +57,16 @@ async function runLinterFromPRData (pullRequests, context, headSha) {
const checkRunResponse = apiHelper.inProgressAPIresponse(context, headSha)

try {
// For now only deal with one PR
const PR = pullRequests[0]

// Loads the Precaution configuration file
const rawObj = await apiHelper.getConfigFile(context)
const configObj = rawObj ? yaml.safeLoad(rawObj.data) : null
// Process all pull requests associated with check suite
const PRsDownloadedPromise = pullRequests.map(pr => processPullRequest(pr, context))
const PRsDownloadedPromise = pullRequests.map(pr => processPullRequest(pr, context, configObj))
const resolvedPRs = await Promise.all(PRsDownloadedPromise)

// For now only deal with one PR
const PR = pullRequests[0]
// Sometimes the resolverPR list contains undefined members
const inputFiles = resolvedPRs[0].filter((file) => file)

Expand All @@ -85,15 +90,16 @@ async function runLinterFromPRData (pullRequests, context, headSha) {
/**
* Retrieve list of files modified by PR and download them to cache
* @param {import('probot').Context} context
* @param {Object} configObj configuration object populated by a config file
* @returns {Promise<string[]>} Paths to the downloaded PR files
*/
async function processPullRequest (pullRequest, context) {
async function processPullRequest (pullRequest, context, configObj) {
const number = pullRequest.number
const ref = pullRequest.head.ref
const id = pullRequest.id
const repoID = context.payload.repository.id

const response = await apiHelper.getPRFiles(context, number)
const response = await apiHelper.getPRFiles(context, number, configObj)
const filesDownloadedPromise = response
.map(async filename => {
const headRevision = await apiHelper.getContents(context, filename, ref, pullRequest.head)
Expand Down