netlify-cli 17.3.2 → 17.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +2 -138
  2. package/npm-shrinkwrap.json +76 -76
  3. package/package.json +16 -15
  4. package/src/commands/addons/addons-auth.mjs +27 -30
  5. package/src/commands/addons/addons-config.mjs +145 -154
  6. package/src/commands/addons/addons-create.mjs +94 -108
  7. package/src/commands/addons/addons-delete.mjs +36 -41
  8. package/src/commands/addons/addons-list.mjs +38 -42
  9. package/src/commands/addons/addons.mjs +26 -28
  10. package/src/commands/addons/index.mjs +1 -1
  11. package/src/commands/api/api.mjs +45 -53
  12. package/src/commands/api/index.mjs +1 -1
  13. package/src/commands/base-command.mjs +597 -684
  14. package/src/commands/blobs/blobs-delete.mjs +35 -0
  15. package/src/commands/blobs/blobs-get.mjs +44 -0
  16. package/src/commands/blobs/blobs-list.mjs +48 -0
  17. package/src/commands/blobs/blobs-set.mjs +54 -0
  18. package/src/commands/blobs/blobs.mjs +32 -0
  19. package/src/commands/blobs/index.mjs +1 -0
  20. package/src/commands/build/build.mjs +55 -67
  21. package/src/commands/build/index.mjs +1 -1
  22. package/src/commands/completion/completion.mjs +41 -46
  23. package/src/commands/completion/index.mjs +1 -1
  24. package/src/commands/deploy/deploy.mjs +675 -710
  25. package/src/commands/deploy/index.mjs +1 -1
  26. package/src/commands/dev/dev-exec.mjs +20 -32
  27. package/src/commands/dev/dev.mjs +217 -302
  28. package/src/commands/dev/index.mjs +1 -1
  29. package/src/commands/dev/types.d.ts +30 -0
  30. package/src/commands/env/env-clone.mjs +157 -184
  31. package/src/commands/env/env-get.mjs +49 -68
  32. package/src/commands/env/env-import.mjs +100 -119
  33. package/src/commands/env/env-list.mjs +104 -129
  34. package/src/commands/env/env-set.mjs +160 -185
  35. package/src/commands/env/env-unset.mjs +104 -122
  36. package/src/commands/env/env.mjs +28 -30
  37. package/src/commands/env/index.mjs +1 -1
  38. package/src/commands/functions/functions-build.mjs +29 -41
  39. package/src/commands/functions/functions-create.mjs +533 -601
  40. package/src/commands/functions/functions-invoke.mjs +193 -216
  41. package/src/commands/functions/functions-list.mjs +45 -55
  42. package/src/commands/functions/functions-serve.mjs +51 -61
  43. package/src/commands/functions/functions.mjs +26 -32
  44. package/src/commands/functions/index.mjs +1 -1
  45. package/src/commands/index.mjs +2 -2
  46. package/src/commands/init/index.mjs +1 -1
  47. package/src/commands/init/init.mjs +138 -167
  48. package/src/commands/integration/deploy.mjs +337 -399
  49. package/src/commands/integration/index.mjs +12 -13
  50. package/src/commands/link/index.mjs +1 -1
  51. package/src/commands/link/link.mjs +298 -317
  52. package/src/commands/lm/index.mjs +1 -1
  53. package/src/commands/lm/lm-info.mjs +23 -31
  54. package/src/commands/lm/lm-install.mjs +13 -17
  55. package/src/commands/lm/lm-setup.mjs +80 -84
  56. package/src/commands/lm/lm-uninstall.mjs +7 -12
  57. package/src/commands/lm/lm.mjs +18 -22
  58. package/src/commands/login/index.mjs +1 -1
  59. package/src/commands/login/login.mjs +35 -41
  60. package/src/commands/logout/index.mjs +1 -1
  61. package/src/commands/logout/logout.mjs +25 -31
  62. package/src/commands/main.mjs +166 -201
  63. package/src/commands/open/index.mjs +1 -1
  64. package/src/commands/open/open-admin.mjs +15 -18
  65. package/src/commands/open/open-site.mjs +16 -19
  66. package/src/commands/open/open.mjs +24 -27
  67. package/src/commands/recipes/common.mjs +23 -34
  68. package/src/commands/recipes/index.mjs +1 -1
  69. package/src/commands/recipes/recipes-list.mjs +13 -20
  70. package/src/commands/recipes/recipes.mjs +59 -72
  71. package/src/commands/serve/index.mjs +1 -1
  72. package/src/commands/serve/serve.mjs +142 -189
  73. package/src/commands/sites/index.mjs +2 -2
  74. package/src/commands/sites/sites-create-template.mjs +214 -236
  75. package/src/commands/sites/sites-create.mjs +145 -157
  76. package/src/commands/sites/sites-delete.mjs +75 -81
  77. package/src/commands/sites/sites-list.mjs +63 -66
  78. package/src/commands/sites/sites.mjs +18 -20
  79. package/src/commands/status/index.mjs +1 -1
  80. package/src/commands/status/status-hooks.mjs +32 -34
  81. package/src/commands/status/status.mjs +99 -106
  82. package/src/commands/switch/index.mjs +1 -1
  83. package/src/commands/switch/switch.mjs +32 -37
  84. package/src/commands/types.d.ts +31 -0
  85. package/src/commands/unlink/index.mjs +1 -1
  86. package/src/commands/unlink/unlink.mjs +23 -29
  87. package/src/commands/watch/index.mjs +1 -1
  88. package/src/commands/watch/watch.mjs +91 -105
  89. package/src/functions-templates/javascript/hello/{{name}}.js +2 -3
  90. package/src/lib/account.mjs +4 -5
  91. package/src/lib/api.mjs +22 -20
  92. package/src/lib/blobs/blobs.mjs +36 -45
  93. package/src/lib/build.mjs +82 -85
  94. package/src/lib/completion/constants.mjs +2 -4
  95. package/src/lib/completion/generate-autocompletion.mjs +33 -36
  96. package/src/lib/completion/get-autocompletion.mjs +31 -35
  97. package/src/lib/completion/index.mjs +1 -1
  98. package/src/lib/completion/script.mjs +12 -19
  99. package/src/lib/edge-functions/bootstrap.mjs +3 -5
  100. package/src/lib/edge-functions/consts.mjs +9 -10
  101. package/src/lib/edge-functions/deploy.mjs +28 -34
  102. package/src/lib/edge-functions/editor-helper.mjs +29 -42
  103. package/src/lib/edge-functions/headers.mjs +24 -26
  104. package/src/lib/edge-functions/internal.mjs +38 -44
  105. package/src/lib/edge-functions/proxy.mjs +229 -228
  106. package/src/lib/edge-functions/registry.mjs +473 -574
  107. package/src/lib/exec-fetcher.mjs +115 -122
  108. package/src/lib/fs.mjs +28 -27
  109. package/src/lib/functions/background.mjs +16 -20
  110. package/src/lib/functions/config.mjs +12 -9
  111. package/src/lib/functions/form-submissions-handler.mjs +143 -149
  112. package/src/lib/functions/local-proxy.mjs +40 -44
  113. package/src/lib/functions/memoized-build.mjs +19 -21
  114. package/src/lib/functions/netlify-function.mjs +269 -249
  115. package/src/lib/functions/registry.mjs +509 -568
  116. package/src/lib/functions/runtimes/go/index.mjs +62 -71
  117. package/src/lib/functions/runtimes/index.mjs +8 -15
  118. package/src/lib/functions/runtimes/js/builders/netlify-lambda.mjs +55 -64
  119. package/src/lib/functions/runtimes/js/builders/zisi.mjs +135 -154
  120. package/src/lib/functions/runtimes/js/constants.mjs +1 -1
  121. package/src/lib/functions/runtimes/js/index.mjs +92 -109
  122. package/src/lib/functions/runtimes/js/worker.mjs +43 -45
  123. package/src/lib/functions/runtimes/rust/index.mjs +64 -73
  124. package/src/lib/functions/scheduled.mjs +70 -88
  125. package/src/lib/functions/server.mjs +269 -327
  126. package/src/lib/functions/synchronous.mjs +118 -147
  127. package/src/lib/functions/utils.mjs +38 -46
  128. package/src/lib/geo-location.mjs +69 -81
  129. package/src/lib/http-agent.mjs +87 -90
  130. package/src/lib/images/proxy.mjs +97 -99
  131. package/src/lib/log.mjs +6 -9
  132. package/src/lib/path.mjs +2 -1
  133. package/src/lib/render-error-template.mjs +19 -20
  134. package/src/lib/settings.mjs +17 -19
  135. package/src/lib/spinner.mjs +21 -23
  136. package/src/lib/string.mjs +4 -2
  137. package/src/recipes/vscode/index.mjs +69 -85
  138. package/src/recipes/vscode/settings.mjs +53 -58
  139. package/src/utils/addons/compare.mjs +31 -32
  140. package/src/utils/addons/diffs/index.mjs +16 -17
  141. package/src/utils/addons/diffs/options.mjs +99 -101
  142. package/src/utils/addons/prepare.mjs +100 -97
  143. package/src/utils/addons/prompts.mjs +73 -76
  144. package/src/utils/addons/render.mjs +33 -36
  145. package/src/utils/addons/validation.mjs +19 -15
  146. package/src/utils/banner.mjs +11 -16
  147. package/src/utils/build-info.mjs +65 -66
  148. package/src/utils/command-helpers.mjs +185 -199
  149. package/src/utils/create-deferred.mjs +9 -12
  150. package/src/utils/create-stream-promise.mjs +54 -47
  151. package/src/utils/deploy/constants.mjs +9 -11
  152. package/src/utils/deploy/deploy-site.mjs +162 -182
  153. package/src/utils/deploy/hash-config.mjs +21 -21
  154. package/src/utils/deploy/hash-files.mjs +34 -38
  155. package/src/utils/deploy/hash-fns.mjs +149 -154
  156. package/src/utils/deploy/hasher-segments.mjs +58 -52
  157. package/src/utils/deploy/upload-files.mjs +99 -113
  158. package/src/utils/deploy/util.mjs +85 -91
  159. package/src/utils/detect-server-settings.mjs +236 -268
  160. package/src/utils/dev.mjs +163 -178
  161. package/src/utils/dot-env.mjs +37 -42
  162. package/src/utils/env/index.mjs +148 -148
  163. package/src/utils/execa.mjs +9 -13
  164. package/src/utils/feature-flags.mjs +6 -5
  165. package/src/utils/framework-server.mjs +43 -52
  166. package/src/utils/functions/constants.mjs +1 -1
  167. package/src/utils/functions/functions.mjs +30 -40
  168. package/src/utils/functions/get-functions.mjs +28 -29
  169. package/src/utils/functions/index.mjs +3 -3
  170. package/src/utils/get-global-config.mjs +33 -36
  171. package/src/utils/get-package-json.mjs +14 -15
  172. package/src/utils/get-repo-data.mjs +54 -64
  173. package/src/utils/get-site.mjs +14 -14
  174. package/src/utils/gh-auth.mjs +79 -100
  175. package/src/utils/gitignore.mjs +37 -40
  176. package/src/utils/headers.mjs +33 -35
  177. package/src/utils/hooks/requires-site-info.mjs +26 -22
  178. package/src/utils/init/config-github.mjs +207 -219
  179. package/src/utils/init/config-manual.mjs +83 -100
  180. package/src/utils/init/config.mjs +25 -26
  181. package/src/utils/init/node-version.mjs +23 -30
  182. package/src/utils/init/plugins.mjs +12 -8
  183. package/src/utils/init/utils.mjs +152 -172
  184. package/src/utils/live-tunnel.mjs +118 -141
  185. package/src/utils/lm/install.mjs +220 -259
  186. package/src/utils/lm/requirements.mjs +54 -63
  187. package/src/utils/lm/steps.mjs +31 -31
  188. package/src/utils/lm/ui.mjs +13 -20
  189. package/src/utils/open-browser.mjs +31 -32
  190. package/src/utils/parse-raw-flags.mjs +39 -35
  191. package/src/utils/proxy-server.mjs +84 -71
  192. package/src/utils/proxy.mjs +696 -750
  193. package/src/utils/read-repo-url.mjs +48 -47
  194. package/src/utils/redirects.mjs +49 -49
  195. package/src/utils/request-id.mjs +2 -4
  196. package/src/utils/rules-proxy.mjs +96 -100
  197. package/src/utils/run-build.mjs +109 -132
  198. package/src/utils/shell.mjs +99 -106
  199. package/src/utils/sign-redirect.mjs +14 -14
  200. package/src/utils/sites/utils.mjs +48 -55
  201. package/src/utils/state-config.mjs +101 -101
  202. package/src/utils/static-server.mjs +28 -34
  203. package/src/utils/telemetry/index.mjs +2 -2
  204. package/src/utils/telemetry/report-error.mjs +45 -49
  205. package/src/utils/telemetry/request.mjs +36 -43
  206. package/src/utils/telemetry/telemetry.mjs +90 -105
  207. package/src/utils/telemetry/utils.mjs +5 -6
  208. package/src/utils/telemetry/validation.mjs +55 -53
  209. package/src/utils/types.d.ts +46 -0
  210. package/src/utils/validation.mjs +10 -13
@@ -1,443 +1,381 @@
1
- import fs from 'fs'
2
- import { resolve } from 'path'
3
- import { exit, env } from 'process'
4
-
5
- import inquirer from 'inquirer'
6
- import yaml from 'js-yaml'
7
- import fetch from 'node-fetch'
8
- import { z } from 'zod'
9
-
10
- import { getBuildOptions } from '../../lib/build.mjs'
11
- import { getToken, chalk, log } from '../../utils/command-helpers.mjs'
12
- import { getSiteInformation } from '../../utils/dev.mjs'
13
- import { checkOptions } from '../build/build.mjs'
14
- import { deploy as siteDeploy } from '../deploy/deploy.mjs'
15
-
1
+ import fs from 'fs';
2
+ import { resolve } from 'path';
3
+ import { exit, env } from 'process';
4
+ import inquirer from 'inquirer';
5
+ // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'js-y... Remove this comment to see the full error message
6
+ import yaml from 'js-yaml';
7
+ // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'node... Remove this comment to see the full error message
8
+ import fetch from 'node-fetch';
9
+ import { z } from 'zod';
10
+ import { getBuildOptions } from '../../lib/build.mjs';
11
+ import { getToken, chalk, log } from '../../utils/command-helpers.mjs';
12
+ import { getSiteInformation } from '../../utils/dev.mjs';
13
+ import { checkOptions } from '../build/build.mjs';
14
+ import { deploy as siteDeploy } from '../deploy/deploy.mjs';
16
15
  function getIntegrationAPIUrl() {
17
- return env.INTEGRATION_URL || 'https://api.netlifysdk.com'
16
+ return env.INTEGRATION_URL || 'https://api.netlifysdk.com';
18
17
  }
19
-
18
+ // @ts-expect-error TS(7006) FIXME: Parameter 'localScopes' implicitly has an 'any' ty... Remove this comment to see the full error message
20
19
  export function areScopesEqual(localScopes, remoteScopes) {
21
- if (localScopes.length !== remoteScopes.length) {
22
- return false
23
- }
24
-
25
- return localScopes.every((scope) => remoteScopes.includes(scope))
20
+ if (localScopes.length !== remoteScopes.length) {
21
+ return false;
22
+ }
23
+ // @ts-expect-error TS(7006) FIXME: Parameter 'scope' implicitly has an 'any' type.
24
+ return localScopes.every((scope) => remoteScopes.includes(scope));
26
25
  }
27
-
26
+ // @ts-expect-error TS(7006) FIXME: Parameter 'localScopes' implicitly has an 'any' ty... Remove this comment to see the full error message
28
27
  function logScopeConfirmationMessage(localScopes, remoteScopes) {
29
- log(chalk.yellow(`This integration is already registered. The current required scopes are:`))
30
- for (const scope of remoteScopes) {
31
- log(chalk.green(`- ${scope}`))
32
- }
33
- log(chalk.yellow('and will be updated to:'))
34
- for (const scope of localScopes) {
35
- log(chalk.green(`- ${scope}`))
36
- }
37
- log(chalk.yellow('if you continue. This will only affect future installations of the integration.'))
28
+ log(chalk.yellow(`This integration is already registered. The current required scopes are:`));
29
+ for (const scope of remoteScopes) {
30
+ log(chalk.green(`- ${scope}`));
31
+ }
32
+ log(chalk.yellow('and will be updated to:'));
33
+ for (const scope of localScopes) {
34
+ log(chalk.green(`- ${scope}`));
35
+ }
36
+ log(chalk.yellow('if you continue. This will only affect future installations of the integration.'));
38
37
  }
39
-
38
+ // @ts-expect-error TS(7006) FIXME: Parameter 'registeredIntegrationScopes' implicitly... Remove this comment to see the full error message
40
39
  function formatScopesToWrite(registeredIntegrationScopes) {
41
- let scopesToWrite = {}
42
-
43
- for (const scope of registeredIntegrationScopes) {
44
- const [resource, permission] = scope.split(':')
45
- if (resource === 'all') {
46
- scopesToWrite = { all: true }
47
- break
48
- } else {
49
- if (!scopesToWrite[resource]) {
50
- scopesToWrite[resource] = []
51
- }
52
- scopesToWrite[resource].push(permission)
40
+ let scopesToWrite = {};
41
+ for (const scope of registeredIntegrationScopes) {
42
+ const [resource, permission] = scope.split(':');
43
+ if (resource === 'all') {
44
+ scopesToWrite = { all: true };
45
+ break;
46
+ }
47
+ else {
48
+ // @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
49
+ if (!scopesToWrite[resource]) {
50
+ // @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
51
+ scopesToWrite[resource] = [];
52
+ }
53
+ // @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
54
+ scopesToWrite[resource].push(permission);
55
+ }
53
56
  }
54
- }
55
- return scopesToWrite
57
+ return scopesToWrite;
56
58
  }
57
-
59
+ // @ts-expect-error TS(7006) FIXME: Parameter 'scopes' implicitly has an 'any' type.
58
60
  function formatScopesForRemote(scopes) {
59
- const scopesToWrite = []
60
- if (scopes.all) {
61
- scopesToWrite.push('all')
62
- } else {
63
- const scopeResources = Object.keys(scopes)
64
- scopeResources.forEach((resource) => {
65
- const permissionsRequested = scopes[resource]
66
- permissionsRequested.forEach((permission) => {
67
- scopesToWrite.push(`${resource}:${permission}`)
68
- })
69
- })
70
- }
71
- return scopesToWrite.join(',')
61
+ const scopesToWrite = [];
62
+ if (scopes.all) {
63
+ scopesToWrite.push('all');
64
+ }
65
+ else {
66
+ const scopeResources = Object.keys(scopes);
67
+ scopeResources.forEach((resource) => {
68
+ const permissionsRequested = scopes[resource];
69
+ // @ts-expect-error TS(7006) FIXME: Parameter 'permission' implicitly has an 'any' typ... Remove this comment to see the full error message
70
+ permissionsRequested.forEach((permission) => {
71
+ scopesToWrite.push(`${resource}:${permission}`);
72
+ });
73
+ });
74
+ }
75
+ return scopesToWrite.join(',');
72
76
  }
73
-
77
+ // @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type.
74
78
  function verifyRequiredFieldsAreInConfig(name, description, scopes, integrationLevel) {
75
- const missingFields = []
76
-
77
- if (!name) {
78
- missingFields.push('name')
79
- }
80
- if (!description) {
81
- missingFields.push('description')
82
- }
83
- if (!scopes) {
84
- missingFields.push('scopes')
85
- }
86
- if (!integrationLevel) {
87
- missingFields.push('integrationLevel')
88
- }
89
- if (missingFields.length !== 0) {
90
- log(
91
- chalk.yellow(
92
- `You are missing the following fields for the integration to be deployed: ${missingFields.join(
93
- ', ',
94
- )}. Please add a these fields as an entry to the integration.yaml file and try again.`,
95
- ),
96
- )
97
- log(
98
- chalk.yellow(
99
- 'For more information on the required fields, please see the documentation: https://ntl.fyi/create-private-integration',
100
- ),
101
- )
102
- return false
103
- }
104
- return true
79
+ const missingFields = [];
80
+ if (!name) {
81
+ missingFields.push('name');
82
+ }
83
+ if (!description) {
84
+ missingFields.push('description');
85
+ }
86
+ if (!scopes) {
87
+ missingFields.push('scopes');
88
+ }
89
+ if (!integrationLevel) {
90
+ missingFields.push('integrationLevel');
91
+ }
92
+ if (missingFields.length !== 0) {
93
+ log(chalk.yellow(`You are missing the following fields for the integration to be deployed: ${missingFields.join(', ')}. Please add a these fields as an entry to the integration.yaml file and try again.`));
94
+ log(chalk.yellow('For more information on the required fields, please see the documentation: https://ntl.fyi/create-private-integration'));
95
+ return false;
96
+ }
97
+ return true;
105
98
  }
106
-
99
+ // @ts-expect-error TS(7006) FIXME: Parameter 'workingDir' implicitly has an 'any' typ... Remove this comment to see the full error message
107
100
  // eslint-disable-next-line max-params
108
101
  export async function registerIntegration(workingDir, siteId, accountId, localIntegrationConfig, token) {
109
- const { description, integrationLevel, name, scopes, slug } = localIntegrationConfig
110
- log(chalk.yellow(`An integration associated with the site ID ${siteId} is not registered.`))
111
- const registerPrompt = await inquirer.prompt([
112
- {
113
- type: 'confirm',
114
- name: 'registerIntegration',
115
- message: `Would you like to register this site as a private integration now?`,
116
- default: false,
117
- },
118
- ])
119
-
120
- if (!registerPrompt.registerIntegration) {
121
- log(
122
- chalk.white(
123
- "Cancelling deployment. Please run 'netlify int deploy' again when you are ready to register the integration.",
124
- ),
125
- )
126
- log(
127
- chalk.white(
128
- "You can also register the integration through the Netlify UI on the 'Integrations' > 'Create private integration' page",
129
- ),
130
- )
131
- exit(1)
132
- }
133
-
134
- if (!verifyRequiredFieldsAreInConfig(name, description, scopes, integrationLevel)) {
135
- exit(1)
136
- }
137
-
138
- log(chalk.white('Registering the integration...'))
139
-
140
- const { body, statusCode } = await fetch(`${getIntegrationAPIUrl()}/${accountId}/integrations`, {
141
- method: 'POST',
142
- headers: {
143
- 'netlify-token': token,
144
- },
145
- body: JSON.stringify({
146
- name,
147
- slug,
148
- description,
149
- hostSiteId: siteId,
150
- scopes: formatScopesForRemote(scopes),
151
- integrationLevel,
152
- }),
153
- }).then(async (res) => {
154
- const response = await res.json()
155
- return { body: response, statusCode: res.status }
156
- })
157
-
158
- if (statusCode !== 201) {
159
- log(chalk.red(`There was an error registering the integration:`))
160
- log()
161
- log(chalk.red(`-----------------------------------------------`))
162
- log(chalk.red(body.msg))
163
- log(chalk.red(`-----------------------------------------------`))
164
- log()
165
- log(chalk.red(`Please try again. If the problem persists, please contact support.`))
166
- exit(1)
167
- }
168
-
169
- log(chalk.green(`Successfully registered the integration with the slug: ${body.slug}`))
170
-
171
- const updatedIntegrationConfig = yaml.dump({
172
- config: { name, description, slug: body.slug, scopes, integrationLevel },
173
- })
174
-
175
- const filePath = resolve(workingDir, 'integration.yaml')
176
- await fs.promises.writeFile(filePath, updatedIntegrationConfig)
177
-
178
- log(chalk.yellow('Your integration.yaml file has been updated. Please commit and push these changes.'))
179
- }
180
-
181
- // eslint-disable-next-line max-params
182
- export async function updateIntegration(
183
- workingDir,
184
- options,
185
- siteId,
186
- accountId,
187
- localIntegrationConfig,
188
- token,
189
- registeredIntegration,
190
- ) {
191
- let { description, integrationLevel, name, scopes, slug } = localIntegrationConfig
192
-
193
- let integrationSlug = slug
194
- if (slug !== registeredIntegration.slug) {
195
- // Update the project's integration.yaml file with the remote slug since that will
196
- // be considered the source of truth and is a value that can't be edited by the user.
197
- // Let the user know they need to commit and push the changes.
198
- integrationSlug = registeredIntegration.slug
199
- }
200
-
201
- if (!name) {
202
- // Disabling this lint rule because the destructuring was not assigning the variable correct and leading to a bug
203
- // eslint-disable-next-line prefer-destructuring
204
- name = registeredIntegration.name
205
- }
206
-
207
- if (!description) {
208
- // eslint-disable-next-line prefer-destructuring
209
- description = registeredIntegration.description
210
- }
211
-
212
- if (!integrationLevel) {
213
- // eslint-disable-next-line prefer-destructuring
214
- integrationLevel = registeredIntegration.integrationLevel
215
- }
216
-
217
- // This is returned as a comma separated string and will be easier to manage here as an array
218
- const registeredIntegrationScopes = registeredIntegration.scopes.split(',')
219
-
220
- const scopeResources = Object.keys(scopes)
221
- let localScopes = []
222
-
223
- if (scopeResources.includes('all')) {
224
- localScopes = ['all']
225
- } else {
226
- scopeResources.forEach((resource) => {
227
- const permissionsRequested = scopes[resource]
228
- permissionsRequested.forEach((permission) => {
229
- localScopes.push(`${resource}:${permission}`)
230
- })
231
- })
232
- }
233
-
234
- if (!areScopesEqual(localScopes, registeredIntegrationScopes)) {
235
- logScopeConfirmationMessage(localScopes, registeredIntegrationScopes)
236
-
237
- const scopePrompt = await inquirer.prompt([
238
- {
239
- type: 'confirm',
240
- name: 'updateScopes',
241
- message: `Do you want to update the scopes?`,
242
- default: false,
243
- },
244
- ])
245
-
246
- let scopesToWrite
247
- if (scopePrompt.updateScopes) {
248
- // Update the scopes in remote
249
- scopesToWrite = scopes
250
- const { statusCode, updateResponse } = await fetch(
251
- `${getIntegrationAPIUrl()}/${accountId}/integrations/${integrationSlug}`,
102
+ const { description, integrationLevel, name, scopes, slug } = localIntegrationConfig;
103
+ log(chalk.yellow(`An integration associated with the site ID ${siteId} is not registered.`));
104
+ const registerPrompt = await inquirer.prompt([
252
105
  {
253
- method: 'PUT',
254
- headers: {
106
+ type: 'confirm',
107
+ name: 'registerIntegration',
108
+ message: `Would you like to register this site as a private integration now?`,
109
+ default: false,
110
+ },
111
+ ]);
112
+ if (!registerPrompt.registerIntegration) {
113
+ log(chalk.white("Cancelling deployment. Please run 'netlify int deploy' again when you are ready to register the integration."));
114
+ log(chalk.white("You can also register the integration through the Netlify UI on the 'Integrations' > 'Create private integration' page"));
115
+ exit(1);
116
+ }
117
+ if (!verifyRequiredFieldsAreInConfig(name, description, scopes, integrationLevel)) {
118
+ exit(1);
119
+ }
120
+ log(chalk.white('Registering the integration...'));
121
+ const { body, statusCode } = await fetch(`${getIntegrationAPIUrl()}/${accountId}/integrations`, {
122
+ method: 'POST',
123
+ headers: {
255
124
  'netlify-token': token,
256
- },
257
- body: JSON.stringify({
125
+ },
126
+ body: JSON.stringify({
258
127
  name,
128
+ slug,
259
129
  description,
260
130
  hostSiteId: siteId,
261
- scopes: localScopes.join(','),
131
+ scopes: formatScopesForRemote(scopes),
262
132
  integrationLevel,
263
- }),
264
- },
265
- ).then(async (res) => {
266
- const response = await res.json()
267
- return { updateResponse: response, statusCode: res.status }
268
- })
269
-
270
- if (statusCode !== 200) {
271
- log(
272
- chalk.red(`There was an error updating the integration: ${updateResponse}`),
273
- chalk.red('Please try again. If the problem persists, please contact support.'),
274
- )
275
- exit(1)
276
- }
277
- } else {
278
- const useRegisteredScopesPrompt = await inquirer.prompt([
279
- {
280
- type: 'confirm',
281
- name: 'useRegisteredScopes',
282
- message: `Do you want to save the scopes registered for your integration in your local configuration file?`,
283
- default: false,
284
- },
285
- ])
286
-
287
- if (useRegisteredScopesPrompt.useRegisteredScopes) {
288
- // Use the scopes that are already registered
289
- log(chalk.white('Saving the currently registered scopes to the integration.yaml file.'))
290
- scopesToWrite = formatScopesToWrite(registeredIntegrationScopes)
291
- }
292
-
293
- if (!useRegisteredScopesPrompt.useRegisteredScopes && options.prod) {
294
- log(chalk.red('Unable to deploy your integration to production without updating the registered scopes.'))
295
- exit(1)
296
- }
133
+ }),
134
+ // @ts-expect-error TS(7006) FIXME: Parameter 'res' implicitly has an 'any' type.
135
+ }).then(async (res) => {
136
+ const response = await res.json();
137
+ return { body: response, statusCode: res.status };
138
+ });
139
+ if (statusCode !== 201) {
140
+ log(chalk.red(`There was an error registering the integration:`));
141
+ log();
142
+ log(chalk.red(`-----------------------------------------------`));
143
+ log(chalk.red(body.msg));
144
+ log(chalk.red(`-----------------------------------------------`));
145
+ log();
146
+ log(chalk.red(`Please try again. If the problem persists, please contact support.`));
147
+ exit(1);
297
148
  }
298
-
149
+ log(chalk.green(`Successfully registered the integration with the slug: ${body.slug}`));
299
150
  const updatedIntegrationConfig = yaml.dump({
300
- config: { name, description, slug: integrationSlug, scopes: scopesToWrite, integrationLevel },
301
- })
302
-
303
- const filePath = resolve(workingDir, 'integration.yaml')
304
- await fs.promises.writeFile(filePath, updatedIntegrationConfig)
305
-
306
- log(chalk.yellow('Changes to the integration.yaml file are complete. Please commit and push these changes.'))
307
- }
151
+ config: { name, description, slug: body.slug, scopes, integrationLevel },
152
+ });
153
+ const filePath = resolve(workingDir, 'integration.yaml');
154
+ await fs.promises.writeFile(filePath, updatedIntegrationConfig);
155
+ log(chalk.yellow('Your integration.yaml file has been updated. Please commit and push these changes.'));
156
+ }
157
+ // eslint-disable-next-line max-params
158
+ export async function updateIntegration(
159
+ // @ts-expect-error TS(7006) FIXME: Parameter 'workingDir' implicitly has an 'any' typ... Remove this comment to see the full error message
160
+ workingDir,
161
+ // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type.
162
+ options,
163
+ // @ts-expect-error TS(7006) FIXME: Parameter 'siteId' implicitly has an 'any' type.
164
+ siteId,
165
+ // @ts-expect-error TS(7006) FIXME: Parameter 'accountId' implicitly has an 'any' type... Remove this comment to see the full error message
166
+ accountId,
167
+ // @ts-expect-error TS(7006) FIXME: Parameter 'localIntegrationConfig' implicitly has ... Remove this comment to see the full error message
168
+ localIntegrationConfig,
169
+ // @ts-expect-error TS(7006) FIXME: Parameter 'token' implicitly has an 'any' type.
170
+ token,
171
+ // @ts-expect-error TS(7006) FIXME: Parameter 'registeredIntegration' implicitly has a... Remove this comment to see the full error message
172
+ registeredIntegration) {
173
+ let { description, integrationLevel, name, scopes, slug } = localIntegrationConfig;
174
+ let integrationSlug = slug;
175
+ if (slug !== registeredIntegration.slug) {
176
+ // Update the project's integration.yaml file with the remote slug since that will
177
+ // be considered the source of truth and is a value that can't be edited by the user.
178
+ // Let the user know they need to commit and push the changes.
179
+ integrationSlug = registeredIntegration.slug;
180
+ }
181
+ if (!name) {
182
+ // Disabling this lint rule because the destructuring was not assigning the variable correct and leading to a bug
183
+ // eslint-disable-next-line prefer-destructuring
184
+ name = registeredIntegration.name;
185
+ }
186
+ if (!description) {
187
+ // eslint-disable-next-line prefer-destructuring
188
+ description = registeredIntegration.description;
189
+ }
190
+ if (!integrationLevel) {
191
+ // eslint-disable-next-line prefer-destructuring
192
+ integrationLevel = registeredIntegration.integrationLevel;
193
+ }
194
+ // This is returned as a comma separated string and will be easier to manage here as an array
195
+ const registeredIntegrationScopes = registeredIntegration.scopes.split(',');
196
+ const scopeResources = Object.keys(scopes);
197
+ // @ts-expect-error TS(7034) FIXME: Variable 'localScopes' implicitly has type 'any[]'... Remove this comment to see the full error message
198
+ let localScopes = [];
199
+ if (scopeResources.includes('all')) {
200
+ localScopes = ['all'];
201
+ }
202
+ else {
203
+ scopeResources.forEach((resource) => {
204
+ const permissionsRequested = scopes[resource];
205
+ // @ts-expect-error TS(7006) FIXME: Parameter 'permission' implicitly has an 'any' typ... Remove this comment to see the full error message
206
+ permissionsRequested.forEach((permission) => {
207
+ localScopes.push(`${resource}:${permission}`);
208
+ });
209
+ });
210
+ }
211
+ // @ts-expect-error TS(7005) FIXME: Variable 'localScopes' implicitly has an 'any[]' t... Remove this comment to see the full error message
212
+ if (!areScopesEqual(localScopes, registeredIntegrationScopes)) {
213
+ // @ts-expect-error TS(7005) FIXME: Variable 'localScopes' implicitly has an 'any[]' t... Remove this comment to see the full error message
214
+ logScopeConfirmationMessage(localScopes, registeredIntegrationScopes);
215
+ const scopePrompt = await inquirer.prompt([
216
+ {
217
+ type: 'confirm',
218
+ name: 'updateScopes',
219
+ message: `Do you want to update the scopes?`,
220
+ default: false,
221
+ },
222
+ ]);
223
+ let scopesToWrite;
224
+ if (scopePrompt.updateScopes) {
225
+ // Update the scopes in remote
226
+ scopesToWrite = scopes;
227
+ const { statusCode, updateResponse } = await fetch(`${getIntegrationAPIUrl()}/${accountId}/integrations/${integrationSlug}`, {
228
+ method: 'PUT',
229
+ headers: {
230
+ 'netlify-token': token,
231
+ },
232
+ body: JSON.stringify({
233
+ name,
234
+ description,
235
+ hostSiteId: siteId,
236
+ // @ts-expect-error TS(7005) FIXME: Variable 'localScopes' implicitly has an 'any[]' t... Remove this comment to see the full error message
237
+ scopes: localScopes.join(','),
238
+ integrationLevel,
239
+ }),
240
+ }).then(async (res) => {
241
+ const response = await res.json();
242
+ return { updateResponse: response, statusCode: res.status };
243
+ });
244
+ if (statusCode !== 200) {
245
+ log(chalk.red(`There was an error updating the integration: ${updateResponse}`), chalk.red('Please try again. If the problem persists, please contact support.'));
246
+ exit(1);
247
+ }
248
+ }
249
+ else {
250
+ const useRegisteredScopesPrompt = await inquirer.prompt([
251
+ {
252
+ type: 'confirm',
253
+ name: 'useRegisteredScopes',
254
+ message: `Do you want to save the scopes registered for your integration in your local configuration file?`,
255
+ default: false,
256
+ },
257
+ ]);
258
+ if (useRegisteredScopesPrompt.useRegisteredScopes) {
259
+ // Use the scopes that are already registered
260
+ log(chalk.white('Saving the currently registered scopes to the integration.yaml file.'));
261
+ scopesToWrite = formatScopesToWrite(registeredIntegrationScopes);
262
+ }
263
+ if (!useRegisteredScopesPrompt.useRegisteredScopes && options.prod) {
264
+ log(chalk.red('Unable to deploy your integration to production without updating the registered scopes.'));
265
+ exit(1);
266
+ }
267
+ }
268
+ const updatedIntegrationConfig = yaml.dump({
269
+ config: { name, description, slug: integrationSlug, scopes: scopesToWrite, integrationLevel },
270
+ });
271
+ const filePath = resolve(workingDir, 'integration.yaml');
272
+ await fs.promises.writeFile(filePath, updatedIntegrationConfig);
273
+ log(chalk.yellow('Changes to the integration.yaml file are complete. Please commit and push these changes.'));
274
+ }
308
275
  }
309
-
310
- const possibleFiles = ['integration.yaml', 'integration.yml', 'integration.netlify.yaml', 'integration.netlify.yml']
276
+ const possibleFiles = ['integration.yaml', 'integration.yml', 'integration.netlify.yaml', 'integration.netlify.yml'];
311
277
  const IntegrationConfigurationSchema = z.object({
312
- name: z.string().optional(),
313
- description: z.string().optional(),
314
- slug: z.string().regex(/^[a-z\d-]+$/, 'slug must be lowercase with dashes'),
315
- scopes: z
316
- .object({
317
- all: z.boolean().optional(),
318
- site: z.array(z.enum(['read', 'write'])).optional(),
319
- env: z.array(z.enum(['read', 'write', 'delete'])).optional(),
320
- user: z.array(z.enum(['read', 'write'])).optional(),
278
+ name: z.string().optional(),
279
+ description: z.string().optional(),
280
+ slug: z.string().regex(/^[a-z\d-]+$/, 'slug must be lowercase with dashes'),
281
+ scopes: z
282
+ .object({
283
+ all: z.boolean().optional(),
284
+ site: z.array(z.enum(['read', 'write'])).optional(),
285
+ env: z.array(z.enum(['read', 'write', 'delete'])).optional(),
286
+ user: z.array(z.enum(['read', 'write'])).optional(),
321
287
  })
322
- .optional(),
323
- integrationLevel: z.enum(['site', 'team', 'team-and-site']).optional(),
324
- })
325
-
288
+ .optional(),
289
+ integrationLevel: z.enum(['site', 'team', 'team-and-site']).optional(),
290
+ });
291
+ // @ts-expect-error TS(7006) FIXME: Parameter 'workingDir' implicitly has an 'any' typ... Remove this comment to see the full error message
326
292
  const getConfigurationFile = (workingDir) => {
327
- const pwd = workingDir
328
-
329
- const fileName = possibleFiles.find((configFileName) => fs.existsSync(resolve(pwd, configFileName)))
330
-
331
- return fileName
332
- }
333
-
293
+ const pwd = workingDir;
294
+ const fileName = possibleFiles.find((configFileName) => fs.existsSync(resolve(pwd, configFileName)));
295
+ return fileName;
296
+ };
297
+ // @ts-expect-error TS(7006) FIXME: Parameter 'workingDir' implicitly has an 'any' typ... Remove this comment to see the full error message
334
298
  export const getConfiguration = (workingDir) => {
335
- const pwd = workingDir
336
-
337
- const fileName = getConfigurationFile(workingDir)
338
-
339
- if (!fileName) {
340
- throw new Error('No configuration file found')
341
- }
342
-
343
- try {
344
- const { config } = yaml.load(fs.readFileSync(resolve(pwd, fileName), 'utf-8'))
345
-
346
- if (!config) {
347
- throw new Error('No configuration found')
299
+ const pwd = workingDir;
300
+ const fileName = getConfigurationFile(workingDir);
301
+ if (!fileName) {
302
+ throw new Error('No configuration file found');
348
303
  }
349
-
350
- const parseResult = IntegrationConfigurationSchema.safeParse(config)
351
-
352
- if (!parseResult.success) {
353
- console.error(parseResult.error.message)
354
- throw new Error('Invalid Configuration')
304
+ try {
305
+ const { config } = yaml.load(fs.readFileSync(resolve(pwd, fileName), 'utf-8'));
306
+ if (!config) {
307
+ throw new Error('No configuration found');
308
+ }
309
+ const parseResult = IntegrationConfigurationSchema.safeParse(config);
310
+ if (!parseResult.success) {
311
+ console.error(parseResult.error.message);
312
+ throw new Error('Invalid Configuration');
313
+ }
314
+ return config;
355
315
  }
356
-
357
- return config
358
- } catch (error) {
359
- console.error(error)
360
- console.error(`No configuration found in ${fileName} in ${pwd}`)
361
- exit(1)
362
- }
363
- }
316
+ catch (error) {
317
+ console.error(error);
318
+ console.error(`No configuration found in ${fileName} in ${pwd}`);
319
+ exit(1);
320
+ }
321
+ };
364
322
  /**
365
323
  * The deploy command for Netlify Integrations
366
324
  * @param {import('commander').OptionValues} options
367
325
  * * @param {import('../base-command.mjs').default} command
368
326
  */
327
+ // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type.
369
328
  const deploy = async (options, command) => {
370
- const { api, cachedConfig, site, siteInfo } = command.netlify
371
- const { id: siteId } = site
372
- const [token] = await getToken()
373
- const workingDir = resolve(command.workingDir)
374
- const buildOptions = await getBuildOptions({
375
- cachedConfig,
376
- packagePath: command.workspacePackage,
377
- currentDir: command.workingDir,
378
- token,
379
- options,
380
- })
381
-
382
- // Confirm that a site is linked and that the user is logged in
383
- checkOptions(buildOptions)
384
-
385
- const { description, integrationLevel, name, scopes, slug } = await getConfiguration(command.workingDir)
386
- const localIntegrationConfig = { name, description, scopes, slug, integrationLevel }
387
-
388
- const { accountId } = await getSiteInformation({
389
- api,
390
- site,
391
- siteInfo,
392
- })
393
-
394
- const { body: registeredIntegration, statusCode } = await fetch(
395
- `${getIntegrationAPIUrl()}/${accountId}/integrations?site_id=${siteId}`,
396
- {
397
- headers: {
398
- 'netlify-token': token,
399
- },
400
- },
401
- ).then(async (res) => {
402
- const body = await res.json()
403
- return { body, statusCode: res.status }
404
- })
405
-
406
- // The integration is registered on the remote
407
- statusCode === 200
408
- ? await updateIntegration(
409
- workingDir,
410
- options,
411
- siteId,
412
- accountId,
413
- localIntegrationConfig,
329
+ const { api, cachedConfig, site, siteInfo } = command.netlify;
330
+ const { id: siteId } = site;
331
+ // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0.
332
+ const [token] = await getToken();
333
+ const workingDir = resolve(command.workingDir);
334
+ // @ts-expect-error TS(2345) FIXME: Argument of type '{ cachedConfig: any; packagePath... Remove this comment to see the full error message
335
+ const buildOptions = await getBuildOptions({
336
+ cachedConfig,
337
+ packagePath: command.workspacePackage,
338
+ currentDir: command.workingDir,
414
339
  token,
415
- registeredIntegration,
416
- )
417
- : await registerIntegration(workingDir, siteId, accountId, localIntegrationConfig, token)
418
-
419
- // Set the prod flag to true if the integration is being initially registered because we don't want the user
420
- // to be in a weird state where the card is appearing in the integrations list but there's no production
421
- // version of the integration deployed
422
- options = statusCode === 200 ? options : { ...options, prod: true }
423
-
424
- // Deploy the integration to that site
425
- await siteDeploy(options, command)
426
-
427
- log(
428
- `${chalk.cyanBright.bold(
429
- `Your integration has been deployed. Next step is to enable it for a team or site.`,
430
- )} https://ntl.fyi/create-private-integration`,
431
- )
432
- }
433
-
340
+ options,
341
+ });
342
+ // Confirm that a site is linked and that the user is logged in
343
+ checkOptions(buildOptions);
344
+ const { description, integrationLevel, name, scopes, slug } = await getConfiguration(command.workingDir);
345
+ const localIntegrationConfig = { name, description, scopes, slug, integrationLevel };
346
+ // @ts-expect-error TS(2345) FIXME: Argument of type '{ api: any; site: any; siteInfo:... Remove this comment to see the full error message
347
+ const { accountId } = await getSiteInformation({
348
+ api,
349
+ site,
350
+ siteInfo,
351
+ });
352
+ const { body: registeredIntegration, statusCode } = await fetch(`${getIntegrationAPIUrl()}/${accountId}/integrations?site_id=${siteId}`, {
353
+ headers: {
354
+ 'netlify-token': token,
355
+ },
356
+ }).then(async (res) => {
357
+ const body = await res.json();
358
+ return { body, statusCode: res.status };
359
+ });
360
+ // The integration is registered on the remote
361
+ statusCode === 200
362
+ ? await updateIntegration(workingDir, options, siteId, accountId, localIntegrationConfig, token, registeredIntegration)
363
+ : await registerIntegration(workingDir, siteId, accountId, localIntegrationConfig, token);
364
+ // Set the prod flag to true if the integration is being initially registered because we don't want the user
365
+ // to be in a weird state where the card is appearing in the integrations list but there's no production
366
+ // version of the integration deployed
367
+ options = statusCode === 200 ? options : { ...options, prod: true };
368
+ // Deploy the integration to that site
369
+ await siteDeploy(options, command);
370
+ log(`${chalk.cyanBright.bold(`Your integration has been deployed. Next step is to enable it for a team or site.`)} https://ntl.fyi/create-private-integration`);
371
+ };
434
372
  /**
435
373
  * Creates the `netlify int deploy` command
436
374
  * @param {import('../base-command.mjs').default} program
437
375
  * @returns
438
376
  */
439
- export const createDeployCommand = (program) =>
440
- program
377
+ // @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type.
378
+ export const createDeployCommand = (program) => program
441
379
  .command('integration:deploy')
442
380
  .alias('int:deploy')
443
381
  .description('Register, build, and deploy a private integration on Netlify')
@@ -445,4 +383,4 @@ export const createDeployCommand = (program) =>
445
383
  .option('-b, --build', 'Build the integration', false)
446
384
  .option('-a, --auth <token>', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN)
447
385
  .option('-s, --site <name-or-id>', 'A site name or ID to deploy to', env.NETLIFY_SITE_ID)
448
- .action(deploy)
386
+ .action(deploy);