diff --git a/lib/config/index.js b/lib/config/index.js
index 028897b2f0fbb596559dab3ba7abdbeed289fea2..a6a04ed1cb6ee34da6353306d6fdbb35b604f41e 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -13,7 +13,7 @@ const githubAppHelper = require('../helpers/github-app');
 
 module.exports = {
   parseConfigs,
-  getRepoConfig,
+  getRepositoryConfig,
   getPackageFileConfig,
 };
 
@@ -117,7 +117,7 @@ async function parseConfigs(env, argv) {
   return config;
 }
 
-function getRepoConfig(globalConfig, index) {
+function getRepositoryConfig(globalConfig, index) {
   let repository = globalConfig.repositories[index];
   if (typeof repository === 'string') {
     repository = { repository };
diff --git a/lib/workers/global.js b/lib/workers/global.js
index 63ff782035139f5a85f209209e8578b72a43ec51..a55382ee044b07bef226599da2a7ccc95a32b45e 100644
--- a/lib/workers/global.js
+++ b/lib/workers/global.js
@@ -12,9 +12,9 @@ async function start() {
     const config = await configParser.parseConfigs(process.env, process.argv);
     // Iterate through repositories sequentially
     for (let index = 0; index < config.repositories.length; index += 1) {
-      const repoConfig = configParser.getRepoConfig(config, index);
+      const repoConfig = configParser.getRepositoryConfig(config, index);
       repoConfig.logger.info('Renovating repository');
-      await repositoryWorker.processRepo(repoConfig);
+      await repositoryWorker.renovateRepository(repoConfig);
       repoConfig.logger.info('Finished repository');
     }
     logger.info('Renovate finished');
diff --git a/lib/workers/repository.js b/lib/workers/repository.js
deleted file mode 100644
index 2f80b20644c1ca1bc208f17892603a384bc38b91..0000000000000000000000000000000000000000
--- a/lib/workers/repository.js
+++ /dev/null
@@ -1,234 +0,0 @@
-// Third party requires
-const handlebars = require('handlebars');
-const ini = require('ini');
-const stringify = require('json-stringify-pretty-compact');
-// Config
-const configParser = require('../config');
-const defaultsParser = require('../config/defaults');
-// API
-const githubApi = require('../api/github');
-const gitlabApi = require('../api/gitlab');
-const npmApi = require('../api/npm');
-// Workers
-const branchWorker = require('./branch');
-const packageFileWorker = require('./package-file');
-
-module.exports = {
-  setNpmrc,
-  initApis,
-  mergeRenovateJson,
-  onboardRepository,
-  getOnboardingStatus,
-  detectPackageFiles,
-  determineRepoUpgrades,
-  groupUpgradesByBranch,
-  updateBranchesSequentially,
-  processRepo,
-};
-
-// Check for .npmrc in repository and pass it to npm api if found
-async function setNpmrc(config) {
-  try {
-    let npmrc = null;
-    const npmrcContent = await config.api.getFileContent('.npmrc');
-    if (npmrcContent) {
-      config.logger.debug('Found .npmrc file in repository');
-      npmrc = ini.parse(npmrcContent);
-    }
-    npmApi.setNpmrc(npmrc);
-  } catch (err) {
-    config.logger.error('Failed to set .npmrc');
-  }
-}
-
-async function initApis(inputConfig) {
-  function getPlatformApi(platform) {
-    if (platform === 'github') {
-      return githubApi;
-    } else if (platform === 'gitlab') {
-      return gitlabApi;
-    }
-    throw new Error(`Unknown platform: ${platform}`);
-  }
-
-  const config = Object.assign({}, inputConfig);
-  config.api = getPlatformApi(config.platform);
-  await config.api.initRepo(
-    config.repository,
-    config.token,
-    config.endpoint,
-    config.logger
-  );
-  // Check for presence of .npmrc in repository
-  await module.exports.setNpmrc(config);
-  return config;
-}
-
-// Check for config in `renovate.json`
-async function mergeRenovateJson(config) {
-  const renovateJson = await config.api.getFileJson('renovate.json');
-  if (!renovateJson) {
-    config.logger.debug('No renovate.json found');
-    return config;
-  }
-  config.logger.debug({ config: renovateJson }, 'renovate.json config');
-  return Object.assign({}, config, renovateJson, { renovateJsonPresent: true });
-}
-
-async function onboardRepository(config) {
-  const defaultConfig = defaultsParser.getOnboardingConfig();
-  let prBody = `Welcome to [Renovate](https://keylocation.sg/our-tech/renovate)! Once you close this Pull Request, we will begin keeping your dependencies up-to-date via automated Pull Requests.
-
-The [Configuration](https://github.com/singapore/renovate/blob/master/docs/configuration.md) and [Configuration FAQ](https://github.com/singapore/renovate/blob/master/docs/faq.md) documents should be helpful.
-
-#### Important!
-
-You do not need to *merge* this Pull Request - renovate will begin even if it's closed *unmerged*.
-In fact, you only need to add a \`renovate.json\` file to your repository if you wish to override any default settings. The file is included as part of this PR only in case you wish to change default settings before you start.
-If the default settings are all suitable for you, simply close this Pull Request unmerged and your first renovation will begin the next time the program is run.`;
-
-  if (config.platform === 'gitlab') {
-    defaultConfig.platform = 'gitlab';
-    prBody = prBody.replace(/Pull Request/g, 'Merge Request');
-  }
-  const defaultConfigString = `${stringify(defaultConfig)}\n`;
-  await config.api.commitFilesToBranch(
-    'renovate/configure',
-    [
-      {
-        name: 'renovate.json',
-        contents: defaultConfigString,
-      },
-    ],
-    'Add renovate.json'
-  );
-  const pr = await config.api.createPr(
-    'renovate/configure',
-    'Configure Renovate',
-    prBody
-  );
-  config.logger.debug(`Created ${pr.displayNumber} for configuration`);
-}
-
-async function getOnboardingStatus(config) {
-  config.logger.debug('Checking if repo is configured');
-  // Check if repository is configured
-  if (config.onboarding === false) {
-    config.logger.debug('Repo onboarding is disabled');
-    return true;
-  }
-  if (config.renovateJsonPresent) {
-    config.logger.debug('Repo onboarded');
-    return true;
-  }
-  const pr = await config.api.findPr(
-    'renovate/configure',
-    'Configure Renovate'
-  );
-  if (pr) {
-    if (pr.isClosed) {
-      config.logger.debug('Found closed Configure Renovate PR');
-      return true;
-    }
-    // PR exists but hasn't been closed yet
-    config.logger.debug(
-      `PR #${pr.displayNumber} needs to be closed to enable renovate to continue`
-    );
-    return false;
-  }
-  await module.exports.onboardRepository(config);
-  return false;
-}
-
-async function detectPackageFiles(config) {
-  config.logger.trace({ config }, 'detectPackageFiles');
-  const packageFiles = await config.api.findFilePaths('package.json');
-  config.logger.debug(`Found ${packageFiles.length} package file(s)`);
-  return Object.assign({}, config, { packageFiles });
-}
-
-async function determineRepoUpgrades(config) {
-  config.logger.trace({ config }, 'determineRepoUpgrades');
-  if (config.packageFiles.length === 0) {
-    config.logger.warn('No package files found');
-  }
-  let upgrades = [];
-  // Iterate through repositories sequentially
-  for (let index = 0; index < config.packageFiles.length; index += 1) {
-    const packageFileConfig = configParser.getPackageFileConfig(config, index);
-    packageFileConfig.logger.info('Renovating package file');
-    upgrades = upgrades.concat(
-      await packageFileWorker.processPackageFile(packageFileConfig)
-    );
-    packageFileConfig.logger.info('Finished repository');
-  }
-  return upgrades;
-}
-
-async function groupUpgradesByBranch(upgrades, logger) {
-  logger.trace({ config: upgrades }, 'groupUpgradesByBranch');
-  logger.info(`Processing ${upgrades.length} dependency upgrade(s)`);
-  const branchUpgrades = {};
-  for (const upg of upgrades) {
-    const upgrade = Object.assign({}, upg);
-    // Check whether to use a group name
-    let branchName;
-    if (upgrade.groupName) {
-      upgrade.groupSlug =
-        upgrade.groupSlug ||
-        upgrade.groupName.toLowerCase().replace(/[^a-z0-9+]+/g, '-');
-      branchName = handlebars.compile(upgrade.groupBranchName)(upgrade);
-      logger.debug(
-        { branchName },
-        `Dependency ${upgrade.depName} is part of group '${upgrade.groupName}'`
-      );
-      if (branchUpgrades[branchName]) {
-        upgrade.commitMessage = upgrade.groupCommitMessage;
-        upgrade.prTitle = upgrade.groupPrTitle;
-        upgrade.prBody = upgrade.groupPrBody;
-      }
-    } else {
-      branchName = handlebars.compile(upgrade.branchName)(upgrade);
-    }
-    branchUpgrades[branchName] = branchUpgrades[branchName] || [];
-    branchUpgrades[branchName] = [upgrade].concat(branchUpgrades[branchName]);
-  }
-  logger.debug(`Returning ${Object.keys(branchUpgrades).length} branch(es)`);
-  return branchUpgrades;
-}
-
-async function updateBranchesSequentially(branchUpgrades, logger) {
-  logger.trace({ config: branchUpgrades }, 'updateBranchesSequentially');
-  logger.debug(`Updating ${Object.keys(branchUpgrades).length} branch(es)`);
-  for (const branchName of Object.keys(branchUpgrades)) {
-    await branchWorker.updateBranch(branchUpgrades[branchName]);
-  }
-}
-
-async function processRepo(packageFileConfig) {
-  let config = Object.assign({}, packageFileConfig);
-  config.logger.trace({ config }, 'processRepo');
-  try {
-    config = await module.exports.initApis(config);
-    config = await module.exports.mergeRenovateJson(config);
-    const repoIsOnboarded = await module.exports.getOnboardingStatus(config);
-    if (!repoIsOnboarded) {
-      config.logger.info('"Configure Renovate" PR needs to be closed first');
-      return;
-    }
-    const hasConfiguredPackageFiles = config.packageFiles.length > 0;
-    if (!hasConfiguredPackageFiles) {
-      config = await module.exports.detectPackageFiles(config);
-    }
-    const allUpgrades = await module.exports.determineRepoUpgrades(config);
-    const branchUpgrades = await module.exports.groupUpgradesByBranch(
-      allUpgrades,
-      config.logger
-    );
-    await updateBranchesSequentially(branchUpgrades, config.logger);
-  } catch (error) {
-    // Swallow this error so that other repositories can be processed
-    config.logger.error(`Failed to process repository: ${error.message}`);
-    config.logger.debug({ error });
-  }
-}
diff --git a/lib/workers/repository/apis.js b/lib/workers/repository/apis.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad4bb61b91256ae991100592f636243dc1bb3c23
--- /dev/null
+++ b/lib/workers/repository/apis.js
@@ -0,0 +1,68 @@
+const ini = require('ini');
+// API
+const githubApi = require('../../api/github');
+const gitlabApi = require('../../api/gitlab');
+const npmApi = require('../../api/npm');
+
+module.exports = {
+  setNpmrc,
+  initApis,
+  mergeRenovateJson,
+  detectPackageFiles,
+};
+
+// Check for .npmrc in repository and pass it to npm api if found
+async function setNpmrc(config) {
+  try {
+    let npmrc = null;
+    const npmrcContent = await config.api.getFileContent('.npmrc');
+    if (npmrcContent) {
+      config.logger.debug('Found .npmrc file in repository');
+      npmrc = ini.parse(npmrcContent);
+    }
+    npmApi.setNpmrc(npmrc);
+  } catch (err) {
+    config.logger.error('Failed to set .npmrc');
+  }
+}
+
+async function initApis(inputConfig) {
+  function getPlatformApi(platform) {
+    if (platform === 'github') {
+      return githubApi;
+    } else if (platform === 'gitlab') {
+      return gitlabApi;
+    }
+    throw new Error(`Unknown platform: ${platform}`);
+  }
+
+  const config = Object.assign({}, inputConfig);
+  config.api = getPlatformApi(config.platform);
+  await config.api.initRepo(
+    config.repository,
+    config.token,
+    config.endpoint,
+    config.logger
+  );
+  // Check for presence of .npmrc in repository
+  await module.exports.setNpmrc(config);
+  return config;
+}
+
+// Check for config in `renovate.json`
+async function mergeRenovateJson(config) {
+  const renovateJson = await config.api.getFileJson('renovate.json');
+  if (!renovateJson) {
+    config.logger.debug('No renovate.json found');
+    return config;
+  }
+  config.logger.debug({ config: renovateJson }, 'renovate.json config');
+  return Object.assign({}, config, renovateJson, { renovateJsonPresent: true });
+}
+
+async function detectPackageFiles(config) {
+  config.logger.trace({ config }, 'detectPackageFiles');
+  const packageFiles = await config.api.findFilePaths('package.json');
+  config.logger.debug(`Found ${packageFiles.length} package file(s)`);
+  return Object.assign({}, config, { packageFiles });
+}
diff --git a/lib/workers/repository/index.js b/lib/workers/repository/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..0d28c43ea13512612d11ad82d7139c7c49953a2b
--- /dev/null
+++ b/lib/workers/repository/index.js
@@ -0,0 +1,47 @@
+// Workers
+const branchWorker = require('../branch');
+// children
+const apis = require('./apis');
+const onboarding = require('./onboarding');
+const upgrades = require('./upgrades');
+
+module.exports = {
+  renovateRepository,
+};
+
+async function renovateRepository(packageFileConfig) {
+  let config = Object.assign({}, packageFileConfig);
+  config.logger.trace({ config }, 'renovateRepository');
+  try {
+    config = await apis.initApis(config);
+    config = await apis.mergeRenovateJson(config);
+    const repoIsOnboarded = await onboarding.getOnboardingStatus(config);
+    if (!repoIsOnboarded) {
+      config.logger.info('"Configure Renovate" PR needs to be closed first');
+      return;
+    }
+    const hasConfiguredPackageFiles = config.packageFiles.length > 0;
+    if (!hasConfiguredPackageFiles) {
+      config = await apis.detectPackageFiles(config);
+    }
+    const allUpgrades = await upgrades.determineRepoUpgrades(config);
+    const branchUpgrades = await upgrades.groupUpgradesByBranch(
+      allUpgrades,
+      config.logger
+    );
+    config.logger.trace(
+      { config: branchUpgrades },
+      'updateBranchesSequentially'
+    );
+    config.logger.debug(
+      `Updating ${Object.keys(branchUpgrades).length} branch(es)`
+    );
+    for (const branchName of Object.keys(branchUpgrades)) {
+      await branchWorker.updateBranch(branchUpgrades[branchName]);
+    }
+  } catch (error) {
+    // Swallow this error so that other repositories can be processed
+    config.logger.error(`Failed to process repository: ${error.message}`);
+    config.logger.debug({ error });
+  }
+}
diff --git a/lib/workers/repository/onboarding.js b/lib/workers/repository/onboarding.js
new file mode 100644
index 0000000000000000000000000000000000000000..591d636e3505ff4c11890422aa488576ae23c2ee
--- /dev/null
+++ b/lib/workers/repository/onboarding.js
@@ -0,0 +1,73 @@
+const stringify = require('json-stringify-pretty-compact');
+
+const defaultsParser = require('../../config/defaults');
+
+module.exports = {
+  onboardRepository,
+  getOnboardingStatus,
+};
+
+async function onboardRepository(config) {
+  const defaultConfig = defaultsParser.getOnboardingConfig();
+  let prBody = `Welcome to [Renovate](https://keylocation.sg/our-tech/renovate)! Once you close this Pull Request, we will begin keeping your dependencies up-to-date via automated Pull Requests.
+
+The [Configuration](https://github.com/singapore/renovate/blob/master/docs/configuration.md) and [Configuration FAQ](https://github.com/singapore/renovate/blob/master/docs/faq.md) documents should be helpful.
+
+#### Important!
+
+You do not need to *merge* this Pull Request - renovate will begin even if it's closed *unmerged*.
+In fact, you only need to add a \`renovate.json\` file to your repository if you wish to override any default settings. The file is included as part of this PR only in case you wish to change default settings before you start.
+If the default settings are all suitable for you, simply close this Pull Request unmerged and your first renovation will begin the next time the program is run.`;
+
+  if (config.platform === 'gitlab') {
+    defaultConfig.platform = 'gitlab';
+    prBody = prBody.replace(/Pull Request/g, 'Merge Request');
+  }
+  const defaultConfigString = `${stringify(defaultConfig)}\n`;
+  await config.api.commitFilesToBranch(
+    'renovate/configure',
+    [
+      {
+        name: 'renovate.json',
+        contents: defaultConfigString,
+      },
+    ],
+    'Add renovate.json'
+  );
+  const pr = await config.api.createPr(
+    'renovate/configure',
+    'Configure Renovate',
+    prBody
+  );
+  config.logger.debug(`Created ${pr.displayNumber} for configuration`);
+}
+
+async function getOnboardingStatus(config) {
+  config.logger.debug('Checking if repo is configured');
+  // Check if repository is configured
+  if (config.onboarding === false) {
+    config.logger.debug('Repo onboarding is disabled');
+    return true;
+  }
+  if (config.renovateJsonPresent) {
+    config.logger.debug('Repo onboarded');
+    return true;
+  }
+  const pr = await config.api.findPr(
+    'renovate/configure',
+    'Configure Renovate'
+  );
+  if (pr) {
+    if (pr.isClosed) {
+      config.logger.debug('Found closed Configure Renovate PR');
+      return true;
+    }
+    // PR exists but hasn't been closed yet
+    config.logger.debug(
+      `PR #${pr.displayNumber} needs to be closed to enable renovate to continue`
+    );
+    return false;
+  }
+  await module.exports.onboardRepository(config);
+  return false;
+}
diff --git a/lib/workers/repository/upgrades.js b/lib/workers/repository/upgrades.js
new file mode 100644
index 0000000000000000000000000000000000000000..e9e52b77882f302008d029ff2dccbae62bc4c271
--- /dev/null
+++ b/lib/workers/repository/upgrades.js
@@ -0,0 +1,58 @@
+const handlebars = require('handlebars');
+const configParser = require('../../config');
+const packageFileWorker = require('../package-file');
+
+module.exports = {
+  determineRepoUpgrades,
+  groupUpgradesByBranch,
+};
+
+async function determineRepoUpgrades(config) {
+  config.logger.trace({ config }, 'determineRepoUpgrades');
+  if (config.packageFiles.length === 0) {
+    config.logger.warn('No package files found');
+  }
+  let upgrades = [];
+  // Iterate through repositories sequentially
+  for (let index = 0; index < config.packageFiles.length; index += 1) {
+    const packageFileConfig = configParser.getPackageFileConfig(config, index);
+    packageFileConfig.logger.info('Renovating package file');
+    upgrades = upgrades.concat(
+      await packageFileWorker.processPackageFile(packageFileConfig)
+    );
+    packageFileConfig.logger.info('Finished repository');
+  }
+  return upgrades;
+}
+
+async function groupUpgradesByBranch(upgrades, logger) {
+  logger.trace({ config: upgrades }, 'groupUpgradesByBranch');
+  logger.info(`Processing ${upgrades.length} dependency upgrade(s)`);
+  const branchUpgrades = {};
+  for (const upg of upgrades) {
+    const upgrade = Object.assign({}, upg);
+    // Check whether to use a group name
+    let branchName;
+    if (upgrade.groupName) {
+      upgrade.groupSlug =
+        upgrade.groupSlug ||
+        upgrade.groupName.toLowerCase().replace(/[^a-z0-9+]+/g, '-');
+      branchName = handlebars.compile(upgrade.groupBranchName)(upgrade);
+      logger.debug(
+        { branchName },
+        `Dependency ${upgrade.depName} is part of group '${upgrade.groupName}'`
+      );
+      if (branchUpgrades[branchName]) {
+        upgrade.commitMessage = upgrade.groupCommitMessage;
+        upgrade.prTitle = upgrade.groupPrTitle;
+        upgrade.prBody = upgrade.groupPrBody;
+      }
+    } else {
+      branchName = handlebars.compile(upgrade.branchName)(upgrade);
+    }
+    branchUpgrades[branchName] = branchUpgrades[branchName] || [];
+    branchUpgrades[branchName] = [upgrade].concat(branchUpgrades[branchName]);
+  }
+  logger.debug(`Returning ${Object.keys(branchUpgrades).length} branch(es)`);
+  return branchUpgrades;
+}
diff --git a/test/config/index.spec.js b/test/config/index.spec.js
index d4e185952749f52073c023ceb789d21f224f5017..3efd7b517719b9bf5398bd53a9961c1da31ead57 100644
--- a/test/config/index.spec.js
+++ b/test/config/index.spec.js
@@ -178,7 +178,7 @@ describe('config/index', () => {
       ],
     };
     it('massages string repos', () => {
-      const res = configParser.getRepoConfig(config, 0);
+      const res = configParser.getRepositoryConfig(config, 0);
       expect(res.githubAppKey).not.toBeDefined();
       expect(res.maintainYarnLock).toBeDefined();
       expect(res.logger).toBeDefined();
@@ -186,7 +186,7 @@ describe('config/index', () => {
       expect(res).toMatchSnapshot();
     });
     it('handles object repos', () => {
-      const res = configParser.getRepoConfig(config, 1);
+      const res = configParser.getRepositoryConfig(config, 1);
       expect(res.githubAppKey).not.toBeDefined();
       expect(res.maintainYarnLock).toBeDefined();
       expect(res.logger).toBeDefined();
diff --git a/test/workers/__snapshots__/repository-functions.spec.js.snap b/test/workers/__snapshots__/repository-functions.spec.js.snap
deleted file mode 100644
index 4ac3130f129f5566009bc624ac4f826f9b8be154..0000000000000000000000000000000000000000
--- a/test/workers/__snapshots__/repository-functions.spec.js.snap
+++ /dev/null
@@ -1,127 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`workers/repository detectPackageFiles(config) adds package files to object 1`] = `
-Array [
-  "package.json",
-  "backend/package.json",
-]
-`;
-
-exports[`workers/repository groupUpgradesByBranch(upgrades, logger) does not group if different compiled branch names 1`] = `
-Object {
-  "bar-1.1.0": Array [
-    Object {
-      "branchName": "bar-{{version}}",
-      "version": "1.1.0",
-    },
-  ],
-  "foo-1.1.0": Array [
-    Object {
-      "branchName": "foo-{{version}}",
-      "version": "1.1.0",
-    },
-  ],
-  "foo-2.0.0": Array [
-    Object {
-      "branchName": "foo-{{version}}",
-      "version": "2.0.0",
-    },
-  ],
-}
-`;
-
-exports[`workers/repository groupUpgradesByBranch(upgrades, logger) groups if same compiled branch names 1`] = `
-Object {
-  "bar-1.1.0": Array [
-    Object {
-      "branchName": "bar-{{version}}",
-      "version": "1.1.0",
-    },
-  ],
-  "foo": Array [
-    Object {
-      "branchName": "foo",
-      "version": "2.0.0",
-    },
-    Object {
-      "branchName": "foo",
-      "version": "1.1.0",
-    },
-  ],
-}
-`;
-
-exports[`workers/repository groupUpgradesByBranch(upgrades, logger) groups if same compiled group name 1`] = `
-Object {
-  "foo": Array [
-    Object {
-      "branchName": "foo",
-      "version": "2.0.0",
-    },
-  ],
-  "renovate/my-group": Array [
-    Object {
-      "branchName": "bar-{{version}}",
-      "commitMessage": undefined,
-      "groupBranchName": "renovate/my-group",
-      "groupName": "My Group",
-      "groupSlug": "my-group",
-      "prBody": undefined,
-      "prTitle": undefined,
-      "version": "1.1.0",
-    },
-    Object {
-      "branchName": "foo",
-      "groupBranchName": "renovate/{{groupSlug}}",
-      "groupName": "My Group",
-      "groupSlug": "my-group",
-      "version": "1.1.0",
-    },
-  ],
-}
-`;
-
-exports[`workers/repository groupUpgradesByBranch(upgrades, logger) returns one branch if one input 1`] = `
-Object {
-  "foo-1.1.0": Array [
-    Object {
-      "branchName": "foo-{{version}}",
-      "version": "1.1.0",
-    },
-  ],
-}
-`;
-
-exports[`workers/repository initApis(config) throws if unknown platform 1`] = `"Unknown platform: foo"`;
-
-exports[`workers/repository onboardRepository(config) should commit files and create PR 1`] = `
-Array [
-  Array [
-    "renovate/configure",
-    Array [
-      Object {
-        "contents": "{
-  \\"enabled\\": true,
-  \\"packageFiles\\": [],
-  \\"depTypes\\": [\\"dependencies\\", \\"devDependencies\\", \\"optionalDependencies\\"],
-  \\"pinVersions\\": true,
-  \\"separateMajorReleases\\": true,
-  \\"ignoreDeps\\": [],
-  \\"rebaseStalePrs\\": false,
-  \\"prCreation\\": \\"immediate\\",
-  \\"automerge\\": \\"none\\",
-  \\"branchName\\": \\"renovate/{{depName}}-{{newVersionMajor}}.x\\",
-  \\"commitMessage\\": \\"Update dependency {{depName}} to version {{newVersion}}\\",
-  \\"maintainYarnLock\\": false,
-  \\"labels\\": [],
-  \\"assignees\\": [],
-  \\"reviewers\\": []
-}
-",
-        "name": "renovate.json",
-      },
-    ],
-    "Add renovate.json",
-  ],
-]
-`;
diff --git a/test/workers/global.spec.js b/test/workers/global.spec.js
index a4eb2fc8b18fc8eb8632d564a48e6d046eb59feb..5bb78253c1cc8bfad592eb8f1ccca987a8959310 100644
--- a/test/workers/global.spec.js
+++ b/test/workers/global.spec.js
@@ -7,8 +7,8 @@ describe('lib/workers/global', () => {
   beforeEach(() => {
     jest.resetAllMocks();
     configParser.parseConfigs = jest.fn();
-    configParser.getRepoConfig = jest.fn();
-    repositoryWorker.processRepo = jest.fn();
+    configParser.getRepositoryConfig = jest.fn();
+    repositoryWorker.renovateRepository = jest.fn();
   });
   it('handles zero repos', async () => {
     configParser.parseConfigs.mockReturnValueOnce({
@@ -21,14 +21,14 @@ describe('lib/workers/global', () => {
       foo: 1,
       repositories: ['a', 'b'],
     });
-    configParser.getRepoConfig.mockReturnValue({
+    configParser.getRepositoryConfig.mockReturnValue({
       repository: 'foo',
       logger,
     });
     await globalWorker.start();
     expect(configParser.parseConfigs.mock.calls.length).toBe(1);
-    expect(configParser.getRepoConfig.mock.calls).toMatchSnapshot();
-    expect(repositoryWorker.processRepo.mock.calls.length).toBe(2);
+    expect(configParser.getRepositoryConfig.mock.calls).toMatchSnapshot();
+    expect(repositoryWorker.renovateRepository.mock.calls.length).toBe(2);
   });
   it('catches errors', async () => {
     configParser.parseConfigs.mockImplementationOnce(() => {
diff --git a/test/workers/repository-functions.spec.js b/test/workers/repository-functions.spec.js
deleted file mode 100644
index 6bf7d95d98de80b48635391f10b8920c0748ac5d..0000000000000000000000000000000000000000
--- a/test/workers/repository-functions.spec.js
+++ /dev/null
@@ -1,344 +0,0 @@
-const repositoryWorker = require('../../lib/workers/repository');
-const packageFileWorker = require('../../lib/workers/package-file');
-const branchWorker = require('../../lib/workers/branch');
-const logger = require('../_fixtures/logger');
-
-const githubApi = require('../../lib/api/github');
-const gitlabApi = require('../../lib/api/gitlab');
-const npmApi = require('../../lib/api/npm');
-
-jest.mock('../../lib/api/github');
-jest.mock('../../lib/api/gitlab');
-jest.mock('../../lib/api/npm');
-jest.mock('../../lib/workers/branch');
-jest.mock('../../lib/workers/package-file');
-
-describe('workers/repository', () => {
-  describe('setNpmrc(config)', () => {
-    it('Skips if npmrc not found', async () => {
-      const config = {
-        api: {
-          getFileContent: jest.fn(),
-        },
-      };
-      await repositoryWorker.setNpmrc(config);
-    });
-    it('Parses if npmrc found', async () => {
-      const config = {
-        api: {
-          getFileContent: jest.fn(() => 'a = b'),
-        },
-        logger,
-      };
-      await repositoryWorker.setNpmrc(config);
-    });
-    it('Catches errors', async () => {
-      const config = {
-        api: {
-          getFileContent: jest.fn(() => {
-            throw new Error('file error');
-          }),
-        },
-        logger,
-      };
-      await repositoryWorker.setNpmrc(config);
-    });
-  });
-  describe('initApis(config)', () => {
-    beforeEach(() => {
-      jest.resetAllMocks();
-    });
-    it('returns github api', async () => {
-      const config = { platform: 'github' };
-      const res = await repositoryWorker.initApis(config);
-      expect(res.platform).toEqual('github');
-      expect(githubApi.initRepo.mock.calls.length).toBe(1);
-      expect(gitlabApi.initRepo.mock.calls.length).toBe(0);
-      expect(npmApi.setNpmrc.mock.calls.length).toBe(1);
-    });
-    it('returns gitlab api', async () => {
-      const config = { platform: 'gitlab' };
-      const res = await repositoryWorker.initApis(config);
-      expect(res.platform).toEqual('gitlab');
-      expect(githubApi.initRepo.mock.calls.length).toBe(0);
-      expect(gitlabApi.initRepo.mock.calls.length).toBe(1);
-      expect(npmApi.setNpmrc.mock.calls.length).toBe(1);
-    });
-    it('throws if unknown platform', async () => {
-      const config = { platform: 'foo' };
-      let e;
-      try {
-        await repositoryWorker.initApis(config);
-      } catch (err) {
-        e = err;
-      }
-      expect(e.message).toMatchSnapshot();
-      expect(githubApi.initRepo.mock.calls.length).toBe(0);
-      expect(gitlabApi.initRepo.mock.calls.length).toBe(0);
-      expect(npmApi.setNpmrc.mock.calls.length).toBe(0);
-    });
-  });
-  describe('mergeRenovateJson(config)', () => {
-    let config;
-    beforeEach(() => {
-      config = {
-        api: {
-          getFileJson: jest.fn(),
-        },
-        logger,
-      };
-    });
-    it('returns same config if no renovate.json found', async () => {
-      expect(await repositoryWorker.mergeRenovateJson(config)).toEqual(config);
-    });
-    it('returns extended config if renovate.json found', async () => {
-      config.api.getFileJson.mockReturnValueOnce({ foo: 1 });
-      const returnConfig = await repositoryWorker.mergeRenovateJson(config);
-      expect(returnConfig.foo).toBe(1);
-      expect(returnConfig.renovateJsonPresent).toBe(true);
-    });
-  });
-  describe('onboardRepository(config)', () => {
-    let config;
-    beforeEach(() => {
-      config = {
-        api: {
-          commitFilesToBranch: jest.fn(),
-          createPr: jest.fn(() => ({ displayNumber: 1 })),
-        },
-        logger,
-      };
-    });
-    it('should commit files and create PR', async () => {
-      config.platform = 'github';
-      await repositoryWorker.onboardRepository(config);
-      expect(config.api.commitFilesToBranch.mock.calls.length).toBe(1);
-      expect(config.api.createPr.mock.calls.length).toBe(1);
-      expect(
-        config.api.createPr.mock.calls[0][2].indexOf('Pull Request')
-      ).not.toBe(-1);
-      expect(
-        config.api.createPr.mock.calls[0][2].indexOf('Merge Request')
-      ).toBe(-1);
-      expect(config.api.commitFilesToBranch.mock.calls).toMatchSnapshot();
-    });
-    it('should adapt for gitlab phrasing', async () => {
-      config.platform = 'gitlab';
-      await repositoryWorker.onboardRepository(config);
-      expect(config.api.createPr.mock.calls[0][2].indexOf('Pull Request')).toBe(
-        -1
-      );
-      expect(
-        config.api.createPr.mock.calls[0][2].indexOf('Merge Request')
-      ).not.toBe(-1);
-    });
-  });
-  describe('getOnboardingStatus(config)', () => {
-    let config;
-    beforeEach(() => {
-      config = {
-        api: {
-          commitFilesToBranch: jest.fn(),
-          createPr: jest.fn(() => ({ displayNumber: 1 })),
-          findPr: jest.fn(),
-        },
-        logger,
-      };
-    });
-    it('returns true if onboarding is false', async () => {
-      config.onboarding = false;
-      const res = await repositoryWorker.getOnboardingStatus(config);
-      expect(res).toEqual(true);
-      expect(config.api.findPr.mock.calls.length).toBe(0);
-    });
-    it('returns complete if renovate onboarded', async () => {
-      config.renovateJsonPresent = true;
-      const res = await repositoryWorker.getOnboardingStatus(config);
-      expect(res).toEqual(true);
-      expect(config.api.findPr.mock.calls.length).toBe(0);
-    });
-    it('returns complete if pr and pr is closed', async () => {
-      config.api.findPr.mockReturnValueOnce({ isClosed: true });
-      const res = await repositoryWorker.getOnboardingStatus(config);
-      expect(res).toEqual(true);
-      expect(config.api.findPr.mock.calls.length).toBe(1);
-    });
-    it('returns in progres if pr and pr is not closed', async () => {
-      config.api.findPr.mockReturnValueOnce({});
-      const res = await repositoryWorker.getOnboardingStatus(config);
-      expect(res).toEqual(false);
-      expect(config.api.findPr.mock.calls.length).toBe(1);
-    });
-    it('returns none if no pr', async () => {
-      const res = await repositoryWorker.getOnboardingStatus(config);
-      expect(res).toEqual(false);
-      expect(config.api.findPr.mock.calls.length).toBe(1);
-    });
-  });
-  describe('detectPackageFiles(config)', () => {
-    it('adds package files to object', async () => {
-      const config = {
-        api: {
-          findFilePaths: jest.fn(() => [
-            'package.json',
-            'backend/package.json',
-          ]),
-        },
-        logger,
-      };
-      const res = await repositoryWorker.detectPackageFiles(config);
-      expect(res).toMatchObject(config);
-      expect(res.packageFiles).toMatchSnapshot();
-    });
-  });
-  describe('determineRepoUpgrades(config)', () => {
-    let config;
-    beforeEach(() => {
-      config = {
-        logger,
-      };
-    });
-    it('returns empty array if no packageFiles', async () => {
-      config.packageFiles = [];
-      const upgrades = await repositoryWorker.determineRepoUpgrades(config);
-      expect(upgrades.length).toBe(0);
-    });
-    it('returns empty array if none found', async () => {
-      config.packageFiles = [
-        'package.json',
-        {
-          packageFile: 'backend/package.json',
-        },
-      ];
-      packageFileWorker.processPackageFile.mockReturnValue([]);
-      const upgrades = await repositoryWorker.determineRepoUpgrades(config);
-      expect(upgrades.length).toBe(0);
-    });
-    it('returns array if upgrades found', async () => {
-      config.packageFiles = [
-        'package.json',
-        {
-          packageFile: 'backend/package.json',
-        },
-        {
-          fileName: 'frontend/package.json',
-        },
-      ];
-      packageFileWorker.processPackageFile.mockReturnValueOnce(['a']);
-      packageFileWorker.processPackageFile.mockReturnValueOnce(['b', 'c']);
-      const upgrades = await repositoryWorker.determineRepoUpgrades(config);
-      expect(upgrades.length).toBe(3);
-    });
-  });
-  describe('groupUpgradesByBranch(upgrades, logger)', () => {
-    it('returns empty object if no input array', async () => {
-      const res = await repositoryWorker.groupUpgradesByBranch([], logger);
-      expect(res).toEqual({});
-    });
-    it('returns one branch if one input', async () => {
-      const upgrades = [
-        {
-          branchName: 'foo-{{version}}',
-          version: '1.1.0',
-        },
-      ];
-      const res = await repositoryWorker.groupUpgradesByBranch(
-        upgrades,
-        logger
-      );
-      expect(Object.keys(res).length).toBe(1);
-      expect(res).toMatchSnapshot();
-    });
-    it('does not group if different compiled branch names', async () => {
-      const upgrades = [
-        {
-          branchName: 'foo-{{version}}',
-          version: '1.1.0',
-        },
-        {
-          branchName: 'foo-{{version}}',
-          version: '2.0.0',
-        },
-        {
-          branchName: 'bar-{{version}}',
-          version: '1.1.0',
-        },
-      ];
-      const res = await repositoryWorker.groupUpgradesByBranch(
-        upgrades,
-        logger
-      );
-      expect(Object.keys(res).length).toBe(3);
-      expect(res).toMatchSnapshot();
-    });
-    it('groups if same compiled branch names', async () => {
-      const upgrades = [
-        {
-          branchName: 'foo',
-          version: '1.1.0',
-        },
-        {
-          branchName: 'foo',
-          version: '2.0.0',
-        },
-        {
-          branchName: 'bar-{{version}}',
-          version: '1.1.0',
-        },
-      ];
-      const res = await repositoryWorker.groupUpgradesByBranch(
-        upgrades,
-        logger
-      );
-      expect(Object.keys(res).length).toBe(2);
-      expect(res).toMatchSnapshot();
-    });
-    it('groups if same compiled group name', async () => {
-      const upgrades = [
-        {
-          branchName: 'foo',
-          version: '1.1.0',
-          groupName: 'My Group',
-          groupBranchName: 'renovate/{{groupSlug}}',
-        },
-        {
-          branchName: 'foo',
-          version: '2.0.0',
-        },
-        {
-          branchName: 'bar-{{version}}',
-          version: '1.1.0',
-          groupName: 'My Group',
-          groupBranchName: 'renovate/my-group',
-        },
-      ];
-      const res = await repositoryWorker.groupUpgradesByBranch(
-        upgrades,
-        logger
-      );
-      expect(Object.keys(res).length).toBe(2);
-      expect(res).toMatchSnapshot();
-    });
-  });
-  describe('updateBranchesSequentially(branchUpgrades, logger)', () => {
-    beforeEach(() => {
-      jest.resetAllMocks();
-    });
-    it('handles empty case', async () => {
-      await repositoryWorker.updateBranchesSequentially({}, logger);
-      expect(branchWorker.updateBranch.mock.calls.length).toBe(0);
-    });
-    it('updates branches', async () => {
-      const branchUpgrades = {
-        foo: {},
-        bar: {},
-        baz: {},
-      };
-      await repositoryWorker.updateBranchesSequentially(branchUpgrades, logger);
-      expect(branchWorker.updateBranch.mock.calls.length).toBe(3);
-    });
-  });
-  describe('processRepo(repoConfig)', () => {
-    // TODO
-  });
-});
diff --git a/test/workers/repository.spec.js b/test/workers/repository.spec.js
deleted file mode 100644
index b79da639bb29be68107719c703e12834007a5a77..0000000000000000000000000000000000000000
--- a/test/workers/repository.spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const repositoryWorker = require('../../lib/workers/repository');
-const logger = require('../_fixtures/logger');
-
-repositoryWorker.initApis = jest.fn(input => input);
-repositoryWorker.mergeRenovateJson = jest.fn(input => input);
-repositoryWorker.getOnboardingStatus = jest.fn(() => true);
-repositoryWorker.detectPackageFiles = jest.fn(input => input);
-
-describe('workers/repository', () => {
-  describe('processRepo', () => {
-    let config;
-    beforeEach(() => {
-      config = {
-        logger,
-        packageFiles: [],
-      };
-    });
-    it('returns early if repo is not onboarded', async () => {
-      repositoryWorker.getOnboardingStatus.mockReturnValueOnce(false);
-      await repositoryWorker.processRepo(config);
-    });
-    it('detects package files if none configured', async () => {
-      repositoryWorker.getOnboardingStatus.mockReturnValueOnce(true);
-      await repositoryWorker.processRepo(config);
-    });
-    it('swallows errors', async () => {
-      repositoryWorker.initApis.mockImplementationOnce(() => {
-        throw new Error('bad init');
-      });
-      await repositoryWorker.processRepo(config);
-    });
-  });
-});
diff --git a/test/workers/repository/__snapshots__/apis.spec.js.snap b/test/workers/repository/__snapshots__/apis.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..3092fd6671a37ab142e4f5f6b13673c66135a67b
--- /dev/null
+++ b/test/workers/repository/__snapshots__/apis.spec.js.snap
@@ -0,0 +1,10 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`workers/repository/apis detectPackageFiles(config) adds package files to object 1`] = `
+Array [
+  "package.json",
+  "backend/package.json",
+]
+`;
+
+exports[`workers/repository/apis initApis(config) throws if unknown platform 1`] = `"Unknown platform: foo"`;
diff --git a/test/workers/repository/__snapshots__/onboarding.spec.js.snap b/test/workers/repository/__snapshots__/onboarding.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..fd5c58b180779777a25fea7e3774fb9a85e795f3
--- /dev/null
+++ b/test/workers/repository/__snapshots__/onboarding.spec.js.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`lib/workers/repository/onboarding onboardRepository(config) should commit files and create PR 1`] = `
+Array [
+  Array [
+    "renovate/configure",
+    Array [
+      Object {
+        "contents": "{
+  \\"enabled\\": true,
+  \\"packageFiles\\": [],
+  \\"depTypes\\": [\\"dependencies\\", \\"devDependencies\\", \\"optionalDependencies\\"],
+  \\"pinVersions\\": true,
+  \\"separateMajorReleases\\": true,
+  \\"ignoreDeps\\": [],
+  \\"rebaseStalePrs\\": false,
+  \\"prCreation\\": \\"immediate\\",
+  \\"automerge\\": \\"none\\",
+  \\"branchName\\": \\"renovate/{{depName}}-{{newVersionMajor}}.x\\",
+  \\"commitMessage\\": \\"Update dependency {{depName}} to version {{newVersion}}\\",
+  \\"maintainYarnLock\\": false,
+  \\"labels\\": [],
+  \\"assignees\\": [],
+  \\"reviewers\\": []
+}
+",
+        "name": "renovate.json",
+      },
+    ],
+    "Add renovate.json",
+  ],
+]
+`;
diff --git a/test/workers/repository/__snapshots__/upgrades.spec.js.snap b/test/workers/repository/__snapshots__/upgrades.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..06103cebda13e58885229f3033f8604860cb6f3d
--- /dev/null
+++ b/test/workers/repository/__snapshots__/upgrades.spec.js.snap
@@ -0,0 +1,86 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`workers/repository/upgrades groupUpgradesByBranch(upgrades, logger) does not group if different compiled branch names 1`] = `
+Object {
+  "bar-1.1.0": Array [
+    Object {
+      "branchName": "bar-{{version}}",
+      "version": "1.1.0",
+    },
+  ],
+  "foo-1.1.0": Array [
+    Object {
+      "branchName": "foo-{{version}}",
+      "version": "1.1.0",
+    },
+  ],
+  "foo-2.0.0": Array [
+    Object {
+      "branchName": "foo-{{version}}",
+      "version": "2.0.0",
+    },
+  ],
+}
+`;
+
+exports[`workers/repository/upgrades groupUpgradesByBranch(upgrades, logger) groups if same compiled branch names 1`] = `
+Object {
+  "bar-1.1.0": Array [
+    Object {
+      "branchName": "bar-{{version}}",
+      "version": "1.1.0",
+    },
+  ],
+  "foo": Array [
+    Object {
+      "branchName": "foo",
+      "version": "2.0.0",
+    },
+    Object {
+      "branchName": "foo",
+      "version": "1.1.0",
+    },
+  ],
+}
+`;
+
+exports[`workers/repository/upgrades groupUpgradesByBranch(upgrades, logger) groups if same compiled group name 1`] = `
+Object {
+  "foo": Array [
+    Object {
+      "branchName": "foo",
+      "version": "2.0.0",
+    },
+  ],
+  "renovate/my-group": Array [
+    Object {
+      "branchName": "bar-{{version}}",
+      "commitMessage": undefined,
+      "groupBranchName": "renovate/my-group",
+      "groupName": "My Group",
+      "groupSlug": "my-group",
+      "prBody": undefined,
+      "prTitle": undefined,
+      "version": "1.1.0",
+    },
+    Object {
+      "branchName": "foo",
+      "groupBranchName": "renovate/{{groupSlug}}",
+      "groupName": "My Group",
+      "groupSlug": "my-group",
+      "version": "1.1.0",
+    },
+  ],
+}
+`;
+
+exports[`workers/repository/upgrades groupUpgradesByBranch(upgrades, logger) returns one branch if one input 1`] = `
+Object {
+  "foo-1.1.0": Array [
+    Object {
+      "branchName": "foo-{{version}}",
+      "version": "1.1.0",
+    },
+  ],
+}
+`;
diff --git a/test/workers/repository/apis.spec.js b/test/workers/repository/apis.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..299d3cbd8d17a2c3b4e535a1aff4b976dcd5e44c
--- /dev/null
+++ b/test/workers/repository/apis.spec.js
@@ -0,0 +1,113 @@
+const apis = require('../../../lib/workers/repository/apis');
+const logger = require('../../_fixtures/logger');
+
+const githubApi = require('../../../lib/api/github');
+const gitlabApi = require('../../../lib/api/gitlab');
+const npmApi = require('../../../lib/api/npm');
+
+jest.mock('../../../lib/api/github');
+jest.mock('../../../lib/api/gitlab');
+jest.mock('../../../lib/api/npm');
+
+describe('workers/repository/apis', () => {
+  describe('setNpmrc(config)', () => {
+    it('Skips if npmrc not found', async () => {
+      const config = {
+        api: {
+          getFileContent: jest.fn(),
+        },
+      };
+      await apis.setNpmrc(config);
+    });
+    it('Parses if npmrc found', async () => {
+      const config = {
+        api: {
+          getFileContent: jest.fn(() => 'a = b'),
+        },
+        logger,
+      };
+      await apis.setNpmrc(config);
+    });
+    it('Catches errors', async () => {
+      const config = {
+        api: {
+          getFileContent: jest.fn(() => {
+            throw new Error('file error');
+          }),
+        },
+        logger,
+      };
+      await apis.setNpmrc(config);
+    });
+  });
+  describe('initApis(config)', () => {
+    beforeEach(() => {
+      jest.resetAllMocks();
+    });
+    it('returns github api', async () => {
+      const config = { platform: 'github' };
+      const res = await apis.initApis(config);
+      expect(res.platform).toEqual('github');
+      expect(githubApi.initRepo.mock.calls.length).toBe(1);
+      expect(gitlabApi.initRepo.mock.calls.length).toBe(0);
+      expect(npmApi.setNpmrc.mock.calls.length).toBe(1);
+    });
+    it('returns gitlab api', async () => {
+      const config = { platform: 'gitlab' };
+      const res = await apis.initApis(config);
+      expect(res.platform).toEqual('gitlab');
+      expect(githubApi.initRepo.mock.calls.length).toBe(0);
+      expect(gitlabApi.initRepo.mock.calls.length).toBe(1);
+      expect(npmApi.setNpmrc.mock.calls.length).toBe(1);
+    });
+    it('throws if unknown platform', async () => {
+      const config = { platform: 'foo' };
+      let e;
+      try {
+        await apis.initApis(config);
+      } catch (err) {
+        e = err;
+      }
+      expect(e.message).toMatchSnapshot();
+      expect(githubApi.initRepo.mock.calls.length).toBe(0);
+      expect(gitlabApi.initRepo.mock.calls.length).toBe(0);
+      expect(npmApi.setNpmrc.mock.calls.length).toBe(0);
+    });
+  });
+  describe('mergeRenovateJson(config)', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        api: {
+          getFileJson: jest.fn(),
+        },
+        logger,
+      };
+    });
+    it('returns same config if no renovate.json found', async () => {
+      expect(await apis.mergeRenovateJson(config)).toEqual(config);
+    });
+    it('returns extended config if renovate.json found', async () => {
+      config.api.getFileJson.mockReturnValueOnce({ foo: 1 });
+      const returnConfig = await apis.mergeRenovateJson(config);
+      expect(returnConfig.foo).toBe(1);
+      expect(returnConfig.renovateJsonPresent).toBe(true);
+    });
+  });
+  describe('detectPackageFiles(config)', () => {
+    it('adds package files to object', async () => {
+      const config = {
+        api: {
+          findFilePaths: jest.fn(() => [
+            'package.json',
+            'backend/package.json',
+          ]),
+        },
+        logger,
+      };
+      const res = await apis.detectPackageFiles(config);
+      expect(res).toMatchObject(config);
+      expect(res.packageFiles).toMatchSnapshot();
+    });
+  });
+});
diff --git a/test/workers/repository/index.spec.js b/test/workers/repository/index.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d7103e6ff821612141462aebe5a8223ff31e3eb
--- /dev/null
+++ b/test/workers/repository/index.spec.js
@@ -0,0 +1,52 @@
+const repositoryWorker = require('../../../lib/workers/repository/index');
+const branchWorker = require('../../../lib/workers/branch');
+
+const apis = require('../../../lib/workers/repository/apis');
+const onboarding = require('../../../lib/workers/repository/onboarding');
+const upgrades = require('../../../lib/workers/repository/upgrades');
+
+const logger = require('../../_fixtures/logger');
+
+jest.mock('../../../lib/workers/repository/onboarding');
+jest.mock('../../../lib/workers/repository/upgrades');
+jest.mock('../../../lib/workers/branch');
+
+apis.initApis = jest.fn(input => input);
+apis.mergeRenovateJson = jest.fn(input => input);
+apis.detectPackageFiles = jest.fn(input => input);
+
+describe('workers/repository', () => {
+  describe('renovateRepository', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        logger,
+        packageFiles: [],
+      };
+    });
+    it('returns early if repo is not onboarded', async () => {
+      onboarding.getOnboardingStatus.mockReturnValueOnce(false);
+      await repositoryWorker.renovateRepository(config);
+    });
+    it('detects package files if none configured', async () => {
+      onboarding.getOnboardingStatus.mockReturnValueOnce(true);
+      await repositoryWorker.renovateRepository(config);
+    });
+    it('calls branchWorker', async () => {
+      onboarding.getOnboardingStatus.mockReturnValueOnce(true);
+      upgrades.groupUpgradesByBranch.mockReturnValueOnce({
+        foo: {},
+        bar: {},
+        baz: {},
+      });
+      await repositoryWorker.renovateRepository(config);
+      expect(branchWorker.updateBranch.mock.calls.length).toBe(3);
+    });
+    it('swallows errors', async () => {
+      apis.initApis.mockImplementationOnce(() => {
+        throw new Error('bad init');
+      });
+      await repositoryWorker.renovateRepository(config);
+    });
+  });
+});
diff --git a/test/workers/repository/onboarding.spec.js b/test/workers/repository/onboarding.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..d11814fda22c71b61616a2b73e0b02ad2c991957
--- /dev/null
+++ b/test/workers/repository/onboarding.spec.js
@@ -0,0 +1,82 @@
+const onboarding = require('../../../lib/workers/repository/onboarding');
+const logger = require('../../_fixtures/logger');
+
+describe('lib/workers/repository/onboarding', () => {
+  describe('onboardRepository(config)', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        api: {
+          commitFilesToBranch: jest.fn(),
+          createPr: jest.fn(() => ({ displayNumber: 1 })),
+        },
+        logger,
+      };
+    });
+    it('should commit files and create PR', async () => {
+      config.platform = 'github';
+      await onboarding.onboardRepository(config);
+      expect(config.api.commitFilesToBranch.mock.calls.length).toBe(1);
+      expect(config.api.createPr.mock.calls.length).toBe(1);
+      expect(
+        config.api.createPr.mock.calls[0][2].indexOf('Pull Request')
+      ).not.toBe(-1);
+      expect(
+        config.api.createPr.mock.calls[0][2].indexOf('Merge Request')
+      ).toBe(-1);
+      expect(config.api.commitFilesToBranch.mock.calls).toMatchSnapshot();
+    });
+    it('should adapt for gitlab phrasing', async () => {
+      config.platform = 'gitlab';
+      await onboarding.onboardRepository(config);
+      expect(config.api.createPr.mock.calls[0][2].indexOf('Pull Request')).toBe(
+        -1
+      );
+      expect(
+        config.api.createPr.mock.calls[0][2].indexOf('Merge Request')
+      ).not.toBe(-1);
+    });
+  });
+  describe('getOnboardingStatus(config)', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        api: {
+          commitFilesToBranch: jest.fn(),
+          createPr: jest.fn(() => ({ displayNumber: 1 })),
+          findPr: jest.fn(),
+        },
+        logger,
+      };
+    });
+    it('returns true if onboarding is false', async () => {
+      config.onboarding = false;
+      const res = await onboarding.getOnboardingStatus(config);
+      expect(res).toEqual(true);
+      expect(config.api.findPr.mock.calls.length).toBe(0);
+    });
+    it('returns complete if renovate onboarded', async () => {
+      config.renovateJsonPresent = true;
+      const res = await onboarding.getOnboardingStatus(config);
+      expect(res).toEqual(true);
+      expect(config.api.findPr.mock.calls.length).toBe(0);
+    });
+    it('returns complete if pr and pr is closed', async () => {
+      config.api.findPr.mockReturnValueOnce({ isClosed: true });
+      const res = await onboarding.getOnboardingStatus(config);
+      expect(res).toEqual(true);
+      expect(config.api.findPr.mock.calls.length).toBe(1);
+    });
+    it('returns in progres if pr and pr is not closed', async () => {
+      config.api.findPr.mockReturnValueOnce({});
+      const res = await onboarding.getOnboardingStatus(config);
+      expect(res).toEqual(false);
+      expect(config.api.findPr.mock.calls.length).toBe(1);
+    });
+    it('returns none if no pr', async () => {
+      const res = await onboarding.getOnboardingStatus(config);
+      expect(res).toEqual(false);
+      expect(config.api.findPr.mock.calls.length).toBe(1);
+    });
+  });
+});
diff --git a/test/workers/repository/upgrades.spec.js b/test/workers/repository/upgrades.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..02d338eb7c4d2a327cc5d6e32ef1f639c0b0bf25
--- /dev/null
+++ b/test/workers/repository/upgrades.spec.js
@@ -0,0 +1,125 @@
+const upgrades = require('../../../lib/workers/repository/upgrades');
+const packageFileWorker = require('../../../lib/workers/package-file');
+const logger = require('../../_fixtures/logger');
+
+jest.mock('../../../lib/workers/package-file');
+
+describe('workers/repository/upgrades', () => {
+  describe('determineRepoUpgrades(config)', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        logger,
+      };
+    });
+    it('returns empty array if no packageFiles', async () => {
+      config.packageFiles = [];
+      const res = await upgrades.determineRepoUpgrades(config);
+      expect(res.length).toBe(0);
+    });
+    it('returns empty array if none found', async () => {
+      config.packageFiles = [
+        'package.json',
+        {
+          packageFile: 'backend/package.json',
+        },
+      ];
+      packageFileWorker.processPackageFile.mockReturnValue([]);
+      const res = await upgrades.determineRepoUpgrades(config);
+      expect(res.length).toBe(0);
+    });
+    it('returns array if upgrades found', async () => {
+      config.packageFiles = [
+        'package.json',
+        {
+          packageFile: 'backend/package.json',
+        },
+        {
+          fileName: 'frontend/package.json',
+        },
+      ];
+      packageFileWorker.processPackageFile.mockReturnValueOnce(['a']);
+      packageFileWorker.processPackageFile.mockReturnValueOnce(['b', 'c']);
+      const res = await upgrades.determineRepoUpgrades(config);
+      expect(res.length).toBe(3);
+    });
+  });
+  describe('groupUpgradesByBranch(upgrades, logger)', () => {
+    it('returns empty object if no input array', async () => {
+      const res = await upgrades.groupUpgradesByBranch([], logger);
+      expect(res).toEqual({});
+    });
+    it('returns one branch if one input', async () => {
+      const input = [
+        {
+          branchName: 'foo-{{version}}',
+          version: '1.1.0',
+        },
+      ];
+      const res = await upgrades.groupUpgradesByBranch(input, logger);
+      expect(Object.keys(res).length).toBe(1);
+      expect(res).toMatchSnapshot();
+    });
+    it('does not group if different compiled branch names', async () => {
+      const input = [
+        {
+          branchName: 'foo-{{version}}',
+          version: '1.1.0',
+        },
+        {
+          branchName: 'foo-{{version}}',
+          version: '2.0.0',
+        },
+        {
+          branchName: 'bar-{{version}}',
+          version: '1.1.0',
+        },
+      ];
+      const res = await upgrades.groupUpgradesByBranch(input, logger);
+      expect(Object.keys(res).length).toBe(3);
+      expect(res).toMatchSnapshot();
+    });
+    it('groups if same compiled branch names', async () => {
+      const input = [
+        {
+          branchName: 'foo',
+          version: '1.1.0',
+        },
+        {
+          branchName: 'foo',
+          version: '2.0.0',
+        },
+        {
+          branchName: 'bar-{{version}}',
+          version: '1.1.0',
+        },
+      ];
+      const res = await upgrades.groupUpgradesByBranch(input, logger);
+      expect(Object.keys(res).length).toBe(2);
+      expect(res).toMatchSnapshot();
+    });
+    it('groups if same compiled group name', async () => {
+      const input = [
+        {
+          branchName: 'foo',
+          version: '1.1.0',
+          groupName: 'My Group',
+          groupBranchName: 'renovate/{{groupSlug}}',
+        },
+        {
+          branchName: 'foo',
+          version: '2.0.0',
+        },
+        {
+          branchName: 'bar-{{version}}',
+          version: '1.1.0',
+          groupName: 'My Group',
+          groupBranchName: 'renovate/my-group',
+        },
+      ];
+      const res = await upgrades.groupUpgradesByBranch(input, logger);
+      expect(Object.keys(res).length).toBe(2);
+      expect(res).toMatchSnapshot();
+    });
+  });
+});