diff --git a/lib/config/presets.js b/lib/config/presets.js index 8635ff19b8f6a81d29feb0c6e99e3352e081ecba..fa3a747e7f92d14f655d8a6e7eb04fc6a9ad4bb9 100644 --- a/lib/config/presets.js +++ b/lib/config/presets.js @@ -2,8 +2,14 @@ const is = require('@sindresorhus/is'); const configParser = require('./index'); const massage = require('./massage'); const migration = require('./migration'); +const github = require('../datasource/github'); const npm = require('../datasource/npm'); +const datasources = { + github, + npm, +}; + module.exports = { resolveConfigPresets, replaceArgs, @@ -117,9 +123,16 @@ function replaceArgs(obj, argMapping) { function parsePreset(input) { let str = input; + let datasource; let packageName; let presetName; let params; + if (str.startsWith('github>')) { + datasource = 'github'; + str = str.substring('github>'.length); + } + str = str.replace(/^npm>/, ''); + datasource = datasource || 'npm'; if (str.includes('(')) { params = str .slice(str.indexOf('(') + 1, -1) @@ -147,20 +160,23 @@ function parsePreset(input) { // non-scoped namespace [, packageName] = str.match(/(.*?)(:|$)/); presetName = str.slice(packageName.length + 1); - if (!packageName.startsWith('renovate-config-')) { + if (datasource === 'npm' && !packageName.startsWith('renovate-config-')) { packageName = `renovate-config-${packageName}`; } if (presetName === '') { presetName = 'default'; } } - return { packageName, presetName, params }; + return { datasource, packageName, presetName, params }; } async function getPreset(preset) { logger.trace(`getPreset(${preset})`); - const { packageName, presetName, params } = parsePreset(preset); - let presetConfig = await npm.getPreset(packageName, presetName); + const { datasource, packageName, presetName, params } = parsePreset(preset); + let presetConfig = await datasources[datasource].getPreset( + packageName, + presetName + ); logger.trace({ presetConfig }, `Found preset ${preset}`); if (params) { const argMapping = {}; diff --git a/lib/datasource/github.js b/lib/datasource/github.js index 30563b1d413afd81f82b8b2460face27d3fb5791..3949cca124cdb4a1ee8bd8c43277479d1938f9bb 100644 --- a/lib/datasource/github.js +++ b/lib/datasource/github.js @@ -2,9 +2,33 @@ const ghGot = require('../platform/github/gh-got-wrapper'); const versioning = require('../versioning'); module.exports = { + getPreset, getPkgReleases, }; +async function getPreset(pkgName, presetName = 'default') { + if (presetName !== 'default') { + throw new Error( + { pkgName, presetName }, + 'Sub-preset names are not supported with GitHub datasource' + ); + } + let res; + try { + const url = `repos/${pkgName}/contents/renovate.json`; + res = Buffer.from((await ghGot(url)).body.content, 'base64').toString(); + } catch (err) { + logger.debug('Failed to retrieve renovate.json from repo'); + throw new Error('dep not found'); + } + try { + return JSON.parse(res); + } catch (err) { + logger.debug('Failed to parse renovate.json'); + throw new Error('invalid preset JSON'); + } +} + async function getPkgReleases(purl, config) { const { versionScheme } = config || {}; const { fullname: repo, qualifiers: options } = purl; diff --git a/test/config/__snapshots__/presets.spec.js.snap b/test/config/__snapshots__/presets.spec.js.snap index 2d174c2e52bef8f0d730a5d4f644aad768c60160..6e278403f1597df8fcf523aefde6dd8dd165e5f1 100644 --- a/test/config/__snapshots__/presets.spec.js.snap +++ b/test/config/__snapshots__/presets.spec.js.snap @@ -80,8 +80,18 @@ Object { } `; +exports[`config/presets parsePreset parses github 1`] = ` +Object { + "datasource": "github", + "packageName": "some/repo", + "params": undefined, + "presetName": "default", +} +`; + exports[`config/presets parsePreset returns default package name 1`] = ` Object { + "datasource": "npm", "packageName": "renovate-config-default", "params": undefined, "presetName": "base", @@ -90,6 +100,7 @@ Object { exports[`config/presets parsePreset returns default package name with params 1`] = ` Object { + "datasource": "npm", "packageName": "renovate-config-default", "params": Array [ "packages/eslint", @@ -101,6 +112,7 @@ Object { exports[`config/presets parsePreset returns non-scoped default 1`] = ` Object { + "datasource": "npm", "packageName": "renovate-config-somepackage", "params": undefined, "presetName": "default", @@ -109,6 +121,7 @@ Object { exports[`config/presets parsePreset returns non-scoped package name 1`] = ` Object { + "datasource": "npm", "packageName": "renovate-config-somepackage", "params": undefined, "presetName": "webapp", @@ -117,6 +130,7 @@ Object { exports[`config/presets parsePreset returns non-scoped package name full 1`] = ` Object { + "datasource": "npm", "packageName": "renovate-config-somepackage", "params": undefined, "presetName": "webapp", @@ -125,6 +139,7 @@ Object { exports[`config/presets parsePreset returns non-scoped package name with params 1`] = ` Object { + "datasource": "npm", "packageName": "renovate-config-somepackage", "params": Array [ "param1", @@ -135,6 +150,7 @@ Object { exports[`config/presets parsePreset returns scope with packageName and default 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/somepackagename", "params": undefined, "presetName": "default", @@ -143,6 +159,7 @@ Object { exports[`config/presets parsePreset returns scope with packageName and params and default 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/somepackagename", "params": Array [ "param1", @@ -155,6 +172,7 @@ Object { exports[`config/presets parsePreset returns scope with packageName and presetName 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/somepackagename", "params": undefined, "presetName": "somePresetName", @@ -163,6 +181,7 @@ Object { exports[`config/presets parsePreset returns scope with packageName and presetName and params 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/somepackagename", "params": Array [ "param1", @@ -174,6 +193,7 @@ Object { exports[`config/presets parsePreset returns scope with presetName 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/renovate-config", "params": undefined, "presetName": "somePresetName", @@ -182,6 +202,7 @@ Object { exports[`config/presets parsePreset returns scope with presetName and params 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/renovate-config", "params": Array [ "param1", @@ -192,6 +213,7 @@ Object { exports[`config/presets parsePreset returns simple scope 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/renovate-config", "params": undefined, "presetName": "default", @@ -200,6 +222,7 @@ Object { exports[`config/presets parsePreset returns simple scope and params 1`] = ` Object { + "datasource": "npm", "packageName": "@somescope/renovate-config", "params": Array [ "param1", diff --git a/test/config/presets.spec.js b/test/config/presets.spec.js index 44b056bac87f5fd548ecfe7a3de1b55966ea473a..7bb69d34ef5b92db2b4f657b3a73de6608597a5e 100644 --- a/test/config/presets.spec.js +++ b/test/config/presets.spec.js @@ -207,6 +207,9 @@ describe('config/presets', () => { it('returns default package name', () => { expect(presets.parsePreset(':base')).toMatchSnapshot(); }); + it('parses github', () => { + expect(presets.parsePreset('github>some/repo')).toMatchSnapshot(); + }); it('returns default package name with params', () => { expect( presets.parsePreset(':group(packages/eslint, eslint)') diff --git a/test/datasource/github.spec.js b/test/datasource/github.spec.js index 82e57a4f6175877373bc4163a5d2b5e6c28ede65..86d35bfc6a829eb865d5fbd6ec1c5fc6ee09dc11 100644 --- a/test/datasource/github.spec.js +++ b/test/datasource/github.spec.js @@ -1,10 +1,41 @@ const datasource = require('../../lib/datasource'); +const github = require('../../lib/datasource/github'); const ghGot = require('../../lib/platform/github/gh-got-wrapper'); jest.mock('../../lib/platform/github/gh-got-wrapper'); jest.mock('got'); describe('datasource/github', () => { + describe('getPreset()', () => { + it('throws if non-default', async () => { + await expect( + github.getPreset('some/repo', 'non-default') + ).rejects.toThrow(); + }); + it('throws if no content', async () => { + ghGot.mockImplementationOnce(() => ({ + body: {}, + })); + await expect(github.getPreset('some/repo')).rejects.toThrow(); + }); + it('throws if fails to parse', async () => { + ghGot.mockImplementationOnce(() => ({ + body: { + content: Buffer.from('not json').toString('base64'), + }, + })); + await expect(github.getPreset('some/repo')).rejects.toThrow(); + }); + it('should return the preset', async () => { + ghGot.mockImplementationOnce(() => ({ + body: { + content: Buffer.from('{"foo":"bar"}').toString('base64'), + }, + })); + const content = await github.getPreset('some/repo'); + expect(content).toEqual({ foo: 'bar' }); + }); + }); describe('getPkgReleases', () => { it('returns cleaned tags', async () => { const body = [