Last active
June 8, 2022 14:31
-
-
Save enapupe/6ac7cc11aba1e493d0eca9a56a73a9c8 to your computer and use it in GitHub Desktop.
serverless plugin that will check required environment variables against provided environment variables
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
const fs = require('fs') | |
const path = require('path') | |
const { sync: resolveSync } = require('resolve') | |
const acornLoose = require('acorn-loose') | |
const walk = require('acorn-walk') | |
const resolveImportSourcePathSync = (filePath, importSource) => { | |
return resolveSync(importSource, { | |
basedir: path.dirname(filePath), | |
}) | |
} | |
const getImportSourcesAndEnvs = (path) => { | |
const data = fs.readFileSync(path).toString() | |
const parsed = acornLoose.parse(data, { | |
ecmaVersion: 2020, | |
}) | |
const required = [] | |
const envs = [] | |
walk.simple(parsed, { | |
VariableDeclaration: (node) => { | |
node.declarations.forEach((declaration) => { | |
if (declaration.init && declaration.init.type === 'CallExpression') { | |
declaration?.init?.callee?.expressions?.forEach((exp) => { | |
if ( | |
exp.type === 'MemberExpression' && | |
// the transpilled name of the function: | |
exp.object.name === 'get_env_1' && | |
// the second argument is not defined, | |
// meaning the env IS required: | |
typeof declaration.init.arguments[1] === 'undefined' | |
) { | |
envs.push(declaration.init.arguments[0].value) | |
} | |
}) | |
} | |
if (declaration.init?.callee?.name === '__importDefault') { | |
required.push(declaration.init.arguments[0].arguments[0].value) | |
} | |
if ( | |
declaration.init && | |
declaration.init.type === 'CallExpression' && | |
declaration?.init?.callee?.name === 'require' | |
) { | |
required.push(declaration.init.arguments[0].value) | |
} | |
}) | |
}, | |
}) | |
return { required, envs } | |
} | |
const INTERNAL_MODULE_SOURCE = /^\./ | |
const collectEnvs = (entry) => { | |
const visited = {} | |
const queue = [entry] | |
const environs = [] | |
while (queue.length) { | |
const filePath = queue.shift() | |
const { required, envs } = getImportSourcesAndEnvs(filePath) | |
if (!visited[filePath] && envs.length) { | |
environs.push([filePath, envs]) | |
} | |
for (let importSource of required) { | |
if (INTERNAL_MODULE_SOURCE.test(importSource)) { | |
const resolved = resolveImportSourcePathSync(filePath, importSource) | |
if (!visited[resolved]) { | |
queue.push(resolved) | |
} | |
} | |
} | |
visited[filePath] = true | |
} | |
return environs | |
} | |
const getRequiredEnvs = (functionHandler) => { | |
const fileName = `./${functionHandler.replace(/\.\w+$/, '.js')}` | |
return collectEnvs(fileName) | |
} | |
class EnvironChecker { | |
constructor(serverless, cliOptions, { log }) { | |
this.log = log | |
this.serverless = serverless | |
this.hooks = { | |
'before:package:initialize': () => this.beforeDeploy(), | |
} | |
} | |
beforeDeploy() { | |
const { functions, provider } = this.serverless.service | |
const errors = {} | |
Object.entries(functions).forEach(([nada, fn]) => { | |
const requiredEnvs = getRequiredEnvs(fn.handler) | |
const providedEnvs = Object.keys({ | |
...provider.environment, | |
...fn.environment, | |
}) | |
requiredEnvs.forEach(([path, envs]) => { | |
envs.forEach((env) => { | |
if (!providedEnvs.includes(env)) { | |
if (errors[fn.name]) { | |
errors[fn.name].push([path, env]) | |
} else { | |
errors[fn.name] = [path, env] | |
} | |
} | |
}) | |
}) | |
}) | |
if (Object.keys(errors).length) { | |
this.log.error('The following envs are missing:') | |
this.log.error(errors) | |
throw new this.serverless.classes.Error('Required envs are missing') | |
} | |
} | |
} | |
module.exports = EnvironChecker |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
service: name | |
provider: [...] | |
functions: [...] | |
plugins: | |
- ./env-check.js |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment