netlify-cli 10.14.0 → 10.17.0

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/src/utils/dev.js CHANGED
@@ -14,7 +14,7 @@ const { loadDotEnvFiles } = require('./dot-env')
14
14
  // Possible sources of environment variables. For the purpose of printing log messages only. Order does not matter.
15
15
  const ENV_VAR_SOURCES = {
16
16
  account: {
17
- name: 'shared build settings',
17
+ name: 'shared',
18
18
  printFn: chalk.magenta,
19
19
  },
20
20
  addons: {
@@ -34,7 +34,7 @@ const ENV_VAR_SOURCES = {
34
34
  printFn: chalk.red,
35
35
  },
36
36
  ui: {
37
- name: 'build settings',
37
+ name: 'site settings',
38
38
  printFn: chalk.blue,
39
39
  },
40
40
  }
@@ -1,3 +1,153 @@
1
+ /**
2
+ * Finds a matching environment variable value from a given context
3
+ * @param {Array<object>} values - An array of environment variable values from Envelope
4
+ * @param {enum<dev,branch-deploy,deploy-preview,production>} context - The deploy context of the environment variable value
5
+ * @returns {object<context: enum<dev,branch-deploy,deploy-preview,production>, value: string>} The matching environment variable value object
6
+ */
7
+ const findValueFromContext = (values, context) => values.find((val) => [context, 'all'].includes(val.context))
8
+
9
+ /**
10
+ * Finds environment variables that match a given source
11
+ * @param {object} env - The dictionary of environment variables
12
+ * @param {enum<general,account,addons,ui,configFile>} source - The source of the environment variable
13
+ * @returns {object} The dictionary of env vars that match the given source
14
+ */
15
+ const filterEnvBySource = (env, source) =>
16
+ Object.fromEntries(Object.entries(env).filter(([, variable]) => variable.sources[0] === source))
17
+
18
+ /**
19
+ * Fetches data from Envelope
20
+ * @param {string} accountId - The account id
21
+ * @param {object} api - The api singleton object
22
+ * @param {string} key - If present, fetch a single key (case-sensitive)
23
+ * @param {string} siteId - The site id
24
+ * @returns {Array<object>} An array of environment variables from the Envelope service
25
+ */
26
+ const fetchEnvelopeItems = async function ({ accountId, api, key, siteId }) {
27
+ if (accountId === undefined) {
28
+ return {}
29
+ }
30
+ try {
31
+ // if a single key is passed, fetch that single env var
32
+ if (key) {
33
+ const envelopeItem = await api.getEnvVar({ accountId, key, siteId })
34
+ return [envelopeItem]
35
+ }
36
+ // otherwise, fetch the entire list of env vars
37
+ const envelopeItems = await api.getEnvVars({ accountId, siteId })
38
+ return envelopeItems
39
+ } catch {
40
+ // Collaborators aren't allowed to read shared env vars,
41
+ // so return an empty array silently in that case
42
+ return []
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Filters and sorts data from Envelope by a given context and/or scope
48
+ * @param {enum<dev,branch-deploy,deploy-preview,production>} context - The deploy context of the environment variable value
49
+ * @param {Array<object>} envelopeItems - An array of environment variables from the Envelope service
50
+ * @param {enum<any,builds,functions,runtime,post_processing>} scope - The scope of the environment variables
51
+ * @param {enum<general,account,addons,ui,configFile>} source - The source of the environment variable
52
+ * @returns {object} A dicionary in the following format:
53
+ * {
54
+ * FOO: {
55
+ * context: 'dev',
56
+ * scopes: ['builds', 'functions'],
57
+ * sources: ['ui'],
58
+ * value: 'bar',
59
+ * },
60
+ * BAZ: {
61
+ * context: 'dev',
62
+ * scopes: ['runtime'],
63
+ * sources: ['account'],
64
+ * value: 'bang',
65
+ * },
66
+ * }
67
+ */
68
+ const formatEnvelopeData = ({ context = 'dev', envelopeItems = [], scope = 'any', source }) =>
69
+ envelopeItems
70
+ // filter by context
71
+ .filter(({ values }) => Boolean(findValueFromContext(values, context)))
72
+ // filter by scope
73
+ .filter(({ scopes }) => (scope === 'any' ? true : scopes.includes(scope)))
74
+ // sort alphabetically, case insensitive
75
+ .sort((left, right) => (left.key.toLowerCase() < right.key.toLowerCase() ? -1 : 1))
76
+ // format the data
77
+ .reduce((acc, cur) => {
78
+ const { context: ctx, value } = findValueFromContext(cur.values, context)
79
+ return {
80
+ ...acc,
81
+ [cur.key]: {
82
+ context: ctx,
83
+ scopes: cur.scopes,
84
+ sources: [source],
85
+ value,
86
+ },
87
+ }
88
+ }, {})
89
+
90
+ /**
91
+ * Collects env vars from multiple sources and arranges them in the correct order of precedence
92
+ * @param {object} api - The api singleton object
93
+ * @param {enum<dev,branch-deploy,deploy-preview,production>} context - The deploy context of the environment variable
94
+ * @param {object} env - The dictionary of environment variables
95
+ * @param {string} key - If present, fetch a single key (case-sensitive)
96
+ * @param {enum<any,builds,functions,runtime,post_processing>} scope - The scope of the environment variables
97
+ * @param {object} siteInfo - The site object
98
+ * @returns {object} An object of environment variables keys and their metadata
99
+ */
100
+ const getEnvelopeEnv = async ({ api, context = 'dev', env, key = '', scope = 'any', siteInfo }) => {
101
+ const { account_slug: accountId, id: siteId } = siteInfo
102
+
103
+ const [accountEnvelopeItems, siteEnvelopeItems] = await Promise.all([
104
+ fetchEnvelopeItems({ api, accountId, key }),
105
+ fetchEnvelopeItems({ api, accountId, key, siteId }),
106
+ ])
107
+
108
+ const accountEnv = formatEnvelopeData({ context, envelopeItems: accountEnvelopeItems, scope, source: 'account' })
109
+ const siteEnv = formatEnvelopeData({ context, envelopeItems: siteEnvelopeItems, scope, source: 'ui' })
110
+ const generalEnv = filterEnvBySource(env, 'general')
111
+ const addonsEnv = filterEnvBySource(env, 'addons')
112
+ const configFileEnv = filterEnvBySource(env, 'configFile')
113
+
114
+ // filter out configFile env vars if a non-configFile scope is passed
115
+ const includeConfigEnvVars = ['any', 'builds', 'post_processing'].includes(scope)
116
+
117
+ // Sources of environment variables, in ascending order of precedence.
118
+ return {
119
+ ...generalEnv,
120
+ ...accountEnv,
121
+ ...(includeConfigEnvVars ? addonsEnv : {}),
122
+ ...siteEnv,
123
+ ...(includeConfigEnvVars ? configFileEnv : {}),
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Returns a human-readable, comma-separated list of scopes
129
+ * @param {Array<enum<builds,functions,runtime,post_processing>>} scopes - An array of scopes
130
+ * @returns {string} A human-readable, comma-separated list of scopes
131
+ */
132
+ const getHumanReadableScopes = (scopes) => {
133
+ const AVAILABLE_SCOPES = {
134
+ builds: 'Builds',
135
+ functions: 'Functions',
136
+ post_processing: 'Post processing',
137
+ runtime: 'Runtime',
138
+ }
139
+ if (!scopes) {
140
+ // if `scopes` is not available, the env var comes from netlify.toml
141
+ // env vars specified in netlify.toml are present in the `builds` and `post_processing` scope
142
+ return 'Builds, Post processing'
143
+ }
144
+ if (scopes.length === Object.keys(AVAILABLE_SCOPES).length) {
145
+ // shorthand instead of listing every available scope
146
+ return 'All'
147
+ }
148
+ return scopes.map((scope) => AVAILABLE_SCOPES[scope]).join(', ')
149
+ }
150
+
1
151
  /**
2
152
  * Translates a Mongo env into an Envelope env
3
153
  * @param {object} env - The site's env as it exists in Mongo
@@ -38,6 +188,11 @@ const translateFromEnvelopeToMongo = (envVars = []) =>
38
188
  }, {})
39
189
 
40
190
  module.exports = {
41
- translateFromMongoToEnvelope,
191
+ findValueFromContext,
192
+ filterEnvBySource,
193
+ formatEnvelopeData,
194
+ getEnvelopeEnv,
195
+ getHumanReadableScopes,
42
196
  translateFromEnvelopeToMongo,
197
+ translateFromMongoToEnvelope,
43
198
  }
@@ -465,6 +465,7 @@ const startProxy = async function ({
465
465
  addonsUrls,
466
466
  config,
467
467
  configPath,
468
+ env,
468
469
  geoCountry,
469
470
  geolocationMode,
470
471
  getUpdatedConfig,
@@ -479,6 +480,7 @@ const startProxy = async function ({
479
480
  const edgeFunctionsProxy = await edgeFunctions.initializeProxy({
480
481
  config,
481
482
  configPath,
483
+ env,
482
484
  geolocationMode,
483
485
  geoCountry,
484
486
  getUpdatedConfig,