From d27d35e535b0c32b51b4e22983a7a445608e1ccf Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@keylocation.sg>
Date: Fri, 11 Aug 2017 07:29:16 +0200
Subject: [PATCH] feat: allow strings instead of lists (#665)

Updates definitions for schedule and package rules to allow strings to be massaged to lists - instead of erroring.

Closes #662
---
 lib/config/definitions.js                     |  5 ++++
 lib/config/massage.js                         | 30 +++++++++++++++++++
 lib/config/migration.js                       |  3 --
 lib/workers/repository/apis.js                |  8 +++--
 .../__snapshots__/migration.spec.js.snap      |  4 +--
 test/workers/repository/apis.spec.js          |  2 +-
 6 files changed, 42 insertions(+), 10 deletions(-)
 create mode 100644 lib/config/massage.js

diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 77aa0b5135..05191aeeaa 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -65,6 +65,7 @@ const options = [
     name: 'schedule',
     description: 'Times of day/week to renovate',
     type: 'list',
+    allowString: true,
     cli: false,
     env: false,
   },
@@ -201,6 +202,7 @@ const options = [
     description:
       'Package names to match. Valid only within `packageRules` object',
     type: 'list',
+    allowString: true,
     stage: 'depType',
     cli: false,
     env: false,
@@ -210,6 +212,7 @@ const options = [
     description:
       'Package names to exclude. Valid only within `packageRules` object',
     type: 'list',
+    allowString: true,
     stage: 'depType',
     cli: false,
     env: false,
@@ -219,6 +222,7 @@ const options = [
     description:
       'Package name patterns to match. Valid only within `packageRules` object.',
     type: 'list',
+    allowString: true,
     stage: 'depType',
     cli: false,
     env: false,
@@ -228,6 +232,7 @@ const options = [
     description:
       'Package name patterns to exclude. Valid only within `packageRules` object.',
     type: 'list',
+    allowString: true,
     stage: 'depType',
     cli: false,
     env: false,
diff --git a/lib/config/massage.js b/lib/config/massage.js
new file mode 100644
index 0000000000..780fbcce2d
--- /dev/null
+++ b/lib/config/massage.js
@@ -0,0 +1,30 @@
+const options = require('./definitions').getOptions();
+
+const allowedStrings = [];
+options.forEach(option => {
+  if (option.allowString) {
+    allowedStrings.push(option.name);
+  }
+});
+
+module.exports = {
+  massageConfig,
+};
+
+// Returns a massaged config
+function massageConfig(config) {
+  const massagedConfig = { ...config };
+  for (const key of Object.keys(config)) {
+    const val = config[key];
+    if (allowedStrings.includes(key) && typeof val === 'string') {
+      massagedConfig[key] = [val];
+    } else if (isObject(val)) {
+      massagedConfig[key] = massageConfig(val);
+    }
+  }
+  return massagedConfig;
+}
+
+function isObject(obj) {
+  return Object.prototype.toString.call(obj) === '[object Object]';
+}
diff --git a/lib/config/migration.js b/lib/config/migration.js
index b5dbd7bab0..fff7d460f4 100644
--- a/lib/config/migration.js
+++ b/lib/config/migration.js
@@ -30,9 +30,6 @@ function migrateConfig(config, parentConfig) {
         isMigrated = true;
         delete migratedConfig[key];
       }
-    } else if (key === 'schedule' && typeof val === 'string') {
-      isMigrated = true;
-      migratedConfig.schedule = [val];
     } else if (key === 'packages') {
       isMigrated = true;
       migratedConfig.packageRules = migratedConfig.packages.map(
diff --git a/lib/workers/repository/apis.js b/lib/workers/repository/apis.js
index ac97d7b6ad..a90bb92fc5 100644
--- a/lib/workers/repository/apis.js
+++ b/lib/workers/repository/apis.js
@@ -4,6 +4,7 @@ const path = require('path');
 const jsonValidator = require('json-dup-key-validator');
 const configParser = require('../../config');
 const configMigration = require('../../config/migration');
+const configMassage = require('../../config/massage');
 const configValidation = require('../../config/validation');
 // API
 const githubApi = require('../../api/github');
@@ -108,13 +109,14 @@ function migrateAndValidate(config, input) {
       'Config migration necessary'
     );
   }
-  const { warnings, errors } = configValidation.validateConfig(migratedConfig);
+  const massagedConfig = configMassage.massageConfig(migratedConfig);
+  const { warnings, errors } = configValidation.validateConfig(massagedConfig);
   // istanbul ignore if
   if (warnings.length) {
-    config.logger.debug({ warnings }, 'Found renovate.json warnings');
+    config.logger.debug({ warnings }, 'Found renovate config warnings');
   }
   if (errors.length) {
-    config.logger.warn({ errors }, 'Found renovate.json errors');
+    config.logger.warn({ errors }, 'Found renovate config errors');
     /* TODO #556
     renovateJsonErrors.forEach(error => {
       config.errors.push(
diff --git a/test/config/__snapshots__/migration.spec.js.snap b/test/config/__snapshots__/migration.spec.js.snap
index b1571c58b0..da1984e40a 100644
--- a/test/config/__snapshots__/migration.spec.js.snap
+++ b/test/config/__snapshots__/migration.spec.js.snap
@@ -19,9 +19,7 @@ Object {
     },
   ],
   "prTitle": "some pr title",
-  "schedule": Array [
-    "after 5pm",
-  ],
+  "schedule": "after 5pm",
   "semanticPrefix": "fix(deps):",
 }
 `;
diff --git a/test/workers/repository/apis.spec.js b/test/workers/repository/apis.spec.js
index 1c19c77cfd..48e42687de 100644
--- a/test/workers/repository/apis.spec.js
+++ b/test/workers/repository/apis.spec.js
@@ -160,7 +160,7 @@ describe('workers/repository/apis', () => {
     });
     it('returns warning + error plus extended config if unknown keys', async () => {
       config.api.getFileContent.mockReturnValueOnce(
-        '{ "enabled": true, "foo": false, "maintainYarnLock": true }'
+        '{ "enabled": true, "foo": false, "maintainYarnLock": true, "schedule": "before 5am", "minor": {} }'
       );
       const returnConfig = await apis.mergeRenovateJson(config);
       expect(returnConfig.enabled).toBe(true);
-- 
GitLab