netlify-cli 8.1.0 → 8.1.5

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 (193) hide show
  1. package/README.md +12 -13
  2. package/bin/run +38 -2
  3. package/npm-shrinkwrap.json +1256 -4181
  4. package/package.json +15 -39
  5. package/scripts/postinstall.js +13 -0
  6. package/src/commands/addons/addons-auth.js +50 -0
  7. package/src/commands/addons/addons-config.js +180 -0
  8. package/src/commands/addons/addons-create.js +131 -0
  9. package/src/commands/addons/addons-delete.js +60 -0
  10. package/src/commands/addons/addons-list.js +62 -0
  11. package/src/commands/addons/addons.js +44 -0
  12. package/src/commands/addons/index.js +3 -24
  13. package/src/commands/api/api.js +75 -0
  14. package/src/commands/api/index.js +5 -0
  15. package/src/commands/base-command.js +502 -0
  16. package/src/commands/build/build.js +58 -0
  17. package/src/commands/build/index.js +3 -61
  18. package/src/commands/completion/completion.js +56 -0
  19. package/src/commands/completion/index.js +5 -0
  20. package/src/commands/{deploy.js → deploy/deploy.js} +295 -275
  21. package/src/commands/deploy/index.js +5 -0
  22. package/src/commands/dev/dev-exec.js +35 -0
  23. package/src/commands/dev/dev-trace.js +47 -0
  24. package/src/commands/dev/dev.js +340 -0
  25. package/src/commands/dev/index.js +3 -335
  26. package/src/commands/env/env-get.js +51 -0
  27. package/src/commands/env/env-import.js +93 -0
  28. package/src/commands/env/env-list.js +63 -0
  29. package/src/commands/env/env-set.js +67 -0
  30. package/src/commands/env/env-unset.js +66 -0
  31. package/src/commands/env/env.js +42 -0
  32. package/src/commands/env/index.js +3 -23
  33. package/src/commands/functions/functions-build.js +59 -0
  34. package/src/commands/functions/{create.js → functions-create.js} +130 -94
  35. package/src/commands/functions/functions-invoke.js +273 -0
  36. package/src/commands/functions/functions-list.js +106 -0
  37. package/src/commands/functions/functions-serve.js +63 -0
  38. package/src/commands/functions/functions.js +47 -0
  39. package/src/commands/functions/index.js +3 -45
  40. package/src/commands/index.js +7 -0
  41. package/src/commands/init/index.js +6 -0
  42. package/src/commands/{init.js → init/init.js} +79 -68
  43. package/src/commands/link/index.js +6 -0
  44. package/src/{utils/link/link-by-prompt.js → commands/link/link.js} +141 -14
  45. package/src/commands/lm/index.js +3 -19
  46. package/src/commands/lm/lm-info.js +42 -0
  47. package/src/commands/lm/lm-install.js +33 -0
  48. package/src/commands/lm/lm-setup.js +106 -0
  49. package/src/commands/lm/lm-uninstall.js +25 -0
  50. package/src/commands/lm/lm.js +34 -0
  51. package/src/commands/login/index.js +6 -0
  52. package/src/commands/login/login.js +55 -0
  53. package/src/commands/logout/index.js +5 -0
  54. package/src/commands/logout/logout.js +43 -0
  55. package/src/commands/main.js +206 -0
  56. package/src/commands/open/index.js +3 -39
  57. package/src/commands/open/open-admin.js +60 -0
  58. package/src/commands/open/open-site.js +53 -0
  59. package/src/commands/open/open.js +40 -0
  60. package/src/commands/sites/index.js +5 -20
  61. package/src/commands/sites/sites-create.js +187 -0
  62. package/src/commands/sites/sites-delete.js +104 -0
  63. package/src/commands/sites/sites-list.js +90 -0
  64. package/src/commands/sites/sites.js +32 -0
  65. package/src/commands/status/index.js +3 -118
  66. package/src/commands/status/status-hooks.js +69 -0
  67. package/src/commands/status/status.js +124 -0
  68. package/src/commands/switch/index.js +5 -0
  69. package/src/commands/switch/switch.js +50 -0
  70. package/src/commands/unlink/index.js +5 -0
  71. package/src/commands/unlink/unlink.js +48 -0
  72. package/src/commands/watch/index.js +5 -0
  73. package/src/commands/watch/watch.js +131 -0
  74. package/src/functions-templates/javascript/stripe-charge/package-lock.json +13 -13
  75. package/src/functions-templates/javascript/stripe-subscription/package-lock.json +13 -13
  76. package/src/functions-templates/rust/hello-world/Cargo.toml +1 -1
  77. package/src/functions-templates/typescript/hello-world/package-lock.json +6 -6
  78. package/src/lib/build.js +21 -7
  79. package/src/lib/completion/constants.js +6 -0
  80. package/src/lib/completion/generate-autocompletion.js +36 -0
  81. package/src/lib/completion/index.js +5 -0
  82. package/src/lib/completion/script.js +72 -0
  83. package/src/lib/exec-fetcher.js +5 -3
  84. package/src/lib/fs.js +54 -36
  85. package/src/lib/functions/background.js +1 -1
  86. package/src/lib/functions/form-submissions-handler.js +2 -1
  87. package/src/lib/functions/local-proxy.js +2 -1
  88. package/src/lib/functions/netlify-function.js +4 -1
  89. package/src/lib/functions/registry.js +4 -6
  90. package/src/lib/functions/runtimes/go/index.js +2 -1
  91. package/src/lib/functions/runtimes/js/builders/netlify-lambda.js +6 -4
  92. package/src/lib/functions/runtimes/js/builders/zisi.js +3 -3
  93. package/src/lib/functions/runtimes/rust/index.js +4 -3
  94. package/src/lib/functions/server.js +2 -3
  95. package/src/lib/functions/synchronous.js +2 -1
  96. package/src/lib/functions/utils.js +2 -3
  97. package/src/lib/functions/watcher.js +1 -0
  98. package/src/lib/http-agent.js +5 -5
  99. package/src/lib/log.js +2 -1
  100. package/src/lib/settings.js +16 -1
  101. package/src/lib/spinner.js +22 -0
  102. package/src/utils/addons/diffs/index.js +1 -0
  103. package/src/utils/addons/diffs/options.js +3 -1
  104. package/src/utils/addons/prepare.js +13 -6
  105. package/src/utils/addons/prompts.js +2 -1
  106. package/src/utils/addons/render.js +3 -1
  107. package/src/utils/command-helpers.js +136 -42
  108. package/src/utils/create-stream-promise.js +5 -5
  109. package/src/utils/deferred.js +1 -0
  110. package/src/utils/deploy/deploy-site.js +1 -1
  111. package/src/utils/deploy/index.js +4 -0
  112. package/src/utils/detect-server-settings.js +10 -12
  113. package/src/utils/dev.js +18 -10
  114. package/src/utils/dot-env.js +4 -2
  115. package/src/utils/{edge-handlers.js → functions/edge-handlers.js} +8 -7
  116. package/src/utils/functions/functions.js +36 -0
  117. package/src/utils/{get-functions.js → functions/get-functions.js} +2 -1
  118. package/src/utils/functions/index.js +8 -26
  119. package/src/utils/get-global-config.js +3 -2
  120. package/src/utils/get-repo-data.js +8 -1
  121. package/src/utils/gh-auth.js +1 -0
  122. package/src/utils/gitignore.js +7 -5
  123. package/src/utils/headers.js +1 -2
  124. package/src/utils/index.js +42 -0
  125. package/src/utils/init/config-github.js +12 -5
  126. package/src/utils/init/config-manual.js +9 -2
  127. package/src/utils/init/config.js +13 -7
  128. package/src/utils/init/frameworks.js +1 -0
  129. package/src/utils/init/node-version.js +4 -2
  130. package/src/utils/init/utils.js +10 -6
  131. package/src/utils/live-tunnel.js +3 -4
  132. package/src/utils/lm/install.js +10 -15
  133. package/src/utils/lm/requirements.js +3 -1
  134. package/src/utils/lm/steps.js +1 -1
  135. package/src/utils/lm/ui.js +7 -3
  136. package/src/utils/open-browser.js +8 -2
  137. package/src/utils/parse-raw-flags.js +4 -4
  138. package/src/utils/proxy.js +6 -5
  139. package/src/utils/read-repo-url.js +1 -0
  140. package/src/utils/redirects.js +2 -2
  141. package/src/utils/rules-proxy.js +2 -1
  142. package/src/utils/state-config.js +1 -1
  143. package/src/utils/telemetry/index.js +2 -113
  144. package/src/utils/telemetry/request.js +3 -1
  145. package/src/utils/telemetry/telemetry.js +117 -0
  146. package/src/utils/telemetry/validation.js +13 -12
  147. package/src/utils/traffic-mesh.js +3 -3
  148. package/oclif.manifest.json +0 -1
  149. package/src/commands/addons/auth.js +0 -42
  150. package/src/commands/addons/config.js +0 -177
  151. package/src/commands/addons/create.js +0 -127
  152. package/src/commands/addons/delete.js +0 -69
  153. package/src/commands/addons/list.js +0 -54
  154. package/src/commands/api.js +0 -84
  155. package/src/commands/dev/exec.js +0 -32
  156. package/src/commands/dev/trace.js +0 -61
  157. package/src/commands/env/get.js +0 -44
  158. package/src/commands/env/import.js +0 -90
  159. package/src/commands/env/list.js +0 -49
  160. package/src/commands/env/set.js +0 -64
  161. package/src/commands/env/unset.js +0 -58
  162. package/src/commands/functions/build.js +0 -60
  163. package/src/commands/functions/invoke.js +0 -277
  164. package/src/commands/functions/list.js +0 -102
  165. package/src/commands/functions/serve.js +0 -70
  166. package/src/commands/link.js +0 -133
  167. package/src/commands/lm/info.js +0 -36
  168. package/src/commands/lm/install.js +0 -30
  169. package/src/commands/lm/setup.js +0 -107
  170. package/src/commands/lm/uninstall.js +0 -17
  171. package/src/commands/login.js +0 -54
  172. package/src/commands/logout.js +0 -37
  173. package/src/commands/open/admin.js +0 -51
  174. package/src/commands/open/site.js +0 -43
  175. package/src/commands/sites/create.js +0 -191
  176. package/src/commands/sites/delete.js +0 -116
  177. package/src/commands/sites/list.js +0 -84
  178. package/src/commands/status/hooks.js +0 -60
  179. package/src/commands/switch.js +0 -44
  180. package/src/commands/unlink.js +0 -38
  181. package/src/commands/watch.js +0 -115
  182. package/src/hooks/init.js +0 -46
  183. package/src/index.js +0 -25
  184. package/src/lib/help.js +0 -26
  185. package/src/utils/chalk.js +0 -16
  186. package/src/utils/check-command-inputs.js +0 -21
  187. package/src/utils/command.js +0 -261
  188. package/src/utils/detect-functions-builder.js +0 -25
  189. package/src/utils/difference.js +0 -4
  190. package/src/utils/header.js +0 -18
  191. package/src/utils/logo.js +0 -11
  192. package/src/utils/show-help.js +0 -5
  193. 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', 'Reinitialize CI hooks if the linked site is already configured to use 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,21 @@
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 { chalk, ensureNetlifyIgnore, error, exit, getRepoData, log, track } = require('../../utils')
11
11
 
12
- module.exports = async function linkPrompts(context, flags = {}) {
13
- const { api, state } = context.netlify
12
+ /**
13
+ *
14
+ * @param {import('../base-command').NetlifyOptions} netlify
15
+ * @param {import('commander').OptionValues} options
16
+ */
17
+ const linkPrompt = async (netlify, options) => {
18
+ const { api, state } = netlify
14
19
 
15
20
  const SITE_NAME_PROMPT = 'Search by full or partial site name'
16
21
  const SITE_LIST_PROMPT = 'Choose from a list of your recently updated sites'
@@ -19,7 +24,7 @@ module.exports = async function linkPrompts(context, flags = {}) {
19
24
  let GIT_REMOTE_PROMPT = 'Use the current git remote origin URL'
20
25
  let site
21
26
  // Get git remote data if exists
22
- const repoData = await getRepoData({ remoteName: flags.gitRemoteName })
27
+ const repoData = await getRepoData({ remoteName: options.gitRemoteName })
23
28
 
24
29
  let linkChoices = [SITE_NAME_PROMPT, SITE_LIST_PROMPT, SITE_ID_PROMPT]
25
30
 
@@ -51,9 +56,7 @@ module.exports = async function linkPrompts(context, flags = {}) {
51
56
  const sites = await listSites({ api, options: { filter: 'all' } })
52
57
 
53
58
  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
- )
59
+ error(`You don't have any sites yet. Run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
57
60
  }
58
61
 
59
62
  const matchingSites = sites.filter(
@@ -79,7 +82,7 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`)
79
82
  site = firstSite
80
83
  } else if (matchingSites.length > 1) {
81
84
  // Matches multiple sites. Users must choose which to link.
82
- console.log(`Found ${matchingSites.length} matching sites!`)
85
+ log(`Found ${matchingSites.length} matching sites!`)
83
86
 
84
87
  // Prompt which options
85
88
  const { selectedSite } = await inquirer.prompt([
@@ -134,7 +137,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
134
137
  }
135
138
 
136
139
  if (matchingSites.length > 1) {
137
- console.log(`Found ${matchingSites.length} matching sites!`)
140
+ log(`Found ${matchingSites.length} matching sites!`)
138
141
  const { selectedSite } = await inquirer.prompt([
139
142
  {
140
143
  type: 'list',
@@ -145,7 +148,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
145
148
  },
146
149
  ])
147
150
  if (!selectedSite) {
148
- context.error('No site selected')
151
+ error('No site selected')
149
152
  }
150
153
  site = selectedSite
151
154
  } else {
@@ -196,6 +199,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
196
199
  ])
197
200
 
198
201
  try {
202
+ // @ts-ignore types from API are wrong they cannot recognize `getSite` of API
199
203
  site = await api.getSite({ siteId })
200
204
  } catch (error_) {
201
205
  if (error_.status === 404) {
@@ -231,10 +235,133 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
231
235
  log(`Site url: ${chalk.cyanBright(site.ssl_url || site.url)}`)
232
236
  log()
233
237
 
234
- log(`Site id saved to ${path.join(context.netlify.site.root, '/.netlify/state.json')}`)
238
+ log(`Site id saved to ${path.join(netlify.site.root, '/.netlify/state.json')}`)
235
239
  // log(`Local Config: .netlify/config.json`)
236
240
  log()
237
241
  log(`You can now run other \`netlify\` cli commands in this directory`)
238
242
 
239
243
  return site
240
244
  }
245
+
246
+ /**
247
+ * The link command
248
+ * @param {import('commander').OptionValues} options
249
+ * @param {import('../base-command').BaseCommand} command
250
+ */
251
+ const link = async (options, command) => {
252
+ await command.authenticate()
253
+
254
+ const {
255
+ api,
256
+ repositoryRoot,
257
+ site: { id: siteId },
258
+ state,
259
+ } = command.netlify
260
+
261
+ let siteData
262
+ try {
263
+ // @ts-ignore types from API are wrong they cannot recognize `getSite` of API
264
+ siteData = await api.getSite({ siteId })
265
+ } catch {
266
+ // silent api error
267
+ }
268
+
269
+ // Add .netlify to .gitignore file
270
+ await ensureNetlifyIgnore(repositoryRoot)
271
+
272
+ // Site id is incorrect
273
+ if (siteId && !siteData) {
274
+ log(`"${siteId}" was not found in your Netlify account.`)
275
+ log(`Please double check your siteID and which account you are logged into via \`netlify status\`.`)
276
+ return exit()
277
+ }
278
+
279
+ // If already linked to site. exit and prompt for unlink
280
+ if (siteData) {
281
+ log(`Site already linked to "${siteData.name}"`)
282
+ log(`Admin url: ${siteData.admin_url}`)
283
+ log()
284
+ log(`To unlink this site, run: ${chalk.cyanBright('netlify unlink')}`)
285
+ return exit()
286
+ }
287
+
288
+ if (options.id) {
289
+ try {
290
+ // @ts-ignore types from API are wrong they cannot recognize `getSite` of API
291
+ siteData = await api.getSite({ site_id: options.id })
292
+ } catch (error_) {
293
+ if (error_.status === 404) {
294
+ error(new Error(`Site id ${options.id} not found`))
295
+ } else {
296
+ error(error_)
297
+ }
298
+ }
299
+
300
+ // Save site ID
301
+ state.set('siteId', siteData.id)
302
+ log(`Linked to ${siteData.name} in ${state.path}`)
303
+
304
+ await track('sites_linked', {
305
+ siteId: siteData.id,
306
+ linkType: 'manual',
307
+ kind: 'byId',
308
+ })
309
+
310
+ return exit()
311
+ }
312
+
313
+ if (options.name) {
314
+ let results
315
+ try {
316
+ results = await listSites({
317
+ api,
318
+ options: {
319
+ name: options.name,
320
+ filter: 'all',
321
+ },
322
+ })
323
+ } catch (error_) {
324
+ if (error_.status === 404) {
325
+ error(new Error(`${options.name} not found`))
326
+ } else {
327
+ error(error_)
328
+ }
329
+ }
330
+
331
+ if (results.length === 0) {
332
+ error(new Error(`No sites found named ${options.name}`))
333
+ }
334
+ const [firstSiteData] = results
335
+ state.set('siteId', firstSiteData.id)
336
+
337
+ log(`Linked to ${firstSiteData.name} in ${relative(join(cwd(), '..'), state.path)}`)
338
+
339
+ await track('sites_linked', {
340
+ siteId: (firstSiteData && firstSiteData.id) || siteId,
341
+ linkType: 'manual',
342
+ kind: 'byName',
343
+ })
344
+
345
+ return exit()
346
+ }
347
+
348
+ siteData = await linkPrompt(command.netlify, options)
349
+ return siteData
350
+ }
351
+
352
+ /**
353
+ * Creates the `netlify link` command
354
+ * @param {import('../base-command').BaseCommand} program
355
+ * @returns
356
+ */
357
+ const createLinkCommand = (program) =>
358
+ program
359
+ .command('link')
360
+ .description('Link a local repo or project folder to an existing site on Netlify')
361
+ .option('--id <id>', 'ID of site to link to')
362
+ .option('--name <name>', 'Name of site to link to')
363
+ .option('--gitRemoteName <name>', 'Name of Git remote to use. e.g. "origin"')
364
+ .addExamples(['netlify link', 'netlify link --id 123-123-123-123', 'netlify link --name my-site-name'])
365
+ .action(link)
366
+
367
+ 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,33 @@
1
+ // @ts-check
2
+ const { installPlatform } = require('../../utils/lm/install')
3
+ const { printBanner } = require('../../utils/lm/ui')
4
+
5
+ /**
6
+ * The lm:install command
7
+ * @param {import('commander').OptionValues} options
8
+ */
9
+ const lmInstall = async ({ force }) => {
10
+ const installed = await installPlatform({ force })
11
+ if (installed) {
12
+ printBanner(force)
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Creates the `netlify lm:install` command
18
+ * @param {import('../base-command').BaseCommand} program
19
+ * @returns
20
+ */
21
+ const createLmInstallCommand = (program) =>
22
+ program
23
+ .command('lm:install')
24
+ .alias('lm:init')
25
+ .description(
26
+ `Configures your computer to use Netlify Large Media
27
+ It installs the required credentials helper for Git,
28
+ and configures your Git environment with the right credentials.`,
29
+ )
30
+ .option('-f, --force', 'Force the credentials helper installation')
31
+ .action(lmInstall)
32
+
33
+ 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 }