diff --git a/src/cmds/deploy.js b/src/cmds/deploy.js index db690b9..459c52a 100644 --- a/src/cmds/deploy.js +++ b/src/cmds/deploy.js @@ -5,6 +5,7 @@ const version = require('../../package.json').version; const log = require('../log'); const messages = require('../messages'); const shim = require('../shim'); +const env = require('process').env; const { stripIndent } = require('common-tags'); const indent = (text, depth = 2) => { @@ -168,10 +169,35 @@ exports.handler = async args => { return; } + // Replace environment variable $-references with the actual values + // If the environment variable is not defined, store in an array an present + // an error to the user. + let undefinedEnvRefs = []; + + const rawConfigWithSecrets = rawConfig.replace( + /\$([A-Za-z0-9_]+)/gi, + (_match, variableName) => { + let value = env[variableName]; + if (value) return value; + undefinedEnvRefs.push(variableName); + } + ); + + if (undefinedEnvRefs.length > 0) { + log.error( + `The following environment variables were referenced but are not defined: ${undefinedEnvRefs.join( + ', ' + )}` + ); + + process.exitCode = 1; + return; + } + let config; try { - config = JSON.parse(rawConfig); + config = JSON.parse(rawConfigWithSecrets); } catch (err) { log.error('Configuration could not be parsed'); process.exitCode = 1; diff --git a/test/cmds/__snapshots__/deploy.test.js.snap b/test/cmds/__snapshots__/deploy.test.js.snap index 61bfee3..0127b5e 100644 --- a/test/cmds/__snapshots__/deploy.test.js.snap +++ b/test/cmds/__snapshots__/deploy.test.js.snap @@ -181,3 +181,19 @@ Array [ ], ] `; + +exports[`substitutes all referenced environment variables 1`] = ` +Array [ + Array [ + "✔ Deployment succeeded (xxxx-xxxx-xxxx)", + ], +] +`; + +exports[`throws an error if undefined env vars are referenced 1`] = ` +Array [ + Array [ + "✕ The following environment variables were referenced but are not defined: MY_SECRET_1, API_KEY_1", + ], +] +`; diff --git a/test/cmds/deploy.test.js b/test/cmds/deploy.test.js index ebf739d..887591e 100644 --- a/test/cmds/deploy.test.js +++ b/test/cmds/deploy.test.js @@ -1,7 +1,9 @@ const deploy = require('@statickit/deploy'); const version = require('../../package.json').version; -jest.mock('process', () => ({ env: {} })); +jest.mock('process', () => ({ + env: { MY_SECRET: 'pa$$w0rd', API_KEY: '12345' } +})); jest.mock('@statickit/deploy'); jest.mock('../../src/shim'); @@ -190,3 +192,32 @@ it('displays general validation errors', async () => { await cmd.handler({ config: '{}', key: 'xxx' }); expect(console.error.mock.calls).toMatchSnapshot(); }); + +it('substitutes all referenced environment variables', async () => { + deploy.request.mockImplementation(params => { + expect(params.config.mySecret).toBe('pa$$w0rd'); + expect(params.config.apiKey).toBe('12345'); + return Promise.resolve({ + status: 200, + data: { id: 'xxxx-xxxx-xxxx', shim: null } + }); + }); + + const config = { + mySecret: '$MY_SECRET', + apiKey: '$API_KEY' + }; + + await cmd.handler({ config: JSON.stringify(config), key: 'xxx' }); + expect(console.log.mock.calls).toMatchSnapshot(); +}); + +it('throws an error if undefined env vars are referenced', async () => { + const config = { + mySecret: '$MY_SECRET_1', + apiKey: '$API_KEY_1' + }; + + await cmd.handler({ config: JSON.stringify(config), key: 'xxx' }); + expect(console.error.mock.calls).toMatchSnapshot(); +});