netlify-cli 8.0.7 → 8.1.0-rc

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.
Files changed (183) hide show
  1. package/bin/run +81 -3
  2. package/npm-shrinkwrap.json +305 -2105
  3. package/package.json +10 -33
  4. package/src/commands/addons/addons-auth.js +50 -0
  5. package/src/commands/addons/addons-config.js +180 -0
  6. package/src/commands/addons/addons-create.js +129 -0
  7. package/src/commands/addons/addons-delete.js +59 -0
  8. package/src/commands/addons/addons-list.js +62 -0
  9. package/src/commands/addons/addons.js +49 -0
  10. package/src/commands/addons/index.js +3 -24
  11. package/src/commands/api/api.js +83 -0
  12. package/src/commands/api/index.js +5 -0
  13. package/src/commands/base-command.js +322 -0
  14. package/src/commands/build/build.js +58 -0
  15. package/src/commands/build/index.js +3 -61
  16. package/src/commands/completion/completion.js +18 -0
  17. package/src/commands/completion/index.js +5 -0
  18. package/src/commands/{deploy.js → deploy/deploy.js} +306 -278
  19. package/src/commands/deploy/index.js +5 -0
  20. package/src/commands/dev/dev-exec.js +39 -0
  21. package/src/commands/dev/dev-trace.js +50 -0
  22. package/src/commands/dev/dev.js +349 -0
  23. package/src/commands/dev/index.js +3 -335
  24. package/src/commands/env/env-get.js +51 -0
  25. package/src/commands/env/env-import.js +93 -0
  26. package/src/commands/env/env-list.js +63 -0
  27. package/src/commands/env/env-set.js +67 -0
  28. package/src/commands/env/env-unset.js +66 -0
  29. package/src/commands/env/env.js +47 -0
  30. package/src/commands/env/index.js +3 -23
  31. package/src/commands/functions/functions-build.js +59 -0
  32. package/src/commands/functions/{create.js → functions-create.js} +133 -94
  33. package/src/commands/functions/functions-invoke.js +276 -0
  34. package/src/commands/functions/functions-list.js +107 -0
  35. package/src/commands/functions/functions-serve.js +63 -0
  36. package/src/commands/functions/functions.js +53 -0
  37. package/src/commands/functions/index.js +3 -45
  38. package/src/commands/index.js +5 -0
  39. package/src/commands/init/index.js +6 -0
  40. package/src/commands/{init.js → init/init.js} +79 -68
  41. package/src/commands/link/index.js +6 -0
  42. package/src/{utils/link/link-by-prompt.js → commands/link/link.js} +153 -14
  43. package/src/commands/lm/index.js +3 -19
  44. package/src/commands/lm/lm-info.js +42 -0
  45. package/src/commands/lm/lm-install.js +36 -0
  46. package/src/commands/lm/lm-setup.js +106 -0
  47. package/src/commands/lm/lm-uninstall.js +25 -0
  48. package/src/commands/lm/lm.js +39 -0
  49. package/src/commands/login/index.js +6 -0
  50. package/src/commands/login/login.js +52 -0
  51. package/src/commands/logout/index.js +5 -0
  52. package/src/commands/logout/logout.js +43 -0
  53. package/src/commands/main.js +117 -0
  54. package/src/commands/open/index.js +3 -39
  55. package/src/commands/open/open-admin.js +56 -0
  56. package/src/commands/open/open-site.js +49 -0
  57. package/src/commands/open/open.js +42 -0
  58. package/src/commands/sites/index.js +5 -20
  59. package/src/commands/sites/sites-create.js +184 -0
  60. package/src/commands/sites/sites-delete.js +108 -0
  61. package/src/commands/sites/sites-list.js +89 -0
  62. package/src/commands/sites/sites.js +36 -0
  63. package/src/commands/status/index.js +3 -118
  64. package/src/commands/status/status-hooks.js +73 -0
  65. package/src/commands/status/status.js +125 -0
  66. package/src/commands/switch/index.js +5 -0
  67. package/src/commands/switch/switch.js +50 -0
  68. package/src/commands/unlink/index.js +5 -0
  69. package/src/commands/unlink/unlink.js +48 -0
  70. package/src/commands/watch/index.js +5 -0
  71. package/src/commands/watch/watch.js +121 -0
  72. package/src/lib/build.js +21 -7
  73. package/src/lib/exec-fetcher.js +5 -3
  74. package/src/lib/fs.js +54 -36
  75. package/src/lib/functions/background.js +1 -1
  76. package/src/lib/functions/form-submissions-handler.js +2 -1
  77. package/src/lib/functions/local-proxy.js +2 -1
  78. package/src/lib/functions/netlify-function.js +4 -1
  79. package/src/lib/functions/registry.js +4 -6
  80. package/src/lib/functions/runtimes/go/index.js +2 -1
  81. package/src/lib/functions/runtimes/js/builders/netlify-lambda.js +6 -4
  82. package/src/lib/functions/runtimes/js/builders/zisi.js +3 -3
  83. package/src/lib/functions/runtimes/rust/index.js +4 -3
  84. package/src/lib/functions/server.js +2 -3
  85. package/src/lib/functions/synchronous.js +2 -1
  86. package/src/lib/functions/utils.js +2 -3
  87. package/src/lib/functions/watcher.js +1 -0
  88. package/src/lib/http-agent.js +3 -5
  89. package/src/lib/log.js +2 -1
  90. package/src/lib/spinner.js +22 -0
  91. package/src/utils/addons/diffs/index.js +1 -0
  92. package/src/utils/addons/diffs/options.js +3 -1
  93. package/src/utils/addons/prepare.js +13 -6
  94. package/src/utils/addons/prompts.js +2 -1
  95. package/src/utils/addons/render.js +3 -1
  96. package/src/utils/command-helpers.js +156 -43
  97. package/src/utils/create-stream-promise.js +5 -5
  98. package/src/utils/deferred.js +1 -0
  99. package/src/utils/deploy/deploy-site.js +1 -1
  100. package/src/utils/deploy/index.js +4 -0
  101. package/src/utils/detect-server-settings.js +10 -12
  102. package/src/utils/dev.js +18 -10
  103. package/src/utils/dot-env.js +4 -2
  104. package/src/utils/{edge-handlers.js → functions/edge-handlers.js} +8 -7
  105. package/src/utils/functions/functions.js +36 -0
  106. package/src/utils/{get-functions.js → functions/get-functions.js} +2 -1
  107. package/src/utils/functions/index.js +8 -26
  108. package/src/utils/get-global-config.js +3 -2
  109. package/src/utils/get-repo-data.js +1 -0
  110. package/src/utils/gh-auth.js +1 -0
  111. package/src/utils/gitignore.js +7 -5
  112. package/src/utils/header.js +2 -2
  113. package/src/utils/headers.js +1 -2
  114. package/src/utils/index.js +42 -0
  115. package/src/utils/init/config-github.js +12 -5
  116. package/src/utils/init/config-manual.js +9 -2
  117. package/src/utils/init/config.js +13 -7
  118. package/src/utils/init/frameworks.js +1 -0
  119. package/src/utils/init/node-version.js +4 -2
  120. package/src/utils/init/plugins.js +1 -0
  121. package/src/utils/init/utils.js +10 -6
  122. package/src/utils/live-tunnel.js +3 -4
  123. package/src/utils/lm/install.js +10 -15
  124. package/src/utils/lm/requirements.js +3 -1
  125. package/src/utils/lm/steps.js +1 -1
  126. package/src/utils/lm/ui.js +7 -3
  127. package/src/utils/open-browser.js +8 -2
  128. package/src/utils/parse-raw-flags.js +4 -4
  129. package/src/utils/proxy.js +6 -5
  130. package/src/utils/read-repo-url.js +1 -0
  131. package/src/utils/redirects.js +2 -2
  132. package/src/utils/rules-proxy.js +2 -1
  133. package/src/utils/state-config.js +1 -1
  134. package/src/utils/telemetry/index.js +2 -113
  135. package/src/utils/telemetry/request.js +3 -1
  136. package/src/utils/telemetry/telemetry.js +117 -0
  137. package/src/utils/telemetry/validation.js +13 -12
  138. package/src/utils/traffic-mesh.js +3 -3
  139. package/oclif.manifest.json +0 -1
  140. package/src/commands/addons/auth.js +0 -42
  141. package/src/commands/addons/config.js +0 -177
  142. package/src/commands/addons/create.js +0 -127
  143. package/src/commands/addons/delete.js +0 -69
  144. package/src/commands/addons/list.js +0 -54
  145. package/src/commands/api.js +0 -84
  146. package/src/commands/dev/exec.js +0 -32
  147. package/src/commands/dev/trace.js +0 -61
  148. package/src/commands/env/get.js +0 -44
  149. package/src/commands/env/import.js +0 -90
  150. package/src/commands/env/list.js +0 -49
  151. package/src/commands/env/set.js +0 -64
  152. package/src/commands/env/unset.js +0 -58
  153. package/src/commands/functions/build.js +0 -60
  154. package/src/commands/functions/invoke.js +0 -277
  155. package/src/commands/functions/list.js +0 -102
  156. package/src/commands/functions/serve.js +0 -70
  157. package/src/commands/link.js +0 -133
  158. package/src/commands/lm/info.js +0 -36
  159. package/src/commands/lm/install.js +0 -30
  160. package/src/commands/lm/setup.js +0 -107
  161. package/src/commands/lm/uninstall.js +0 -17
  162. package/src/commands/login.js +0 -54
  163. package/src/commands/logout.js +0 -37
  164. package/src/commands/open/admin.js +0 -51
  165. package/src/commands/open/site.js +0 -43
  166. package/src/commands/sites/create.js +0 -191
  167. package/src/commands/sites/delete.js +0 -116
  168. package/src/commands/sites/list.js +0 -84
  169. package/src/commands/status/hooks.js +0 -60
  170. package/src/commands/switch.js +0 -44
  171. package/src/commands/unlink.js +0 -38
  172. package/src/commands/watch.js +0 -115
  173. package/src/hooks/init.js +0 -46
  174. package/src/index.js +0 -25
  175. package/src/lib/help.js +0 -26
  176. package/src/utils/chalk.js +0 -16
  177. package/src/utils/check-command-inputs.js +0 -21
  178. package/src/utils/command.js +0 -262
  179. package/src/utils/detect-functions-builder.js +0 -25
  180. package/src/utils/difference.js +0 -4
  181. package/src/utils/logo.js +0 -11
  182. package/src/utils/show-help.js +0 -5
  183. package/src/utils/telemetry/tracked-command.js +0 -51
@@ -1,337 +1,5 @@
1
- // @ts-check
2
- const path = require('path')
3
- const process = require('process')
4
- const { promisify } = require('util')
1
+ const { createDevCommand } = require('./dev')
5
2
 
6
- const { flags: flagsLib } = require('@oclif/command')
7
- const boxen = require('boxen')
8
- const chalk = require('chalk')
9
- const execa = require('execa')
10
- const StaticServer = require('static-server')
11
- const stripAnsiCc = require('strip-ansi-control-characters')
12
- const waitPort = require('wait-port')
13
-
14
- const { startFunctionsServer } = require('../../lib/functions/server')
15
- const Command = require('../../utils/command')
16
- const { exit, log, warn } = require('../../utils/command-helpers')
17
- const { detectServerSettings } = require('../../utils/detect-server-settings')
18
- const { getSiteInformation, injectEnvVariables } = require('../../utils/dev')
19
- const { startLiveTunnel } = require('../../utils/live-tunnel')
20
- const { NETLIFYDEV, NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN } = require('../../utils/logo')
21
- const { openBrowser } = require('../../utils/open-browser')
22
- const { startProxy } = require('../../utils/proxy')
23
- const { startForwardProxy } = require('../../utils/traffic-mesh')
24
-
25
- const startStaticServer = async ({ settings }) => {
26
- const server = new StaticServer({
27
- rootPath: settings.dist,
28
- name: 'netlify-dev',
29
- port: settings.frameworkPort,
30
- templates: {
31
- notFound: path.join(settings.dist, '404.html'),
32
- },
33
- })
34
-
35
- await promisify(server.start.bind(server))()
36
- log(`\n${NETLIFYDEVLOG} Static server listening to`, settings.frameworkPort)
37
- }
38
-
39
- const isNonExistingCommandError = ({ command, error }) => {
40
- // `ENOENT` is only returned for non Windows systems
41
- // See https://github.com/sindresorhus/execa/pull/447
42
- if (error.code === 'ENOENT') {
43
- return true
44
- }
45
-
46
- // if the command is a package manager we let it report the error
47
- if (['yarn', 'npm'].includes(command)) {
48
- return false
49
- }
50
-
51
- // this only works on English versions of Windows
52
- return (
53
- typeof error.message === 'string' && error.message.includes('is not recognized as an internal or external command')
54
- )
55
- }
56
-
57
- /**
58
- * Run a command and pipe stdout, stderr and stdin
59
- * @param {string} command
60
- * @param {NodeJS.ProcessEnv} env
61
- * @returns {execa.ExecaChildProcess<string>}
62
- */
63
- const runCommand = (command, env = {}) => {
64
- const commandProcess = execa.command(command, {
65
- preferLocal: true,
66
- // we use reject=false to avoid rejecting synchronously when the command doesn't exist
67
- reject: false,
68
- env,
69
- // windowsHide needs to be false for child process to terminate properly on Windows
70
- windowsHide: false,
71
- })
72
-
73
- commandProcess.stdout.pipe(stripAnsiCc.stream()).pipe(process.stdout)
74
- commandProcess.stderr.pipe(stripAnsiCc.stream()).pipe(process.stderr)
75
- process.stdin.pipe(commandProcess.stdin)
76
-
77
- // we can't try->await->catch since we don't want to block on the framework server which
78
- // is a long running process
79
- // eslint-disable-next-line promise/catch-or-return,promise/prefer-await-to-then
80
- commandProcess.then(async () => {
81
- const result = await commandProcess
82
- const [commandWithoutArgs] = command.split(' ')
83
- // eslint-disable-next-line promise/always-return
84
- if (result.failed && isNonExistingCommandError({ command: commandWithoutArgs, error: result })) {
85
- log(
86
- NETLIFYDEVERR,
87
- `Failed running command: ${command}. Please verify ${chalk.magenta(`'${commandWithoutArgs}'`)} exists`,
88
- )
89
- } else {
90
- const errorMessage = result.failed
91
- ? `${NETLIFYDEVERR} ${result.shortMessage}`
92
- : `${NETLIFYDEVWARN} "${command}" exited with code ${result.exitCode}`
93
-
94
- log(`${errorMessage}. Shutting down Netlify Dev server`)
95
- }
96
- process.exit(1)
97
- })
98
- ;['SIGINT', 'SIGTERM', 'SIGQUIT', 'SIGHUP', 'exit'].forEach((signal) => {
99
- process.on(signal, () => {
100
- commandProcess.kill('SIGTERM', { forceKillAfterTimeout: 500 })
101
- process.exit()
102
- })
103
- })
104
-
105
- return commandProcess
106
- }
107
-
108
- /**
109
- * Start a static server if the `useStaticServer` is provided or a framework specific server
110
- * @param {object} config
111
- * @param {import('../../utils/types').ServerSettings} config.settings
112
- * @returns {Promise<void>}
113
- */
114
- const startFrameworkServer = async function ({ settings }) {
115
- if (settings.useStaticServer) {
116
- if (settings.command) {
117
- runCommand(settings.command, settings.env)
118
- }
119
- return await startStaticServer({ settings })
120
- }
121
-
122
- log(`${NETLIFYDEVLOG} Starting Netlify Dev with ${settings.framework || 'custom config'}`)
123
-
124
- runCommand(settings.command, settings.env)
125
-
126
- try {
127
- const open = await waitPort({
128
- port: settings.frameworkPort,
129
- output: 'silent',
130
- timeout: FRAMEWORK_PORT_TIMEOUT,
131
- ...(settings.pollingStrategies.includes('HTTP') && { protocol: 'http' }),
132
- })
133
-
134
- if (!open) {
135
- throw new Error(`Timed out waiting for port '${settings.frameworkPort}' to be open`)
136
- }
137
- } catch {
138
- log(NETLIFYDEVERR, `Netlify Dev could not connect to localhost:${settings.frameworkPort}.`)
139
- log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`)
140
- exit(1)
141
- }
142
- }
143
-
144
- // 10 minutes
145
- const FRAMEWORK_PORT_TIMEOUT = 6e5
146
-
147
- const startProxyServer = async ({ addonsUrls, flags, settings, site }) => {
148
- let url
149
- if (flags.edgeHandlers || flags.trafficMesh) {
150
- url = await startForwardProxy({
151
- port: settings.port,
152
- frameworkPort: settings.frameworkPort,
153
- functionsPort: settings.functionsPort,
154
- publishDir: settings.dist,
155
- debug: flags.debug,
156
- locationDb: flags.locationDb,
157
- jwtRolesPath: settings.jwtRolePath,
158
- jwtSecret: settings.jwtSecret,
159
- })
160
- if (!url) {
161
- log(NETLIFYDEVERR, `Unable to start forward proxy on port '${settings.port}'`)
162
- exit(1)
163
- }
164
- } else {
165
- url = await startProxy(settings, addonsUrls, site.configPath, site.root)
166
- if (!url) {
167
- log(NETLIFYDEVERR, `Unable to start proxy server on port '${settings.port}'`)
168
- exit(1)
169
- }
170
- }
171
- return url
172
- }
173
-
174
- const handleLiveTunnel = async ({ api, flags, settings, site }) => {
175
- if (flags.live) {
176
- const sessionUrl = await startLiveTunnel({
177
- siteId: site.id,
178
- netlifyApiToken: api.accessToken,
179
- localPort: settings.port,
180
- })
181
- process.env.BASE_URL = sessionUrl
182
- return sessionUrl
183
- }
184
- }
185
-
186
- const printBanner = ({ url }) => {
187
- const banner = chalk.bold(`${NETLIFYDEVLOG} Server now ready on ${url}`)
188
-
189
- log(
190
- boxen(banner, {
191
- padding: 1,
192
- margin: 1,
193
- align: 'center',
194
- borderColor: '#00c7b7',
195
- }),
196
- )
3
+ module.exports = {
4
+ createDevCommand,
197
5
  }
198
-
199
- class DevCommand extends Command {
200
- async init() {
201
- this.commandContext = 'dev'
202
- await super.init()
203
- }
204
-
205
- async run() {
206
- log(`${NETLIFYDEV}`)
207
- const { flags } = this.parse(DevCommand)
208
- const { api, config, site, siteInfo } = this.netlify
209
- config.dev = { ...config.dev }
210
- config.build = { ...config.build }
211
- /** @type {import('./types').DevConfig} */
212
- const devConfig = {
213
- framework: '#auto',
214
- ...(config.functionsDirectory && { functions: config.functionsDirectory }),
215
- ...(config.build.publish && { publish: config.build.publish }),
216
- ...config.dev,
217
- ...flags,
218
- }
219
-
220
- if (flags.trafficMesh) {
221
- warn(
222
- '--trafficMesh and -t are deprecated and will be removed in the near future. Please use --edgeHandlers or -e instead.',
223
- )
224
- }
225
-
226
- await injectEnvVariables({ env: this.netlify.cachedConfig.env, site })
227
- const { addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
228
- flags,
229
- api,
230
- site,
231
- siteInfo,
232
- })
233
-
234
- /** @type {Partial<import('../../utils/types').ServerSettings>} */
235
- let settings = {}
236
- try {
237
- settings = await detectServerSettings(devConfig, flags, site.root)
238
- } catch (error) {
239
- log(NETLIFYDEVERR, error.message)
240
- exit(1)
241
- }
242
-
243
- this.setAnalyticsPayload({ projectType: settings.framework || 'custom', live: flags.live })
244
-
245
- await startFunctionsServer({
246
- config,
247
- settings,
248
- site,
249
- siteUrl,
250
- capabilities,
251
- timeouts,
252
- })
253
- await startFrameworkServer({ settings })
254
-
255
- let url = await startProxyServer({ flags, settings, site, addonsUrls })
256
-
257
- const liveTunnelUrl = await handleLiveTunnel({ flags, site, api, settings })
258
- url = liveTunnelUrl || url
259
-
260
- if (devConfig.autoLaunch !== false) {
261
- await openBrowser({ url, silentBrowserNoneError: true })
262
- }
263
-
264
- process.env.URL = url
265
- process.env.DEPLOY_URL = url
266
-
267
- printBanner({ url })
268
- }
269
- }
270
-
271
- DevCommand.description = `Local dev server
272
- The dev command will run a local dev server with Netlify's proxy and redirect rules
273
- `
274
-
275
- DevCommand.examples = [
276
- '$ netlify dev',
277
- '$ netlify dev -d public',
278
- '$ netlify dev -c "hugo server -w" --targetPort 1313',
279
- ]
280
-
281
- DevCommand.strict = false
282
-
283
- DevCommand.flags = {
284
- command: flagsLib.string({
285
- char: 'c',
286
- description: 'command to run',
287
- }),
288
- port: flagsLib.integer({
289
- char: 'p',
290
- description: 'port of netlify dev',
291
- }),
292
- targetPort: flagsLib.integer({
293
- description: 'port of target app server',
294
- }),
295
- framework: flagsLib.string({
296
- description: 'framework to use. Defaults to #auto which automatically detects a framework',
297
- }),
298
- staticServerPort: flagsLib.integer({
299
- description: 'port of the static app server used when no framework is detected',
300
- hidden: true,
301
- }),
302
- dir: flagsLib.string({
303
- char: 'd',
304
- description: 'dir with static files',
305
- }),
306
- functions: flagsLib.string({
307
- char: 'f',
308
- description: 'specify a functions folder to serve',
309
- }),
310
- offline: flagsLib.boolean({
311
- char: 'o',
312
- description: 'disables any features that require network access',
313
- }),
314
- live: flagsLib.boolean({
315
- char: 'l',
316
- description: 'start a public live session',
317
- default: false,
318
- }),
319
- edgeHandlers: flagsLib.boolean({
320
- char: 'e',
321
- hidden: true,
322
- description: 'activates the Edge Handlers runtime',
323
- }),
324
- trafficMesh: flagsLib.boolean({
325
- char: 't',
326
- hidden: true,
327
- description: '(DEPRECATED: use --edgeHandlers or -e instead) uses Traffic Mesh for proxying requests',
328
- }),
329
- locationDb: flagsLib.string({
330
- description: 'specify the path to a local GeoIP location database in MMDB format',
331
- char: 'g',
332
- hidden: true,
333
- }),
334
- ...DevCommand.flags,
335
- }
336
-
337
- module.exports = DevCommand
@@ -0,0 +1,51 @@
1
+ // @ts-check
2
+ const { log, logJson } = require('../../utils')
3
+
4
+ /**
5
+ * The env:get command
6
+ * @param {string} name Environment variable name
7
+ * @param {import('commander').OptionValues} options
8
+ * @param {import('../base-command').BaseCommand} command
9
+ */
10
+ const envGet = async (name, options, command) => {
11
+ const { api, cachedConfig, site } = command.netlify
12
+ const siteId = site.id
13
+
14
+ if (!siteId) {
15
+ log('No site id found, please run inside a site folder or `netlify link`')
16
+ return false
17
+ }
18
+
19
+ const siteData = await api.getSite({ siteId })
20
+
21
+ const { value } = cachedConfig.env[name] || {}
22
+
23
+ // Return json response for piping commands
24
+ if (options.json) {
25
+ logJson(value ? { [name]: value } : {})
26
+ return false
27
+ }
28
+
29
+ if (!value) {
30
+ log(`Environment variable ${name} not set for site ${siteData.name}`)
31
+ return false
32
+ }
33
+
34
+ log(value)
35
+ }
36
+
37
+ /**
38
+ * Creates the `netlify env:get` command
39
+ * @param {import('../base-command').BaseCommand} program
40
+ * @returns
41
+ */
42
+ const createEnvGetCommand = (program) =>
43
+ program
44
+ .command('env:get')
45
+ .argument('<name>', 'Environment variable name')
46
+ .description('Get resolved value of specified environment variable (includes netlify.toml)')
47
+ .action(async (name, options, command) => {
48
+ await envGet(name, options, command)
49
+ })
50
+
51
+ module.exports = { createEnvGetCommand }
@@ -0,0 +1,93 @@
1
+ // @ts-check
2
+ const { readFile } = require('fs').promises
3
+
4
+ const AsciiTable = require('ascii-table')
5
+ const dotenv = require('dotenv')
6
+ const isEmpty = require('lodash/isEmpty')
7
+
8
+ const { exit, log, logJson } = require('../../utils')
9
+
10
+ /**
11
+ * The env:import command
12
+ * @param {string} fileName .env file to import
13
+ * @param {import('commander').OptionValues} options
14
+ * @param {import('../base-command').BaseCommand} command
15
+ * @returns {Promise<boolean>}
16
+ */
17
+ const envImport = async (fileName, options, command) => {
18
+ const { api, site } = command.netlify
19
+ const siteId = site.id
20
+
21
+ if (!siteId) {
22
+ log('No site id found, please run inside a site folder or `netlify link`')
23
+ return false
24
+ }
25
+
26
+ const siteData = await api.getSite({ siteId })
27
+
28
+ // Get current environment variables set in the UI
29
+ const {
30
+ build_settings: { env = {} },
31
+ } = siteData
32
+
33
+ let importedEnv = {}
34
+ try {
35
+ const envFileContents = await readFile(fileName, 'utf-8')
36
+ importedEnv = dotenv.parse(envFileContents)
37
+ } catch (error) {
38
+ log(error.message)
39
+ exit(1)
40
+ }
41
+
42
+ if (isEmpty(importedEnv)) {
43
+ log(`No environment variables found in file ${fileName} to import`)
44
+ return false
45
+ }
46
+
47
+ // Apply environment variable updates
48
+ const siteResult = await api.updateSite({
49
+ siteId,
50
+ body: {
51
+ build_settings: {
52
+ // Only set imported variables if --replaceExisting or otherwise merge
53
+ // imported ones with the current environment variables.
54
+ env: options.replaceExisting ? importedEnv : { ...env, ...importedEnv },
55
+ },
56
+ },
57
+ })
58
+
59
+ // Return new environment variables of site if using json flag
60
+ if (options.json) {
61
+ logJson(siteResult.build_settings.env)
62
+ return false
63
+ }
64
+
65
+ // List newly imported environment variables in a table
66
+ log(`site: ${siteData.name}`)
67
+ const table = new AsciiTable(`Imported environment variables`)
68
+
69
+ table.setHeading('Key', 'Value')
70
+ table.addRowMatrix(Object.entries(importedEnv))
71
+ log(table.toString())
72
+ }
73
+
74
+ /**
75
+ * Creates the `netlify env:import` command
76
+ * @param {import('../base-command').BaseCommand} program
77
+ * @returns
78
+ */
79
+ const createEnvImportCommand = (program) =>
80
+ program
81
+ .command('env:import')
82
+ .argument('<fileName>', '.env file to import')
83
+ .option(
84
+ '-r, --replaceExisting',
85
+ 'Replace all existing variables instead of merging them with the current ones',
86
+ false,
87
+ )
88
+ .description('Import and set environment variables from .env file')
89
+ .action(async (fileName, options, command) => {
90
+ await envImport(fileName, options, command)
91
+ })
92
+
93
+ module.exports = { createEnvImportCommand }
@@ -0,0 +1,63 @@
1
+ // @ts-check
2
+ const AsciiTable = require('ascii-table')
3
+ const isEmpty = require('lodash/isEmpty')
4
+
5
+ const { log, logJson } = require('../../utils')
6
+
7
+ /**
8
+ * The env:list command
9
+ * @param {import('commander').OptionValues} options
10
+ * @param {import('../base-command').BaseCommand} command
11
+ * @returns {Promise<boolean>}
12
+ */
13
+ const envList = async (options, command) => {
14
+ const { api, cachedConfig, site } = command.netlify
15
+ const siteId = site.id
16
+
17
+ if (!siteId) {
18
+ log('No site id found, please run inside a site folder or `netlify link`')
19
+ return false
20
+ }
21
+
22
+ const siteData = await api.getSite({ siteId })
23
+ const environment = Object.fromEntries(
24
+ Object.entries(cachedConfig.env)
25
+ // Omitting general variables to reduce noise.
26
+ .filter(([, variable]) => variable.sources[0] !== 'general')
27
+ .map(([key, variable]) => [key, variable.value]),
28
+ )
29
+
30
+ // Return json response for piping commands
31
+ if (options.json) {
32
+ logJson(environment)
33
+ return false
34
+ }
35
+
36
+ if (isEmpty(environment)) {
37
+ log(`No environment variables set for site ${siteData.name}`)
38
+ return false
39
+ }
40
+
41
+ // List environment variables using a table
42
+ log(`site: ${siteData.name}`)
43
+ const table = new AsciiTable(`Environment variables`)
44
+
45
+ table.setHeading('Key', 'Value')
46
+ table.addRowMatrix(Object.entries(environment))
47
+ log(table.toString())
48
+ }
49
+
50
+ /**
51
+ * Creates the `netlify env:list` command
52
+ * @param {import('../base-command').BaseCommand} program
53
+ * @returns
54
+ */
55
+ const createEnvListCommand = (program) =>
56
+ program
57
+ .command('env:list')
58
+ .description('Lists resolved environment variables for site (includes netlify.toml)')
59
+ .action(async (options, command) => {
60
+ await envList(options, command)
61
+ })
62
+
63
+ module.exports = { createEnvListCommand }
@@ -0,0 +1,67 @@
1
+ // @ts-check
2
+ const { log, logJson } = require('../../utils')
3
+
4
+ /**
5
+ * The env:set command
6
+ * @param {string} name Environment variable name
7
+ * @param {string} value Value to set to
8
+ * @param {import('commander').OptionValues} options
9
+ * @param {import('../base-command').BaseCommand} command
10
+ * @returns {Promise<boolean>}
11
+ */
12
+ const envSet = async (name, value, options, command) => {
13
+ const { api, site } = command.netlify
14
+ const siteId = site.id
15
+
16
+ if (!siteId) {
17
+ log('No site id found, please run inside a site folder or `netlify link`')
18
+ return false
19
+ }
20
+
21
+ const siteData = await api.getSite({ siteId })
22
+
23
+ // Get current environment variables set in the UI
24
+ const {
25
+ build_settings: { env = {} },
26
+ } = siteData
27
+
28
+ const newEnv = {
29
+ ...env,
30
+ [name]: value,
31
+ }
32
+
33
+ // Apply environment variable updates
34
+ const siteResult = await api.updateSite({
35
+ siteId,
36
+ body: {
37
+ build_settings: {
38
+ env: newEnv,
39
+ },
40
+ },
41
+ })
42
+
43
+ // Return new environment variables of site if using json flag
44
+ if (options.json) {
45
+ logJson(siteResult.build_settings.env)
46
+ return false
47
+ }
48
+
49
+ log(`Set environment variable ${name}=${value} for site ${siteData.name}`)
50
+ }
51
+
52
+ /**
53
+ * Creates the `netlify env:set` command
54
+ * @param {import('../base-command').BaseCommand} program
55
+ * @returns
56
+ */
57
+ const createEnvSetCommand = (program) =>
58
+ program
59
+ .command('env:set')
60
+ .argument('<name>', 'Environment variable name')
61
+ .argument('[value]', 'Value to set to', '')
62
+ .description('Set value of environment variable')
63
+ .action(async (name, value, options, command) => {
64
+ await envSet(name, value, options, command)
65
+ })
66
+
67
+ module.exports = { createEnvSetCommand }
@@ -0,0 +1,66 @@
1
+ // @ts-check
2
+ const { log, logJson } = require('../../utils')
3
+
4
+ /**
5
+ * The env:unset command
6
+ * @param {string} name Environment variable name
7
+ * @param {import('commander').OptionValues} options
8
+ * @param {import('../base-command').BaseCommand} command
9
+ * @returns {Promise<boolean>}
10
+ */
11
+ const envUnset = async (name, options, command) => {
12
+ const { api, site } = command.netlify
13
+ const siteId = site.id
14
+
15
+ if (!siteId) {
16
+ log('No site id found, please run inside a site folder or `netlify link`')
17
+ return false
18
+ }
19
+
20
+ const siteData = await api.getSite({ siteId })
21
+
22
+ // Get current environment variables set in the UI
23
+ const {
24
+ build_settings: { env = {} },
25
+ } = siteData
26
+
27
+ const newEnv = env
28
+
29
+ // Delete environment variable from current variables
30
+ delete newEnv[name]
31
+
32
+ // Apply environment variable updates
33
+ const siteResult = await api.updateSite({
34
+ siteId,
35
+ body: {
36
+ build_settings: {
37
+ env: newEnv,
38
+ },
39
+ },
40
+ })
41
+
42
+ // Return new environment variables of site if using json flag
43
+ if (options.json) {
44
+ logJson(siteResult.build_settings.env)
45
+ return false
46
+ }
47
+
48
+ log(`Unset environment variable ${name} for site ${siteData.name}`)
49
+ }
50
+
51
+ /**
52
+ * Creates the `netlify env:unset` command
53
+ * @param {import('../base-command').BaseCommand} program
54
+ * @returns
55
+ */
56
+ const createEnvUnsetCommand = (program) =>
57
+ program
58
+ .command('env:unset')
59
+ .aliases(['env:delete', 'env:remove'])
60
+ .argument('<name>', 'Environment variable name')
61
+ .description('Unset an environment variable which removes it from the UI')
62
+ .action(async (name, options, command) => {
63
+ await envUnset(name, options, command)
64
+ })
65
+
66
+ module.exports = { createEnvUnsetCommand }