Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion bin/repolinter.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ require('yargs')
'Specify an alternate URL repolinter configuration to use (This will default to repolinter.json/repolinter.yaml at the root of the project, or the internal default ruleset if none is found).',
type: 'string'
})
.option('rulesetEncoded', {
alias: 'c',
describe:
'Specify a base64 encoded ruleset that repolinter will decode and use instead.',
type: 'string'
})
.option('git', {
alias: 'g',
describe:
Expand Down Expand Up @@ -77,11 +83,16 @@ require('yargs')
return
}
}
var encodedIsUsed = false
if (argv.rulesetEncoded) {
encodedIsUsed = true
}
// run the linter
const output = await repolinter.lint(
tmpDir || path.resolve(process.cwd(), argv.directory),
argv.allowPaths,
argv.rulesetUrl || argv.rulesetFile,
argv.rulesetUrl || argv.rulesetFile || argv.rulesetEncoded,
encodedIsUsed,
argv.dryRun
)
// create the output
Expand Down
58 changes: 32 additions & 26 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,15 @@ module.exports.resultFormatter = exports.defaultFormatter
* @param {string[]} [filterPaths] A list of directories to allow linting of, or [] for all.
* @param {Object|string|null} [ruleset] A custom ruleset object with the same structure as the JSON ruleset configs, or a string path to a JSON config.
* Set to null for repolinter to automatically find it in the repository.
* @param {boolean} [encodedIsUsed] If true, repolinter expects base64 encoded string in the ruleset, we will decode the base64 encoded ruleset.
* @param {boolean} [dryRun] If true, repolinter will report suggested fixes, but will make no disk modifications.
* @returns {Promise<LintResult>} An object representing the output of the linter
*/
async function lint(
targetDir,
filterPaths = [],
ruleset = null,
encodedIsUsed = false,
dryRun = false
) {
const fileSystem = new FileSystem()
Expand All @@ -120,36 +122,40 @@ async function lint(
}

let rulesetPath = null
if (typeof ruleset === 'string') {
if (config.isAbsoluteURL(ruleset)) {
rulesetPath = ruleset
} else {
rulesetPath = path.resolve(targetDir, ruleset)
if (!encodedIsUsed) {
if (typeof ruleset === 'string') {
if (config.isAbsoluteURL(ruleset)) {
rulesetPath = ruleset
} else {
rulesetPath = path.resolve(targetDir, ruleset)
}
} else if (!ruleset) {
rulesetPath = config.findConfig(targetDir)
}
} else if (!ruleset) {
rulesetPath = config.findConfig(targetDir)
}

if (rulesetPath !== null) {
try {
ruleset = await config.loadConfig(rulesetPath)
} catch (e) {
return {
params: {
targetDir,
filterPaths,
rulesetPath,
ruleset
},
passed: false,
errored: true,
/** @ignore */
errMsg: e && e.toString(),
results: [],
targets: {},
formatOptions: ruleset && ruleset.formatOptions
if (rulesetPath !== null) {
try {
ruleset = await config.loadConfig(rulesetPath)
} catch (e) {
return {
params: {
targetDir,
filterPaths,
rulesetPath,
ruleset
},
passed: false,
errored: true,
/** @ignore */
errMsg: e && e.toString(),
results: [],
targets: {},
formatOptions: ruleset && ruleset.formatOptions
}
}
}
} else {
ruleset = await config.decodeConfig(ruleset)
}

// validate config
Expand Down
52 changes: 52 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,60 @@ function parseConfig(config) {
)
}

/**
* Decodes a base64 encoded string into a config
*
* @param {string} encodedRuleSet A base64 encoded string that needs decoding
* @param {array} [processed] List of config files already processed, used to prevent loops
* @returns {Object} The loaded repolinter json config
* @throws Will throw an error if unable to parse config or if config is invalid
*/
async function decodeConfig(encodedRuleSet, processed = []) {
if (!encodedRuleSet) {
throw new Error('must give base64 encoded string')
}

const configData = Buffer.from(encodedRuleSet, 'base64').toString()

let ruleset
// try parsing as JSON, then YAML
try {
ruleset = JSON.parse(configData)
} catch (je) {
try {
ruleset = yaml.safeLoad(configData)
} catch (ye) {
throw new Error(
`unable to parse ruleset as either JSON (error: ${je}) or YAML (error: ${ye})`
)
}
}

// merge extended rulesets
// TODO: Verify functionality when used in conjunction with encoded rulesets... no config location available
if (ruleset.extends) {
processed.push(encodedRuleSet)
if (processed.length > 20) {
// safeguard against infinite loops. expose as flag one day if needed
throw new Error('exceeded maximum 20 ruleset extensions')
}

let parent
if (isAbsoluteURL(ruleset.extends)) {
parent = ruleset.extends
}
if (!processed.includes(parent)) {
const parentRuleset = await loadConfig(parent, processed)
ruleset = lodash.merge({}, parentRuleset, ruleset)
}
}

return ruleset
}

module.exports.findConfig = findConfig
module.exports.isAbsoluteURL = isAbsoluteURL
module.exports.loadConfig = loadConfig
module.exports.decodeConfig = decodeConfig
module.exports.validateConfig = validateConfig
module.exports.parseConfig = parseConfig
Loading