@architect/inventory 3.0.0-RC.2 → 3.0.0-RC.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +16 -4
- package/package.json +10 -8
- package/readme.md +5 -4
- package/src/config/arc.js +3 -3
- package/src/config/pragmas/populate-lambda/index.js +1 -1
- package/src/config/project/index.js +26 -15
- package/src/config/project/plugins/env.js +33 -0
- package/src/config/project/plugins/index.js +2 -0
- package/src/config/project/plugins/runtimes.js +1 -1
- package/src/config/project/prefs/dotenv.js +112 -0
- package/src/config/project/{prefs.js → prefs/index.js} +31 -13
- package/src/config/project/validate/index.js +14 -7
- package/src/defaults/index.js +6 -2
- package/src/env/index.js +76 -61
- package/src/lib/asap-src.js +3 -3
- package/src/lib/index.js +3 -1
- package/src/lib/merge-env-vars.js +32 -0
- package/src/read/index.js +1 -3
- package/src/read/reader.js +1 -1
package/changelog.md
CHANGED
|
@@ -6,15 +6,23 @@
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- Architect 10 plugin API support! Specifically:
|
|
10
|
+
- `plugins.set.runtimes` - custom runtime support (still in beta)
|
|
11
|
+
- `plugins.set.env` - add environment variables to all Lambdas
|
|
12
|
+
- `plugins.set.events|http|scheduled|tables-streams|ws` - generate or drop in Lambdas in Architect pragmas
|
|
13
|
+
- `plugins.set.customLambdas` - generate or drop in unique Lambdas with custom event sources
|
|
14
|
+
- More below...
|
|
9
15
|
- Added `inv|get.plugins` tree + methods
|
|
10
16
|
- What used to be `plugins` in the plugins beta is now `customLambdas` (see next item)
|
|
11
17
|
- Added `inv|get.customLambdas`
|
|
12
18
|
- Formerly `inv|get.plugins`
|
|
13
19
|
- Added `inv._project.customRuntimes`
|
|
14
|
-
- Added
|
|
20
|
+
- Added low-level support for `build` destinations to runtime plugins that register type `transpiled` or `compiled`
|
|
15
21
|
- Added `handlerModuleSystem` property for `nodejs14.x` Lambdas, with a value of `cjs` or `esm` based on Lambda + Node.js conventions
|
|
16
22
|
- Added `handlerFile` detection for `nodejs14.x` + `deno` Lambdas
|
|
23
|
+
- This will detect the correct handler file on the filesystem, and fall back to a default handler file if none are found (e.g. `index.js` in `nodejs14.x`)
|
|
17
24
|
- Added `inv._arc.deployStage` property, enabling Inventory to be aware of an intended deploy stage; (this property may change, consider it in beta!)
|
|
25
|
+
- Added built-in support for reading `.env` files when enumerating local env var preferences
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
### Changed
|
|
@@ -22,15 +30,19 @@
|
|
|
22
30
|
- Breaking change: changed `_project.src`, added `_project.cwd`, making both the pair significantly more literal and descriptive
|
|
23
31
|
- `_project.src` is now the default source tree folder (eg `$cwd/src`)
|
|
24
32
|
- `_project.cwd` refers to the current working directory of the project
|
|
33
|
+
- Breaking change: `_project.env` is now by default an object populated by three properties: `local`, `plugins`, and `aws`, reflecting the env vars found for each environment
|
|
25
34
|
- Breaking change: AWS region prioritizes a region passed via param over `AWS_REGION` env var; this should realistically have little or no effect in practice
|
|
26
35
|
- Breaking change: legacy `@tables-streams` folders (`src/tables/...` and `src/streams/...`) are now deprecated
|
|
27
36
|
- Existing functions can be simply moved to `src/tables-streams/{name}` (or use a custom `src` property)
|
|
28
37
|
- Breaking change: renamed `lambda.handlerFunction` to `lambda.handlerMethod`
|
|
29
|
-
- Breaking change: prioritize mod.ts|js in Deno
|
|
30
|
-
-
|
|
38
|
+
- Breaking change: prioritize `mod.ts|js` handlers in Deno Lambdas
|
|
39
|
+
- Breaking change: removed `toml` support
|
|
40
|
+
- Performance improvements to building `inv.shared` + `inv.views`
|
|
31
41
|
- Improved memory footprint of Inventory object by preserving references in `lambdaSrcDirs`, `lambdasBySrcDir`
|
|
32
|
-
- Added `pragma` property to all Lambdas to
|
|
42
|
+
- Added `pragma` property to all Lambdas to aid in reference preservation
|
|
33
43
|
- Tidy up order of enumerated properties in each Lambda
|
|
44
|
+
Update CI
|
|
45
|
+
- Stop publishing to the GitHub Package registry
|
|
34
46
|
|
|
35
47
|
---
|
|
36
48
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@architect/inventory",
|
|
3
|
-
"version": "3.0.0-RC.
|
|
3
|
+
"version": "3.0.0-RC.6",
|
|
4
4
|
"description": "Architect project resource enumeration utility",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"test:integration": "cross-env tape 'test/integration/**/*-test.js' | tap-spec",
|
|
10
10
|
"coverage": "nyc --reporter=lcov --reporter=text npm run test:unit",
|
|
11
11
|
"lint": "eslint . --fix",
|
|
12
|
-
"rc": "npm version prerelease --preid RC"
|
|
12
|
+
"rc": "npm version prerelease --preid RC",
|
|
13
|
+
"vendor": "scripts/vendor"
|
|
13
14
|
},
|
|
14
15
|
"engines": {
|
|
15
16
|
"node": ">=14"
|
|
@@ -20,22 +21,23 @@
|
|
|
20
21
|
},
|
|
21
22
|
"license": "Apache-2.0",
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@architect/asap": "~
|
|
24
|
-
"@architect/parser": "~
|
|
24
|
+
"@architect/asap": "~5.0.0-RC.0",
|
|
25
|
+
"@architect/parser": "~6.0.0-RC.0",
|
|
25
26
|
"@architect/utils": "~3.0.4",
|
|
26
27
|
"lambda-runtimes": "~1.1.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@architect/eslint-config": "~2.0.1",
|
|
30
|
-
"aws-sdk": "2.
|
|
31
|
-
"aws-sdk-mock": "~5.
|
|
31
|
+
"aws-sdk": "2.1001.0",
|
|
32
|
+
"aws-sdk-mock": "~5.6.0",
|
|
32
33
|
"cross-env": "~7.0.3",
|
|
33
|
-
"
|
|
34
|
+
"dotenv": "~14.3.2",
|
|
35
|
+
"eslint": "~8.7.0",
|
|
34
36
|
"mock-fs": "~5.1.2",
|
|
35
37
|
"mock-require": "~3.0.3",
|
|
36
38
|
"nyc": "~15.1.0",
|
|
37
39
|
"tap-spec": "^5.0.0",
|
|
38
|
-
"tape": "^5.
|
|
40
|
+
"tape": "^5.4.1"
|
|
39
41
|
},
|
|
40
42
|
"eslintConfig": {
|
|
41
43
|
"extends": "@architect/eslint-config"
|
package/readme.md
CHANGED
|
@@ -43,7 +43,7 @@ The inventory object contains the entirety of a project's data, including Archit
|
|
|
43
43
|
|
|
44
44
|
Top-level inventory parameters that start with an underscore (e.g. `_arc`, `_project`) denote project metadata or internal diagnostic data; all other parameters represent userland project resources.
|
|
45
45
|
|
|
46
|
-
In a project inventory, `null` values are used as placeholders for known values or options that were not user-defined. The existence of a non-`null` value can be inferred as a user having specifically defined a setting. For example: `arc.http
|
|
46
|
+
In a project inventory, `null` values are used as placeholders for known values or options that were not user-defined. The existence of a non-`null` value can be inferred as a user having specifically defined a setting. For example: `arc.http: null` can be construed as the user having **not** defined an `@http` pragma. This rule has some exceptions:
|
|
47
47
|
|
|
48
48
|
- A handful of settings that must be backfilled if not supplied
|
|
49
49
|
- Example: `inv.aws.region`, which is required by the `aws-sdk` to function, and will be backfilled if not defined
|
|
@@ -51,9 +51,10 @@ In a project inventory, `null` values are used as placeholders for known values
|
|
|
51
51
|
- Example: while `@static` can be defined on its own without any other pragmas, the existence of `@http` infers `@static`
|
|
52
52
|
- Thus, the act of adding `@http` will necessarily make `inv.static` non-`null`
|
|
53
53
|
- Settings that generate related resources
|
|
54
|
-
- Example: DynamoDB streams can be defined in `@tables` with `stream true`; Inventory would interpret a table with `stream true` as a new `inv
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
- Example: DynamoDB streams can be defined in `@tables` with `stream true`; Inventory would interpret a table with `stream true` as a new `inv['tables-streams']` resource and thus make `inv['tables-streams']` non-`null`
|
|
55
|
+
- Lambda `handlerFile` file path property is present even if the file is not
|
|
56
|
+
- This differs from Lambda `configFile` file path properties, which will be `null` if no file is present
|
|
57
|
+
- This exception is namely because some workflows may need the computed default handler path (example: when running `arc create`)
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
#### `get`
|
package/src/config/arc.js
CHANGED
|
@@ -6,14 +6,14 @@ let { join } = require('path')
|
|
|
6
6
|
*/
|
|
7
7
|
module.exports = function getArcConfig (params) {
|
|
8
8
|
let { cwd, inventory } = params
|
|
9
|
-
let
|
|
9
|
+
let _arc = { ...inventory._arc }
|
|
10
10
|
|
|
11
11
|
// Version
|
|
12
12
|
let installed = join(cwd, 'node_modules', '@architect', 'architect', 'package.json')
|
|
13
13
|
if (existsSync(installed)) {
|
|
14
14
|
let { version } = JSON.parse(readFileSync(installed))
|
|
15
|
-
|
|
15
|
+
_arc.version = version
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
return
|
|
18
|
+
return _arc
|
|
19
19
|
}
|
|
@@ -14,7 +14,7 @@ function populateLambda (type, { arc, inventory, errors, pragma }) {
|
|
|
14
14
|
let pluginLambda = []
|
|
15
15
|
if (plugins) {
|
|
16
16
|
let pluginResults = plugins.flatMap(fn => {
|
|
17
|
-
let result = fn({ arc, inventory })
|
|
17
|
+
let result = fn({ arc, inventory: { inv: inventory } })
|
|
18
18
|
if (!result) {
|
|
19
19
|
errors.push(`Setter plugins must return a valid response: plugin: ${fn.plugin}, method: set.${type}`)
|
|
20
20
|
return []
|
|
@@ -2,25 +2,25 @@ let { join } = require('path')
|
|
|
2
2
|
let upsert = require('../_upsert')
|
|
3
3
|
let prefs = require('./prefs')
|
|
4
4
|
let plugins = require('./plugins')
|
|
5
|
-
let { is } = require('../../lib')
|
|
5
|
+
let { is, mergeEnvVars } = require('../../lib')
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Get the project-level configuration, overlaying arc.aws settings (if present)
|
|
9
9
|
*/
|
|
10
10
|
module.exports = function getProjectConfig (params) {
|
|
11
11
|
let { arc, errors, raw, filepath, inventory } = params
|
|
12
|
-
let
|
|
12
|
+
let _project = {
|
|
13
13
|
...inventory._project,
|
|
14
14
|
arc,
|
|
15
15
|
raw,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (arc.aws) {
|
|
19
|
-
|
|
19
|
+
_project.defaultFunctionConfig = upsert(_project.defaultFunctionConfig, arc.aws)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
if (filepath) {
|
|
23
|
-
|
|
23
|
+
_project.manifest = filepath
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Parse local and global project preferences
|
|
@@ -29,37 +29,48 @@ module.exports = function getProjectConfig (params) {
|
|
|
29
29
|
let p = prefs({ scope, inventory, errors })
|
|
30
30
|
if (p) {
|
|
31
31
|
// Set up the scoped metadata
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
_project[`${scope}Preferences`] = p.preferences
|
|
33
|
+
_project[`${scope}PreferencesFile`] = p.preferencesFile
|
|
34
34
|
|
|
35
35
|
// Build out the final preferences
|
|
36
36
|
/* istanbul ignore else: jic */
|
|
37
|
-
if (!
|
|
37
|
+
if (!_project.preferences) _project.preferences = {}
|
|
38
38
|
Object.keys(p.preferences).forEach(pragma => {
|
|
39
39
|
// Ignore the raw data
|
|
40
40
|
if (pragma === '_arc' || pragma === '_raw') return
|
|
41
41
|
// Allow booleans, etc.
|
|
42
42
|
if (!is.object(p.preferences[pragma])) {
|
|
43
|
-
|
|
43
|
+
_project.preferences[pragma] = p.preferences[pragma]
|
|
44
44
|
return
|
|
45
45
|
}
|
|
46
46
|
// Traverse and merge individual settings
|
|
47
47
|
/* istanbul ignore else: jic */
|
|
48
|
-
if (!
|
|
48
|
+
if (!_project.preferences[pragma]) _project.preferences[pragma] = {}
|
|
49
49
|
Object.entries(p.preferences[pragma]).forEach(([ setting, value ]) => {
|
|
50
|
-
|
|
50
|
+
_project.preferences[pragma][setting] = value
|
|
51
51
|
})
|
|
52
52
|
})
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// Populate local env from preferences
|
|
57
|
+
if (_project.preferences?.env) {
|
|
58
|
+
_project.env.local = _project.preferences.env
|
|
59
|
+
}
|
|
60
|
+
|
|
56
61
|
if (inventory.plugins?._methods) {
|
|
57
|
-
|
|
62
|
+
_project.env.plugins = plugins.env(params, _project)
|
|
63
|
+
_project.env.local = mergeEnvVars({
|
|
64
|
+
name: 'Local',
|
|
65
|
+
source: _project.env.plugins,
|
|
66
|
+
target: _project.env.local,
|
|
67
|
+
errors,
|
|
68
|
+
})
|
|
58
69
|
|
|
59
|
-
let { build, runtimes } = plugins.runtimes(params,
|
|
60
|
-
if (build)
|
|
61
|
-
if (runtimes)
|
|
70
|
+
let { build, runtimes } = plugins.runtimes(params, _project)
|
|
71
|
+
if (build) _project.build = join(_project.cwd, build)
|
|
72
|
+
if (runtimes) _project.customRuntimes = runtimes
|
|
62
73
|
}
|
|
63
74
|
|
|
64
|
-
return
|
|
75
|
+
return _project
|
|
65
76
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
let { is } = require('../../../lib')
|
|
2
|
+
|
|
3
|
+
module.exports = function setEnvPlugins (params, project) {
|
|
4
|
+
let { errors, inventory } = params
|
|
5
|
+
let envPlugins = inventory.plugins?._methods?.set?.env
|
|
6
|
+
if (envPlugins?.length) {
|
|
7
|
+
let env = {}
|
|
8
|
+
|
|
9
|
+
// inventory._project is not yet built, so provide as much as we can to plugins for now
|
|
10
|
+
let inv = { ...inventory, _project: project }
|
|
11
|
+
envPlugins.forEach(fn => {
|
|
12
|
+
let errType = `plugin: ${fn.plugin}, method: set.env`
|
|
13
|
+
try {
|
|
14
|
+
let result = fn({ inventory: { inv } })
|
|
15
|
+
if (!is.object(result) || !Object.keys(result).length) {
|
|
16
|
+
return errors.push(`Env plugin returned invalid data, must return an Object with one or more keys + values: ${errType}`)
|
|
17
|
+
}
|
|
18
|
+
Object.entries(result).forEach(([ k, v ]) => {
|
|
19
|
+
if (env[k]) {
|
|
20
|
+
return errors.push(`Env var '${k}' already registered: ${errType}`)
|
|
21
|
+
}
|
|
22
|
+
if (is.object(v) || is.array(v)) env[k] = JSON.stringify(v)
|
|
23
|
+
else env[k] = String(v)
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
errors.push(`Runtime plugin '${fn.plugin}' failed: ${err.message}`)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
return { testing: env, staging: env, production: env }
|
|
31
|
+
}
|
|
32
|
+
return inventory._project.env.plugins
|
|
33
|
+
}
|
|
@@ -15,7 +15,7 @@ module.exports = function setRuntimePlugins (params, project) {
|
|
|
15
15
|
runtimePlugins.forEach(fn => {
|
|
16
16
|
let errType = `plugin: ${fn.plugin}, method: set.runtimes`
|
|
17
17
|
try {
|
|
18
|
-
let result = fn({ inventory: inv })
|
|
18
|
+
let result = fn({ inventory: { inv } })
|
|
19
19
|
result = is.array(result) ? result : [ result ]
|
|
20
20
|
result.forEach(runtime => {
|
|
21
21
|
// TODO add more validation
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Copyright (c) 2015, Scott Motte
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
|
|
4
|
+
/* istanbul ignore file */
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
// node_modules/dotenv/lib/main.js
|
|
7
|
+
var fs = require("fs");
|
|
8
|
+
var path = require("path");
|
|
9
|
+
var os = require("os");
|
|
10
|
+
function log(message) {
|
|
11
|
+
console.log(`[dotenv][DEBUG] ${message}`);
|
|
12
|
+
}
|
|
13
|
+
var NEWLINE = "\n";
|
|
14
|
+
var RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*("[^"]*"|'[^']*'|.*?)(\s+#.*)?$/;
|
|
15
|
+
var RE_NEWLINES = /\\n/g;
|
|
16
|
+
var NEWLINES_MATCH = /\r\n|\n|\r/;
|
|
17
|
+
function parse(src, options) {
|
|
18
|
+
const debug = Boolean(options && options.debug);
|
|
19
|
+
const multiline = Boolean(options && options.multiline);
|
|
20
|
+
const obj = {};
|
|
21
|
+
const lines = src.toString().split(NEWLINES_MATCH);
|
|
22
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
23
|
+
let line = lines[idx];
|
|
24
|
+
const keyValueArr = line.match(RE_INI_KEY_VAL);
|
|
25
|
+
if (keyValueArr != null) {
|
|
26
|
+
const key = keyValueArr[1];
|
|
27
|
+
let val = keyValueArr[2] || "";
|
|
28
|
+
let end = val.length - 1;
|
|
29
|
+
const isDoubleQuoted = val[0] === '"' && val[end] === '"';
|
|
30
|
+
const isSingleQuoted = val[0] === "'" && val[end] === "'";
|
|
31
|
+
const isMultilineDoubleQuoted = val[0] === '"' && val[end] !== '"';
|
|
32
|
+
const isMultilineSingleQuoted = val[0] === "'" && val[end] !== "'";
|
|
33
|
+
if (multiline && (isMultilineDoubleQuoted || isMultilineSingleQuoted)) {
|
|
34
|
+
const quoteChar = isMultilineDoubleQuoted ? '"' : "'";
|
|
35
|
+
val = val.substring(1);
|
|
36
|
+
while (idx++ < lines.length - 1) {
|
|
37
|
+
line = lines[idx];
|
|
38
|
+
end = line.length - 1;
|
|
39
|
+
if (line[end] === quoteChar) {
|
|
40
|
+
val += NEWLINE + line.substring(0, end);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
val += NEWLINE + line;
|
|
44
|
+
}
|
|
45
|
+
} else if (isSingleQuoted || isDoubleQuoted) {
|
|
46
|
+
val = val.substring(1, end);
|
|
47
|
+
if (isDoubleQuoted) {
|
|
48
|
+
val = val.replace(RE_NEWLINES, NEWLINE);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
val = val.trim();
|
|
52
|
+
}
|
|
53
|
+
obj[key] = val;
|
|
54
|
+
} else if (debug) {
|
|
55
|
+
const trimmedLine = line.trim();
|
|
56
|
+
if (trimmedLine.length && trimmedLine[0] !== "#") {
|
|
57
|
+
log(`Failed to match key and value when parsing line ${idx + 1}: ${line}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return obj;
|
|
62
|
+
}
|
|
63
|
+
function resolveHome(envPath) {
|
|
64
|
+
return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
65
|
+
}
|
|
66
|
+
function config(options) {
|
|
67
|
+
let dotenvPath = path.resolve(process.cwd(), ".env");
|
|
68
|
+
let encoding = "utf8";
|
|
69
|
+
const debug = Boolean(options && options.debug);
|
|
70
|
+
const override = Boolean(options && options.override);
|
|
71
|
+
const multiline = Boolean(options && options.multiline);
|
|
72
|
+
if (options) {
|
|
73
|
+
if (options.path != null) {
|
|
74
|
+
dotenvPath = resolveHome(options.path);
|
|
75
|
+
}
|
|
76
|
+
if (options.encoding != null) {
|
|
77
|
+
encoding = options.encoding;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }), { debug, multiline });
|
|
82
|
+
Object.keys(parsed).forEach(function(key) {
|
|
83
|
+
if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
|
|
84
|
+
process.env[key] = parsed[key];
|
|
85
|
+
} else {
|
|
86
|
+
if (override === true) {
|
|
87
|
+
process.env[key] = parsed[key];
|
|
88
|
+
}
|
|
89
|
+
if (debug) {
|
|
90
|
+
if (override === true) {
|
|
91
|
+
log(`"${key}" is already defined in \`process.env\` and WAS overwritten`);
|
|
92
|
+
} else {
|
|
93
|
+
log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return { parsed };
|
|
99
|
+
} catch (e) {
|
|
100
|
+
if (debug) {
|
|
101
|
+
log(`Failed to load ${dotenvPath} ${e.message}`);
|
|
102
|
+
}
|
|
103
|
+
return { error: e };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
var DotenvModule = {
|
|
107
|
+
config,
|
|
108
|
+
parse
|
|
109
|
+
};
|
|
110
|
+
module.exports.config = DotenvModule.config;
|
|
111
|
+
module.exports.parse = DotenvModule.parse;
|
|
112
|
+
module.exports = DotenvModule;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
let
|
|
2
|
-
let
|
|
3
|
-
let
|
|
1
|
+
let { join } = require('path')
|
|
2
|
+
let { existsSync, readFileSync } = require('fs')
|
|
3
|
+
let read = require('../../../read')
|
|
4
|
+
let validate = require('../validate')
|
|
5
|
+
let { is } = require('../../../lib')
|
|
6
|
+
let { parse } = require('./dotenv')
|
|
4
7
|
let { homedir } = require('os')
|
|
5
8
|
|
|
6
9
|
module.exports = function getPrefs ({ scope, inventory, errors }) {
|
|
@@ -8,10 +11,16 @@ module.exports = function getPrefs ({ scope, inventory, errors }) {
|
|
|
8
11
|
? homedir()
|
|
9
12
|
: inventory._project.cwd
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
let envFilepath = join(cwd, '.env')
|
|
15
|
+
let hasEnvFile = scope === 'local' && existsSync(envFilepath)
|
|
12
16
|
let prefs = read({ type: 'preferences', cwd, errors })
|
|
17
|
+
|
|
18
|
+
if (!prefs.filepath && !hasEnvFile) return null
|
|
19
|
+
|
|
20
|
+
let preferences = {}
|
|
21
|
+
|
|
22
|
+
// Populate Architect preferences
|
|
13
23
|
if (prefs.filepath) {
|
|
14
|
-
let preferences = {}
|
|
15
24
|
// Ok, this gets a bit hairy
|
|
16
25
|
// Arc outputs an object of nested arrays
|
|
17
26
|
// Basically, construct a pared-down intermediate prefs obj for consumers
|
|
@@ -45,6 +54,7 @@ module.exports = function getPrefs ({ scope, inventory, errors }) {
|
|
|
45
54
|
if (is.array(val)) preferences.env[e][key] = val.join(' ')
|
|
46
55
|
})
|
|
47
56
|
}
|
|
57
|
+
else preferences.env[e] = null
|
|
48
58
|
})
|
|
49
59
|
}
|
|
50
60
|
// Turn Sandbox scripts into commands
|
|
@@ -58,16 +68,24 @@ module.exports = function getPrefs ({ scope, inventory, errors }) {
|
|
|
58
68
|
})
|
|
59
69
|
|
|
60
70
|
validate(preferences, errors)
|
|
71
|
+
}
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
// Populate .env (testing environment only, disables other env vars)
|
|
74
|
+
if (hasEnvFile) {
|
|
75
|
+
let dotenv = parse(readFileSync(envFilepath))
|
|
76
|
+
preferences.env = {
|
|
77
|
+
testing: Object.keys(dotenv).length ? dotenv : null,
|
|
78
|
+
staging: null,
|
|
79
|
+
production: null,
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
return
|
|
83
|
+
return {
|
|
84
|
+
preferences: {
|
|
85
|
+
...preferences,
|
|
86
|
+
_arc: prefs.arc,
|
|
87
|
+
_raw: prefs.raw,
|
|
88
|
+
},
|
|
89
|
+
preferencesFile: prefs.filepath
|
|
90
|
+
}
|
|
73
91
|
}
|
|
@@ -2,12 +2,19 @@ let { is } = require('./../../../lib')
|
|
|
2
2
|
|
|
3
3
|
module.exports = function validatePreferences (preferences, errors) {
|
|
4
4
|
// Env checks
|
|
5
|
-
let { env } = preferences
|
|
6
|
-
if (!env) return
|
|
7
|
-
if (env && !is.object(env)) errors.push(`Invalid preferences setting: @env ${env}`)
|
|
8
|
-
|
|
5
|
+
let { env, sandbox } = preferences
|
|
9
6
|
let envs = [ 'testing', 'staging', 'production' ]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
|
|
8
|
+
if (env && !is.object(env)) {
|
|
9
|
+
errors.push(`Invalid preferences setting: @env ${env}`)
|
|
10
|
+
}
|
|
11
|
+
else if (env) {
|
|
12
|
+
envs.forEach(e => {
|
|
13
|
+
if (env[e] && !is.object(env[e])) errors.push(`Invalid preferences setting: @env ${e}`)
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (sandbox?.env && !envs.includes(sandbox.env)) {
|
|
18
|
+
errors.push(`Invalid preferences setting: @sandbox env ${sandbox.env}`)
|
|
19
|
+
}
|
|
13
20
|
}
|
package/src/defaults/index.js
CHANGED
|
@@ -25,14 +25,18 @@ module.exports = function inventoryDefaults (params = {}) {
|
|
|
25
25
|
src: join(cwd, 'src'), // Default source tree dir
|
|
26
26
|
build: null, // Optional build artifact dir
|
|
27
27
|
manifest: null, // Root project manifest filename
|
|
28
|
-
preferences: null, // Realized preferences obj, resolved from
|
|
28
|
+
preferences: null, // Realized preferences obj, resolved from local > global
|
|
29
29
|
localPreferences: null, // Local preferences obj
|
|
30
30
|
localPreferencesFile: null, // Local preferences file path
|
|
31
31
|
globalPreferences: null, // Global preferences obj
|
|
32
32
|
globalPreferencesFile: null, // Global preferences file path
|
|
33
33
|
defaultFunctionConfig, // Project-level function config
|
|
34
34
|
rootHandler: null, // null | configured | arcStaticAssetProxy | proxy
|
|
35
|
-
env:
|
|
35
|
+
env: { // Env vars pulled from:
|
|
36
|
+
local: null, // Local/global prefs or .env
|
|
37
|
+
plugins: null, // Plugins
|
|
38
|
+
aws: null, // SSM
|
|
39
|
+
},
|
|
36
40
|
customRuntimes: null, // Runtime plugins
|
|
37
41
|
arc: [], // Raw arc obj
|
|
38
42
|
raw: '', // Raw arc string
|
package/src/env/index.js
CHANGED
|
@@ -1,76 +1,91 @@
|
|
|
1
|
+
let { mergeEnvVars } = require('../lib')
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Read env vars out of SSM
|
|
3
5
|
*/
|
|
4
6
|
module.exports = function env (params, inventory, callback) {
|
|
5
|
-
if (params.env) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// eslint-disable-next-line
|
|
9
|
-
var aws = require('aws-sdk')
|
|
10
|
-
}
|
|
11
|
-
catch (err) {
|
|
12
|
-
let msg = `'aws-sdk' not found, please install locally or globally (see also readme#aws-sdk-caveat)`
|
|
13
|
-
return callback(Error(msg))
|
|
14
|
-
}
|
|
15
|
-
let name = inventory.app
|
|
16
|
-
let { region } = inventory.aws
|
|
17
|
-
let ssm = new aws.SSM({ region })
|
|
18
|
-
let result = []
|
|
19
|
-
|
|
20
|
-
function getSomeEnvVars (name, NextToken, callback) {
|
|
21
|
-
// Base query to ssm
|
|
22
|
-
let query = {
|
|
23
|
-
Path: `/${name}`,
|
|
24
|
-
Recursive: true,
|
|
25
|
-
MaxResults: 10,
|
|
26
|
-
WithDecryption: true
|
|
27
|
-
}
|
|
7
|
+
if (!params.env) {
|
|
8
|
+
return callback()
|
|
9
|
+
}
|
|
28
10
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
11
|
+
/* istanbul ignore next */
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line
|
|
14
|
+
var aws = require('aws-sdk')
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
let msg = `'aws-sdk' not found, please install locally or globally (see also readme#aws-sdk-caveat)`
|
|
18
|
+
return callback(Error(msg))
|
|
19
|
+
}
|
|
20
|
+
let name = inventory.app
|
|
21
|
+
let { region } = inventory.aws
|
|
22
|
+
let ssm = new aws.SSM({ region })
|
|
23
|
+
let result = []
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
app: name, // jic
|
|
42
|
-
env: bits[2],
|
|
43
|
-
name: bits[3],
|
|
44
|
-
value: param.Value,
|
|
45
|
-
}
|
|
46
|
-
}))
|
|
47
|
-
// Check for more data and, if so, recurse
|
|
48
|
-
/* istanbul ignore if: Sadly no way to easily mock this for testing */
|
|
49
|
-
if (data.NextToken) {
|
|
50
|
-
getSomeEnvVars(name, data.NextToken, callback)
|
|
51
|
-
}
|
|
52
|
-
else callback(null, result)
|
|
53
|
-
}
|
|
54
|
-
})
|
|
25
|
+
function getSomeEnvVars (name, NextToken, callback) {
|
|
26
|
+
// Base query to ssm
|
|
27
|
+
let query = {
|
|
28
|
+
Path: `/${name}`,
|
|
29
|
+
Recursive: true,
|
|
30
|
+
MaxResults: 10,
|
|
31
|
+
WithDecryption: true
|
|
55
32
|
}
|
|
56
33
|
|
|
57
|
-
|
|
34
|
+
// Check if we're paginating
|
|
35
|
+
/* istanbul ignore if */
|
|
36
|
+
if (NextToken) query.NextToken = NextToken
|
|
37
|
+
|
|
38
|
+
// Perform the query
|
|
39
|
+
ssm.getParametersByPath(query, function _query (err, data) {
|
|
58
40
|
if (err) callback(err)
|
|
59
41
|
else {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
let
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
42
|
+
// Tidy up the response
|
|
43
|
+
result = result.concat(data.Parameters.map(function (param) {
|
|
44
|
+
let bits = param.Name.split('/')
|
|
45
|
+
return {
|
|
46
|
+
app: name, // jic
|
|
47
|
+
env: bits[2],
|
|
48
|
+
name: bits[3],
|
|
49
|
+
value: param.Value,
|
|
50
|
+
}
|
|
51
|
+
}))
|
|
52
|
+
// Check for more data and, if so, recurse
|
|
53
|
+
/* istanbul ignore if: Sadly no way to easily mock this for testing */
|
|
54
|
+
if (data.NextToken) {
|
|
55
|
+
getSomeEnvVars(name, data.NextToken, callback)
|
|
70
56
|
}
|
|
71
|
-
callback()
|
|
57
|
+
else callback(null, result)
|
|
72
58
|
}
|
|
73
59
|
})
|
|
74
60
|
}
|
|
75
|
-
|
|
61
|
+
|
|
62
|
+
getSomeEnvVars(name, false, function done (err, result) {
|
|
63
|
+
if (err) callback(err)
|
|
64
|
+
else {
|
|
65
|
+
let testing = null
|
|
66
|
+
let staging = null
|
|
67
|
+
let production = null
|
|
68
|
+
if (result.length) {
|
|
69
|
+
// TODO refactor into a reducer?
|
|
70
|
+
result.forEach(({ env, name: k, value: v }) => {
|
|
71
|
+
if (env === 'testing') testing = Object.assign({}, testing, { [k]: v })
|
|
72
|
+
if (env === 'staging') staging = Object.assign({}, staging, { [k]: v })
|
|
73
|
+
if (env === 'production') production = Object.assign({}, production, { [k]: v })
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let errors = []
|
|
78
|
+
inventory._project.env.aws = mergeEnvVars({
|
|
79
|
+
env: 'Application',
|
|
80
|
+
source: inventory._project.env.plugins,
|
|
81
|
+
target: { testing, staging, production },
|
|
82
|
+
errors,
|
|
83
|
+
})
|
|
84
|
+
if (errors.length) {
|
|
85
|
+
callback(Error(errors[0]))
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
callback()
|
|
89
|
+
}
|
|
90
|
+
})
|
|
76
91
|
}
|
package/src/lib/asap-src.js
CHANGED
package/src/lib/index.js
CHANGED
|
@@ -2,6 +2,7 @@ let asapSrc = require('./asap-src')
|
|
|
2
2
|
let errorFmt = require('./error-fmt')
|
|
3
3
|
let getLambdaDirs = require('./get-lambda-dirs')
|
|
4
4
|
let is = require('./is')
|
|
5
|
+
let mergeEnvVars = require('./merge-env-vars')
|
|
5
6
|
let pragmas = require('./pragmas')
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -19,8 +20,9 @@ module.exports = {
|
|
|
19
20
|
compiledRuntimes,
|
|
20
21
|
errorFmt,
|
|
21
22
|
getLambdaDirs,
|
|
22
|
-
normalizeSrc: getLambdaDirs.normalizeSrc,
|
|
23
23
|
httpMethods,
|
|
24
24
|
is,
|
|
25
|
+
mergeEnvVars,
|
|
26
|
+
normalizeSrc: getLambdaDirs.normalizeSrc,
|
|
25
27
|
pragmas,
|
|
26
28
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
let envs = [ 'testing', 'staging', 'production' ]
|
|
2
|
+
|
|
3
|
+
module.exports = function mergeEnvVars (params) {
|
|
4
|
+
let { name, source, target, errors } = params
|
|
5
|
+
if (source === null) return target
|
|
6
|
+
if (target === null) return source
|
|
7
|
+
let probs = []
|
|
8
|
+
|
|
9
|
+
// Deep copy to reset any potential refs
|
|
10
|
+
let merged = JSON.parse(JSON.stringify(target))
|
|
11
|
+
envs.forEach(env => {
|
|
12
|
+
if (!source[env]) return
|
|
13
|
+
Object.keys(source[env]).forEach(k => {
|
|
14
|
+
if (merged[env]?.[k]) {
|
|
15
|
+
probs.push(`'${env}' variable '${k}'`)
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
if (!merged[env]) merged[env] = {}
|
|
19
|
+
merged[env][k] = source[env][k]
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (probs.length) {
|
|
25
|
+
let s = probs.length > 1 ? 's' : ''
|
|
26
|
+
let msg = `${name} env var${s} conflicts with plugin:\n` +
|
|
27
|
+
`- ${probs.join('\n- ')}`
|
|
28
|
+
errors.push(msg)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return merged
|
|
32
|
+
}
|
package/src/read/index.js
CHANGED
|
@@ -5,7 +5,6 @@ let projectManifest = {
|
|
|
5
5
|
arc: [ 'app.arc', '.arc' ],
|
|
6
6
|
json: [ 'arc.json' ],
|
|
7
7
|
yaml: [ 'arc.yaml', 'arc.yml' ],
|
|
8
|
-
toml: [ 'arc.toml' ],
|
|
9
8
|
manifest: [ 'package.json' ],
|
|
10
9
|
_default: `@app\napp-default\n@http\n@static`,
|
|
11
10
|
}
|
|
@@ -15,13 +14,12 @@ let functionConfig = {
|
|
|
15
14
|
arc: [ 'config.arc', '.arc-config' ],
|
|
16
15
|
json: [ 'arc.json', 'arc-config.json' ],
|
|
17
16
|
yaml: [ 'config.yaml', 'config.yml', 'arc-config.yaml', 'arc-config.yml' ],
|
|
18
|
-
toml: [ 'config.toml', 'arc-config.toml' ],
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
// Local preferences
|
|
22
20
|
let preferences = {
|
|
23
21
|
arc: [ 'preferences.arc', 'prefs.arc', '.preferences.arc', '.prefs.arc', ],
|
|
24
|
-
// TODO add json, yaml
|
|
22
|
+
// TODO add json, yaml later if folks want it?
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
let reads = { projectManifest, functionConfig, preferences }
|
package/src/read/reader.js
CHANGED
|
@@ -37,7 +37,7 @@ module.exports = function reader (reads, cwd, errors) {
|
|
|
37
37
|
if (raw.trim() === '') return errors.push(`Empty file: ${f}`)
|
|
38
38
|
arc = type === 'arc'
|
|
39
39
|
? parse(raw)
|
|
40
|
-
: parse[type](raw) // Parser has convenient json
|
|
40
|
+
: parse[type](raw) // Parser has convenient json + yaml methods!
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
43
|
let pkg = JSON.parse(read(file))
|