netlify-cli 8.0.10 → 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 +471 -2182
  3. package/package.json +12 -35
  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 +5 -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 +7 -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 +4 -2
  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 -261
  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,18 +1,12 @@
1
- const { flags: flagsLib } = require('@oclif/command')
2
- const chalk = require('chalk')
1
+ // @ts-check
3
2
  const dotProp = require('dot-prop')
4
3
  const inquirer = require('inquirer')
5
4
  const isEmpty = require('lodash/isEmpty')
6
5
 
7
- const Command = require('../utils/command')
8
- const { exit, log } = require('../utils/command-helpers')
9
- const { getRepoData } = require('../utils/get-repo-data')
10
- const ensureNetlifyIgnore = require('../utils/gitignore')
11
- const { configureRepo } = require('../utils/init/config')
12
- const { track } = require('../utils/telemetry')
13
-
14
- const LinkCommand = require('./link')
15
- const SitesCreateCommand = require('./sites/create')
6
+ const { chalk, ensureNetlifyIgnore, exit, getRepoData, log, track } = require('../../utils')
7
+ const { configureRepo } = require('../../utils/init/config')
8
+ const { link } = require('../link')
9
+ const { sitesCreate } = require('../sites')
16
10
 
17
11
  const persistState = ({ siteInfo, state }) => {
18
12
  // Save to .netlify/state.json file
@@ -37,8 +31,14 @@ const logExistingAndExit = ({ siteInfo }) => {
37
31
  exit()
38
32
  }
39
33
 
40
- const createNewSiteAndExit = async ({ state }) => {
41
- const siteInfo = await SitesCreateCommand.run([])
34
+ /**
35
+ * Creates and new site and exits the process
36
+ * @param {object} config
37
+ * @param {*} config.state
38
+ * @param {import('../base-command').BaseCommand} config.command
39
+ */
40
+ const createNewSiteAndExit = async ({ command, state }) => {
41
+ const siteInfo = await sitesCreate({}, command)
42
42
 
43
43
  log(`"${siteInfo.name}" site was created`)
44
44
  log()
@@ -82,7 +82,14 @@ const logGitSetupInstructionsAndExit = () => {
82
82
  exit()
83
83
  }
84
84
 
85
- const handleNoGitRemoteAndExit = async ({ error, state }) => {
85
+ /**
86
+ * Handles the case where no git remote was found.
87
+ * @param {object} config
88
+ * @param {import('../base-command').BaseCommand} config.command
89
+ * @param {object} config.error
90
+ * @param {object} config.state
91
+ */
92
+ const handleNoGitRemoteAndExit = async ({ command, error, state }) => {
86
93
  log()
87
94
  log(`${chalk.yellow('No git remote was found, would you like to set one up?')}`)
88
95
  log(`
@@ -112,13 +119,17 @@ git remote add origin https://github.com/YourUserName/RepoName.git
112
119
  ])
113
120
 
114
121
  if (noGitRemoteChoice === NEW_SITE_NO_GIT) {
115
- await createNewSiteAndExit({ state })
122
+ await createNewSiteAndExit({ state, command })
116
123
  } else if (noGitRemoteChoice === NO_ABORT) {
117
124
  logGitSetupInstructionsAndExit()
118
125
  }
119
126
  }
120
127
 
121
- const createOrLinkSiteToRepo = async () => {
128
+ /**
129
+ * Creates a new site or links an existing one to the repository
130
+ * @param {import('../base-command').BaseCommand} command
131
+ */
132
+ const createOrLinkSiteToRepo = async (command) => {
122
133
  const NEW_SITE = '+ Create & configure a new site'
123
134
  const EXISTING_SITE = '⇄ Connect this directory to an existing Netlify site'
124
135
 
@@ -139,11 +150,11 @@ const createOrLinkSiteToRepo = async () => {
139
150
  type: 'new site',
140
151
  })
141
152
  // run site:create command
142
- return await SitesCreateCommand.run([])
153
+ return await sitesCreate({}, command)
143
154
  }
144
155
  if (initChoice === EXISTING_SITE) {
145
156
  // run link command
146
- return await LinkCommand.run([], false)
157
+ return await link({}, command)
147
158
  }
148
159
  }
149
160
 
@@ -155,65 +166,65 @@ const logExistingRepoSetupAndExit = ({ repoUrl, siteName }) => {
155
166
  exit()
156
167
  }
157
168
 
158
- class InitCommand extends Command {
159
- async run() {
160
- const { flags } = this.parse(InitCommand)
161
-
162
- this.setAnalyticsPayload({ manual: flags.manual, force: flags.force })
163
-
164
- const { netlify } = this
165
- const { repositoryRoot, state } = netlify
166
- let { siteInfo } = this.netlify
169
+ /**
170
+ * The init command
171
+ * @param {import('commander').OptionValues} options
172
+ * @param {import('../base-command').BaseCommand} command
173
+ */
174
+ const init = async (options, command) => {
175
+ command.setAnalyticsPayload({ manual: options.manual, force: options.force })
167
176
 
168
- // Check logged in status
169
- await this.authenticate()
177
+ const { repositoryRoot, state } = command.netlify
178
+ let { siteInfo } = command.netlify
170
179
 
171
- // Add .netlify to .gitignore file
172
- await ensureNetlifyIgnore(repositoryRoot)
180
+ // Check logged in status
181
+ await command.authenticate()
173
182
 
174
- const repoUrl = getRepoUrl({ siteInfo })
175
- if (repoUrl && !flags.force) {
176
- logExistingAndExit({ siteInfo })
177
- }
183
+ // Add .netlify to .gitignore file
184
+ await ensureNetlifyIgnore(repositoryRoot)
178
185
 
179
- // Look for local repo
180
- const repoData = await getRepoData({ remoteName: flags.gitRemoteName })
181
- if (repoData.error) {
182
- await handleNoGitRemoteAndExit({ error: repoData.error, state })
183
- }
186
+ const repoUrl = getRepoUrl({ siteInfo })
187
+ if (repoUrl && !options.force) {
188
+ logExistingAndExit({ siteInfo })
189
+ }
184
190
 
185
- if (isEmpty(siteInfo)) {
186
- siteInfo = await createOrLinkSiteToRepo()
187
- }
191
+ // Look for local repo
192
+ const repoData = await getRepoData({ remoteName: options.gitRemoteName })
193
+ if (repoData.error) {
194
+ await handleNoGitRemoteAndExit({ command, error: repoData.error, state })
195
+ }
188
196
 
189
- // Check for existing CI setup
190
- const remoteBuildRepo = getRepoUrl({ siteInfo })
191
- if (remoteBuildRepo && !flags.force) {
192
- logExistingRepoSetupAndExit({ siteName: siteInfo.name, repoUrl: remoteBuildRepo })
193
- }
197
+ if (isEmpty(siteInfo)) {
198
+ siteInfo = await createOrLinkSiteToRepo(command)
199
+ }
194
200
 
195
- persistState({ state, siteInfo })
201
+ // Check for existing CI setup
202
+ const remoteBuildRepo = getRepoUrl({ siteInfo })
203
+ if (remoteBuildRepo && !options.force) {
204
+ logExistingRepoSetupAndExit({ siteName: siteInfo.name, repoUrl: remoteBuildRepo })
205
+ }
196
206
 
197
- await configureRepo({ context: this, siteId: siteInfo.id, repoData, manual: flags.manual })
207
+ persistState({ state, siteInfo })
198
208
 
199
- return siteInfo
200
- }
201
- }
209
+ await configureRepo({ command, siteId: siteInfo.id, repoData, manual: options.manual })
202
210
 
203
- InitCommand.description = `Configure continuous deployment for a new or existing site. To create a new site without continuous deployment, use \`netlify sites:create\``
204
-
205
- InitCommand.flags = {
206
- manual: flagsLib.boolean({
207
- char: 'm',
208
- description: 'Manually configure a git remote for CI',
209
- }),
210
- force: flagsLib.boolean({
211
- description: 'Reinitialize CI hooks if the linked site is already configured to use CI',
212
- }),
213
- gitRemoteName: flagsLib.string({
214
- description: 'Name of Git remote to use. e.g. "origin"',
215
- }),
216
- ...InitCommand.flags,
211
+ return siteInfo
217
212
  }
218
213
 
219
- module.exports = InitCommand
214
+ /**
215
+ * Creates the `netlify init` command
216
+ * @param {import('../base-command').BaseCommand} program
217
+ * @returns
218
+ */
219
+ const createInitCommand = (program) =>
220
+ program
221
+ .command('init')
222
+ .description(
223
+ 'Configure continuous deployment for a new or existing site. To create a new site without continuous deployment, use `netlify sites:create`',
224
+ )
225
+ .option('-m, --manual', 'Manually configure a git remote for CI')
226
+ .option('--force', 'Manually configure a git remote for CI')
227
+ .option('--gitRemoteName <name>', 'Name of Git remote to use. e.g. "origin"')
228
+ .action(init)
229
+
230
+ module.exports = { createInitCommand, init }
@@ -0,0 +1,6 @@
1
+ const { createLinkCommand, link } = require('./link')
2
+
3
+ module.exports = {
4
+ createLinkCommand,
5
+ link,
6
+ }
@@ -1,16 +1,30 @@
1
+ // @ts-check
2
+ const { join, relative } = require('path')
1
3
  const path = require('path')
4
+ const { cwd } = require('process')
2
5
 
3
- const chalk = require('chalk')
4
6
  const inquirer = require('inquirer')
5
7
  const isEmpty = require('lodash/isEmpty')
6
8
 
7
9
  const { listSites } = require('../../lib/api')
8
- const { error, exit, log } = require('../command-helpers')
9
- const { getRepoData } = require('../get-repo-data')
10
- const { track } = require('../telemetry')
10
+ const {
11
+ chalk,
12
+ ensureNetlifyIgnore,
13
+ error,
14
+ exit,
15
+ generateExamplesHelp,
16
+ getRepoData,
17
+ log,
18
+ track,
19
+ } = require('../../utils')
11
20
 
12
- module.exports = async function linkPrompts(context, flags = {}) {
13
- const { api, state } = context.netlify
21
+ /**
22
+ *
23
+ * @param {import('../base-command').NetlifyOptions} netlify
24
+ * @param {import('commander').OptionValues} options
25
+ */
26
+ const linkPrompt = async (netlify, options) => {
27
+ const { api, state } = netlify
14
28
 
15
29
  const SITE_NAME_PROMPT = 'Search by full or partial site name'
16
30
  const SITE_LIST_PROMPT = 'Choose from a list of your recently updated sites'
@@ -19,7 +33,7 @@ module.exports = async function linkPrompts(context, flags = {}) {
19
33
  let GIT_REMOTE_PROMPT = 'Use the current git remote origin URL'
20
34
  let site
21
35
  // Get git remote data if exists
22
- const repoData = await getRepoData({ remoteName: flags.gitRemoteName })
36
+ const repoData = await getRepoData({ remoteName: options.gitRemoteName })
23
37
 
24
38
  let linkChoices = [SITE_NAME_PROMPT, SITE_LIST_PROMPT, SITE_ID_PROMPT]
25
39
 
@@ -51,9 +65,7 @@ module.exports = async function linkPrompts(context, flags = {}) {
51
65
  const sites = await listSites({ api, options: { filter: 'all' } })
52
66
 
53
67
  if (isEmpty(sites)) {
54
- context.error(
55
- new Error(`You don't have any sites yet. Run ${chalk.cyanBright('netlify sites:create')} to create a site.`),
56
- )
68
+ error(`You don't have any sites yet. Run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
57
69
  }
58
70
 
59
71
  const matchingSites = sites.filter(
@@ -79,7 +91,7 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`)
79
91
  site = firstSite
80
92
  } else if (matchingSites.length > 1) {
81
93
  // Matches multiple sites. Users must choose which to link.
82
- console.log(`Found ${matchingSites.length} matching sites!`)
94
+ log(`Found ${matchingSites.length} matching sites!`)
83
95
 
84
96
  // Prompt which options
85
97
  const { selectedSite } = await inquirer.prompt([
@@ -134,7 +146,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
134
146
  }
135
147
 
136
148
  if (matchingSites.length > 1) {
137
- console.log(`Found ${matchingSites.length} matching sites!`)
149
+ log(`Found ${matchingSites.length} matching sites!`)
138
150
  const { selectedSite } = await inquirer.prompt([
139
151
  {
140
152
  type: 'list',
@@ -145,7 +157,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
145
157
  },
146
158
  ])
147
159
  if (!selectedSite) {
148
- context.error('No site selected')
160
+ error('No site selected')
149
161
  }
150
162
  site = selectedSite
151
163
  } else {
@@ -196,6 +208,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
196
208
  ])
197
209
 
198
210
  try {
211
+ // @ts-ignore types from API are wrong they cannot recognize `getSite` of API
199
212
  site = await api.getSite({ siteId })
200
213
  } catch (error_) {
201
214
  if (error_.status === 404) {
@@ -231,10 +244,136 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
231
244
  log(`Site url: ${chalk.cyanBright(site.ssl_url || site.url)}`)
232
245
  log()
233
246
 
234
- log(`Site id saved to ${path.join(context.netlify.site.root, '/.netlify/state.json')}`)
247
+ log(`Site id saved to ${path.join(netlify.site.root, '/.netlify/state.json')}`)
235
248
  // log(`Local Config: .netlify/config.json`)
236
249
  log()
237
250
  log(`You can now run other \`netlify\` cli commands in this directory`)
238
251
 
239
252
  return site
240
253
  }
254
+
255
+ /**
256
+ * The link command
257
+ * @param {import('commander').OptionValues} options
258
+ * @param {import('../base-command').BaseCommand} command
259
+ */
260
+ const link = async (options, command) => {
261
+ await command.authenticate()
262
+
263
+ const {
264
+ api,
265
+ repositoryRoot,
266
+ site: { id: siteId },
267
+ state,
268
+ } = command.netlify
269
+
270
+ let siteData
271
+ try {
272
+ // @ts-ignore types from API are wrong they cannot recognize `getSite` of API
273
+ siteData = await api.getSite({ siteId })
274
+ } catch {
275
+ // silent api error
276
+ }
277
+
278
+ // Add .netlify to .gitignore file
279
+ await ensureNetlifyIgnore(repositoryRoot)
280
+
281
+ // Site id is incorrect
282
+ if (siteId && !siteData) {
283
+ log(`"${siteId}" was not found in your Netlify account.`)
284
+ log(`Please double check your siteID and which account you are logged into via \`netlify status\`.`)
285
+ return exit()
286
+ }
287
+
288
+ // If already linked to site. exit and prompt for unlink
289
+ if (siteData) {
290
+ log(`Site already linked to "${siteData.name}"`)
291
+ log(`Admin url: ${siteData.admin_url}`)
292
+ log()
293
+ log(`To unlink this site, run: ${chalk.cyanBright('netlify unlink')}`)
294
+ return exit()
295
+ }
296
+
297
+ if (options.id) {
298
+ try {
299
+ // @ts-ignore types from API are wrong they cannot recognize `getSite` of API
300
+ siteData = await api.getSite({ site_id: options.id })
301
+ } catch (error_) {
302
+ if (error_.status === 404) {
303
+ error(new Error(`Site id ${options.id} not found`))
304
+ } else {
305
+ error(error_)
306
+ }
307
+ }
308
+
309
+ // Save site ID
310
+ state.set('siteId', siteData.id)
311
+ log(`Linked to ${siteData.name} in ${state.path}`)
312
+
313
+ await track('sites_linked', {
314
+ siteId: siteData.id,
315
+ linkType: 'manual',
316
+ kind: 'byId',
317
+ })
318
+
319
+ return exit()
320
+ }
321
+
322
+ if (options.name) {
323
+ let results
324
+ try {
325
+ results = await listSites({
326
+ api,
327
+ options: {
328
+ name: options.name,
329
+ filter: 'all',
330
+ },
331
+ })
332
+ } catch (error_) {
333
+ if (error_.status === 404) {
334
+ error(new Error(`${options.name} not found`))
335
+ } else {
336
+ error(error_)
337
+ }
338
+ }
339
+
340
+ if (results.length === 0) {
341
+ error(new Error(`No sites found named ${options.name}`))
342
+ }
343
+ const [firstSiteData] = results
344
+ state.set('siteId', firstSiteData.id)
345
+
346
+ log(`Linked to ${firstSiteData.name} in ${relative(join(cwd(), '..'), state.path)}`)
347
+
348
+ await track('sites_linked', {
349
+ siteId: (firstSiteData && firstSiteData.id) || siteId,
350
+ linkType: 'manual',
351
+ kind: 'byName',
352
+ })
353
+
354
+ return exit()
355
+ }
356
+
357
+ siteData = await linkPrompt(command.netlify, options)
358
+ return siteData
359
+ }
360
+
361
+ /**
362
+ * Creates the `netlify link` command
363
+ * @param {import('../base-command').BaseCommand} program
364
+ * @returns
365
+ */
366
+ const createLinkCommand = (program) =>
367
+ program
368
+ .command('link')
369
+ .description('Link a local repo or project folder to an existing site on Netlify')
370
+ .option('--id <id>', 'ID of site to link to')
371
+ .option('--name <name>', 'Name of site to link to')
372
+ .option('--gitRemoteName <name>', 'Name of Git remote to use. e.g. "origin"')
373
+ .addHelpText(
374
+ 'after',
375
+ generateExamplesHelp(['netlify link', 'netlify link --id 123-123-123-123', 'netlify link --name my-site-name']),
376
+ )
377
+ .action(link)
378
+
379
+ module.exports = { createLinkCommand, link }
@@ -1,21 +1,5 @@
1
- const { isEmptyCommand } = require('../../utils/check-command-inputs')
2
- const Command = require('../../utils/command')
3
- const showHelp = require('../../utils/show-help')
1
+ const { createLmCommand } = require('./lm')
4
2
 
5
- class LmCommand extends Command {
6
- run() {
7
- const { args, flags } = this.parse(LmCommand)
8
-
9
- // Show help on empty sub command
10
- if (isEmptyCommand(flags, args)) {
11
- showHelp(this.id)
12
- }
13
- }
3
+ module.exports = {
4
+ createLmCommand,
14
5
  }
15
-
16
- LmCommand.description = `Handle Netlify Large Media operations
17
- The lm command will help you manage large media for a site
18
- `
19
- LmCommand.examples = ['netlify lm:info', 'netlify lm:install', 'netlify lm:setup']
20
-
21
- module.exports = LmCommand
@@ -0,0 +1,42 @@
1
+ // @ts-check
2
+ const Listr = require('listr')
3
+
4
+ const {
5
+ checkGitLFSVersionStep,
6
+ checkGitVersionStep,
7
+ checkHelperVersionStep,
8
+ checkLFSFiltersStep,
9
+ } = require('../../utils/lm/steps')
10
+
11
+ /**
12
+ * The lm:info command
13
+ */
14
+ const lmInfo = async () => {
15
+ const steps = [
16
+ checkGitVersionStep,
17
+ checkGitLFSVersionStep,
18
+ checkLFSFiltersStep((ctx, task, installed) => {
19
+ if (!installed) {
20
+ throw new Error('Git LFS filters are not installed, run `git lfs install` to install them')
21
+ }
22
+ }),
23
+ checkHelperVersionStep,
24
+ ]
25
+
26
+ const tasks = new Listr(steps, { concurrent: true, exitOnError: false })
27
+ try {
28
+ await tasks.run()
29
+ } catch {
30
+ // an error is already reported when a task fails
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Creates the `netlify lm:info` command
36
+ * @param {import('../base-command').BaseCommand} program
37
+ * @returns
38
+ */
39
+ const createLmInfoCommand = (program) =>
40
+ program.command('lm:info').description('Show large media requirements information.').action(lmInfo)
41
+
42
+ module.exports = { createLmInfoCommand }
@@ -0,0 +1,36 @@
1
+ // @ts-check
2
+
3
+ const { generateDescriptionHelp } = require('../../utils')
4
+ const { installPlatform } = require('../../utils/lm/install')
5
+ const { printBanner } = require('../../utils/lm/ui')
6
+
7
+ /**
8
+ * The lm:install command
9
+ * @param {import('commander').OptionValues} options
10
+ */
11
+ const lmInstall = async ({ force }) => {
12
+ const installed = await installPlatform({ force })
13
+ if (installed) {
14
+ printBanner(force)
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Creates the `netlify lm:install` command
20
+ * @param {import('../base-command').BaseCommand} program
21
+ * @returns
22
+ */
23
+ const createLmInstallCommand = (program) =>
24
+ program
25
+ .command('lm:install')
26
+ .alias('lm:init')
27
+ .description('Configures your computer to use Netlify Large Media')
28
+ .option('-f, --force', 'Force the credentials helper installation')
29
+ .addHelpText(
30
+ 'after',
31
+ generateDescriptionHelp(`It installs the required credentials helper for Git,
32
+ and configures your Git environment with the right credentials.`),
33
+ )
34
+ .action(lmInstall)
35
+
36
+ module.exports = { createLmInstallCommand }
@@ -0,0 +1,106 @@
1
+ // @ts-check
2
+ const Listr = require('listr')
3
+
4
+ const { error, execa } = require('../../utils')
5
+ const { installPlatform } = require('../../utils/lm/install')
6
+ const { checkHelperVersion } = require('../../utils/lm/requirements')
7
+ const { printBanner } = require('../../utils/lm/ui')
8
+
9
+ const installHelperIfMissing = async function ({ force }) {
10
+ let installHelper = false
11
+ try {
12
+ const version = await checkHelperVersion()
13
+ if (!version) {
14
+ installHelper = true
15
+ }
16
+ } catch {
17
+ installHelper = true
18
+ }
19
+
20
+ if (installHelper) {
21
+ return installPlatform({ force })
22
+ }
23
+
24
+ return false
25
+ }
26
+
27
+ const provisionService = async function (siteId, api) {
28
+ const addonName = 'large-media'
29
+
30
+ if (!siteId) {
31
+ throw new Error('No site id found, please run inside a site folder or `netlify link`')
32
+ }
33
+ try {
34
+ await api.createServiceInstance({
35
+ siteId,
36
+ addon: addonName,
37
+ body: {},
38
+ })
39
+ } catch (error_) {
40
+ // error is JSONHTTPError
41
+ throw new Error(error_.json.error)
42
+ }
43
+ }
44
+
45
+ const configureLFSURL = async function (siteId, api) {
46
+ const siteInfo = await api.getSite({ siteId })
47
+ const url = `https://${siteInfo.id_domain}/.netlify/large-media`
48
+
49
+ return execa('git', ['config', '-f', '.lfsconfig', 'lfs.url', url])
50
+ }
51
+
52
+ /**
53
+ * The lm:setup command
54
+ * @param {import('commander').OptionValues} options
55
+ * @param {import('../base-command').BaseCommand} command
56
+ */
57
+ const lmSetup = async (options, command) => {
58
+ await command.authenticate()
59
+
60
+ const { api, site } = command.netlify
61
+
62
+ let helperInstalled = false
63
+ if (!options.skipInstall) {
64
+ try {
65
+ helperInstalled = await installHelperIfMissing({ force: options.forceInstall })
66
+ } catch (error_) {
67
+ error(error_)
68
+ }
69
+ }
70
+
71
+ const tasks = new Listr([
72
+ {
73
+ title: 'Provisioning Netlify Large Media',
74
+ async task() {
75
+ await provisionService(site.id, api)
76
+ },
77
+ },
78
+ {
79
+ title: 'Configuring Git LFS for this site',
80
+ async task() {
81
+ await configureLFSURL(site.id, api)
82
+ },
83
+ },
84
+ ])
85
+ await tasks.run().catch(() => {})
86
+
87
+ if (helperInstalled) {
88
+ printBanner(options.forceInstall)
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Creates the `netlify lm:setup` command
94
+ * @param {import('../base-command').BaseCommand} program
95
+ * @returns
96
+ */
97
+ const createLmSetupCommand = (program) =>
98
+ program
99
+ .command('lm:setup')
100
+ .description('Configures your site to use Netlify Large Media')
101
+ .option('-s, --skip-install', 'Skip the credentials helper installation check')
102
+ .option('-f, --force-install', 'Force the credentials helper installation')
103
+ .addHelpText('after', 'It runs the install command if you have not installed the dependencies yet.')
104
+ .action(lmSetup)
105
+
106
+ module.exports = { createLmSetupCommand }
@@ -0,0 +1,25 @@
1
+ // @ts-check
2
+ const { uninstall } = require('../../utils/lm/install')
3
+
4
+ /**
5
+ * The lm:uninstall command
6
+ */
7
+ const lmUninstall = async () => {
8
+ await uninstall()
9
+ }
10
+
11
+ /**
12
+ * Creates the `netlify lm:uninstall` command
13
+ * @param {import('../base-command').BaseCommand} program
14
+ * @returns
15
+ */
16
+ const createLmUninstallCommand = (program) =>
17
+ program
18
+ .command('lm:uninstall', { hidden: true })
19
+ .alias('lm:remove')
20
+ .description(
21
+ 'Uninstalls Netlify git credentials helper and cleans up any related configuration changes made by the install command.',
22
+ )
23
+ .action(lmUninstall)
24
+
25
+ module.exports = { createLmUninstallCommand }