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,30 +1,40 @@
1
- const path = require('path')
2
- const process = require('process')
1
+ // @ts-check
2
+ const { stat } = require('fs').promises
3
+ const { basename, resolve } = require('path')
4
+ const { cwd, env } = require('process')
3
5
 
4
6
  const { restoreConfig, updateConfig } = require('@netlify/config')
5
- const { flags: flagsLib } = require('@oclif/command')
6
- const chalk = require('chalk')
7
7
  const { get } = require('dot-prop')
8
8
  const inquirer = require('inquirer')
9
9
  const isObject = require('lodash/isObject')
10
10
  const prettyjson = require('prettyjson')
11
11
 
12
- const { cancelDeploy } = require('../lib/api')
13
- const { getBuildOptions, runBuild } = require('../lib/build')
14
- const { statAsync } = require('../lib/fs')
15
- const { normalizeFunctionsConfig } = require('../lib/functions/config')
16
- const { getLogMessage } = require('../lib/log')
17
- const { startSpinner, stopSpinner } = require('../lib/spinner')
18
- const Command = require('../utils/command')
19
- const { error, exit, getToken, log, logJson, warn } = require('../utils/command-helpers')
20
- const { deploySite } = require('../utils/deploy/deploy-site')
21
- const { deployEdgeHandlers } = require('../utils/edge-handlers')
22
- const { getFunctionsManifestPath, getInternalFunctionsDir } = require('../utils/functions')
23
- const { NETLIFYDEV, NETLIFYDEVERR, NETLIFYDEVLOG } = require('../utils/logo')
24
- const { openBrowser } = require('../utils/open-browser')
25
-
26
- const LinkCommand = require('./link')
27
- const SitesCreateCommand = require('./sites/create')
12
+ const { cancelDeploy } = require('../../lib/api')
13
+ const { getBuildOptions, runBuild } = require('../../lib/build')
14
+ const { normalizeFunctionsConfig } = require('../../lib/functions/config')
15
+ const { getLogMessage } = require('../../lib/log')
16
+ const { startSpinner, stopSpinner } = require('../../lib/spinner')
17
+ const {
18
+ NETLIFYDEV,
19
+ NETLIFYDEVERR,
20
+ NETLIFYDEVLOG,
21
+ chalk,
22
+ deployEdgeHandlers,
23
+ deploySite,
24
+ error,
25
+ exit,
26
+ generateDescriptionHelp,
27
+ generateExamplesHelp,
28
+ getFunctionsManifestPath,
29
+ getInternalFunctionsDir,
30
+ getToken,
31
+ log,
32
+ logJson,
33
+ openBrowser,
34
+ warn,
35
+ } = require('../../utils')
36
+ const { link } = require('../link')
37
+ const { sitesCreate } = require('../sites')
28
38
 
29
39
  const DEFAULT_DEPLOY_TIMEOUT = 1.2e6
30
40
 
@@ -43,26 +53,35 @@ const triggerDeploy = async ({ api, siteData, siteId }) => {
43
53
  }
44
54
  }
45
55
 
46
- const getDeployFolder = async ({ config, flags, site, siteData }) => {
56
+ /**
57
+ * g
58
+ * @param {object} config
59
+ * @param {object} config.config
60
+ * @param {import('commander').OptionValues} config.options
61
+ * @param {object} config.site
62
+ * @param {object} config.siteData
63
+ * @returns {Promise<string>}
64
+ */
65
+ const getDeployFolder = async ({ config, options, site, siteData }) => {
47
66
  let deployFolder
48
- if (flags.dir) {
49
- deployFolder = path.resolve(process.cwd(), flags.dir)
67
+ if (options.dir) {
68
+ deployFolder = resolve(cwd(), options.dir)
50
69
  } else if (get(config, 'build.publish')) {
51
- deployFolder = path.resolve(site.root, get(config, 'build.publish'))
70
+ deployFolder = resolve(site.root, get(config, 'build.publish'))
52
71
  } else if (get(siteData, 'build_settings.dir')) {
53
- deployFolder = path.resolve(site.root, get(siteData, 'build_settings.dir'))
72
+ deployFolder = resolve(site.root, get(siteData, 'build_settings.dir'))
54
73
  }
55
74
 
56
75
  if (!deployFolder) {
57
76
  log('Please provide a publish directory (e.g. "public" or "dist" or "."):')
58
- log(process.cwd())
77
+ log(cwd())
59
78
  const { promptPath } = await inquirer.prompt([
60
79
  {
61
80
  type: 'input',
62
81
  name: 'promptPath',
63
82
  message: 'Publish directory',
64
83
  default: '.',
65
- filter: (input) => path.resolve(process.cwd(), input),
84
+ filter: (input) => resolve(cwd(), input),
66
85
  },
67
86
  ])
68
87
  deployFolder = promptPath
@@ -72,9 +91,10 @@ const getDeployFolder = async ({ config, flags, site, siteData }) => {
72
91
  }
73
92
 
74
93
  const validateDeployFolder = async ({ deployFolder }) => {
75
- let stat
94
+ /** @type {import('fs').Stats} */
95
+ let stats
76
96
  try {
77
- stat = await statAsync(deployFolder)
97
+ stats = await stat(deployFolder)
78
98
  } catch (error_) {
79
99
  if (error_.code === 'ENOENT') {
80
100
  return error(`No such directory ${deployFolder}! Did you forget to run a build?`)
@@ -87,33 +107,43 @@ const validateDeployFolder = async ({ deployFolder }) => {
87
107
  throw error_
88
108
  }
89
109
 
90
- if (!stat.isDirectory()) {
110
+ if (!stats.isDirectory()) {
91
111
  return error('Deploy target must be a path to a directory')
92
112
  }
93
- return stat
113
+ return stats
94
114
  }
95
115
 
96
- const getFunctionsFolder = ({ config, flags, site, siteData }) => {
116
+ /**
117
+ * get the functions directory
118
+ * @param {object} config
119
+ * @param {object} config.config
120
+ * @param {import('commander').OptionValues} config.options
121
+ * @param {object} config.site
122
+ * @param {object} config.siteData
123
+ * @returns {string}
124
+ */
125
+ const getFunctionsFolder = ({ config, options, site, siteData }) => {
97
126
  let functionsFolder
98
127
  // Support "functions" and "Functions"
99
128
  const funcConfig = config.functionsDirectory
100
- if (flags.functions) {
101
- functionsFolder = path.resolve(process.cwd(), flags.functions)
129
+ if (options.functions) {
130
+ functionsFolder = resolve(cwd(), options.functions)
102
131
  } else if (funcConfig) {
103
- functionsFolder = path.resolve(site.root, funcConfig)
132
+ functionsFolder = resolve(site.root, funcConfig)
104
133
  } else if (get(siteData, 'build_settings.functions_dir')) {
105
- functionsFolder = path.resolve(site.root, get(siteData, 'build_settings.functions_dir'))
134
+ functionsFolder = resolve(site.root, get(siteData, 'build_settings.functions_dir'))
106
135
  }
107
136
  return functionsFolder
108
137
  }
109
138
 
110
139
  const validateFunctionsFolder = async ({ functionsFolder }) => {
111
- let stat
140
+ /** @type {import('fs').Stats} */
141
+ let stats
112
142
  if (functionsFolder) {
113
143
  // we used to hard error if functions folder is specified but doesn't exist
114
144
  // but this was too strict for onboarding. we can just log a warning.
115
145
  try {
116
- stat = await statAsync(functionsFolder)
146
+ stats = await stat(functionsFolder)
117
147
  } catch (error_) {
118
148
  if (error_.code === 'ENOENT') {
119
149
  log(
@@ -127,11 +157,11 @@ const validateFunctionsFolder = async ({ functionsFolder }) => {
127
157
  }
128
158
  }
129
159
 
130
- if (stat && !stat.isDirectory()) {
160
+ if (stats && !stats.isDirectory()) {
131
161
  error('Functions folder must be a path to a directory')
132
162
  }
133
163
 
134
- return stat
164
+ return stats
135
165
  }
136
166
 
137
167
  const validateFolders = async ({ deployFolder, functionsFolder }) => {
@@ -154,12 +184,12 @@ const getDeployFilesFilter = ({ deployFolder, site }) => {
154
184
  return true
155
185
  }
156
186
 
157
- const basename = path.basename(filename)
187
+ const base = basename(filename)
158
188
  const skipFile =
159
- (skipNodeModules && basename === 'node_modules') ||
160
- (basename.startsWith('.') && basename !== '.well-known') ||
161
- basename.startsWith('__MACOSX') ||
162
- basename.includes('/.')
189
+ (skipNodeModules && base === 'node_modules') ||
190
+ (base.startsWith('.') && base !== '.well-known') ||
191
+ base.startsWith('__MACOSX') ||
192
+ base.includes('/.')
163
193
 
164
194
  return !skipFile
165
195
  }
@@ -226,18 +256,50 @@ const reportDeployError = ({ error_, failAndExit }) => {
226
256
  }
227
257
  }
228
258
 
259
+ const deployProgressCb = function () {
260
+ /**
261
+ * @type {Record<string, import('ora').Ora>}
262
+ */
263
+ const events = {}
264
+ return (event) => {
265
+ switch (event.phase) {
266
+ case 'start': {
267
+ events[event.type] = startSpinner({
268
+ text: event.msg,
269
+ })
270
+ return
271
+ }
272
+ case 'progress': {
273
+ const spinner = events[event.type]
274
+ if (spinner) {
275
+ spinner.text = event.msg
276
+ }
277
+ return
278
+ }
279
+ case 'stop':
280
+ default: {
281
+ stopSpinner({ spinner: events[event.type], text: event.msg })
282
+ delete events[event.type]
283
+ }
284
+ }
285
+ }
286
+ }
287
+
229
288
  const runDeploy = async ({
230
289
  alias,
231
290
  api,
232
291
  configPath,
233
292
  deployFolder,
293
+ deployTimeout,
234
294
  deployToProduction,
235
- flags,
236
295
  functionsConfig,
237
296
  functionsFolder,
297
+ silent,
238
298
  site,
239
299
  siteData,
240
300
  siteId,
301
+ skipFunctionsCache,
302
+ title,
241
303
  }) => {
242
304
  let results
243
305
  let deployId
@@ -249,11 +311,9 @@ const runDeploy = async ({
249
311
  }
250
312
 
251
313
  const draft = !deployToProduction && !alias
252
- const title = flags.message
253
314
  results = await api.createSiteDeploy({ siteId, title, body: { draft, branch: alias } })
254
315
  deployId = results.id
255
316
 
256
- const silent = flags.json || flags.silent
257
317
  await deployEdgeHandlers({
258
318
  site,
259
319
  deployId,
@@ -266,15 +326,15 @@ const runDeploy = async ({
266
326
  // functions from the rightmost directories. In this case, we want user
267
327
  // functions to take precedence over internal functions.
268
328
  const functionDirectories = [internalFunctionsFolder, functionsFolder].filter(Boolean)
269
- const skipFunctionsCache = flags['skip-functions-cache'] === true
270
329
  const manifestPath = skipFunctionsCache ? null : await getFunctionsManifestPath({ base: site.root })
271
330
 
331
+ // @ts-ignore
272
332
  results = await deploySite(api, siteId, deployFolder, {
273
333
  configPath,
274
334
  fnDir: functionDirectories,
275
335
  functionsConfig,
276
336
  statusCb: silent ? () => {} : deployProgressCb(),
277
- deployTimeout: flags.timeout * SEC_TO_MILLISEC || DEFAULT_DEPLOY_TIMEOUT,
337
+ deployTimeout,
278
338
  syncFileLimit: SYNC_FILE_LIMIT,
279
339
  // pass an existing deployId to update
280
340
  deployId,
@@ -304,24 +364,40 @@ const runDeploy = async ({
304
364
  }
305
365
  }
306
366
 
307
- const handleBuild = async ({ context, flags }) => {
308
- if (!flags.build) {
367
+ /**
368
+ *
369
+ * @param {object} config
370
+ * @param {*} config.cachedConfig
371
+ * @param {import('commander').OptionValues} config.options The options of the command
372
+ * @returns
373
+ */
374
+ const handleBuild = async ({ cachedConfig, options }) => {
375
+ if (!options.build) {
309
376
  return {}
310
377
  }
311
378
  const [token] = await getToken()
312
- const options = await getBuildOptions({
313
- context,
379
+ const resolvedOptions = await getBuildOptions({
380
+ cachedConfig,
314
381
  token,
315
- flags,
382
+ options,
316
383
  })
317
- const { configMutations, exitCode, newConfig } = await runBuild(options)
384
+ const { configMutations, exitCode, newConfig } = await runBuild(resolvedOptions)
318
385
  if (exitCode !== 0) {
319
386
  exit(exitCode)
320
387
  }
321
388
  return { newConfig, configMutations }
322
389
  }
323
390
 
324
- const printResults = ({ deployToProduction, flags, results }) => {
391
+ /**
392
+ *
393
+ * @param {object} config
394
+ * @param {boolean} config.deployToProduction
395
+ * @param {boolean} config.json If the result should be printed as json message
396
+ * @param {boolean} config.runBuildCommand If the build command should be run
397
+ * @param {object} config.results
398
+ * @returns {void}
399
+ */
400
+ const printResults = ({ deployToProduction, json, results, runBuildCommand }) => {
325
401
  const msgData = {
326
402
  Logs: `${results.logsUrl}`,
327
403
  'Unique Deploy URL': results.deployUrl,
@@ -338,7 +414,7 @@ const printResults = ({ deployToProduction, flags, results }) => {
338
414
  log()
339
415
 
340
416
  // Json response for piping commands
341
- if (flags.json) {
417
+ if (json) {
342
418
  const jsonData = {
343
419
  name: results.name,
344
420
  site_id: results.site_id,
@@ -359,133 +435,181 @@ const printResults = ({ deployToProduction, flags, results }) => {
359
435
  if (!deployToProduction) {
360
436
  log()
361
437
  log('If everything looks good on your draft URL, deploy it to your main site URL with the --prod flag.')
362
- log(`${chalk.cyanBright.bold(`netlify deploy${flags.build ? ' --build' : ''} --prod`)}`)
438
+ log(`${chalk.cyanBright.bold(`netlify deploy${runBuildCommand ? ' --build' : ''} --prod`)}`)
363
439
  log()
364
440
  }
365
441
  }
366
442
  }
367
443
 
368
- class DeployCommand extends Command {
369
- async run() {
370
- const { flags } = this.parse(DeployCommand)
371
- const { api, site } = this.netlify
372
- const alias = flags.alias || flags.branch
444
+ /**
445
+ * The deploy command
446
+ * @param {import('commander').OptionValues} options
447
+ * @param {import('../base-command').BaseCommand} command
448
+ */
449
+ const deploy = async (options, command) => {
450
+ const { api, site } = command.netlify
451
+ const alias = options.alias || options.branch
373
452
 
374
- this.setAnalyticsPayload({ open: flags.open, prod: flags.prod, json: flags.json, alias: Boolean(alias) })
453
+ command.setAnalyticsPayload({ open: options.open, prod: options.prod, json: options.json, alias: Boolean(alias) })
375
454
 
376
- if (flags.branch) {
377
- warn('--branch flag has been renamed to --alias and will be removed in future versions')
378
- }
455
+ if (options.branch) {
456
+ warn('--branch flag has been renamed to --alias and will be removed in future versions')
457
+ }
379
458
 
380
- await this.authenticate(flags.auth)
381
-
382
- let siteId = flags.site || site.id
383
- let siteData = {}
384
- if (siteId) {
385
- try {
386
- siteData = await api.getSite({ siteId })
387
- } catch (error_) {
388
- // TODO specifically handle known cases (e.g. no account access)
389
- if (error_.status === 404) {
390
- error('Site not found')
391
- } else {
392
- error(error_.message)
393
- }
394
- }
395
- } else {
396
- log("This folder isn't linked to a site yet")
397
- const NEW_SITE = '+ Create & configure a new site'
398
- const EXISTING_SITE = 'Link this directory to an existing site'
399
-
400
- const initializeOpts = [EXISTING_SITE, NEW_SITE]
401
-
402
- const { initChoice } = await inquirer.prompt([
403
- {
404
- type: 'list',
405
- name: 'initChoice',
406
- message: 'What would you like to do?',
407
- choices: initializeOpts,
408
- },
409
- ])
410
- // create site or search for one
411
- if (initChoice === NEW_SITE) {
412
- // run site:create command
413
- siteData = await SitesCreateCommand.run([])
414
- site.id = siteData.id
415
- siteId = site.id
416
- } else if (initChoice === EXISTING_SITE) {
417
- // run link command
418
- siteData = await LinkCommand.run([], false)
419
- site.id = siteData.id
420
- siteId = site.id
459
+ await command.authenticate(options.auth)
460
+
461
+ let siteId = options.site || site.id
462
+ let siteData = {}
463
+ if (siteId) {
464
+ try {
465
+ siteData = await api.getSite({ siteId })
466
+ } catch (error_) {
467
+ // TODO specifically handle known cases (e.g. no account access)
468
+ if (error_.status === 404) {
469
+ error('Site not found')
470
+ } else {
471
+ error(error_.message)
421
472
  }
422
473
  }
474
+ } else {
475
+ log("This folder isn't linked to a site yet")
476
+ const NEW_SITE = '+ Create & configure a new site'
477
+ const EXISTING_SITE = 'Link this directory to an existing site'
423
478
 
424
- const deployToProduction = flags.prod || (flags.prodIfUnlocked && !siteData.published_deploy.locked)
479
+ const initializeOpts = [EXISTING_SITE, NEW_SITE]
425
480
 
426
- if (flags.trigger) {
427
- return triggerDeploy({ api, siteId, siteData })
481
+ const { initChoice } = await inquirer.prompt([
482
+ {
483
+ type: 'list',
484
+ name: 'initChoice',
485
+ message: 'What would you like to do?',
486
+ choices: initializeOpts,
487
+ },
488
+ ])
489
+ // create site or search for one
490
+ if (initChoice === NEW_SITE) {
491
+ siteData = await sitesCreate({}, command)
492
+ site.id = siteData.id
493
+ siteId = site.id
494
+ } else if (initChoice === EXISTING_SITE) {
495
+ siteData = await link({}, command)
496
+ site.id = siteData.id
497
+ siteId = site.id
428
498
  }
499
+ }
429
500
 
430
- const { newConfig, configMutations = [] } = await handleBuild({ context: this, flags })
431
- const config = newConfig || this.netlify.config
432
-
433
- const deployFolder = await getDeployFolder({ flags, config, site, siteData })
434
- const functionsFolder = getFunctionsFolder({ flags, config, site, siteData })
435
- const { configPath } = site
436
-
437
- log(
438
- prettyjson.render({
439
- 'Deploy path': deployFolder,
440
- 'Functions path': functionsFolder,
441
- 'Configuration path': configPath,
442
- }),
443
- )
501
+ const deployToProduction = options.prod || (options.prodIfUnlocked && !siteData.published_deploy.locked)
444
502
 
445
- const { functionsFolderStat } = await validateFolders({
446
- deployFolder,
447
- functionsFolder,
448
- })
449
- const functionsConfig = normalizeFunctionsConfig({ functionsConfig: config.functions, projectRoot: site.root })
503
+ if (options.trigger) {
504
+ return triggerDeploy({ api, siteId, siteData })
505
+ }
450
506
 
451
- const redirectsPath = `${deployFolder}/_redirects`
452
- await updateConfig(configMutations, {
453
- buildDir: deployFolder,
454
- configPath,
455
- redirectsPath,
456
- context: this.netlify.cachedConfig.context,
457
- branch: this.netlify.cachedConfig.branch,
458
- })
459
- const results = await runDeploy({
460
- flags,
461
- deployToProduction,
462
- site,
463
- siteData,
464
- api,
465
- siteId,
466
- deployFolder,
467
- functionsConfig,
468
- configPath,
469
- // pass undefined functionsFolder if doesn't exist
470
- functionsFolder: functionsFolderStat && functionsFolder,
471
- alias,
472
- })
507
+ const { newConfig, configMutations = [] } = await handleBuild({
508
+ cachedConfig: command.netlify.cachedConfig,
509
+ options,
510
+ })
511
+ const config = newConfig || command.netlify.config
512
+
513
+ const deployFolder = await getDeployFolder({ options, config, site, siteData })
514
+ const functionsFolder = getFunctionsFolder({ options, config, site, siteData })
515
+ const { configPath } = site
516
+
517
+ log(
518
+ prettyjson.render({
519
+ 'Deploy path': deployFolder,
520
+ 'Functions path': functionsFolder,
521
+ 'Configuration path': configPath,
522
+ }),
523
+ )
524
+
525
+ const { functionsFolderStat } = await validateFolders({
526
+ deployFolder,
527
+ functionsFolder,
528
+ })
529
+ const functionsConfig = normalizeFunctionsConfig({ functionsConfig: config.functions, projectRoot: site.root })
530
+
531
+ const redirectsPath = `${deployFolder}/_redirects`
532
+ // @ts-ignore
533
+ await updateConfig(configMutations, {
534
+ buildDir: deployFolder,
535
+ configPath,
536
+ redirectsPath,
537
+ context: command.netlify.cachedConfig.context,
538
+ branch: command.netlify.cachedConfig.branch,
539
+ })
540
+ const results = await runDeploy({
541
+ alias,
542
+ api,
543
+ configPath,
544
+ deployFolder,
545
+ deployTimeout: options.timeout * SEC_TO_MILLISEC || DEFAULT_DEPLOY_TIMEOUT,
546
+ deployToProduction,
547
+ functionsConfig,
548
+ // pass undefined functionsFolder if doesn't exist
549
+ functionsFolder: functionsFolderStat && functionsFolder,
550
+ silent: options.json || options.silent,
551
+ site,
552
+ siteData,
553
+ siteId,
554
+ skipFunctionsCache: options.skipFunctionsCache,
555
+ title: options.message,
556
+ })
473
557
 
474
- await restoreConfig(configMutations, { buildDir: deployFolder, configPath, redirectsPath })
558
+ // @ts-ignore
559
+ await restoreConfig(configMutations, { buildDir: deployFolder, configPath, redirectsPath })
475
560
 
476
- printResults({ flags, results, deployToProduction })
561
+ printResults({
562
+ runBuildCommand: options.build,
563
+ json: options.json,
564
+ results,
565
+ deployToProduction,
566
+ })
477
567
 
478
- if (flags.open) {
479
- const urlToOpen = deployToProduction ? results.siteUrl : results.deployUrl
480
- await openBrowser({ url: urlToOpen })
481
- exit()
482
- }
568
+ if (options.open) {
569
+ const urlToOpen = deployToProduction ? results.siteUrl : results.deployUrl
570
+ await openBrowser({ url: urlToOpen })
571
+ exit()
483
572
  }
484
573
  }
485
574
 
486
- DeployCommand.description = `Create a new deploy from the contents of a folder
487
-
488
- Deploys from the build settings found in the netlify.toml file, or settings from the API.
575
+ /**
576
+ * Creates the `netlify deploy` command
577
+ * @param {import('../base-command').BaseCommand} program
578
+ * @returns
579
+ */
580
+ const createDeployCommand = (program) =>
581
+ program
582
+ .command('deploy')
583
+ .description('Create a new deploy from the contents of a folder')
584
+ .option('-d, --dir <path>', 'Specify a folder to deploy')
585
+ .option('-f, --functions', 'Specify a functions folder to deploy')
586
+ .option('-p, --prod', 'Deploy to production', false)
587
+ .option('--prodIfUnlocked', 'Deploy to production if unlocked, create a draft otherwise', false)
588
+ .option(
589
+ '--alias <name>',
590
+ 'Specifies the alias for deployment, the string at the beginning of the deploy subdomain. Useful for creating predictable deployment URLs. Avoid setting an alias string to the same value as a deployed branch. `alias` doesn’t create a branch deploy and can’t be used in conjunction with the branch subdomain feature. Maximum 37 characters.',
591
+ )
592
+ .option(
593
+ '-b, --branch <name>',
594
+ 'Serves the same functionality as --alias. Deprecated and will be removed in future versions',
595
+ )
596
+ .option('-o, --open', 'Open site after deploy', false)
597
+ .option('-m, --message <message>', 'A short message to include in the deploy log')
598
+ .option('-a, --auth <token>', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN)
599
+ .option('-s, --site <id>', 'A site ID to deploy to', env.NETLIFY_SITE_ID)
600
+ .option('--json', 'Output deployment data as JSON')
601
+ .option('--timeout <number>', 'Timeout to wait for deployment to finish', (value) => Number.parseInt(value))
602
+ .option('--trigger', 'Trigger a new build of your site on Netlify without uploading local files')
603
+ .option('--build', 'Run build command before deploying')
604
+ .option(
605
+ '--skip-functions-cache',
606
+ 'Ignore any functions created as part of a previous `build` or `deploy` commands, forcing them to be bundled again as part of the deployment',
607
+ false,
608
+ )
609
+ .addHelpText(
610
+ 'after',
611
+ generateDescriptionHelp(
612
+ `Deploys from the build settings found in the netlify.toml file, or settings from the API.
489
613
 
490
614
  The following environment variables can be used to override configuration file lookups and prompts:
491
615
 
@@ -547,121 +671,25 @@ Function entry points are determined by the file name and name of the folder the
547
671
  functions/
548
672
  ├── aFolderlessFunctionEntrypoint.js
549
673
  └── functionName/
550
- ├── notTheEntryPoint.js
551
- └── functionName.js
674
+ ├── notTheEntryPoint.js
675
+ └── functionName.js
552
676
  \`\`\`
553
677
 
554
- Support for package.json's main field, and intrinsic index.js entrypoints are coming soon.
555
- `
556
-
557
- DeployCommand.examples = [
558
- 'netlify deploy',
559
- 'netlify deploy --prod',
560
- 'netlify deploy --prod --open',
561
- 'netlify deploy --prodIfUnlocked',
562
- 'netlify deploy --message "A message with an $ENV_VAR"',
563
- 'netlify deploy --auth $NETLIFY_AUTH_TOKEN',
564
- 'netlify deploy --trigger',
565
- ]
566
-
567
- DeployCommand.flags = {
568
- dir: flagsLib.string({
569
- char: 'd',
570
- description: 'Specify a folder to deploy',
571
- }),
572
- functions: flagsLib.string({
573
- char: 'f',
574
- description: 'Specify a functions folder to deploy',
575
- }),
576
- prod: flagsLib.boolean({
577
- char: 'p',
578
- description: 'Deploy to production',
579
- default: false,
580
- exclusive: ['alias', 'branch', 'prodIfUnlocked'],
581
- }),
582
- prodIfUnlocked: flagsLib.boolean({
583
- description: 'Deploy to production if unlocked, create a draft otherwise',
584
- default: false,
585
- exclusive: ['alias', 'branch', 'prod'],
586
- }),
587
- alias: flagsLib.string({
588
- description:
589
- 'Specifies the alias for deployment, the string at the beginning of the deploy subdomain. Useful for creating predictable deployment URLs. Avoid setting an alias string to the same value as a deployed branch. `alias` doesn’t create a branch deploy and can’t be used in conjunction with the branch subdomain feature. Maximum 37 characters.',
590
- }),
591
- branch: flagsLib.string({
592
- char: 'b',
593
- description: 'Serves the same functionality as --alias. Deprecated and will be removed in future versions',
594
- }),
595
- open: flagsLib.boolean({
596
- char: 'o',
597
- description: 'Open site after deploy',
598
- default: false,
599
- }),
600
- message: flagsLib.string({
601
- char: 'm',
602
- description: 'A short message to include in the deploy log',
603
- }),
604
- auth: flagsLib.string({
605
- char: 'a',
606
- description: 'Netlify auth token to deploy with',
607
- env: 'NETLIFY_AUTH_TOKEN',
608
- }),
609
- site: flagsLib.string({
610
- char: 's',
611
- description: 'A site ID to deploy to',
612
- env: 'NETLIFY_SITE_ID',
613
- }),
614
- json: flagsLib.boolean({
615
- description: 'Output deployment data as JSON',
616
- }),
617
- timeout: flagsLib.integer({
618
- description: 'Timeout to wait for deployment to finish',
619
- }),
620
- trigger: flagsLib.boolean({
621
- description: 'Trigger a new build of your site on Netlify without uploading local files',
622
- exclusive: ['build'],
623
- }),
624
- build: flagsLib.boolean({
625
- description: 'Run build command before deploying',
626
- }),
627
- 'skip-functions-cache': flagsLib.boolean({
628
- description:
629
- 'Ignore any functions created as part of a previous `build` or `deploy` commands, forcing them to be bundled again as part of the deployment',
630
- default: false,
631
- }),
632
- ...DeployCommand.flags,
633
- }
634
-
635
- const deployProgressCb = function () {
636
- const events = {}
637
- // event: {
638
- // type: name-of-step
639
- // msg: msg to print
640
- // phase: [start, progress, stop]
641
- // }
642
- //
643
- return (event) => {
644
- switch (event.phase) {
645
- case 'start': {
646
- events[event.type] = startSpinner({
647
- text: event.msg,
648
- })
649
- return
650
- }
651
- case 'progress': {
652
- const spinner = events[event.type]
653
- if (spinner) {
654
- spinner.text = event.msg
655
- }
656
- return
657
- }
658
- case 'stop':
659
- default: {
660
- stopSpinner({ spinner: events[event.type], text: event.msg })
661
- delete events[event.type]
662
- }
663
- }
664
- }
665
- }
678
+ Support for package.json's main field, and intrinsic index.js entrypoints are coming soon.`,
679
+ ),
680
+ )
681
+ .addHelpText(
682
+ 'after',
683
+ generateExamplesHelp([
684
+ 'netlify deploy',
685
+ 'netlify deploy --prod',
686
+ 'netlify deploy --prod --open',
687
+ 'netlify deploy --prodIfUnlocked',
688
+ 'netlify deploy --message "A message with an $ENV_VAR"',
689
+ 'netlify deploy --auth $NETLIFY_AUTH_TOKEN',
690
+ 'netlify deploy --trigger',
691
+ ]),
692
+ )
693
+ .action(deploy)
666
694
 
667
- module.exports = DeployCommand
695
+ module.exports = { createDeployCommand }