netlify-cli 10.18.0 → 11.1.0-rc.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 +58 -9
- package/package.json +2 -2
- package/src/commands/build/build.js +7 -2
- package/src/commands/dev/dev-exec.js +6 -6
- package/src/commands/dev/dev.js +57 -7
- package/src/commands/env/env-get.js +14 -6
- package/src/commands/env/env-list.js +23 -6
- package/src/commands/env/env-set.js +21 -12
- package/src/commands/env/env-unset.js +23 -16
- package/src/utils/detect-server-settings.js +4 -0
- package/src/utils/env/index.js +49 -15
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.1.0-rc.1",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "netlify-cli",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "11.1.0-rc.1",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@netlify/build": "^27.14.0",
|
|
14
14
|
"@netlify/config": "^18.2.0",
|
|
15
|
-
"@netlify/edge-bundler": "^1.
|
|
15
|
+
"@netlify/edge-bundler": "^1.13.0",
|
|
16
16
|
"@netlify/framework-info": "^9.2.0",
|
|
17
17
|
"@netlify/local-functions-proxy": "^1.1.1",
|
|
18
18
|
"@netlify/plugins-list": "^6.39.0",
|
|
@@ -2343,9 +2343,9 @@
|
|
|
2343
2343
|
}
|
|
2344
2344
|
},
|
|
2345
2345
|
"node_modules/@netlify/edge-bundler": {
|
|
2346
|
-
"version": "1.
|
|
2347
|
-
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.
|
|
2348
|
-
"integrity": "sha512-
|
|
2346
|
+
"version": "1.13.0",
|
|
2347
|
+
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.13.0.tgz",
|
|
2348
|
+
"integrity": "sha512-5LDNouZP2tUt3m6NyKkEgor9PycIEq7wxUgrRSpiUcZVV7CNU/CUxxpXJb11/wEFq2bWZp3/xqzxqbR7vpqFNg==",
|
|
2349
2349
|
"dependencies": {
|
|
2350
2350
|
"common-path-prefix": "^3.0.0",
|
|
2351
2351
|
"del": "^6.0.0",
|
|
@@ -2354,6 +2354,7 @@
|
|
|
2354
2354
|
"glob-to-regexp": "^0.4.1",
|
|
2355
2355
|
"node-fetch": "^3.1.1",
|
|
2356
2356
|
"node-stream-zip": "^1.15.0",
|
|
2357
|
+
"p-retry": "^5.1.1",
|
|
2357
2358
|
"p-wait-for": "^4.1.0",
|
|
2358
2359
|
"path-key": "^4.0.0",
|
|
2359
2360
|
"semver": "^7.3.5",
|
|
@@ -4353,6 +4354,11 @@
|
|
|
4353
4354
|
"@types/node": "*"
|
|
4354
4355
|
}
|
|
4355
4356
|
},
|
|
4357
|
+
"node_modules/@types/retry": {
|
|
4358
|
+
"version": "0.12.1",
|
|
4359
|
+
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz",
|
|
4360
|
+
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g=="
|
|
4361
|
+
},
|
|
4356
4362
|
"node_modules/@types/semver": {
|
|
4357
4363
|
"version": "7.3.9",
|
|
4358
4364
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
|
|
@@ -17355,6 +17361,21 @@
|
|
|
17355
17361
|
"url": "https://github.com/sponsors/sindresorhus"
|
|
17356
17362
|
}
|
|
17357
17363
|
},
|
|
17364
|
+
"node_modules/p-retry": {
|
|
17365
|
+
"version": "5.1.1",
|
|
17366
|
+
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-5.1.1.tgz",
|
|
17367
|
+
"integrity": "sha512-i69WkEU5ZAL8mrmdmVviWwU+DN+IUF8f4sSJThoJ3z5A7Nn5iuO5ROX3Boye0u+uYQLOSfgFl7SuFZCjlAVbQA==",
|
|
17368
|
+
"dependencies": {
|
|
17369
|
+
"@types/retry": "0.12.1",
|
|
17370
|
+
"retry": "^0.13.1"
|
|
17371
|
+
},
|
|
17372
|
+
"engines": {
|
|
17373
|
+
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
17374
|
+
},
|
|
17375
|
+
"funding": {
|
|
17376
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
17377
|
+
}
|
|
17378
|
+
},
|
|
17358
17379
|
"node_modules/p-timeout": {
|
|
17359
17380
|
"version": "4.1.0",
|
|
17360
17381
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz",
|
|
@@ -19173,6 +19194,14 @@
|
|
|
19173
19194
|
"node": ">=0.12"
|
|
19174
19195
|
}
|
|
19175
19196
|
},
|
|
19197
|
+
"node_modules/retry": {
|
|
19198
|
+
"version": "0.13.1",
|
|
19199
|
+
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
|
19200
|
+
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
|
|
19201
|
+
"engines": {
|
|
19202
|
+
"node": ">= 4"
|
|
19203
|
+
}
|
|
19204
|
+
},
|
|
19176
19205
|
"node_modules/reusify": {
|
|
19177
19206
|
"version": "1.0.4",
|
|
19178
19207
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
|
@@ -24046,9 +24075,9 @@
|
|
|
24046
24075
|
}
|
|
24047
24076
|
},
|
|
24048
24077
|
"@netlify/edge-bundler": {
|
|
24049
|
-
"version": "1.
|
|
24050
|
-
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.
|
|
24051
|
-
"integrity": "sha512-
|
|
24078
|
+
"version": "1.13.0",
|
|
24079
|
+
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.13.0.tgz",
|
|
24080
|
+
"integrity": "sha512-5LDNouZP2tUt3m6NyKkEgor9PycIEq7wxUgrRSpiUcZVV7CNU/CUxxpXJb11/wEFq2bWZp3/xqzxqbR7vpqFNg==",
|
|
24052
24081
|
"requires": {
|
|
24053
24082
|
"common-path-prefix": "^3.0.0",
|
|
24054
24083
|
"del": "^6.0.0",
|
|
@@ -24057,6 +24086,7 @@
|
|
|
24057
24086
|
"glob-to-regexp": "^0.4.1",
|
|
24058
24087
|
"node-fetch": "^3.1.1",
|
|
24059
24088
|
"node-stream-zip": "^1.15.0",
|
|
24089
|
+
"p-retry": "^5.1.1",
|
|
24060
24090
|
"p-wait-for": "^4.1.0",
|
|
24061
24091
|
"path-key": "^4.0.0",
|
|
24062
24092
|
"semver": "^7.3.5",
|
|
@@ -25377,6 +25407,11 @@
|
|
|
25377
25407
|
"@types/node": "*"
|
|
25378
25408
|
}
|
|
25379
25409
|
},
|
|
25410
|
+
"@types/retry": {
|
|
25411
|
+
"version": "0.12.1",
|
|
25412
|
+
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz",
|
|
25413
|
+
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g=="
|
|
25414
|
+
},
|
|
25380
25415
|
"@types/semver": {
|
|
25381
25416
|
"version": "7.3.9",
|
|
25382
25417
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
|
|
@@ -35173,6 +35208,15 @@
|
|
|
35173
35208
|
"resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
|
|
35174
35209
|
"integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q=="
|
|
35175
35210
|
},
|
|
35211
|
+
"p-retry": {
|
|
35212
|
+
"version": "5.1.1",
|
|
35213
|
+
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-5.1.1.tgz",
|
|
35214
|
+
"integrity": "sha512-i69WkEU5ZAL8mrmdmVviWwU+DN+IUF8f4sSJThoJ3z5A7Nn5iuO5ROX3Boye0u+uYQLOSfgFl7SuFZCjlAVbQA==",
|
|
35215
|
+
"requires": {
|
|
35216
|
+
"@types/retry": "0.12.1",
|
|
35217
|
+
"retry": "^0.13.1"
|
|
35218
|
+
}
|
|
35219
|
+
},
|
|
35176
35220
|
"p-timeout": {
|
|
35177
35221
|
"version": "4.1.0",
|
|
35178
35222
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz",
|
|
@@ -36530,6 +36574,11 @@
|
|
|
36530
36574
|
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
|
36531
36575
|
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
|
|
36532
36576
|
},
|
|
36577
|
+
"retry": {
|
|
36578
|
+
"version": "0.13.1",
|
|
36579
|
+
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
|
36580
|
+
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
|
|
36581
|
+
},
|
|
36533
36582
|
"reusify": {
|
|
36534
36583
|
"version": "1.0.4",
|
|
36535
36584
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "11.1.0-rc.1",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"@whitep4nth3r (https://twitter.com/whitep4nth3r)",
|
|
@@ -224,7 +224,7 @@
|
|
|
224
224
|
"dependencies": {
|
|
225
225
|
"@netlify/build": "^27.14.0",
|
|
226
226
|
"@netlify/config": "^18.2.0",
|
|
227
|
-
"@netlify/edge-bundler": "^1.
|
|
227
|
+
"@netlify/edge-bundler": "^1.13.0",
|
|
228
228
|
"@netlify/framework-info": "^9.2.0",
|
|
229
229
|
"@netlify/local-functions-proxy": "^1.1.1",
|
|
230
230
|
"@netlify/plugins-list": "^6.39.0",
|
|
@@ -2,7 +2,7 @@ const process = require('process')
|
|
|
2
2
|
|
|
3
3
|
// @ts-check
|
|
4
4
|
const { getBuildOptions, runBuild } = require('../../lib/build')
|
|
5
|
-
const { error, exit, generateNetlifyGraphJWT, getEnvelopeEnv, getToken } = require('../../utils')
|
|
5
|
+
const { error, exit, generateNetlifyGraphJWT, getEnvelopeEnv, getToken, normalizeContext } = require('../../utils')
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {import('../../lib/build').BuildConfig} options
|
|
@@ -85,7 +85,12 @@ const createBuildCommand = (program) =>
|
|
|
85
85
|
program
|
|
86
86
|
.command('build')
|
|
87
87
|
.description('(Beta) Build on your local machine')
|
|
88
|
-
.option(
|
|
88
|
+
.option(
|
|
89
|
+
'--context <context>',
|
|
90
|
+
'Specify a build context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")',
|
|
91
|
+
normalizeContext,
|
|
92
|
+
process.env.CONTEXT || 'production',
|
|
93
|
+
)
|
|
89
94
|
.option('--dry', 'Dry run: show instructions without running them', false)
|
|
90
95
|
.option('-o, --offline', 'disables any features that require network access', false)
|
|
91
96
|
.addExamples(['netlify build'])
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
const { Option } = require('commander')
|
|
2
1
|
const execa = require('execa')
|
|
3
2
|
|
|
4
|
-
const { getEnvelopeEnv, injectEnvVariables } = require('../../utils')
|
|
3
|
+
const { getEnvelopeEnv, injectEnvVariables, normalizeContext } = require('../../utils')
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* The dev:exec command
|
|
@@ -32,10 +31,11 @@ const createDevExecCommand = (program) =>
|
|
|
32
31
|
program
|
|
33
32
|
.command('dev:exec')
|
|
34
33
|
.argument('<...cmd>', `the command that should be executed`)
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
.option(
|
|
35
|
+
'--context <context>',
|
|
36
|
+
'Specify a deploy context or branch for environment variables (contexts: "production", "deploy-preview", "branch-deploy", "dev")',
|
|
37
|
+
normalizeContext,
|
|
38
|
+
'dev',
|
|
39
39
|
)
|
|
40
40
|
.description(
|
|
41
41
|
'Exec command\nRuns a command within the netlify dev environment, e.g. with env variables from any installed addons',
|
package/src/commands/dev/dev.js
CHANGED
|
@@ -44,6 +44,7 @@ const {
|
|
|
44
44
|
injectEnvVariables,
|
|
45
45
|
log,
|
|
46
46
|
normalizeConfig,
|
|
47
|
+
normalizeContext,
|
|
47
48
|
openBrowser,
|
|
48
49
|
processOnExit,
|
|
49
50
|
startLiveTunnel,
|
|
@@ -54,6 +55,8 @@ const {
|
|
|
54
55
|
|
|
55
56
|
const { createDevExecCommand } = require('./dev-exec')
|
|
56
57
|
|
|
58
|
+
const netlifyBuildPromise = import('@netlify/build')
|
|
59
|
+
|
|
57
60
|
const startStaticServer = async ({ settings }) => {
|
|
58
61
|
const server = new StaticServer({
|
|
59
62
|
rootPath: settings.dist,
|
|
@@ -417,7 +420,8 @@ const validateGeoCountryCode = (arg) => {
|
|
|
417
420
|
*/
|
|
418
421
|
const dev = async (options, command) => {
|
|
419
422
|
log(`${NETLIFYDEV}`)
|
|
420
|
-
const { api, config, repositoryRoot, site, siteInfo, state } = command.netlify
|
|
423
|
+
const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify
|
|
424
|
+
const netlifyBuild = await netlifyBuildPromise
|
|
421
425
|
config.dev = { ...config.dev }
|
|
422
426
|
config.build = { ...config.build }
|
|
423
427
|
/** @type {import('./types').DevConfig} */
|
|
@@ -429,7 +433,7 @@ const dev = async (options, command) => {
|
|
|
429
433
|
...options,
|
|
430
434
|
}
|
|
431
435
|
|
|
432
|
-
let { env } =
|
|
436
|
+
let { env } = cachedConfig
|
|
433
437
|
if (!options.offline && siteInfo.use_envelope) {
|
|
434
438
|
env = await getEnvelopeEnv({ api, context: options.context, env, siteInfo })
|
|
435
439
|
}
|
|
@@ -449,6 +453,26 @@ const dev = async (options, command) => {
|
|
|
449
453
|
let settings = {}
|
|
450
454
|
try {
|
|
451
455
|
settings = await detectServerSettings(devConfig, options, site.root)
|
|
456
|
+
|
|
457
|
+
// If there are plugins that we should be running for this site, add them
|
|
458
|
+
// to the config as if they were declared in netlify.toml. We must check
|
|
459
|
+
// whether the plugin has already been added by another source (like the
|
|
460
|
+
// TOML file or the UI), as we don't want to run the same plugin twice.
|
|
461
|
+
if (settings.plugins) {
|
|
462
|
+
const { plugins: existingPlugins = [] } = cachedConfig.config
|
|
463
|
+
const existingPluginNames = new Set(existingPlugins.map((plugin) => plugin.package))
|
|
464
|
+
const newPlugins = settings.plugins
|
|
465
|
+
.map((pluginName) => {
|
|
466
|
+
if (existingPluginNames.has(pluginName)) {
|
|
467
|
+
return
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return { package: pluginName, origin: 'config', inputs: {} }
|
|
471
|
+
})
|
|
472
|
+
.filter(Boolean)
|
|
473
|
+
|
|
474
|
+
cachedConfig.config.plugins = [...newPlugins, ...cachedConfig.config.plugins]
|
|
475
|
+
}
|
|
452
476
|
} catch (error_) {
|
|
453
477
|
log(NETLIFYDEVERR, error_.message)
|
|
454
478
|
exit(1)
|
|
@@ -472,7 +496,19 @@ const dev = async (options, command) => {
|
|
|
472
496
|
capabilities,
|
|
473
497
|
timeouts,
|
|
474
498
|
})
|
|
475
|
-
|
|
499
|
+
|
|
500
|
+
log(`${NETLIFYDEVWARN} Setting up local development server`)
|
|
501
|
+
|
|
502
|
+
const devCommand = () => startFrameworkServer({ settings })
|
|
503
|
+
const startDevOptions = getBuildOptions({
|
|
504
|
+
cachedConfig,
|
|
505
|
+
options,
|
|
506
|
+
})
|
|
507
|
+
const { error: startDevError, success } = await netlifyBuild.startDev(devCommand, startDevOptions)
|
|
508
|
+
|
|
509
|
+
if (!success) {
|
|
510
|
+
error(`Could not start local development server\n\n${startDevError.message}\n\n${startDevError.stack}`)
|
|
511
|
+
}
|
|
476
512
|
|
|
477
513
|
// TODO: We should consolidate this with the existing config watcher.
|
|
478
514
|
const getUpdatedConfig = async () => {
|
|
@@ -612,6 +648,19 @@ const dev = async (options, command) => {
|
|
|
612
648
|
printBanner({ url })
|
|
613
649
|
}
|
|
614
650
|
|
|
651
|
+
const getBuildOptions = ({ cachedConfig, options: { context, cwd = process.cwd(), debug, dry, offline }, token }) => ({
|
|
652
|
+
cachedConfig,
|
|
653
|
+
token,
|
|
654
|
+
dry,
|
|
655
|
+
debug,
|
|
656
|
+
context,
|
|
657
|
+
mode: 'cli',
|
|
658
|
+
telemetry: false,
|
|
659
|
+
buffer: false,
|
|
660
|
+
offline,
|
|
661
|
+
cwd,
|
|
662
|
+
})
|
|
663
|
+
|
|
615
664
|
/**
|
|
616
665
|
* Creates the `netlify dev` command
|
|
617
666
|
* @param {import('../base-command').BaseCommand} program
|
|
@@ -627,10 +676,11 @@ const createDevCommand = (program) => {
|
|
|
627
676
|
`Local dev server\nThe dev command will run a local dev server with Netlify's proxy and redirect rules`,
|
|
628
677
|
)
|
|
629
678
|
.option('-c ,--command <command>', 'command to run')
|
|
630
|
-
.
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
679
|
+
.option(
|
|
680
|
+
'--context <context>',
|
|
681
|
+
'Specify a deploy context or branch for environment variables (contexts: "production", "deploy-preview", "branch-deploy", "dev")',
|
|
682
|
+
normalizeContext,
|
|
683
|
+
'dev',
|
|
634
684
|
)
|
|
635
685
|
.option('-p ,--port <port>', 'port of netlify dev', (value) => Number.parseInt(value))
|
|
636
686
|
.option('--targetPort <port>', 'port of target app server', (value) => Number.parseInt(value))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
const { Option } = require('commander')
|
|
3
3
|
|
|
4
|
-
const { chalk, error, getEnvelopeEnv, log, logJson } = require('../../utils')
|
|
4
|
+
const { AVAILABLE_CONTEXTS, chalk, error, getEnvelopeEnv, log, logJson, normalizeContext } = require('../../utils')
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The env:get command
|
|
@@ -42,7 +42,8 @@ const envGet = async (name, options, command) => {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
if (!value) {
|
|
45
|
-
const
|
|
45
|
+
const contextType = AVAILABLE_CONTEXTS.includes(context) ? 'context' : 'branch'
|
|
46
|
+
const withContext = `in the ${chalk.magenta(context)} ${contextType}`
|
|
46
47
|
const withScope = scope === 'any' ? '' : ` and the ${chalk.magenta(scope)} scope`
|
|
47
48
|
log(`No value set ${withContext}${withScope} for environment variable ${chalk.yellow(name)}`)
|
|
48
49
|
return false
|
|
@@ -60,16 +61,23 @@ const createEnvGetCommand = (program) =>
|
|
|
60
61
|
program
|
|
61
62
|
.command('env:get')
|
|
62
63
|
.argument('<name>', 'Environment variable name')
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
.option(
|
|
65
|
+
'-c, --context <context>',
|
|
66
|
+
'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")',
|
|
67
|
+
normalizeContext,
|
|
68
|
+
'dev',
|
|
67
69
|
)
|
|
68
70
|
.addOption(
|
|
69
71
|
new Option('-s, --scope <scope>', 'Specify a scope')
|
|
70
72
|
.choices(['builds', 'functions', 'post_processing', 'runtime', 'any'])
|
|
71
73
|
.default('any'),
|
|
72
74
|
)
|
|
75
|
+
.addExamples([
|
|
76
|
+
'netlify env:get MY_VAR # get value for MY_VAR in dev context',
|
|
77
|
+
'netlify env:get --context production',
|
|
78
|
+
'netlify env:get --context branch:staging',
|
|
79
|
+
'netlify env:get --scope functions',
|
|
80
|
+
])
|
|
73
81
|
.description('Get resolved value of specified environment variable (includes netlify.toml)')
|
|
74
82
|
.action(async (name, options, command) => {
|
|
75
83
|
await envGet(name, options, command)
|
|
@@ -5,7 +5,16 @@ const { Option } = require('commander')
|
|
|
5
5
|
const inquirer = require('inquirer')
|
|
6
6
|
const isEmpty = require('lodash/isEmpty')
|
|
7
7
|
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
AVAILABLE_CONTEXTS,
|
|
10
|
+
chalk,
|
|
11
|
+
error,
|
|
12
|
+
getEnvelopeEnv,
|
|
13
|
+
getHumanReadableScopes,
|
|
14
|
+
log,
|
|
15
|
+
logJson,
|
|
16
|
+
normalizeContext,
|
|
17
|
+
} = require('../../utils')
|
|
9
18
|
|
|
10
19
|
const [logUpdatePromise, ansiEscapesPromise] = [import('log-update'), import('ansi-escapes')]
|
|
11
20
|
|
|
@@ -77,7 +86,8 @@ const envList = async (options, command) => {
|
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
const forSite = `for site ${chalk.green(siteInfo.name)}`
|
|
80
|
-
const
|
|
89
|
+
const contextType = AVAILABLE_CONTEXTS.includes(context) ? 'context' : 'branch'
|
|
90
|
+
const withContext = isUsingEnvelope ? `in the ${chalk.magenta(options.context)} ${contextType}` : ''
|
|
81
91
|
const withScope = isUsingEnvelope && scope !== 'any' ? `and ${chalk.yellow(options.scope)} scope` : ''
|
|
82
92
|
if (isEmpty(environment)) {
|
|
83
93
|
log(`No environment variables set ${forSite} ${withContext} ${withScope}`)
|
|
@@ -122,16 +132,23 @@ const envList = async (options, command) => {
|
|
|
122
132
|
const createEnvListCommand = (program) =>
|
|
123
133
|
program
|
|
124
134
|
.command('env:list')
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
.option(
|
|
136
|
+
'-c, --context <context>',
|
|
137
|
+
'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")',
|
|
138
|
+
normalizeContext,
|
|
139
|
+
'dev',
|
|
129
140
|
)
|
|
130
141
|
.addOption(
|
|
131
142
|
new Option('-s, --scope <scope>', 'Specify a scope')
|
|
132
143
|
.choices(['builds', 'functions', 'post_processing', 'runtime', 'any'])
|
|
133
144
|
.default('any'),
|
|
134
145
|
)
|
|
146
|
+
.addExamples([
|
|
147
|
+
'netlify env:list # list dev context and any scope',
|
|
148
|
+
'netlify env:list --context production',
|
|
149
|
+
'netlify env:list --context branch:staging',
|
|
150
|
+
'netlify env:list --scope functions',
|
|
151
|
+
])
|
|
135
152
|
.description('Lists resolved environment variables for site (includes netlify.toml)')
|
|
136
153
|
.action(async (options, command) => {
|
|
137
154
|
await envList(options, command)
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
const { Option } = require('commander')
|
|
3
3
|
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const {
|
|
5
|
+
AVAILABLE_CONTEXTS,
|
|
6
|
+
AVAILABLE_SCOPES,
|
|
7
|
+
chalk,
|
|
8
|
+
error,
|
|
9
|
+
log,
|
|
10
|
+
logJson,
|
|
11
|
+
normalizeContext,
|
|
12
|
+
translateFromEnvelopeToMongo,
|
|
13
|
+
} = require('../../utils')
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
16
|
* The env:set command
|
|
@@ -52,10 +59,11 @@ const envSet = async (key, value, options, command) => {
|
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
const withScope = scope ? ` scoped to ${chalk.white(scope)}` : ''
|
|
62
|
+
const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch'
|
|
55
63
|
log(
|
|
56
64
|
`Set environment variable ${chalk.yellow(`${key}${value ? '=' : ''}${value}`)}${withScope} in the ${chalk.magenta(
|
|
57
65
|
context || 'all',
|
|
58
|
-
)}
|
|
66
|
+
)} ${contextType}`,
|
|
59
67
|
)
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -93,7 +101,10 @@ const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
|
93
101
|
const contexts = context || ['all']
|
|
94
102
|
const scopes = scope || AVAILABLE_SCOPES
|
|
95
103
|
|
|
96
|
-
|
|
104
|
+
// if the passed context is unknown, it is actually a branch name
|
|
105
|
+
let values = contexts.map((ctx) =>
|
|
106
|
+
AVAILABLE_CONTEXTS.includes(ctx) ? { context: ctx, value } : { context: 'branch', context_parameter: ctx, value },
|
|
107
|
+
)
|
|
97
108
|
|
|
98
109
|
const existing = envelopeVariables.find((envVar) => envVar.key === key)
|
|
99
110
|
|
|
@@ -144,13 +155,11 @@ const createEnvSetCommand = (program) =>
|
|
|
144
155
|
.command('env:set')
|
|
145
156
|
.argument('<key>', 'Environment variable key')
|
|
146
157
|
.argument('[value]', 'Value to set to', '')
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
'dev',
|
|
153
|
-
]),
|
|
158
|
+
.option(
|
|
159
|
+
'-c, --context <context...>',
|
|
160
|
+
'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)',
|
|
161
|
+
// spread over an array for variadic options
|
|
162
|
+
(context, previous = []) => [...previous, normalizeContext(context)],
|
|
154
163
|
)
|
|
155
164
|
.addOption(
|
|
156
165
|
new Option('-s, --scope <scope...>', 'Specify a scope (default: all scopes)').choices([
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
const { Option } = require('commander')
|
|
2
|
-
|
|
3
1
|
// @ts-check
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const {
|
|
3
|
+
AVAILABLE_CONTEXTS,
|
|
4
|
+
chalk,
|
|
5
|
+
error,
|
|
6
|
+
log,
|
|
7
|
+
logJson,
|
|
8
|
+
normalizeContext,
|
|
9
|
+
translateFromEnvelopeToMongo,
|
|
10
|
+
} = require('../../utils')
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* The env:unset command
|
|
@@ -44,7 +48,8 @@ const envUnset = async (key, options, command) => {
|
|
|
44
48
|
return false
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch'
|
|
52
|
+
log(`Unset environment variable ${chalk.yellow(key)} in the ${chalk.magenta(context || 'all')} ${contextType}`)
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/**
|
|
@@ -98,8 +103,10 @@ const unsetInEnvelope = async ({ api, context, key, siteInfo }) => {
|
|
|
98
103
|
const params = { accountId, siteId, key }
|
|
99
104
|
try {
|
|
100
105
|
if (context) {
|
|
101
|
-
// if context(s) are passed, delete the matching contexts, and the `all` context
|
|
102
|
-
const values = variable.values.filter((val) =>
|
|
106
|
+
// if context(s) are passed, delete the matching contexts / branches, and the `all` context
|
|
107
|
+
const values = variable.values.filter((val) =>
|
|
108
|
+
[...contexts, 'all'].includes(val.context_parameter || val.context),
|
|
109
|
+
)
|
|
103
110
|
if (values) {
|
|
104
111
|
await Promise.all(values.map((value) => api.deleteEnvVarValue({ ...params, id: value.id })))
|
|
105
112
|
// if this was the `all` context, we need to create 3 values in the other contexts
|
|
@@ -107,7 +114,9 @@ const unsetInEnvelope = async ({ api, context, key, siteInfo }) => {
|
|
|
107
114
|
const newContexts = AVAILABLE_CONTEXTS.filter((ctx) => !context.includes(ctx))
|
|
108
115
|
const allValue = values[0].value
|
|
109
116
|
await Promise.all(
|
|
110
|
-
newContexts
|
|
117
|
+
newContexts
|
|
118
|
+
.filter((ctx) => ctx !== 'all')
|
|
119
|
+
.map((ctx) => api.setEnvVarValue({ ...params, body: { context: ctx, value: allValue } })),
|
|
111
120
|
)
|
|
112
121
|
}
|
|
113
122
|
}
|
|
@@ -134,13 +143,11 @@ const createEnvUnsetCommand = (program) =>
|
|
|
134
143
|
.command('env:unset')
|
|
135
144
|
.aliases(['env:delete', 'env:remove'])
|
|
136
145
|
.argument('<key>', 'Environment variable key')
|
|
137
|
-
.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
'dev',
|
|
143
|
-
]),
|
|
146
|
+
.option(
|
|
147
|
+
'-c, --context <context...>',
|
|
148
|
+
'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)',
|
|
149
|
+
// spread over an array for variadic options
|
|
150
|
+
(context, previous = []) => [...previous, normalizeContext(context)],
|
|
144
151
|
)
|
|
145
152
|
.addExamples([
|
|
146
153
|
'netlify env:unset VAR_NAME # unset in all contexts',
|
|
@@ -167,6 +167,7 @@ const getSettingsFromFramework = (framework) => {
|
|
|
167
167
|
name: frameworkName,
|
|
168
168
|
staticAssetsDirectory: staticDir,
|
|
169
169
|
env = {},
|
|
170
|
+
plugins,
|
|
170
171
|
} = framework
|
|
171
172
|
|
|
172
173
|
return {
|
|
@@ -176,6 +177,7 @@ const getSettingsFromFramework = (framework) => {
|
|
|
176
177
|
framework: frameworkName,
|
|
177
178
|
env,
|
|
178
179
|
pollingStrategies: pollingStrategies.map(({ name }) => name),
|
|
180
|
+
plugins,
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
|
@@ -311,6 +313,8 @@ const detectServerSettings = async (devConfig, options, projectDir) => {
|
|
|
311
313
|
validateFrameworkConfig({ devConfig })
|
|
312
314
|
settings = await mergeSettings({ devConfig, frameworkSettings })
|
|
313
315
|
}
|
|
316
|
+
|
|
317
|
+
settings.plugins = frameworkSettings && frameworkSettings.plugins
|
|
314
318
|
} else if (devConfig.framework === '#custom') {
|
|
315
319
|
validateFrameworkConfig({ devConfig })
|
|
316
320
|
// when the users wants to configure `command` and `targetPort`
|
package/src/utils/env/index.js
CHANGED
|
@@ -1,10 +1,39 @@
|
|
|
1
|
+
const AVAILABLE_CONTEXTS = ['all', 'production', 'deploy-preview', 'branch-deploy', 'dev']
|
|
2
|
+
const AVAILABLE_SCOPES = ['builds', 'functions', 'runtime', 'post_processing']
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string|undefined} context - The deploy context or branch of the environment variable value
|
|
6
|
+
* @returns {Array<string|undefined>} The normalized context or branch name
|
|
7
|
+
*/
|
|
8
|
+
const normalizeContext = (context) => {
|
|
9
|
+
if (!context) {
|
|
10
|
+
return context
|
|
11
|
+
}
|
|
12
|
+
const CONTEXT_SYNONYMS = {
|
|
13
|
+
dp: 'deploy-preview',
|
|
14
|
+
prod: 'production',
|
|
15
|
+
}
|
|
16
|
+
context = context.replace(/^branch:/, '')
|
|
17
|
+
if (CONTEXT_SYNONYMS[context]) {
|
|
18
|
+
context = CONTEXT_SYNONYMS[context]
|
|
19
|
+
}
|
|
20
|
+
return context
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
/**
|
|
2
24
|
* Finds a matching environment variable value from a given context
|
|
3
25
|
* @param {Array<object>} values - An array of environment variable values from Envelope
|
|
4
|
-
* @param {
|
|
5
|
-
* @returns {object<context: enum<dev,branch-deploy,deploy-preview,production>, value: string>} The matching environment variable value object
|
|
26
|
+
* @param {string} context - The deploy context or branch of the environment variable value
|
|
27
|
+
* @returns {object<context: enum<dev,branch-deploy,deploy-preview,production,branch>, context_parameter: <string>, value: string>} The matching environment variable value object
|
|
6
28
|
*/
|
|
7
|
-
const
|
|
29
|
+
const findValueInValues = (values, context) =>
|
|
30
|
+
values.find((val) => {
|
|
31
|
+
if (!AVAILABLE_CONTEXTS.includes(context)) {
|
|
32
|
+
// the "context" option passed in is actually the name of a branch
|
|
33
|
+
return ['branch', 'all'].includes(val.context) && val.context_parameter === context
|
|
34
|
+
}
|
|
35
|
+
return [context, 'all'].includes(val.context)
|
|
36
|
+
})
|
|
8
37
|
|
|
9
38
|
/**
|
|
10
39
|
* Finds environment variables that match a given source
|
|
@@ -45,7 +74,7 @@ const fetchEnvelopeItems = async function ({ accountId, api, key, siteId }) {
|
|
|
45
74
|
|
|
46
75
|
/**
|
|
47
76
|
* Filters and sorts data from Envelope by a given context and/or scope
|
|
48
|
-
* @param {
|
|
77
|
+
* @param {string} context - The deploy context or branch of the environment variable value
|
|
49
78
|
* @param {Array<object>} envelopeItems - An array of environment variables from the Envelope service
|
|
50
79
|
* @param {enum<any,builds,functions,runtime,post_processing>} scope - The scope of the environment variables
|
|
51
80
|
* @param {enum<general,account,addons,ui,configFile>} source - The source of the environment variable
|
|
@@ -58,7 +87,8 @@ const fetchEnvelopeItems = async function ({ accountId, api, key, siteId }) {
|
|
|
58
87
|
* value: 'bar',
|
|
59
88
|
* },
|
|
60
89
|
* BAZ: {
|
|
61
|
-
* context: '
|
|
90
|
+
* context: 'branch',
|
|
91
|
+
* branch: 'staging',
|
|
62
92
|
* scopes: ['runtime'],
|
|
63
93
|
* sources: ['account'],
|
|
64
94
|
* value: 'bang',
|
|
@@ -68,18 +98,19 @@ const fetchEnvelopeItems = async function ({ accountId, api, key, siteId }) {
|
|
|
68
98
|
const formatEnvelopeData = ({ context = 'dev', envelopeItems = [], scope = 'any', source }) =>
|
|
69
99
|
envelopeItems
|
|
70
100
|
// filter by context
|
|
71
|
-
.filter(({ values }) => Boolean(
|
|
101
|
+
.filter(({ values }) => Boolean(findValueInValues(values, context)))
|
|
72
102
|
// filter by scope
|
|
73
103
|
.filter(({ scopes }) => (scope === 'any' ? true : scopes.includes(scope)))
|
|
74
104
|
// sort alphabetically, case insensitive
|
|
75
105
|
.sort((left, right) => (left.key.toLowerCase() < right.key.toLowerCase() ? -1 : 1))
|
|
76
106
|
// format the data
|
|
77
107
|
.reduce((acc, cur) => {
|
|
78
|
-
const { context: ctx, value } =
|
|
108
|
+
const { context: ctx, context_parameter: branch, value } = findValueInValues(cur.values, context)
|
|
79
109
|
return {
|
|
80
110
|
...acc,
|
|
81
111
|
[cur.key]: {
|
|
82
112
|
context: ctx,
|
|
113
|
+
branch,
|
|
83
114
|
scopes: cur.scopes,
|
|
84
115
|
sources: [source],
|
|
85
116
|
value,
|
|
@@ -90,7 +121,7 @@ const formatEnvelopeData = ({ context = 'dev', envelopeItems = [], scope = 'any'
|
|
|
90
121
|
/**
|
|
91
122
|
* Collects env vars from multiple sources and arranges them in the correct order of precedence
|
|
92
123
|
* @param {object} api - The api singleton object
|
|
93
|
-
* @param {
|
|
124
|
+
* @param {string} context - The deploy context or branch of the environment variable
|
|
94
125
|
* @param {object} env - The dictionary of environment variables
|
|
95
126
|
* @param {string} key - If present, fetch a single key (case-sensitive)
|
|
96
127
|
* @param {enum<any,builds,functions,runtime,post_processing>} scope - The scope of the environment variables
|
|
@@ -130,7 +161,7 @@ const getEnvelopeEnv = async ({ api, context = 'dev', env, key = '', scope = 'an
|
|
|
130
161
|
* @returns {string} A human-readable, comma-separated list of scopes
|
|
131
162
|
*/
|
|
132
163
|
const getHumanReadableScopes = (scopes) => {
|
|
133
|
-
const
|
|
164
|
+
const HUMAN_SCOPES = {
|
|
134
165
|
builds: 'Builds',
|
|
135
166
|
functions: 'Functions',
|
|
136
167
|
post_processing: 'Post processing',
|
|
@@ -141,11 +172,11 @@ const getHumanReadableScopes = (scopes) => {
|
|
|
141
172
|
// env vars specified in netlify.toml are present in the `builds` and `post_processing` scope
|
|
142
173
|
return 'Builds, Post processing'
|
|
143
174
|
}
|
|
144
|
-
if (scopes.length === Object.keys(
|
|
175
|
+
if (scopes.length === Object.keys(HUMAN_SCOPES).length) {
|
|
145
176
|
// shorthand instead of listing every available scope
|
|
146
177
|
return 'All'
|
|
147
178
|
}
|
|
148
|
-
return scopes.map((scope) =>
|
|
179
|
+
return scopes.map((scope) => HUMAN_SCOPES[scope]).join(', ')
|
|
149
180
|
}
|
|
150
181
|
|
|
151
182
|
/**
|
|
@@ -156,7 +187,7 @@ const getHumanReadableScopes = (scopes) => {
|
|
|
156
187
|
const translateFromMongoToEnvelope = (env = {}) => {
|
|
157
188
|
const envVars = Object.entries(env).map(([key, value]) => ({
|
|
158
189
|
key,
|
|
159
|
-
scopes:
|
|
190
|
+
scopes: AVAILABLE_SCOPES,
|
|
160
191
|
values: [
|
|
161
192
|
{
|
|
162
193
|
context: 'all',
|
|
@@ -171,14 +202,14 @@ const translateFromMongoToEnvelope = (env = {}) => {
|
|
|
171
202
|
/**
|
|
172
203
|
* Translates an Envelope env into a Mongo env
|
|
173
204
|
* @param {Array<object>} envVars - The array of Envelope env vars
|
|
174
|
-
* @param {
|
|
205
|
+
* @param {string} context - The deploy context or branch of the environment variable
|
|
175
206
|
* @returns {object} The env object as compatible with Mongo
|
|
176
207
|
*/
|
|
177
208
|
const translateFromEnvelopeToMongo = (envVars = [], context = 'dev') =>
|
|
178
209
|
envVars
|
|
179
210
|
.sort((left, right) => (left.key.toLowerCase() < right.key.toLowerCase() ? -1 : 1))
|
|
180
211
|
.reduce((acc, cur) => {
|
|
181
|
-
const envVar = cur.values.find((val) => [context, 'all'].includes(val.context))
|
|
212
|
+
const envVar = cur.values.find((val) => [context, 'all'].includes(val.context_parameter || val.context))
|
|
182
213
|
if (envVar && envVar.value) {
|
|
183
214
|
return {
|
|
184
215
|
...acc,
|
|
@@ -189,11 +220,14 @@ const translateFromEnvelopeToMongo = (envVars = [], context = 'dev') =>
|
|
|
189
220
|
}, {})
|
|
190
221
|
|
|
191
222
|
module.exports = {
|
|
192
|
-
|
|
223
|
+
AVAILABLE_CONTEXTS,
|
|
224
|
+
AVAILABLE_SCOPES,
|
|
225
|
+
findValueInValues,
|
|
193
226
|
filterEnvBySource,
|
|
194
227
|
formatEnvelopeData,
|
|
195
228
|
getEnvelopeEnv,
|
|
196
229
|
getHumanReadableScopes,
|
|
230
|
+
normalizeContext,
|
|
197
231
|
translateFromEnvelopeToMongo,
|
|
198
232
|
translateFromMongoToEnvelope,
|
|
199
233
|
}
|