feat: detect languages during onboarding ()

* refactor: simplify onboarding logic

* docker meteor tests

* handle no package files case

* fix coverage
pull/920/head v9.73.0
Rhys Arkins 2017-10-12 16:29:20 +02:00 committed by GitHub
parent ef878b2d08
commit 8b99ce5294
8 changed files with 127 additions and 127 deletions

View File

@ -322,6 +322,9 @@ async function resolvePackageFiles(inputConfig) {
const { logger } = config;
logger.trace({ config }, 'resolvePackageFiles()');
const packageFiles = [];
const contentBranch = config.repoIsOnboarded
? config.baseBranch
: config.onboardingBranch;
for (let packageFile of config.packageFiles) {
packageFile =
typeof packageFile === 'string' ? { packageFile } : packageFile;
@ -329,7 +332,7 @@ async function resolvePackageFiles(inputConfig) {
logger.debug(`Resolving packageFile ${JSON.stringify(packageFile)}`);
const pFileRaw = await config.api.getFileContent(
packageFile.packageFile,
config.baseBranch
contentBranch
);
if (!pFileRaw) {
logger.info(
@ -357,7 +360,7 @@ async function resolvePackageFiles(inputConfig) {
if (!inputConfig.ignoreNpmrcFile) {
packageFile.npmrc = await config.api.getFileContent(
path.join(path.dirname(packageFile.packageFile), '.npmrc'),
config.baseBranch
contentBranch
);
}
if (!packageFile.npmrc) {
@ -365,7 +368,7 @@ async function resolvePackageFiles(inputConfig) {
}
packageFile.yarnrc = await config.api.getFileContent(
path.join(path.dirname(packageFile.packageFile), '.yarnrc'),
config.baseBranch
contentBranch
);
if (!packageFile.yarnrc) {
delete packageFile.yarnrc;
@ -412,7 +415,7 @@ async function resolvePackageFiles(inputConfig) {
);
packageFile.yarnLock = await config.api.getFileContent(
yarnLockFileName,
config.baseBranch
contentBranch
);
if (packageFile.yarnLock) {
logger.debug(
@ -426,7 +429,7 @@ async function resolvePackageFiles(inputConfig) {
);
packageFile.packageLock = await config.api.getFileContent(
packageLockFileName,
config.baseBranch
contentBranch
);
if (packageFile.packageLock) {
logger.debug(
@ -446,7 +449,7 @@ async function resolvePackageFiles(inputConfig) {
logger.debug(`Resolving packageFile ${JSON.stringify(packageFile)}`);
packageFile.content = await config.api.getFileContent(
packageFile.packageFile,
config.baseBranch
contentBranch
);
const strippedComment = packageFile.content.replace(/^(#.*?\n)+/, '');
const fromMatch = strippedComment.match(/^FROM (.*)\n/);

View File

@ -1,6 +1,5 @@
const convertHrTime = require('convert-hrtime');
const tmp = require('tmp');
const presets = require('../../config/presets');
// Workers
const branchWorker = require('../branch');
// children
@ -64,6 +63,7 @@ async function renovateRepository(repoConfig, token) {
logger.warn(message);
}
}
config = await onboarding.getOnboardingStatus(config);
// Detect package files in default branch if not manually provisioned
if (config.packageFiles.length === 0) {
logger.debug('Detecting package files');
@ -84,37 +84,6 @@ async function renovateRepository(repoConfig, token) {
logger.trace({ config }, 'post-packageFiles config');
// TODO: why is this fix needed?!
config.logger = logger;
config = await onboarding.getOnboardingStatus(config);
if (!config.repoIsOnboarded) {
config.contentBaseBranch = `${config.branchPrefix}configure`;
// Remove packageFile list in case they are provisioned in renovate.json
const packageFiles = config.packageFiles.map(
packageFile => packageFile.packageFile
);
config.packageFiles = [];
config = await apis.mergeRenovateJson(config, config.contentBaseBranch);
// Restore previous packageFile list if not provisioned manually
if (config.packageFiles.length === 0) {
config.packageFiles = packageFiles;
}
if (config.baseBranch) {
if (await config.api.branchExists(config.baseBranch)) {
config.contentBaseBranch = config.baseBranch;
} else {
const message = `The configured baseBranch "${config.baseBranch}" is not present. Ignoring`;
config.errors.push({
depName: 'baseBranch',
message,
});
logger.warn(message);
}
}
config = await apis.resolvePackageFiles(config);
config = await apis.checkMonorepos(config);
config = await presets.resolveConfigPresets(config);
config.logger = logger;
logger.trace({ config }, 'onboarding config');
}
config = decryptConfig(config);
logger.trace({ config }, 'post-decrypt config');
const allUpgrades = await upgrades.determineRepoUpgrades(config);
@ -168,6 +137,8 @@ async function renovateRepository(repoConfig, token) {
// Swallow this error so that other repositories can be processed
if (err.message === 'uninitiated') {
logger.info('Repository is uninitiated - skipping');
} else if (err.message === 'no package files') {
logger.info('Repository has no package files - skipping');
} else {
logger.error(`Failed to process repository: ${err.message}`);
logger.debug({ err });

View File

@ -1,8 +1,10 @@
const apis = require('./apis');
const onboardPrTitle = 'Configure Renovate';
module.exports = {
isRepoPrivate,
createBranch,
createOnboardingBranch,
ensurePr,
getOnboardingStatus,
};
@ -20,45 +22,48 @@ async function isRepoPrivate(config) {
return repoIsPrivate === true;
}
async function createBranch(config) {
async function createOnboardingBranch(inputConfig) {
let config = { ...inputConfig };
const { logger } = config;
const onboardBranchName = `${config.branchPrefix}configure`;
config.meteor.enabled = true;
config.docker.enabled = true;
config = await apis.detectPackageFiles(config);
if (config.packageFiles.length === 0) {
throw new Error('no package files');
}
const repoIsPrivate = await module.exports.isRepoPrivate(config);
let onboardingConfigString;
if (repoIsPrivate) {
logger.debug('Repo is private - setting to app type');
onboardingConfigString = `{\n "extends": ["config:js-app"]\n}\n`;
const renovateJson = {
extends: [],
};
if (config.types.npm) {
if (repoIsPrivate) {
logger.debug('Repo is private - setting to app type');
renovateJson.extends.push('config:js-app');
} else {
logger.debug('Repo is not private - setting to library');
renovateJson.extends.push('config:js-lib');
}
} else {
logger.debug('Repo is not private - setting to library');
onboardingConfigString = `{\n "extends": ["config:js-lib"]\n}\n`;
renovateJson.extends.push('config:base');
}
const existingContent = await config.api.getFileContent(
'renovate.json',
onboardBranchName
);
if (existingContent === onboardingConfigString) {
logger.debug('Onboarding branch is already up-to-date');
return;
if (config.types.meteor) {
renovateJson.extends.push(':meteor');
}
if (existingContent) {
logger.debug(
{ existingContent, onboardingConfigString },
'Updating onboarding branch'
);
} else {
logger.debug('Creating onboarding branch');
if (config.types.docker) {
renovateJson.extends.push(':docker');
}
logger.info({ renovateJson }, 'Creating onboarding branch');
await config.api.commitFilesToBranch(
onboardBranchName,
config.onboardingBranch,
[
{
name: 'renovate.json',
contents: onboardingConfigString,
contents: `${JSON.stringify(renovateJson, null, 2)}\n`,
},
],
'Add renovate.json'
);
return config;
}
async function ensurePr(config, branchUpgrades) {
@ -229,39 +234,33 @@ With your current configuration, renovate will initially create the following Pu
}
async function getOnboardingStatus(inputConfig) {
const config = { ...inputConfig };
let config = { ...inputConfig };
const { logger } = config;
logger.debug('Checking if repo is configured');
logger.debug('Checking if repo is onboarded');
// Check if repository is configured
if (config.onboarding === false) {
logger.debug('Repo onboarding is disabled');
return { ...config, repoIsOnboarded: true };
}
if (config.renovateJsonPresent || config.hasPackageJsonRenovateConfig) {
logger.debug('Repo has been configured');
if (config.renovateJsonPresent) {
logger.debug('Repo has renovate.json');
return { ...config, repoIsOnboarded: true };
}
config.onboardingBranch = `${config.branchPrefix}configure`;
const pr = await config.api.findPr(
`${config.branchPrefix}configure`,
config.onboardingBranch,
'Configure Renovate'
);
if (pr) {
logger.debug(`Found existing onboarding PR#${pr.number}`);
if (pr.isClosed) {
logger.debug('Found closed Configure Renovate PR');
return { ...config, repoIsOnboarded: true };
}
// PR exists but hasn't been closed yet
logger.debug(
`PR #${pr.displayNumber} needs to be closed to enable renovate to continue`
);
const prDetails = await config.api.getPr(pr.number);
if (!prDetails.canRebase) {
// Cannot update files if rebasing not possible
return { ...config, repoIsOnboarded: false };
}
if (pr && pr.isClosed) {
logger.debug('Found closed Configure Renovate PR');
return { ...config, repoIsOnboarded: true };
}
// Create or update files, then return
await module.exports.createBranch(config);
if (pr) {
logger.debug(`Found existing onboarding PR #${pr.number}`);
} else {
config = await module.exports.createOnboardingBranch(config);
}
logger.debug('Merging renovate.json from onboarding branch');
config = await apis.mergeRenovateJson(config, config.onboardingBranch);
return { ...config, repoIsOnboarded: false };
}

View File

@ -115,11 +115,9 @@ Array [
"content": Object {
"workspaces": Array [],
},
"errors": Array [],
"npmrc": "npmrc-1",
"packageFile": "package.json",
"packageLock": "packageLock-1",
"warnings": Array [],
"yarnLock": "yarnLock-1",
"yarnrc": "yarnrc-1",
},

View File

@ -374,23 +374,9 @@ Array [
Array [
Object {
"contents": "{
\\"extends\\": [\\"config:js-lib\\"]
}
",
"name": "renovate.json",
},
],
"Add renovate.json",
]
`;
exports[`lib/workers/repository/onboarding getOnboardingStatus(config) commits files if existing content does not match 1`] = `
Array [
"renovate/configure",
Array [
Object {
"contents": "{
\\"extends\\": [\\"config:js-lib\\"]
\\"extends\\": [
\\"config:js-lib\\"
]
}
",
"name": "renovate.json",
@ -406,7 +392,31 @@ Array [
Array [
Object {
"contents": "{
\\"extends\\": [\\"config:js-app\\"]
\\"extends\\": [
\\"config:js-app\\"
]
}
",
"name": "renovate.json",
},
],
"Add renovate.json",
]
`;
exports[`lib/workers/repository/onboarding getOnboardingStatus(config) throws if no packageFiles 1`] = `[Error: no package files]`;
exports[`lib/workers/repository/onboarding getOnboardingStatus(config) uses base + docker + meteor 1`] = `
Array [
"renovate/configure",
Array [
Object {
"contents": "{
\\"extends\\": [
\\"config:base\\",
\\":meteor\\",
\\":docker\\"
]
}
",
"name": "renovate.json",

View File

@ -369,6 +369,7 @@ describe('workers/repository/apis', () => {
expect(res.packageFiles).toEqual([]);
});
it('includes files with content', async () => {
config.repoIsOnboarded = true;
config.api.getFileContent.mockReturnValueOnce(
JSON.stringify({
renovate: {},

View File

@ -101,7 +101,7 @@ describe('workers/repository', () => {
...{ packageFiles: [] },
}));
await repositoryWorker.renovateRepository(config);
expect(onboarding.getOnboardingStatus.mock.calls.length).toBe(0);
expect(apis.resolvePackageFiles.mock.calls.length).toBe(0);
expect(config.logger.error.mock.calls.length).toBe(0);
});
it('does not skip repository if package.json', async () => {
@ -265,5 +265,18 @@ describe('workers/repository', () => {
await repositoryWorker.renovateRepository(config);
expect(config.logger.error.mock.calls.length).toBe(0);
});
it('handles special no package files error', async () => {
apis.initApis.mockImplementationOnce(() => {
// Create a new object, that prototypically inherits from the Error constructor
function MyError() {
this.message = 'no package files';
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
throw new MyError();
});
await repositoryWorker.renovateRepository(config);
expect(config.logger.error.mock.calls.length).toBe(0);
});
});
});

View File

@ -1,4 +1,5 @@
const onboarding = require('../../../lib/workers/repository/onboarding');
const apis = require('../../../lib/workers/repository/apis');
const logger = require('../../_fixtures/logger');
const defaultConfig = require('../../../lib/config/defaults').getConfig();
@ -225,8 +226,10 @@ describe('lib/workers/repository/onboarding', () => {
config.api = {
commitFilesToBranch: jest.fn(),
createPr: jest.fn(() => ({ displayNumber: 1 })),
findFilePaths: jest.fn(() => []),
findPr: jest.fn(),
getFileContent: jest.fn(),
getFileJson: jest.fn(() => ({})),
getPr: jest.fn(() => {}),
getCommitMessages: jest.fn(),
};
@ -256,17 +259,8 @@ describe('lib/workers/repository/onboarding', () => {
expect(config.api.findPr.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0);
});
it('commits files if pr is not closed and is rebaseable', async () => {
config.api.findPr.mockReturnValueOnce({});
config.api.getPr.mockReturnValueOnce({ canRebase: true });
const res = await onboarding.getOnboardingStatus(config);
expect(res.repoIsOnboarded).toEqual(false);
expect(config.api.findPr.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(1);
});
it('skips file update if existing pr is not rebaseable', async () => {
config.api.findPr.mockReturnValueOnce({});
config.api.getPr.mockReturnValueOnce({ canRebase: false });
it('skips commit files and returns false if open pr', async () => {
config.api.findPr.mockReturnValueOnce({ isClosed: false });
const res = await onboarding.getOnboardingStatus(config);
expect(res.repoIsOnboarded).toEqual(false);
expect(config.api.findPr.mock.calls.length).toBe(1);
@ -287,21 +281,32 @@ describe('lib/workers/repository/onboarding', () => {
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0]).toMatchSnapshot();
});
it('commits files if existing content does not match', async () => {
config.api.getFileContent.mockReturnValueOnce('some-different-content');
it('uses base + docker + meteor', async () => {
apis.detectPackageFiles = jest.fn(input => ({
...input,
packageFiles: [{}, {}],
types: {
meteor: true,
docker: true,
},
}));
const res = await onboarding.getOnboardingStatus(config);
expect(res.repoIsOnboarded).toEqual(false);
expect(config.api.findPr.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0]).toMatchSnapshot();
});
it('skips commit files if existing content matches', async () => {
const existingContent = `{\n "extends": ["config:js-lib"]\n}\n`;
config.api.getFileContent.mockReturnValueOnce(existingContent);
const res = await onboarding.getOnboardingStatus(config);
expect(res.repoIsOnboarded).toEqual(false);
expect(config.api.findPr.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0);
it('throws if no packageFiles', async () => {
apis.detectPackageFiles = jest.fn(input => ({
...input,
}));
let e;
try {
await onboarding.getOnboardingStatus(config);
} catch (err) {
e = err;
}
expect(e).toMatchSnapshot();
});
});
});