netlify-cli 16.7.0 → 16.8.1
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.
- package/README.md +10 -0
- package/npm-shrinkwrap.json +16123 -10639
- package/package.json +6 -4
- package/src/commands/build/build.mjs +4 -2
- package/src/commands/deploy/deploy.mjs +11 -3
- package/src/commands/integration/deploy.mjs +397 -0
- package/src/commands/integration/index.mjs +25 -0
- package/src/commands/main.mjs +2 -0
- package/src/lib/build.mjs +3 -1
- package/src/lib/edge-functions/consts.mjs +1 -0
- package/src/lib/edge-functions/proxy.mjs +1 -2
- package/src/lib/edge-functions/registry.mjs +15 -19
- package/src/lib/functions/registry.mjs +31 -4
- package/src/lib/functions/server.mjs +15 -0
- package/src/functions-templates/javascript/apollo-graphql/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/apollo-graphql/package.json +0 -21
- package/src/functions-templates/javascript/apollo-graphql/{{name}}.js +0 -42
- package/src/functions-templates/javascript/apollo-graphql-rest/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/apollo-graphql-rest/package.json +0 -22
- package/src/functions-templates/javascript/apollo-graphql-rest/random-user.js +0 -23
- package/src/functions-templates/javascript/apollo-graphql-rest/{{name}}.js +0 -68
- package/src/functions-templates/javascript/auth-fetch/.netlify-function-template.mjs +0 -11
- package/src/functions-templates/javascript/auth-fetch/package-lock.json +0 -83
- package/src/functions-templates/javascript/auth-fetch/package.json +0 -21
- package/src/functions-templates/javascript/auth-fetch/{{name}}.js +0 -39
- package/src/functions-templates/javascript/create-user/.netlify-function-template.mjs +0 -11
- package/src/functions-templates/javascript/create-user/package.json +0 -21
- package/src/functions-templates/javascript/create-user/{{name}}.js +0 -36
- package/src/functions-templates/javascript/fauna-crud/.netlify-function-template.mjs +0 -17
- package/src/functions-templates/javascript/fauna-crud/create-schema.js +0 -36
- package/src/functions-templates/javascript/fauna-crud/create.js +0 -37
- package/src/functions-templates/javascript/fauna-crud/delete.js +0 -29
- package/src/functions-templates/javascript/fauna-crud/package.json +0 -20
- package/src/functions-templates/javascript/fauna-crud/read-all.js +0 -33
- package/src/functions-templates/javascript/fauna-crud/read.js +0 -30
- package/src/functions-templates/javascript/fauna-crud/update.js +0 -30
- package/src/functions-templates/javascript/fauna-crud/{{name}}.js +0 -62
- package/src/functions-templates/javascript/fauna-graphql/.netlify-function-template.mjs +0 -17
- package/src/functions-templates/javascript/fauna-graphql/package.json +0 -26
- package/src/functions-templates/javascript/fauna-graphql/schema.graphql +0 -8
- package/src/functions-templates/javascript/fauna-graphql/sync-schema.js +0 -38
- package/src/functions-templates/javascript/fauna-graphql/{{name}}.js +0 -46
- package/src/functions-templates/javascript/google-analytics/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/google-analytics/package-lock.json +0 -100
- package/src/functions-templates/javascript/google-analytics/package.json +0 -25
- package/src/functions-templates/javascript/google-analytics/{{name}}.js +0 -114
- package/src/functions-templates/javascript/graphql-gateway/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/graphql-gateway/example-sibling-function-graphql-1.js +0 -42
- package/src/functions-templates/javascript/graphql-gateway/example-sibling-function-graphql-2.js +0 -80
- package/src/functions-templates/javascript/graphql-gateway/package.json +0 -24
- package/src/functions-templates/javascript/graphql-gateway/{{name}}.js +0 -75
- package/src/functions-templates/javascript/hasura-event-triggered/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/hasura-event-triggered/package.json +0 -21
- package/src/functions-templates/javascript/hasura-event-triggered/{{name}}.js +0 -40
- package/src/functions-templates/javascript/node-fetch/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/node-fetch/package.json +0 -19
- package/src/functions-templates/javascript/node-fetch/{{name}}.js +0 -29
- package/src/functions-templates/javascript/oauth-passport/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/oauth-passport/package.json +0 -25
- package/src/functions-templates/javascript/oauth-passport/utils/auth.js +0 -65
- package/src/functions-templates/javascript/oauth-passport/utils/config.js +0 -24
- package/src/functions-templates/javascript/oauth-passport/{{name}}.js +0 -37
- package/src/functions-templates/javascript/protected-function/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/protected-function/{{name}}.js +0 -25
- package/src/functions-templates/javascript/send-email/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/send-email/package.json +0 -21
- package/src/functions-templates/javascript/send-email/validations.js +0 -38
- package/src/functions-templates/javascript/send-email/{{name}}.js +0 -68
- package/src/functions-templates/javascript/serverless-ssr/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/serverless-ssr/app/index.js +0 -116
- package/src/functions-templates/javascript/serverless-ssr/package.json +0 -24
- package/src/functions-templates/javascript/serverless-ssr/serverless-http.js +0 -15
- package/src/functions-templates/javascript/serverless-ssr/{{name}}.js +0 -15
- package/src/functions-templates/javascript/set-cookie/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/set-cookie/package.json +0 -19
- package/src/functions-templates/javascript/set-cookie/{{name}}.js +0 -44
- package/src/functions-templates/javascript/slack-rate-limit/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/slack-rate-limit/package.json +0 -20
- package/src/functions-templates/javascript/slack-rate-limit/{{name}}.js +0 -115
- package/src/functions-templates/javascript/stripe-charge/.netlify-function-template.mjs +0 -28
- package/src/functions-templates/javascript/stripe-charge/package-lock.json +0 -196
- package/src/functions-templates/javascript/stripe-charge/package.json +0 -21
- package/src/functions-templates/javascript/stripe-charge/{{name}}.js +0 -56
- package/src/functions-templates/javascript/stripe-subscription/.netlify-function-template.mjs +0 -28
- package/src/functions-templates/javascript/stripe-subscription/package-lock.json +0 -196
- package/src/functions-templates/javascript/stripe-subscription/package.json +0 -21
- package/src/functions-templates/javascript/stripe-subscription/{{name}}.js +0 -52
- package/src/functions-templates/javascript/token-hider/.netlify-function-template.mjs +0 -29
- package/src/functions-templates/javascript/token-hider/package-lock.json +0 -317
- package/src/functions-templates/javascript/token-hider/package.json +0 -21
- package/src/functions-templates/javascript/token-hider/{{name}}.js +0 -37
- package/src/functions-templates/javascript/url-shortener/.netlify-function-template.mjs +0 -29
- package/src/functions-templates/javascript/url-shortener/generate-route.js +0 -53
- package/src/functions-templates/javascript/url-shortener/get-route.js +0 -32
- package/src/functions-templates/javascript/url-shortener/package-lock.json +0 -126
- package/src/functions-templates/javascript/url-shortener/package.json +0 -22
- package/src/functions-templates/javascript/url-shortener/{{name}}.js +0 -30
- package/src/functions-templates/javascript/using-middleware/.netlify-function-template.mjs +0 -5
- package/src/functions-templates/javascript/using-middleware/package.json +0 -19
- package/src/functions-templates/javascript/using-middleware/{{name}}.js +0 -60
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "16.
|
|
4
|
+
"version": "16.8.1",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -44,13 +44,14 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@bugsnag/js": "7.20.2",
|
|
46
46
|
"@fastify/static": "6.10.2",
|
|
47
|
-
"@netlify/build": "29.
|
|
47
|
+
"@netlify/build": "29.23.1",
|
|
48
48
|
"@netlify/build-info": "7.10.1",
|
|
49
49
|
"@netlify/config": "20.9.0",
|
|
50
|
-
"@netlify/edge-bundler": "9.
|
|
50
|
+
"@netlify/edge-bundler": "9.3.0",
|
|
51
51
|
"@netlify/local-functions-proxy": "1.1.1",
|
|
52
|
+
"@netlify/sdk": "^1.1.5",
|
|
52
53
|
"@netlify/serverless-functions-api": "1.9.1",
|
|
53
|
-
"@netlify/zip-it-and-ship-it": "9.
|
|
54
|
+
"@netlify/zip-it-and-ship-it": "9.25.1",
|
|
54
55
|
"@octokit/rest": "19.0.13",
|
|
55
56
|
"ansi-escapes": "6.2.0",
|
|
56
57
|
"ansi-styles": "6.2.1",
|
|
@@ -104,6 +105,7 @@
|
|
|
104
105
|
"is-stream": "3.0.0",
|
|
105
106
|
"is-wsl": "2.2.0",
|
|
106
107
|
"isexe": "2.0.0",
|
|
108
|
+
"js-yaml": "^4.1.0",
|
|
107
109
|
"jsonwebtoken": "9.0.1",
|
|
108
110
|
"jwt-decode": "3.1.2",
|
|
109
111
|
"lambda-local": "2.1.2",
|
|
@@ -9,9 +9,11 @@ import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.mjs'
|
|
|
9
9
|
/**
|
|
10
10
|
* @param {import('../../lib/build.mjs').BuildConfig} options
|
|
11
11
|
*/
|
|
12
|
-
const checkOptions = ({ cachedConfig: { siteInfo = {} }, token }) => {
|
|
12
|
+
export const checkOptions = ({ cachedConfig: { siteInfo = {} }, token }) => {
|
|
13
13
|
if (!siteInfo.id) {
|
|
14
|
-
error(
|
|
14
|
+
error(
|
|
15
|
+
'Could not find the site ID. If your site is not on Netlify, please run `netlify init` or `netlify deploy` first. If it is, please run `netlify link`.',
|
|
16
|
+
)
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
if (!token) {
|
|
@@ -475,12 +475,13 @@ const bundleEdgeFunctions = async (options, command) => {
|
|
|
475
475
|
*
|
|
476
476
|
* @param {object} config
|
|
477
477
|
* @param {boolean} config.deployToProduction
|
|
478
|
+
* @param {boolean} config.isIntegrationDeploy If the user ran netlify integration:deploy instead of just netlify deploy
|
|
478
479
|
* @param {boolean} config.json If the result should be printed as json message
|
|
479
480
|
* @param {boolean} config.runBuildCommand If the build command should be run
|
|
480
481
|
* @param {object} config.results
|
|
481
482
|
* @returns {void}
|
|
482
483
|
*/
|
|
483
|
-
const printResults = ({ deployToProduction, json, results, runBuildCommand }) => {
|
|
484
|
+
const printResults = ({ deployToProduction, isIntegrationDeploy, json, results, runBuildCommand }) => {
|
|
484
485
|
const msgData = {
|
|
485
486
|
'Build logs': results.logsUrl,
|
|
486
487
|
'Function logs': results.functionLogsUrl,
|
|
@@ -518,7 +519,11 @@ const printResults = ({ deployToProduction, json, results, runBuildCommand }) =>
|
|
|
518
519
|
if (!deployToProduction) {
|
|
519
520
|
log()
|
|
520
521
|
log('If everything looks good on your draft URL, deploy it to your main site URL with the --prod flag.')
|
|
521
|
-
log(
|
|
522
|
+
log(
|
|
523
|
+
`${chalk.cyanBright.bold(
|
|
524
|
+
`netlify ${isIntegrationDeploy ? 'integration:' : ''}deploy${runBuildCommand ? ' --build' : ''} --prod`,
|
|
525
|
+
)}`,
|
|
526
|
+
)
|
|
522
527
|
log()
|
|
523
528
|
}
|
|
524
529
|
}
|
|
@@ -529,7 +534,7 @@ const printResults = ({ deployToProduction, json, results, runBuildCommand }) =>
|
|
|
529
534
|
* @param {import('commander').OptionValues} options
|
|
530
535
|
* @param {import('../base-command.mjs').default} command
|
|
531
536
|
*/
|
|
532
|
-
const deploy = async (options, command) => {
|
|
537
|
+
export const deploy = async (options, command) => {
|
|
533
538
|
const { workingDir } = command
|
|
534
539
|
const { api, site, siteInfo } = command.netlify
|
|
535
540
|
const alias = options.alias || options.branch
|
|
@@ -675,8 +680,11 @@ const deploy = async (options, command) => {
|
|
|
675
680
|
// @ts-ignore
|
|
676
681
|
await restoreConfig(configMutations, { buildDir: deployFolder, configPath, redirectsPath })
|
|
677
682
|
|
|
683
|
+
const isIntegrationDeploy = command.name() === 'integration:deploy'
|
|
684
|
+
|
|
678
685
|
printResults({
|
|
679
686
|
runBuildCommand: options.build,
|
|
687
|
+
isIntegrationDeploy,
|
|
680
688
|
json: options.json,
|
|
681
689
|
results,
|
|
682
690
|
deployToProduction,
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/* eslint-disable import/extensions */
|
|
2
|
+
import { resolve } from 'path'
|
|
3
|
+
import { exit, env } from 'process'
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line n/no-missing-import
|
|
6
|
+
import { getConfiguration } from '@netlify/sdk/cli-utils'
|
|
7
|
+
// eslint-disable-next-line n/no-unpublished-import
|
|
8
|
+
import fs from 'fs-extra'
|
|
9
|
+
import inquirer from 'inquirer'
|
|
10
|
+
import yaml from 'js-yaml'
|
|
11
|
+
import fetch from 'node-fetch'
|
|
12
|
+
|
|
13
|
+
import { getBuildOptions } from '../../lib/build.mjs'
|
|
14
|
+
import { getToken, chalk, log } from '../../utils/command-helpers.mjs'
|
|
15
|
+
import { getSiteInformation } from '../../utils/dev.mjs'
|
|
16
|
+
import { checkOptions } from '../build/build.mjs'
|
|
17
|
+
import { deploy as siteDeploy } from '../deploy/deploy.mjs'
|
|
18
|
+
|
|
19
|
+
function getIntegrationAPIUrl() {
|
|
20
|
+
return env.INTEGRATION_URL || 'https://api.netlifysdk.com'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function areScopesEqual(localScopes, remoteScopes) {
|
|
24
|
+
if (localScopes.length !== remoteScopes.length) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return localScopes.every((scope) => remoteScopes.includes(scope))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function logScopeConfirmationMessage(localScopes, remoteScopes) {
|
|
32
|
+
log(chalk.yellow(`This integration is already registered. The current required scopes are:`))
|
|
33
|
+
for (const scope of remoteScopes) {
|
|
34
|
+
log(chalk.green(`- ${scope}`))
|
|
35
|
+
}
|
|
36
|
+
log(chalk.yellow('and will be updated to:'))
|
|
37
|
+
for (const scope of localScopes) {
|
|
38
|
+
log(chalk.green(`- ${scope}`))
|
|
39
|
+
}
|
|
40
|
+
log(chalk.yellow('if you continue. This will only affect future installations of the integration.'))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function formatScopesToWrite(registeredIntegrationScopes) {
|
|
44
|
+
let scopesToWrite = {}
|
|
45
|
+
|
|
46
|
+
for (const scope of registeredIntegrationScopes) {
|
|
47
|
+
const [resource, permission] = scope.split(':')
|
|
48
|
+
if (resource === 'all') {
|
|
49
|
+
scopesToWrite = { all: true }
|
|
50
|
+
break
|
|
51
|
+
} else {
|
|
52
|
+
if (!scopesToWrite[resource]) {
|
|
53
|
+
scopesToWrite[resource] = []
|
|
54
|
+
}
|
|
55
|
+
scopesToWrite[resource].push(permission)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return scopesToWrite
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function formatScopesForRemote(scopes) {
|
|
62
|
+
const scopesToWrite = []
|
|
63
|
+
if (scopes.all) {
|
|
64
|
+
scopesToWrite.push('all')
|
|
65
|
+
} else {
|
|
66
|
+
const scopeResources = Object.keys(scopes)
|
|
67
|
+
scopeResources.forEach((resource) => {
|
|
68
|
+
const permissionsRequested = scopes[resource]
|
|
69
|
+
permissionsRequested.forEach((permission) => {
|
|
70
|
+
scopesToWrite.push(`${resource}:${permission}`)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
return scopesToWrite.join(',')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function verifyRequiredFieldsAreInConfig(name, description, scopes, integrationLevel) {
|
|
78
|
+
const missingFields = []
|
|
79
|
+
|
|
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(
|
|
94
|
+
chalk.yellow(
|
|
95
|
+
`You are missing the following fields for the integration to be deployed: ${missingFields.join(
|
|
96
|
+
', ',
|
|
97
|
+
)}. Please add a these fields as an entry to the integration.yaml file and try again.`,
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
log(
|
|
101
|
+
chalk.yellow(
|
|
102
|
+
'For more information on the required fields, please see the documentation: https://ntl.fyi/create-private-integration',
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// eslint-disable-next-line max-params
|
|
111
|
+
export async function registerIntegration(workingDir, siteId, accountId, localIntegrationConfig, token) {
|
|
112
|
+
const { description, integrationLevel, name, scopes, slug } = localIntegrationConfig
|
|
113
|
+
log(chalk.yellow(`An integration associated with the site ID ${siteId} is not registered.`))
|
|
114
|
+
const registerPrompt = await inquirer.prompt([
|
|
115
|
+
{
|
|
116
|
+
type: 'confirm',
|
|
117
|
+
name: 'registerIntegration',
|
|
118
|
+
message: `Would you like to register this site as a private integration now?`,
|
|
119
|
+
default: false,
|
|
120
|
+
},
|
|
121
|
+
])
|
|
122
|
+
|
|
123
|
+
if (!registerPrompt.registerIntegration) {
|
|
124
|
+
log(
|
|
125
|
+
chalk.white(
|
|
126
|
+
"Cancelling deployment. Please run 'netlify int deploy' again when you are ready to register the integration.",
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
log(
|
|
130
|
+
chalk.white(
|
|
131
|
+
"You can also register the integration through the Netlify UI on the 'Integrations' > 'Create private integration' page",
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
exit(1)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!verifyRequiredFieldsAreInConfig(name, description, scopes, integrationLevel)) {
|
|
138
|
+
exit(1)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log(chalk.white('Registering the integration...'))
|
|
142
|
+
|
|
143
|
+
const { body, statusCode } = await fetch(`${getIntegrationAPIUrl()}/${accountId}/integrations`, {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: {
|
|
146
|
+
'netlify-token': token,
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
name,
|
|
150
|
+
slug,
|
|
151
|
+
description,
|
|
152
|
+
hostSiteId: siteId,
|
|
153
|
+
scopes: formatScopesForRemote(scopes),
|
|
154
|
+
integrationLevel,
|
|
155
|
+
}),
|
|
156
|
+
}).then(async (res) => {
|
|
157
|
+
const response = await res.json()
|
|
158
|
+
return { body: response, statusCode: res.status }
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if (statusCode !== 201) {
|
|
162
|
+
log(chalk.red(`There was an error registering the integration:`))
|
|
163
|
+
log()
|
|
164
|
+
log(chalk.red(`-----------------------------------------------`))
|
|
165
|
+
log(chalk.red(body.msg))
|
|
166
|
+
log(chalk.red(`-----------------------------------------------`))
|
|
167
|
+
log()
|
|
168
|
+
log(chalk.red(`Please try again. If the problem persists, please contact support.`))
|
|
169
|
+
exit(1)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
log(chalk.green(`Successfully registered the integration with the slug: ${body.slug}`))
|
|
173
|
+
|
|
174
|
+
const updatedIntegrationConfig = yaml.dump({
|
|
175
|
+
config: { name, description, slug: body.slug, scopes, integrationLevel },
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const filePath = resolve(workingDir, 'integration.yaml')
|
|
179
|
+
await fs.writeFile(filePath, updatedIntegrationConfig)
|
|
180
|
+
|
|
181
|
+
log(chalk.yellow('Your integration.yaml file has been updated. Please commit and push these changes.'))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// eslint-disable-next-line max-params
|
|
185
|
+
export async function updateIntegration(
|
|
186
|
+
workingDir,
|
|
187
|
+
options,
|
|
188
|
+
siteId,
|
|
189
|
+
accountId,
|
|
190
|
+
localIntegrationConfig,
|
|
191
|
+
token,
|
|
192
|
+
registeredIntegration,
|
|
193
|
+
) {
|
|
194
|
+
let { description, integrationLevel, name, scopes, slug } = localIntegrationConfig
|
|
195
|
+
|
|
196
|
+
let integrationSlug = slug
|
|
197
|
+
if (slug !== registeredIntegration.slug) {
|
|
198
|
+
// Update the project's integration.yaml file with the remote slug since that will
|
|
199
|
+
// be considered the source of truth and is a value that can't be edited by the user.
|
|
200
|
+
// Let the user know they need to commit and push the changes.
|
|
201
|
+
integrationSlug = registeredIntegration.slug
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!name) {
|
|
205
|
+
// Disabling this lint rule because the destructuring was not assigning the variable correct and leading to a bug
|
|
206
|
+
// eslint-disable-next-line prefer-destructuring
|
|
207
|
+
name = registeredIntegration.name
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!description) {
|
|
211
|
+
// eslint-disable-next-line prefer-destructuring
|
|
212
|
+
description = registeredIntegration.description
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!integrationLevel) {
|
|
216
|
+
// eslint-disable-next-line prefer-destructuring
|
|
217
|
+
integrationLevel = registeredIntegration.integrationLevel
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// This is returned as a comma separated string and will be easier to manage here as an array
|
|
221
|
+
const registeredIntegrationScopes = registeredIntegration.scopes.split(',')
|
|
222
|
+
|
|
223
|
+
const scopeResources = Object.keys(scopes)
|
|
224
|
+
let localScopes = []
|
|
225
|
+
|
|
226
|
+
if (scopeResources.includes('all')) {
|
|
227
|
+
localScopes = ['all']
|
|
228
|
+
} else {
|
|
229
|
+
scopeResources.forEach((resource) => {
|
|
230
|
+
const permissionsRequested = scopes[resource]
|
|
231
|
+
permissionsRequested.forEach((permission) => {
|
|
232
|
+
localScopes.push(`${resource}:${permission}`)
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!areScopesEqual(localScopes, registeredIntegrationScopes)) {
|
|
238
|
+
logScopeConfirmationMessage(localScopes, registeredIntegrationScopes)
|
|
239
|
+
|
|
240
|
+
const scopePrompt = await inquirer.prompt([
|
|
241
|
+
{
|
|
242
|
+
type: 'confirm',
|
|
243
|
+
name: 'updateScopes',
|
|
244
|
+
message: `Do you want to update the scopes?`,
|
|
245
|
+
default: false,
|
|
246
|
+
},
|
|
247
|
+
])
|
|
248
|
+
|
|
249
|
+
let scopesToWrite
|
|
250
|
+
if (scopePrompt.updateScopes) {
|
|
251
|
+
// Update the scopes in remote
|
|
252
|
+
scopesToWrite = scopes
|
|
253
|
+
const { statusCode, updateResponse } = await fetch(
|
|
254
|
+
`${getIntegrationAPIUrl()}/${accountId}/integrations/${integrationSlug}`,
|
|
255
|
+
{
|
|
256
|
+
method: 'PUT',
|
|
257
|
+
headers: {
|
|
258
|
+
'netlify-token': token,
|
|
259
|
+
},
|
|
260
|
+
body: JSON.stringify({
|
|
261
|
+
name,
|
|
262
|
+
description,
|
|
263
|
+
hostSiteId: siteId,
|
|
264
|
+
scopes: localScopes.join(','),
|
|
265
|
+
integrationLevel,
|
|
266
|
+
}),
|
|
267
|
+
},
|
|
268
|
+
).then(async (res) => {
|
|
269
|
+
const response = await res.json()
|
|
270
|
+
return { updateResponse: response, statusCode: res.status }
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
if (statusCode !== 200) {
|
|
274
|
+
log(
|
|
275
|
+
chalk.red(`There was an error updating the integration: ${updateResponse}`),
|
|
276
|
+
chalk.red('Please try again. If the problem persists, please contact support.'),
|
|
277
|
+
)
|
|
278
|
+
exit(1)
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
const useRegisteredScopesPrompt = await inquirer.prompt([
|
|
282
|
+
{
|
|
283
|
+
type: 'confirm',
|
|
284
|
+
name: 'useRegisteredScopes',
|
|
285
|
+
message: `Do you want to save the scopes registered for your integration in your local configuration file?`,
|
|
286
|
+
default: false,
|
|
287
|
+
},
|
|
288
|
+
])
|
|
289
|
+
|
|
290
|
+
if (useRegisteredScopesPrompt.useRegisteredScopes) {
|
|
291
|
+
// Use the scopes that are already registered
|
|
292
|
+
log(chalk.white('Saving the currently registered scopes to the integration.yaml file.'))
|
|
293
|
+
scopesToWrite = formatScopesToWrite(registeredIntegrationScopes)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!useRegisteredScopesPrompt.useRegisteredScopes && options.prod) {
|
|
297
|
+
log(chalk.red('Unable to deploy your integration to production without updating the registered scopes.'))
|
|
298
|
+
exit(1)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const updatedIntegrationConfig = yaml.dump({
|
|
303
|
+
config: { name, description, slug: integrationSlug, scopes: scopesToWrite, integrationLevel },
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
const filePath = resolve(workingDir, 'integration.yaml')
|
|
307
|
+
await fs.writeFile(filePath, updatedIntegrationConfig)
|
|
308
|
+
|
|
309
|
+
log(chalk.yellow('Changes to the integration.yaml file are complete. Please commit and push these changes.'))
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* The deploy command for Netlify Integrations
|
|
315
|
+
* @param {import('commander').OptionValues} options
|
|
316
|
+
* * @param {import('../base-command.mjs').default} command
|
|
317
|
+
*/
|
|
318
|
+
const deploy = async (options, command) => {
|
|
319
|
+
const { api, cachedConfig, site, siteInfo } = command.netlify
|
|
320
|
+
const { id: siteId } = site
|
|
321
|
+
const [token] = await getToken()
|
|
322
|
+
const workingDir = resolve(command.workingDir)
|
|
323
|
+
const buildOptions = await getBuildOptions({
|
|
324
|
+
cachedConfig,
|
|
325
|
+
packagePath: command.workspacePackage,
|
|
326
|
+
token,
|
|
327
|
+
options,
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
// Confirm that a site is linked and that the user is logged in
|
|
331
|
+
checkOptions(buildOptions)
|
|
332
|
+
|
|
333
|
+
const { description, integrationLevel, name, scopes, slug } = await getConfiguration()
|
|
334
|
+
const localIntegrationConfig = { name, description, scopes, slug, integrationLevel }
|
|
335
|
+
|
|
336
|
+
const { accountId } = await getSiteInformation({
|
|
337
|
+
api,
|
|
338
|
+
site,
|
|
339
|
+
siteInfo,
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
const { body: registeredIntegration, statusCode } = await fetch(
|
|
343
|
+
`${getIntegrationAPIUrl()}/${accountId}/integrations?site_id=${siteId}`,
|
|
344
|
+
{
|
|
345
|
+
headers: {
|
|
346
|
+
'netlify-token': token,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
).then(async (res) => {
|
|
350
|
+
const body = await res.json()
|
|
351
|
+
return { body, statusCode: res.status }
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
// The integration is registered on the remote
|
|
355
|
+
statusCode === 200
|
|
356
|
+
? await updateIntegration(
|
|
357
|
+
workingDir,
|
|
358
|
+
options,
|
|
359
|
+
siteId,
|
|
360
|
+
accountId,
|
|
361
|
+
localIntegrationConfig,
|
|
362
|
+
token,
|
|
363
|
+
registeredIntegration,
|
|
364
|
+
)
|
|
365
|
+
: await registerIntegration(workingDir, siteId, accountId, localIntegrationConfig, token)
|
|
366
|
+
|
|
367
|
+
// Set the prod flag to true if the integration is being initially registered because we don't want the user
|
|
368
|
+
// to be in a weird state where the card is appearing in the integrations list but there's no production
|
|
369
|
+
// version of the integration deployed
|
|
370
|
+
options = statusCode === 200 ? options : { ...options, prod: true }
|
|
371
|
+
|
|
372
|
+
// Deploy the integration to that site
|
|
373
|
+
await siteDeploy(options, command)
|
|
374
|
+
|
|
375
|
+
log(
|
|
376
|
+
`${chalk.cyanBright.bold(
|
|
377
|
+
`Your integration has been deployed. Next step is to enable it for a team or site.`,
|
|
378
|
+
)} https://ntl.fyi/create-private-integration`,
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Creates the `netlify int deploy` command
|
|
384
|
+
* @param {import('../base-command.mjs').default} program
|
|
385
|
+
* @returns
|
|
386
|
+
*/
|
|
387
|
+
export const createDeployCommand = (program) =>
|
|
388
|
+
program
|
|
389
|
+
.command('integration:deploy')
|
|
390
|
+
.alias('int:deploy')
|
|
391
|
+
.description('Register, build, and deploy a private integration on Netlify')
|
|
392
|
+
.option('-p, --prod', 'Deploy to production', false)
|
|
393
|
+
.option('-b, --build', 'Build the integration', false)
|
|
394
|
+
.option('-a, --auth <token>', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN)
|
|
395
|
+
.option('-s, --site <name-or-id>', 'A site name or ID to deploy to', env.NETLIFY_SITE_ID)
|
|
396
|
+
.action(deploy)
|
|
397
|
+
/* eslint-enable import/extensions */
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createDeployCommand } from './deploy.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The int command
|
|
5
|
+
* @param {import('commander').OptionValues} options
|
|
6
|
+
* @param {import('../base-command.mjs').default} command
|
|
7
|
+
*/
|
|
8
|
+
const integrations = (options, command) => {
|
|
9
|
+
command.help()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates the `netlify integration` command
|
|
14
|
+
* @param {import('../base-command.mjs').default} program
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
export const createIntegrationCommand = (program) => {
|
|
18
|
+
createDeployCommand(program)
|
|
19
|
+
|
|
20
|
+
return program
|
|
21
|
+
.command('integration')
|
|
22
|
+
.alias('int')
|
|
23
|
+
.description('Manage Netlify Integrations built with the Netlify SDK')
|
|
24
|
+
.action(integrations)
|
|
25
|
+
}
|
package/src/commands/main.mjs
CHANGED
|
@@ -22,6 +22,7 @@ import { createDevCommand } from './dev/index.mjs'
|
|
|
22
22
|
import { createEnvCommand } from './env/index.mjs'
|
|
23
23
|
import { createFunctionsCommand } from './functions/index.mjs'
|
|
24
24
|
import { createInitCommand } from './init/index.mjs'
|
|
25
|
+
import { createIntegrationCommand } from './integration/index.mjs'
|
|
25
26
|
import { createLinkCommand } from './link/index.mjs'
|
|
26
27
|
import { createLmCommand } from './lm/index.mjs'
|
|
27
28
|
import { createLoginCommand } from './login/index.mjs'
|
|
@@ -191,6 +192,7 @@ export const createMainCommand = () => {
|
|
|
191
192
|
createFunctionsCommand(program)
|
|
192
193
|
createRecipesCommand(program)
|
|
193
194
|
createInitCommand(program)
|
|
195
|
+
createIntegrationCommand(program)
|
|
194
196
|
createLinkCommand(program)
|
|
195
197
|
createLmCommand(program)
|
|
196
198
|
createLoginCommand(program)
|
package/src/lib/build.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import process from 'process'
|
|
|
3
3
|
|
|
4
4
|
import build from '@netlify/build'
|
|
5
5
|
|
|
6
|
+
import { isFeatureFlagEnabled } from '../utils/feature-flags.mjs'
|
|
7
|
+
|
|
6
8
|
import { getBootstrapURL } from './edge-functions/bootstrap.mjs'
|
|
7
9
|
import { featureFlags as edgeFunctionsFeatureFlags } from './edge-functions/consts.mjs'
|
|
8
10
|
|
|
@@ -58,7 +60,7 @@ export const getBuildOptions = ({
|
|
|
58
60
|
const getFeatureFlagsFromSiteInfo = (siteInfo) => ({
|
|
59
61
|
...siteInfo.feature_flags,
|
|
60
62
|
// see https://github.com/netlify/pod-dev-foundations/issues/581#issuecomment-1731022753
|
|
61
|
-
zisi_golang_use_al2:
|
|
63
|
+
zisi_golang_use_al2: isFeatureFlagEnabled('cli_golang_use_al2', siteInfo),
|
|
62
64
|
})
|
|
63
65
|
|
|
64
66
|
/**
|
|
@@ -8,7 +8,6 @@ import * as bundler from '@netlify/edge-bundler'
|
|
|
8
8
|
import getAvailablePort from 'get-port'
|
|
9
9
|
|
|
10
10
|
import { NETLIFYDEVERR, NETLIFYDEVWARN, chalk, error as printError, log } from '../../utils/command-helpers.mjs'
|
|
11
|
-
import { isFeatureFlagEnabled } from '../../utils/feature-flags.mjs'
|
|
12
11
|
import { getGeoLocation } from '../geo-location.mjs'
|
|
13
12
|
import { getPathInProject } from '../settings.mjs'
|
|
14
13
|
import { startSpinner, stopSpinner } from '../spinner.mjs'
|
|
@@ -110,7 +109,7 @@ export const initializeProxy = async ({
|
|
|
110
109
|
const userFunctionsPath = config.build.edge_functions
|
|
111
110
|
const isolatePort = await getAvailablePort()
|
|
112
111
|
const buildFeatureFlags = {
|
|
113
|
-
edge_functions_npm_modules:
|
|
112
|
+
edge_functions_npm_modules: true,
|
|
114
113
|
}
|
|
115
114
|
const runtimeFeatureFlags = ['edge_functions_bootstrap_failure_mode']
|
|
116
115
|
|
|
@@ -40,9 +40,6 @@ export class EdgeFunctionsRegistry {
|
|
|
40
40
|
/** @type {RunIsolate} */
|
|
41
41
|
#runIsolate
|
|
42
42
|
|
|
43
|
-
/** @type {boolean} */
|
|
44
|
-
#hasShownNPMWarning = false
|
|
45
|
-
|
|
46
43
|
/** @type {Error | null} */
|
|
47
44
|
#buildError = null
|
|
48
45
|
|
|
@@ -167,22 +164,13 @@ export class EdgeFunctionsRegistry {
|
|
|
167
164
|
*/
|
|
168
165
|
async #build() {
|
|
169
166
|
try {
|
|
170
|
-
const {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
if (features.npmModules && !this.#hasShownNPMWarning) {
|
|
180
|
-
log(
|
|
181
|
-
`${NETLIFYDEVWARN} Support for npm modules in edge functions is an experimental feature. To learn more about the current state of this capability or to report a problem, refer to https://ntl.fyi/edge-functions-npm.`,
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
this.#hasShownNPMWarning = true
|
|
185
|
-
}
|
|
167
|
+
const { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success } = await this.#runIsolate(
|
|
168
|
+
this.#functions,
|
|
169
|
+
this.#env,
|
|
170
|
+
{
|
|
171
|
+
getFunctionsConfig: true,
|
|
172
|
+
},
|
|
173
|
+
)
|
|
186
174
|
|
|
187
175
|
if (!success) {
|
|
188
176
|
throw new Error('Build error')
|
|
@@ -207,6 +195,14 @@ export class EdgeFunctionsRegistry {
|
|
|
207
195
|
)
|
|
208
196
|
|
|
209
197
|
this.#processGraph(graph)
|
|
198
|
+
|
|
199
|
+
if (npmSpecifiersWithExtraneousFiles.length !== 0) {
|
|
200
|
+
const modules = npmSpecifiersWithExtraneousFiles.map((name) => chalk.yellow(name)).join(', ')
|
|
201
|
+
|
|
202
|
+
log(
|
|
203
|
+
`${NETLIFYDEVWARN} The following npm modules, which are directly or indirectly imported by an edge function, may not be supported: ${modules}. For more information, visit https://ntl.fyi/edge-functions-npm.`,
|
|
204
|
+
)
|
|
205
|
+
}
|
|
210
206
|
} catch (error) {
|
|
211
207
|
this.#buildError = error
|
|
212
208
|
|