From 3e22ad0ca320971e8dbc592f0057951a626ea59b Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Sun, 4 Apr 2021 15:33:22 +0200
Subject: [PATCH] feat(config): support RENOVATE_CONFIG (#9374)

---
 docs/usage/self-hosting.md |  9 ++++++++-
 lib/config/env.spec.ts     |  9 +++++++++
 lib/config/env.ts          | 14 +++++++++++++-
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/docs/usage/self-hosting.md b/docs/usage/self-hosting.md
index d213d0826a..6ee632fef2 100644
--- a/docs/usage/self-hosting.md
+++ b/docs/usage/self-hosting.md
@@ -196,11 +196,18 @@ Self-hosted Renovate can be configured using any of the following (or a combinat
 
 - A `config.js` file (can also be named `config.json`, but you can't have both at the same time)
 - CLI parameters
-- Environment parameters
+- Environment variables
 
 Note that some Renovate configuration options are _only_ available for self-hosting, and so can only be configured using one of the above methods.
 These are described in the [Self-hosted Configuration](./self-hosted-configuration.md) doc.
 
+If you are configuring using environment variables, there are two possibilities:
+
+- Upper-cased, camel-cased, `RENOVATE_`-prefixed single config options like `RENOVATE_TOKEN=abc123` or `RENOVATE_GIT_AUTHOR=a@b.com`
+- Set `RENOVATE_CONFIG` to a [stringified](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) version of the full JSON config, e.g. `RENOVATE_CONFIG='{"token":"abc123","gitAuthor":"a@b.com"}'`
+
+If you combine both of the above then any single config option in the environment variable will override what's in `RENOVATE_CONFIG`.
+
 ## Authentication
 
 Regardless of platform, you need to select a user account for `renovate` to assume the identity of, and generate a Personal Access Token.
diff --git a/lib/config/env.spec.ts b/lib/config/env.spec.ts
index 8e51fbcdff..13d2311362 100644
--- a/lib/config/env.spec.ts
+++ b/lib/config/env.spec.ts
@@ -116,6 +116,15 @@ describe('config/env', () => {
       };
       expect(env.getConfig(envParam)).toMatchSnapshot();
     });
+    it('merges full config from env', () => {
+      const envParam: NodeJS.ProcessEnv = {
+        RENOVATE_CONFIG: '{"enabled":false,"token":"foo"}',
+        RENOVATE_TOKEN: 'a',
+      };
+      const config = env.getConfig(envParam);
+      expect(config.enabled).toBe(false);
+      expect(config.token).toBe('a');
+    });
   });
   describe('.getEnvName(definition)', () => {
     it('returns empty', () => {
diff --git a/lib/config/env.ts b/lib/config/env.ts
index 8a7f96c0dd..cb7dcf10a4 100644
--- a/lib/config/env.ts
+++ b/lib/config/env.ts
@@ -20,7 +20,19 @@ export function getEnvName(option: Partial<RenovateOptions>): string {
 export function getConfig(env: NodeJS.ProcessEnv): GlobalConfig {
   const options = getOptions();
 
-  const config: GlobalConfig = { hostRules: [] };
+  let config: GlobalConfig = {};
+
+  if (env.RENOVATE_CONFIG) {
+    try {
+      config = JSON.parse(env.RENOVATE_CONFIG);
+      logger.debug({ config }, 'Detected config in env RENOVATE_CONFIG');
+    } catch (err) /* istanbul ignore next */ {
+      logger.fatal({ err }, 'Could not parse RENOVATE_CONFIG');
+      process.exit(1);
+    }
+  }
+
+  config.hostRules ||= [];
 
   const coersions = {
     boolean: (val: string): boolean => val === 'true',
-- 
GitLab