netlify-cli 15.7.0 → 15.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/npm-shrinkwrap.json +2418 -358
- package/package.json +9 -9
- package/src/commands/base-command.mjs +2 -1
- package/src/commands/env/env-set.mjs +50 -10
- package/src/lib/edge-functions/bootstrap.mjs +1 -1
- package/src/lib/functions/runtimes/js/builders/zisi.mjs +2 -12
- package/src/lib/functions/synchronous.mjs +10 -10
- package/src/utils/proxy.mjs +23 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.8.1",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@bugsnag/js": "7.20.2",
|
|
46
46
|
"@fastify/static": "6.10.2",
|
|
47
|
-
"@netlify/build": "29.
|
|
48
|
-
"@netlify/build-info": "7.
|
|
49
|
-
"@netlify/config": "20.5.
|
|
47
|
+
"@netlify/build": "29.15.2",
|
|
48
|
+
"@netlify/build-info": "7.3.4",
|
|
49
|
+
"@netlify/config": "20.5.2",
|
|
50
50
|
"@netlify/edge-bundler": "8.16.2",
|
|
51
51
|
"@netlify/framework-info": "9.8.10",
|
|
52
52
|
"@netlify/local-functions-proxy": "1.1.1",
|
|
53
53
|
"@netlify/serverless-functions-api": "1.5.1",
|
|
54
|
-
"@netlify/zip-it-and-ship-it": "9.
|
|
54
|
+
"@netlify/zip-it-and-ship-it": "9.12.1",
|
|
55
55
|
"@octokit/rest": "19.0.13",
|
|
56
56
|
"@skn0tt/lambda-local": "2.0.3",
|
|
57
57
|
"ansi-escapes": "6.2.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"ascii-table": "0.0.9",
|
|
61
61
|
"backoff": "2.5.0",
|
|
62
62
|
"better-opn": "3.0.2",
|
|
63
|
-
"boxen": "7.1.
|
|
63
|
+
"boxen": "7.1.1",
|
|
64
64
|
"chalk": "5.2.0",
|
|
65
65
|
"chokidar": "3.5.3",
|
|
66
66
|
"ci-info": "3.8.0",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"from2-array": "0.0.4",
|
|
93
93
|
"fuzzy": "0.1.3",
|
|
94
94
|
"get-port": "5.1.1",
|
|
95
|
-
"gh-release-fetch": "4.0.
|
|
95
|
+
"gh-release-fetch": "4.0.3",
|
|
96
96
|
"git-repo-info": "2.1.1",
|
|
97
97
|
"gitconfiglocal": "2.1.0",
|
|
98
98
|
"hasbin": "1.2.3",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"is-stream": "3.0.0",
|
|
107
107
|
"is-wsl": "2.2.0",
|
|
108
108
|
"isexe": "2.0.0",
|
|
109
|
-
"jsonwebtoken": "9.0.
|
|
109
|
+
"jsonwebtoken": "9.0.1",
|
|
110
110
|
"jwt-decode": "3.1.2",
|
|
111
111
|
"listr": "0.14.3",
|
|
112
112
|
"locate-path": "7.2.0",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"netlify-headers-parser": "7.1.2",
|
|
120
120
|
"netlify-redirect-parser": "14.1.3",
|
|
121
121
|
"netlify-redirector": "0.4.0",
|
|
122
|
-
"node-fetch": "2.6.
|
|
122
|
+
"node-fetch": "2.6.12",
|
|
123
123
|
"node-version-alias": "3.4.1",
|
|
124
124
|
"ora": "6.3.1",
|
|
125
125
|
"p-filter": "3.0.0",
|
|
@@ -407,7 +407,8 @@ export default class BaseCommand extends Command {
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
setAnalyticsPayload(payload) {
|
|
410
|
-
|
|
410
|
+
const newPayload = { ...this.analytics.payload, ...payload }
|
|
411
|
+
this.analytics = { ...this.analytics, payload: newPayload }
|
|
411
412
|
}
|
|
412
413
|
|
|
413
414
|
/**
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
* @returns {Promise<boolean>}
|
|
19
19
|
*/
|
|
20
20
|
const envSet = async (key, value, options, command) => {
|
|
21
|
-
const { context, scope } = options
|
|
21
|
+
const { context, scope, secret } = options
|
|
22
22
|
|
|
23
23
|
const { api, cachedConfig, site } = command.netlify
|
|
24
24
|
const siteId = site.id
|
|
@@ -33,7 +33,7 @@ const envSet = async (key, value, options, command) => {
|
|
|
33
33
|
|
|
34
34
|
// Get current environment variables set in the UI
|
|
35
35
|
if (siteInfo.use_envelope) {
|
|
36
|
-
finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope })
|
|
36
|
+
finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope, secret })
|
|
37
37
|
} else if (context || scope) {
|
|
38
38
|
error(
|
|
39
39
|
`To specify a context or scope, please run ${chalk.yellow(
|
|
@@ -56,11 +56,12 @@ const envSet = async (key, value, options, command) => {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const withScope = scope ? ` scoped to ${chalk.white(scope)}` : ''
|
|
59
|
+
const withSecret = secret ? ` as a ${chalk.blue('secret')}` : ''
|
|
59
60
|
const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch'
|
|
60
61
|
log(
|
|
61
|
-
`Set environment variable ${chalk.yellow(
|
|
62
|
-
|
|
63
|
-
)} ${contextType}`,
|
|
62
|
+
`Set environment variable ${chalk.yellow(
|
|
63
|
+
`${key}${value && !secret ? `=${value}` : ''}`,
|
|
64
|
+
)}${withScope}${withSecret} in the ${chalk.magenta(context || 'all')} ${contextType}`,
|
|
64
65
|
)
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -88,15 +89,35 @@ const setInMongo = async ({ api, key, siteInfo, value }) => {
|
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* Updates the env for a site configured with Envelope with a new key/value pair
|
|
91
|
-
* @returns {Promise<object>}
|
|
92
|
+
* @returns {Promise<object | boolean>}
|
|
92
93
|
*/
|
|
93
|
-
const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
94
|
+
const setInEnvelope = async ({ api, context, key, scope, secret, siteInfo, value }) => {
|
|
94
95
|
const accountId = siteInfo.account_slug
|
|
95
96
|
const siteId = siteInfo.id
|
|
97
|
+
|
|
98
|
+
// secret values may not be used in the post-processing scope
|
|
99
|
+
if (secret && scope && scope.some((sco) => /post[-_]processing/.test(sco))) {
|
|
100
|
+
error(`Secret values cannot be used within the post-processing scope.`)
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// secret values must specify deploy contexts. `all` or `dev` are not allowed
|
|
105
|
+
if (secret && value && (!context || context.includes('dev'))) {
|
|
106
|
+
error(
|
|
107
|
+
`To set a secret environment variable value, please specify a non-development context with the \`--context\` flag.`,
|
|
108
|
+
)
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
// fetch envelope env vars
|
|
97
113
|
const envelopeVariables = await api.getEnvVars({ accountId, siteId })
|
|
98
114
|
const contexts = context || ['all']
|
|
99
|
-
|
|
115
|
+
let scopes = scope || AVAILABLE_SCOPES
|
|
116
|
+
|
|
117
|
+
if (secret) {
|
|
118
|
+
// post_processing (aka post-processing) scope is not allowed with secrets
|
|
119
|
+
scopes = scopes.filter((sco) => !/post[-_]processing/.test(sco))
|
|
120
|
+
}
|
|
100
121
|
|
|
101
122
|
// if the passed context is unknown, it is actually a branch name
|
|
102
123
|
let values = contexts.map((ctx) =>
|
|
@@ -111,6 +132,10 @@ const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
|
111
132
|
if (!value) {
|
|
112
133
|
// eslint-disable-next-line prefer-destructuring
|
|
113
134
|
values = existing.values
|
|
135
|
+
if (!scope) {
|
|
136
|
+
// eslint-disable-next-line prefer-destructuring
|
|
137
|
+
scopes = existing.scopes
|
|
138
|
+
}
|
|
114
139
|
}
|
|
115
140
|
if (context && scope) {
|
|
116
141
|
error(
|
|
@@ -123,12 +148,24 @@ const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
|
123
148
|
await Promise.all(values.map((val) => api.setEnvVarValue({ ...params, body: val })))
|
|
124
149
|
} else {
|
|
125
150
|
// otherwise update whole env var
|
|
126
|
-
|
|
151
|
+
if (secret) {
|
|
152
|
+
scopes = scopes.filter((sco) => !/post[-_]processing/.test(sco))
|
|
153
|
+
if (values.some((val) => val.context === 'all')) {
|
|
154
|
+
log(`This secret's value will be empty in the dev context.`)
|
|
155
|
+
log(`Run \`netlify env:set ${key} <value> --context dev\` to set a new value for the dev context.`)
|
|
156
|
+
values = AVAILABLE_CONTEXTS.filter((ctx) => ctx !== 'all').map((ctx) => ({
|
|
157
|
+
context: ctx,
|
|
158
|
+
// empty out dev value so that secret is indeed secret
|
|
159
|
+
value: ctx === 'dev' ? '' : values.find((val) => val.context === 'all').value,
|
|
160
|
+
}))
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const body = { key, is_secret: secret, scopes, values }
|
|
127
164
|
await api.updateEnvVar({ ...params, body })
|
|
128
165
|
}
|
|
129
166
|
} else {
|
|
130
167
|
// create whole env var
|
|
131
|
-
const body = [{ key, scopes, values }]
|
|
168
|
+
const body = [{ key, is_secret: secret, scopes, values }]
|
|
132
169
|
await api.createEnvVars({ ...params, body })
|
|
133
170
|
}
|
|
134
171
|
} catch (error_) {
|
|
@@ -166,13 +203,16 @@ export const createEnvSetCommand = (program) =>
|
|
|
166
203
|
'runtime',
|
|
167
204
|
]),
|
|
168
205
|
)
|
|
206
|
+
.option('--secret', 'Indicate whether the environment variable value can be read again.')
|
|
169
207
|
.description('Set value of environment variable')
|
|
170
208
|
.addExamples([
|
|
171
209
|
'netlify env:set VAR_NAME value # set in all contexts and scopes',
|
|
172
210
|
'netlify env:set VAR_NAME value --context production',
|
|
173
211
|
'netlify env:set VAR_NAME value --context production deploy-preview',
|
|
212
|
+
'netlify env:set VAR_NAME value --context production --secret',
|
|
174
213
|
'netlify env:set VAR_NAME value --scope builds',
|
|
175
214
|
'netlify env:set VAR_NAME value --scope builds functions',
|
|
215
|
+
'netlify env:set VAR_NAME --secret # convert existing variable to secret',
|
|
176
216
|
])
|
|
177
217
|
.action(async (key, value, options, command) => {
|
|
178
218
|
await envSet(key, value, options, command)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { env } from 'process'
|
|
2
2
|
|
|
3
|
-
const latestBootstrapURL = 'https://
|
|
3
|
+
const latestBootstrapURL = 'https://6494585a67d46e0008867e60--edge.netlify.com/bootstrap/index-combined.ts'
|
|
4
4
|
|
|
5
5
|
export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
|
|
@@ -135,18 +135,8 @@ export default async function handler({ config, directory, errorExit, func, meta
|
|
|
135
135
|
const featureFlags = {}
|
|
136
136
|
|
|
137
137
|
if (metadata.runtimeAPIVersion === 2) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (isTypescript) {
|
|
142
|
-
functionsConfig['*'].nodeBundler = 'nft'
|
|
143
|
-
} else {
|
|
144
|
-
// using esbuild is less performant than `none`, but it emits sourcemaps and thus
|
|
145
|
-
// enables debugging functions
|
|
146
|
-
functionsConfig['*'].nodeBundler = 'esbuild'
|
|
147
|
-
featureFlags.zisi_pure_esm = true
|
|
148
|
-
featureFlags.zisi_pure_esm_mjs = true
|
|
149
|
-
}
|
|
138
|
+
featureFlags.zisi_pure_esm = true
|
|
139
|
+
featureFlags.zisi_pure_esm_mjs = true
|
|
150
140
|
} else {
|
|
151
141
|
// We must use esbuild for certain file extensions.
|
|
152
142
|
const mustTranspile = ['.mjs', '.ts', '.mts', '.cts'].includes(path.extname(func.mainFile))
|
|
@@ -75,20 +75,20 @@ const formatLambdaLocalError = (err, acceptsHtml) =>
|
|
|
75
75
|
})
|
|
76
76
|
: `${err.errorType}: ${err.errorMessage}\n ${err.stackTrace?.join('\n ')}`
|
|
77
77
|
|
|
78
|
-
const processRenderedResponse = async (err, request) => {
|
|
79
|
-
const acceptsHtml = request.headers && request.headers.accept && request.headers.accept.includes('text/html')
|
|
80
|
-
const errorString = typeof err === 'string' ? err : formatLambdaLocalError(err, acceptsHtml)
|
|
81
|
-
|
|
82
|
-
return acceptsHtml
|
|
83
|
-
? await renderErrorTemplate(errorString, './templates/function-error.html', 'function')
|
|
84
|
-
: errorString
|
|
85
|
-
}
|
|
86
|
-
|
|
87
78
|
const handleErr = async (err, request, response) => {
|
|
88
79
|
detectAwsSdkError({ err })
|
|
89
80
|
|
|
81
|
+
const acceptsHtml = request.headers && request.headers.accept && request.headers.accept.includes('text/html')
|
|
82
|
+
const errorString = typeof err === 'string' ? err : formatLambdaLocalError(err, acceptsHtml)
|
|
83
|
+
|
|
90
84
|
response.statusCode = 500
|
|
91
|
-
|
|
85
|
+
|
|
86
|
+
if (acceptsHtml) {
|
|
87
|
+
response.setHeader('Content-Type', 'text/html')
|
|
88
|
+
response.end(await renderErrorTemplate(errorString, './templates/function-error.html', 'function'))
|
|
89
|
+
} else {
|
|
90
|
+
response.end(errorString)
|
|
91
|
+
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
const validateLambdaResponse = (lambdaResponse) => {
|
package/src/utils/proxy.mjs
CHANGED
|
@@ -36,9 +36,29 @@ import { generateRequestID } from './request-id.mjs'
|
|
|
36
36
|
import { createRewriter, onChanges } from './rules-proxy.mjs'
|
|
37
37
|
import { signRedirect } from './sign-redirect.mjs'
|
|
38
38
|
|
|
39
|
-
const
|
|
39
|
+
const gunzip = util.promisify(zlib.gunzip)
|
|
40
|
+
const brotliDecompress = util.promisify(zlib.brotliDecompress)
|
|
41
|
+
const deflate = util.promisify(zlib.deflate)
|
|
40
42
|
const shouldGenerateETag = Symbol('Internal: response should generate ETag')
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @param {Buffer} body
|
|
46
|
+
* @param {string | undefined} contentEncoding
|
|
47
|
+
* @returns {Promise<Buffer>}
|
|
48
|
+
*/
|
|
49
|
+
const decompressResponseBody = async function (body, contentEncoding = '') {
|
|
50
|
+
switch (contentEncoding) {
|
|
51
|
+
case 'gzip':
|
|
52
|
+
return await gunzip(body)
|
|
53
|
+
case 'br':
|
|
54
|
+
return await brotliDecompress(body)
|
|
55
|
+
case 'deflate':
|
|
56
|
+
return await deflate(body)
|
|
57
|
+
default:
|
|
58
|
+
return body
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
42
62
|
const formatEdgeFunctionError = (errorBuffer, acceptsHtml) => {
|
|
43
63
|
const {
|
|
44
64
|
error: { message, name, stack },
|
|
@@ -479,7 +499,7 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,
|
|
|
479
499
|
|
|
480
500
|
if (isEdgeFunctionsRequest(req) && isUncaughtError) {
|
|
481
501
|
const acceptsHtml = req.headers && req.headers.accept && req.headers.accept.includes('text/html')
|
|
482
|
-
const decompressedBody = await
|
|
502
|
+
const decompressedBody = await decompressResponseBody(responseBody, req.headers['content-encoding'])
|
|
483
503
|
const formattedBody = formatEdgeFunctionError(decompressedBody, acceptsHtml)
|
|
484
504
|
const errorResponse = acceptsHtml
|
|
485
505
|
? await renderErrorTemplate(formattedBody, './templates/function-error.html', 'edge function')
|
|
@@ -487,6 +507,7 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,
|
|
|
487
507
|
const contentLength = Buffer.from(errorResponse, 'utf8').byteLength
|
|
488
508
|
|
|
489
509
|
res.setHeader('content-length', contentLength)
|
|
510
|
+
res.statusCode = 500
|
|
490
511
|
res.write(errorResponse)
|
|
491
512
|
return res.end()
|
|
492
513
|
}
|