netlify-cli 10.6.3 → 10.7.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.
- package/npm-shrinkwrap.json +56 -56
- package/package.json +6 -5
- package/src/commands/dev/dev.js +28 -8
- package/src/commands/env/env-import.js +66 -20
- package/src/commands/env/env-migrate.js +140 -7
- package/src/commands/env/env-set.js +53 -18
- package/src/commands/env/env-unset.js +54 -14
- package/src/commands/graph/graph-edit.js +21 -7
- package/src/commands/graph/graph-operations.js +2 -2
- package/src/commands/graph/graph.js +2 -2
- package/src/lib/one-graph/cli-client.js +34 -10
- package/src/utils/env/index.js +43 -0
- package/src/utils/index.js +2 -0
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.7.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "netlify-cli",
|
|
9
|
-
"version": "10.
|
|
9
|
+
"version": "10.7.0",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@netlify/build": "^27.3.
|
|
14
|
-
"@netlify/config": "^18.1.
|
|
15
|
-
"@netlify/edge-bundler": "^1.4.
|
|
13
|
+
"@netlify/build": "^27.3.2",
|
|
14
|
+
"@netlify/config": "^18.1.1",
|
|
15
|
+
"@netlify/edge-bundler": "^1.4.2",
|
|
16
16
|
"@netlify/framework-info": "^9.1.0",
|
|
17
17
|
"@netlify/local-functions-proxy": "^1.1.1",
|
|
18
|
-
"@netlify/plugins-list": "^6.
|
|
18
|
+
"@netlify/plugins-list": "^6.30.0",
|
|
19
19
|
"@netlify/zip-it-and-ship-it": "^5.11.1",
|
|
20
20
|
"@octokit/rest": "^18.0.0",
|
|
21
21
|
"@sindresorhus/slugify": "^1.1.0",
|
|
@@ -1177,13 +1177,13 @@
|
|
|
1177
1177
|
}
|
|
1178
1178
|
},
|
|
1179
1179
|
"node_modules/@netlify/build": {
|
|
1180
|
-
"version": "27.3.
|
|
1181
|
-
"resolved": "https://registry.npmjs.org/@netlify/build/-/build-27.3.
|
|
1182
|
-
"integrity": "sha512-
|
|
1180
|
+
"version": "27.3.2",
|
|
1181
|
+
"resolved": "https://registry.npmjs.org/@netlify/build/-/build-27.3.2.tgz",
|
|
1182
|
+
"integrity": "sha512-QoaRwx+qt7582mXSZP0s3YCsK2/xUOsEfhx2Q3PvYKKdVDGjx5MROMuCdc480e8KPWsTTUheB2+A5RuB9dpcLQ==",
|
|
1183
1183
|
"dependencies": {
|
|
1184
1184
|
"@bugsnag/js": "^7.0.0",
|
|
1185
1185
|
"@netlify/cache-utils": "^4.0.0",
|
|
1186
|
-
"@netlify/config": "^18.1.
|
|
1186
|
+
"@netlify/config": "^18.1.1",
|
|
1187
1187
|
"@netlify/edge-bundler": "^1.4.1",
|
|
1188
1188
|
"@netlify/functions-utils": "^4.2.0",
|
|
1189
1189
|
"@netlify/git-utils": "^4.0.0",
|
|
@@ -1764,9 +1764,9 @@
|
|
|
1764
1764
|
}
|
|
1765
1765
|
},
|
|
1766
1766
|
"node_modules/@netlify/build/node_modules/read-pkg-up/node_modules/type-fest": {
|
|
1767
|
-
"version": "2.
|
|
1768
|
-
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.
|
|
1769
|
-
"integrity": "sha512-
|
|
1767
|
+
"version": "2.15.1",
|
|
1768
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.1.tgz",
|
|
1769
|
+
"integrity": "sha512-LYSjcIz3NmoQksXq/3/B7Nfad+T8mkaI628agAAnHCpXPTBRMK2ygt3eABpzII8CbZZM8dLdVQ4Gr8ousjFjMw==",
|
|
1770
1770
|
"engines": {
|
|
1771
1771
|
"node": ">=12.20"
|
|
1772
1772
|
},
|
|
@@ -1775,9 +1775,9 @@
|
|
|
1775
1775
|
}
|
|
1776
1776
|
},
|
|
1777
1777
|
"node_modules/@netlify/build/node_modules/read-pkg/node_modules/type-fest": {
|
|
1778
|
-
"version": "2.
|
|
1779
|
-
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.
|
|
1780
|
-
"integrity": "sha512-
|
|
1778
|
+
"version": "2.15.1",
|
|
1779
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.1.tgz",
|
|
1780
|
+
"integrity": "sha512-LYSjcIz3NmoQksXq/3/B7Nfad+T8mkaI628agAAnHCpXPTBRMK2ygt3eABpzII8CbZZM8dLdVQ4Gr8ousjFjMw==",
|
|
1781
1781
|
"engines": {
|
|
1782
1782
|
"node": ">=12.20"
|
|
1783
1783
|
},
|
|
@@ -1969,9 +1969,9 @@
|
|
|
1969
1969
|
}
|
|
1970
1970
|
},
|
|
1971
1971
|
"node_modules/@netlify/config": {
|
|
1972
|
-
"version": "18.1.
|
|
1973
|
-
"resolved": "https://registry.npmjs.org/@netlify/config/-/config-18.1.
|
|
1974
|
-
"integrity": "sha512-
|
|
1972
|
+
"version": "18.1.1",
|
|
1973
|
+
"resolved": "https://registry.npmjs.org/@netlify/config/-/config-18.1.1.tgz",
|
|
1974
|
+
"integrity": "sha512-w3rIWV72anBCnAb69rCTnuwkLHhZUgZPf9Gsr0Y5AIf/sWJ7cbxwo9cAewgfqVt7ZOFniiQD5ePOaZOawGdn8A==",
|
|
1975
1975
|
"dependencies": {
|
|
1976
1976
|
"chalk": "^5.0.0",
|
|
1977
1977
|
"cron-parser": "^4.1.0",
|
|
@@ -2279,9 +2279,9 @@
|
|
|
2279
2279
|
}
|
|
2280
2280
|
},
|
|
2281
2281
|
"node_modules/@netlify/config/node_modules/type-fest": {
|
|
2282
|
-
"version": "2.
|
|
2283
|
-
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.
|
|
2284
|
-
"integrity": "sha512-
|
|
2282
|
+
"version": "2.15.1",
|
|
2283
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.1.tgz",
|
|
2284
|
+
"integrity": "sha512-LYSjcIz3NmoQksXq/3/B7Nfad+T8mkaI628agAAnHCpXPTBRMK2ygt3eABpzII8CbZZM8dLdVQ4Gr8ousjFjMw==",
|
|
2285
2285
|
"engines": {
|
|
2286
2286
|
"node": ">=12.20"
|
|
2287
2287
|
},
|
|
@@ -2301,9 +2301,9 @@
|
|
|
2301
2301
|
}
|
|
2302
2302
|
},
|
|
2303
2303
|
"node_modules/@netlify/edge-bundler": {
|
|
2304
|
-
"version": "1.4.
|
|
2305
|
-
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.4.
|
|
2306
|
-
"integrity": "sha512-
|
|
2304
|
+
"version": "1.4.2",
|
|
2305
|
+
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.4.2.tgz",
|
|
2306
|
+
"integrity": "sha512-grllAfoFzZu6m63pkcm8GqoG8wng6AIKNjY/Ay6UTMn+E2lBgB6pw2fF7xT8mGYWSB2TLdRlQ4RyPE/DUoyoIQ==",
|
|
2307
2307
|
"dependencies": {
|
|
2308
2308
|
"common-path-prefix": "^3.0.0",
|
|
2309
2309
|
"del": "^6.0.0",
|
|
@@ -3534,9 +3534,9 @@
|
|
|
3534
3534
|
"integrity": "sha512-ZJF4nbLPfxsWrVgp2reCb23HMxsWl5+r+K0RtIhxcZ/RxbHVihRA9mwyzIcxPWrT5x9uAAxCr1aKGP7IdsDe+Q=="
|
|
3535
3535
|
},
|
|
3536
3536
|
"node_modules/@netlify/plugins-list": {
|
|
3537
|
-
"version": "6.
|
|
3538
|
-
"resolved": "https://registry.npmjs.org/@netlify/plugins-list/-/plugins-list-6.
|
|
3539
|
-
"integrity": "sha512-
|
|
3537
|
+
"version": "6.30.0",
|
|
3538
|
+
"resolved": "https://registry.npmjs.org/@netlify/plugins-list/-/plugins-list-6.30.0.tgz",
|
|
3539
|
+
"integrity": "sha512-NF0G7rMl/FbCkZMDiihoDW70i96aF3fguHvuspucstM4ArAdMai5b3rEKSOO2SXTbmQHaAlQEpiYAPZsGwU7jg==",
|
|
3540
3540
|
"engines": {
|
|
3541
3541
|
"node": "^12.20.0 || ^14.14.0 || >=16.0.0"
|
|
3542
3542
|
}
|
|
@@ -22643,9 +22643,9 @@
|
|
|
22643
22643
|
}
|
|
22644
22644
|
},
|
|
22645
22645
|
"node_modules/winston": {
|
|
22646
|
-
"version": "3.
|
|
22647
|
-
"resolved": "https://registry.npmjs.org/winston/-/winston-3.
|
|
22648
|
-
"integrity": "sha512-
|
|
22646
|
+
"version": "3.8.0",
|
|
22647
|
+
"resolved": "https://registry.npmjs.org/winston/-/winston-3.8.0.tgz",
|
|
22648
|
+
"integrity": "sha512-Iix1w8rIq2kBDkGvclO0db2CVOHYVamCIkVWcUbs567G9i2pdB+gvqLgDgxx4B4HXHYD6U4Zybh6ojepUOqcFQ==",
|
|
22649
22649
|
"dependencies": {
|
|
22650
22650
|
"@dabh/diagnostics": "^2.0.2",
|
|
22651
22651
|
"async": "^3.2.3",
|
|
@@ -23705,13 +23705,13 @@
|
|
|
23705
23705
|
}
|
|
23706
23706
|
},
|
|
23707
23707
|
"@netlify/build": {
|
|
23708
|
-
"version": "27.3.
|
|
23709
|
-
"resolved": "https://registry.npmjs.org/@netlify/build/-/build-27.3.
|
|
23710
|
-
"integrity": "sha512-
|
|
23708
|
+
"version": "27.3.2",
|
|
23709
|
+
"resolved": "https://registry.npmjs.org/@netlify/build/-/build-27.3.2.tgz",
|
|
23710
|
+
"integrity": "sha512-QoaRwx+qt7582mXSZP0s3YCsK2/xUOsEfhx2Q3PvYKKdVDGjx5MROMuCdc480e8KPWsTTUheB2+A5RuB9dpcLQ==",
|
|
23711
23711
|
"requires": {
|
|
23712
23712
|
"@bugsnag/js": "^7.0.0",
|
|
23713
23713
|
"@netlify/cache-utils": "^4.0.0",
|
|
23714
|
-
"@netlify/config": "^18.1.
|
|
23714
|
+
"@netlify/config": "^18.1.1",
|
|
23715
23715
|
"@netlify/edge-bundler": "^1.4.1",
|
|
23716
23716
|
"@netlify/functions-utils": "^4.2.0",
|
|
23717
23717
|
"@netlify/git-utils": "^4.0.0",
|
|
@@ -24059,9 +24059,9 @@
|
|
|
24059
24059
|
},
|
|
24060
24060
|
"dependencies": {
|
|
24061
24061
|
"type-fest": {
|
|
24062
|
-
"version": "2.
|
|
24063
|
-
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.
|
|
24064
|
-
"integrity": "sha512-
|
|
24062
|
+
"version": "2.15.1",
|
|
24063
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.1.tgz",
|
|
24064
|
+
"integrity": "sha512-LYSjcIz3NmoQksXq/3/B7Nfad+T8mkaI628agAAnHCpXPTBRMK2ygt3eABpzII8CbZZM8dLdVQ4Gr8ousjFjMw=="
|
|
24065
24065
|
}
|
|
24066
24066
|
}
|
|
24067
24067
|
},
|
|
@@ -24076,9 +24076,9 @@
|
|
|
24076
24076
|
},
|
|
24077
24077
|
"dependencies": {
|
|
24078
24078
|
"type-fest": {
|
|
24079
|
-
"version": "2.
|
|
24080
|
-
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.
|
|
24081
|
-
"integrity": "sha512-
|
|
24079
|
+
"version": "2.15.1",
|
|
24080
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.1.tgz",
|
|
24081
|
+
"integrity": "sha512-LYSjcIz3NmoQksXq/3/B7Nfad+T8mkaI628agAAnHCpXPTBRMK2ygt3eABpzII8CbZZM8dLdVQ4Gr8ousjFjMw=="
|
|
24082
24082
|
}
|
|
24083
24083
|
}
|
|
24084
24084
|
},
|
|
@@ -24192,9 +24192,9 @@
|
|
|
24192
24192
|
}
|
|
24193
24193
|
},
|
|
24194
24194
|
"@netlify/config": {
|
|
24195
|
-
"version": "18.1.
|
|
24196
|
-
"resolved": "https://registry.npmjs.org/@netlify/config/-/config-18.1.
|
|
24197
|
-
"integrity": "sha512-
|
|
24195
|
+
"version": "18.1.1",
|
|
24196
|
+
"resolved": "https://registry.npmjs.org/@netlify/config/-/config-18.1.1.tgz",
|
|
24197
|
+
"integrity": "sha512-w3rIWV72anBCnAb69rCTnuwkLHhZUgZPf9Gsr0Y5AIf/sWJ7cbxwo9cAewgfqVt7ZOFniiQD5ePOaZOawGdn8A==",
|
|
24198
24198
|
"requires": {
|
|
24199
24199
|
"chalk": "^5.0.0",
|
|
24200
24200
|
"cron-parser": "^4.1.0",
|
|
@@ -24370,9 +24370,9 @@
|
|
|
24370
24370
|
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="
|
|
24371
24371
|
},
|
|
24372
24372
|
"type-fest": {
|
|
24373
|
-
"version": "2.
|
|
24374
|
-
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.
|
|
24375
|
-
"integrity": "sha512-
|
|
24373
|
+
"version": "2.15.1",
|
|
24374
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.1.tgz",
|
|
24375
|
+
"integrity": "sha512-LYSjcIz3NmoQksXq/3/B7Nfad+T8mkaI628agAAnHCpXPTBRMK2ygt3eABpzII8CbZZM8dLdVQ4Gr8ousjFjMw=="
|
|
24376
24376
|
},
|
|
24377
24377
|
"yocto-queue": {
|
|
24378
24378
|
"version": "1.0.0",
|
|
@@ -24382,9 +24382,9 @@
|
|
|
24382
24382
|
}
|
|
24383
24383
|
},
|
|
24384
24384
|
"@netlify/edge-bundler": {
|
|
24385
|
-
"version": "1.4.
|
|
24386
|
-
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.4.
|
|
24387
|
-
"integrity": "sha512-
|
|
24385
|
+
"version": "1.4.2",
|
|
24386
|
+
"resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-1.4.2.tgz",
|
|
24387
|
+
"integrity": "sha512-grllAfoFzZu6m63pkcm8GqoG8wng6AIKNjY/Ay6UTMn+E2lBgB6pw2fF7xT8mGYWSB2TLdRlQ4RyPE/DUoyoIQ==",
|
|
24388
24388
|
"requires": {
|
|
24389
24389
|
"common-path-prefix": "^3.0.0",
|
|
24390
24390
|
"del": "^6.0.0",
|
|
@@ -25065,9 +25065,9 @@
|
|
|
25065
25065
|
"integrity": "sha512-ZJF4nbLPfxsWrVgp2reCb23HMxsWl5+r+K0RtIhxcZ/RxbHVihRA9mwyzIcxPWrT5x9uAAxCr1aKGP7IdsDe+Q=="
|
|
25066
25066
|
},
|
|
25067
25067
|
"@netlify/plugins-list": {
|
|
25068
|
-
"version": "6.
|
|
25069
|
-
"resolved": "https://registry.npmjs.org/@netlify/plugins-list/-/plugins-list-6.
|
|
25070
|
-
"integrity": "sha512-
|
|
25068
|
+
"version": "6.30.0",
|
|
25069
|
+
"resolved": "https://registry.npmjs.org/@netlify/plugins-list/-/plugins-list-6.30.0.tgz",
|
|
25070
|
+
"integrity": "sha512-NF0G7rMl/FbCkZMDiihoDW70i96aF3fguHvuspucstM4ArAdMai5b3rEKSOO2SXTbmQHaAlQEpiYAPZsGwU7jg=="
|
|
25071
25071
|
},
|
|
25072
25072
|
"@netlify/run-utils": {
|
|
25073
25073
|
"version": "4.0.1",
|
|
@@ -39695,9 +39695,9 @@
|
|
|
39695
39695
|
}
|
|
39696
39696
|
},
|
|
39697
39697
|
"winston": {
|
|
39698
|
-
"version": "3.
|
|
39699
|
-
"resolved": "https://registry.npmjs.org/winston/-/winston-3.
|
|
39700
|
-
"integrity": "sha512-
|
|
39698
|
+
"version": "3.8.0",
|
|
39699
|
+
"resolved": "https://registry.npmjs.org/winston/-/winston-3.8.0.tgz",
|
|
39700
|
+
"integrity": "sha512-Iix1w8rIq2kBDkGvclO0db2CVOHYVamCIkVWcUbs567G9i2pdB+gvqLgDgxx4B4HXHYD6U4Zybh6ojepUOqcFQ==",
|
|
39701
39701
|
"requires": {
|
|
39702
39702
|
"@dabh/diagnostics": "^2.0.2",
|
|
39703
39703
|
"async": "^3.2.3",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.7.0",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Abraham Schilling <AbrahamSchilling@gmail.com> (https://gitlab.com/n4bb12)",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"Jake Jarvis <jake@jarv.is> (https://twitter.com/jakejarvis)",
|
|
56
56
|
"Jakob Warkotsch <j.warkotsch@gmail.com>",
|
|
57
57
|
"James George (https://twitter.com/james_madhacks)",
|
|
58
|
+
"Jason Barry (https://twitter.com/jasbarry)",
|
|
58
59
|
"Jason Lengstorf <jason@lengstorf.com> (https://twitter.com/jlengstorf)",
|
|
59
60
|
"Jeremy Monson (www.surfline.com)",
|
|
60
61
|
"Jessica Parsons",
|
|
@@ -212,12 +213,12 @@
|
|
|
212
213
|
"prettier": "--ignore-path .gitignore --loglevel=warn \"{src,tools,scripts,site,tests,.github}/**/*.{mjs,cjs,js,md,yml,json,html}\" \"*.{mjs,cjs,js,yml,json,html}\" \".*.{mjs,cjs,js,yml,json,html}\" \"!CHANGELOG.md\" \"!npm-shrinkwrap.json\" \"!**/*/package-lock.json\" \"!.github/**/*.md\""
|
|
213
214
|
},
|
|
214
215
|
"dependencies": {
|
|
215
|
-
"@netlify/build": "^27.3.
|
|
216
|
-
"@netlify/config": "^18.1.
|
|
217
|
-
"@netlify/edge-bundler": "^1.4.
|
|
216
|
+
"@netlify/build": "^27.3.2",
|
|
217
|
+
"@netlify/config": "^18.1.1",
|
|
218
|
+
"@netlify/edge-bundler": "^1.4.2",
|
|
218
219
|
"@netlify/framework-info": "^9.1.0",
|
|
219
220
|
"@netlify/local-functions-proxy": "^1.1.1",
|
|
220
|
-
"@netlify/plugins-list": "^6.
|
|
221
|
+
"@netlify/plugins-list": "^6.30.0",
|
|
221
222
|
"@netlify/zip-it-and-ship-it": "^5.11.1",
|
|
222
223
|
"@octokit/rest": "^18.0.0",
|
|
223
224
|
"@sindresorhus/slugify": "^1.1.0",
|
package/src/commands/dev/dev.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
getNetlifyGraphConfig,
|
|
27
27
|
readGraphQLOperationsSourceFile,
|
|
28
28
|
} = require('../../lib/one-graph/cli-netlify-graph')
|
|
29
|
+
const { startSpinner, stopSpinner } = require('../../lib/spinner')
|
|
29
30
|
const {
|
|
30
31
|
BANG,
|
|
31
32
|
NETLIFYDEV,
|
|
@@ -115,7 +116,7 @@ const cleanupBeforeExit = async ({ exitCode }) => {
|
|
|
115
116
|
* @param {NodeJS.ProcessEnv} env
|
|
116
117
|
* @returns {execa.ExecaChildProcess<string>}
|
|
117
118
|
*/
|
|
118
|
-
const runCommand = (command, env = {}) => {
|
|
119
|
+
const runCommand = (command, env = {}, spinner = null) => {
|
|
119
120
|
const commandProcess = execa.command(command, {
|
|
120
121
|
preferLocal: true,
|
|
121
122
|
// we use reject=false to avoid rejecting synchronously when the command doesn't exist
|
|
@@ -125,8 +126,23 @@ const runCommand = (command, env = {}) => {
|
|
|
125
126
|
windowsHide: false,
|
|
126
127
|
})
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
// This ensures that an active spinner stays at the bottom of the commandline
|
|
130
|
+
// even though the actual framework command might be outputting stuff
|
|
131
|
+
const pipeDataWithSpinner = (writeStream, chunk) => {
|
|
132
|
+
if (spinner && spinner.isSpinning) {
|
|
133
|
+
spinner.clear()
|
|
134
|
+
spinner.isSilent = true
|
|
135
|
+
}
|
|
136
|
+
writeStream.write(chunk, () => {
|
|
137
|
+
if (spinner && spinner.isSpinning) {
|
|
138
|
+
spinner.isSilent = false
|
|
139
|
+
spinner.render()
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
commandProcess.stdout.pipe(stripAnsiCc.stream()).on('data', pipeDataWithSpinner.bind(null, process.stdout))
|
|
145
|
+
commandProcess.stderr.pipe(stripAnsiCc.stream()).on('data', pipeDataWithSpinner.bind(null, process.stderr))
|
|
130
146
|
process.stdin.pipe(commandProcess.stdin)
|
|
131
147
|
|
|
132
148
|
// we can't try->await->catch since we don't want to block on the framework server which
|
|
@@ -173,15 +189,16 @@ const startFrameworkServer = async function ({ settings }) {
|
|
|
173
189
|
|
|
174
190
|
log(`${NETLIFYDEVLOG} Starting Netlify Dev with ${settings.framework || 'custom config'}`)
|
|
175
191
|
|
|
176
|
-
|
|
192
|
+
const spinner = startSpinner({
|
|
193
|
+
text: `Waiting for framework port ${settings.frameworkPort}. This can be configured using the 'targetPort' property in the netlify.toml`,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
runCommand(settings.command, settings.env, spinner)
|
|
177
197
|
|
|
178
198
|
try {
|
|
179
|
-
log(
|
|
180
|
-
`${NETLIFYDEVLOG} Waiting for framework port ${settings.frameworkPort}. This can be configured using the 'targetPort' property in the netlify.toml`,
|
|
181
|
-
)
|
|
182
199
|
const open = await waitPort({
|
|
183
200
|
port: settings.frameworkPort,
|
|
184
|
-
output: '
|
|
201
|
+
output: 'silent',
|
|
185
202
|
timeout: FRAMEWORK_PORT_TIMEOUT,
|
|
186
203
|
...(settings.pollingStrategies.includes('HTTP') && { protocol: 'http' }),
|
|
187
204
|
})
|
|
@@ -189,7 +206,10 @@ const startFrameworkServer = async function ({ settings }) {
|
|
|
189
206
|
if (!open) {
|
|
190
207
|
throw new Error(`Timed out waiting for port '${settings.frameworkPort}' to be open`)
|
|
191
208
|
}
|
|
209
|
+
|
|
210
|
+
stopSpinner({ error: false, spinner })
|
|
192
211
|
} catch {
|
|
212
|
+
stopSpinner({ error: true, spinner })
|
|
193
213
|
log(NETLIFYDEVERR, `Netlify Dev could not connect to localhost:${settings.frameworkPort}.`)
|
|
194
214
|
log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`)
|
|
195
215
|
exit(1)
|
|
@@ -5,7 +5,7 @@ const AsciiTable = require('ascii-table')
|
|
|
5
5
|
const dotenv = require('dotenv')
|
|
6
6
|
const isEmpty = require('lodash/isEmpty')
|
|
7
7
|
|
|
8
|
-
const { exit, log, logJson } = require('../../utils')
|
|
8
|
+
const { exit, log, logJson, translateFromEnvelopeToMongo, translateFromMongoToEnvelope } = require('../../utils')
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The env:import command
|
|
@@ -23,13 +23,6 @@ const envImport = async (fileName, options, command) => {
|
|
|
23
23
|
return false
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const siteData = await api.getSite({ siteId })
|
|
27
|
-
|
|
28
|
-
// Get current environment variables set in the UI
|
|
29
|
-
const {
|
|
30
|
-
build_settings: { env = {} },
|
|
31
|
-
} = siteData
|
|
32
|
-
|
|
33
26
|
let importedEnv = {}
|
|
34
27
|
try {
|
|
35
28
|
const envFileContents = await readFile(fileName, 'utf-8')
|
|
@@ -44,21 +37,14 @@ const envImport = async (fileName, options, command) => {
|
|
|
44
37
|
return false
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
build_settings: {
|
|
52
|
-
// Only set imported variables if --replaceExisting or otherwise merge
|
|
53
|
-
// imported ones with the current environment variables.
|
|
54
|
-
env: options.replaceExisting ? importedEnv : { ...env, ...importedEnv },
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
})
|
|
40
|
+
const siteData = await api.getSite({ siteId })
|
|
41
|
+
|
|
42
|
+
const importIntoService = siteData.use_envelope ? importIntoEnvelope : importIntoMongo
|
|
43
|
+
const finalEnv = await importIntoService({ api, importedEnv, options, siteData })
|
|
58
44
|
|
|
59
45
|
// Return new environment variables of site if using json flag
|
|
60
46
|
if (options.json) {
|
|
61
|
-
logJson(
|
|
47
|
+
logJson(finalEnv)
|
|
62
48
|
return false
|
|
63
49
|
}
|
|
64
50
|
|
|
@@ -71,6 +57,66 @@ const envImport = async (fileName, options, command) => {
|
|
|
71
57
|
log(table.toString())
|
|
72
58
|
}
|
|
73
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Updates the imported env in the site record
|
|
62
|
+
* @returns {Promise<object>}
|
|
63
|
+
*/
|
|
64
|
+
const importIntoMongo = async ({ api, importedEnv, options, siteData }) => {
|
|
65
|
+
const { env = {} } = siteData.build_settings
|
|
66
|
+
const siteId = siteData.id
|
|
67
|
+
|
|
68
|
+
const finalEnv = options.replaceExisting ? importedEnv : { ...env, ...importedEnv }
|
|
69
|
+
|
|
70
|
+
// Apply environment variable updates
|
|
71
|
+
await api.updateSite({
|
|
72
|
+
siteId,
|
|
73
|
+
body: {
|
|
74
|
+
build_settings: {
|
|
75
|
+
// Only set imported variables if --replaceExisting or otherwise merge
|
|
76
|
+
// imported ones with the current environment variables.
|
|
77
|
+
env: finalEnv,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return finalEnv
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Saves the imported env in the Envelope service
|
|
87
|
+
* @returns {Promise<object>}
|
|
88
|
+
*/
|
|
89
|
+
const importIntoEnvelope = async ({ api, importedEnv, options, siteData }) => {
|
|
90
|
+
// fetch env vars
|
|
91
|
+
const accountId = siteData.account_slug
|
|
92
|
+
const siteId = siteData.id
|
|
93
|
+
const dotEnvKeys = Object.keys(importedEnv)
|
|
94
|
+
const envelopeVariables = await api.getEnvVars({ accountId, siteId })
|
|
95
|
+
const envelopeKeys = envelopeVariables.map(({ key }) => key)
|
|
96
|
+
|
|
97
|
+
// if user intends to replace all existing env vars
|
|
98
|
+
// either replace; delete all existing env vars on the site
|
|
99
|
+
// or, merge; delete only the existing env vars that would collide with new .env entries
|
|
100
|
+
const keysToDelete = options.replaceExisting ? envelopeKeys : envelopeKeys.filter((key) => dotEnvKeys.includes(key))
|
|
101
|
+
|
|
102
|
+
// delete marked env vars in parallel
|
|
103
|
+
await Promise.all(keysToDelete.map((key) => api.deleteEnvVar({ accountId, siteId, key })))
|
|
104
|
+
|
|
105
|
+
// hit create endpoint
|
|
106
|
+
const body = translateFromMongoToEnvelope(importedEnv)
|
|
107
|
+
try {
|
|
108
|
+
await api.createEnvVars({ accountId, siteId, body })
|
|
109
|
+
} catch (error) {
|
|
110
|
+
throw error.json ? error.json.msg : error
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// return final env to aid in --json output (for testing)
|
|
114
|
+
return {
|
|
115
|
+
...translateFromEnvelopeToMongo(envelopeVariables.filter(({ key }) => !keysToDelete.includes(key))),
|
|
116
|
+
...importedEnv,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
74
120
|
/**
|
|
75
121
|
* Creates the `netlify env:import` command
|
|
76
122
|
* @param {import('../base-command').BaseCommand} program
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const { isEmpty } = require('lodash')
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
chalk,
|
|
7
|
+
error: logError,
|
|
8
|
+
log,
|
|
9
|
+
translateFromEnvelopeToMongo,
|
|
10
|
+
translateFromMongoToEnvelope,
|
|
11
|
+
} = require('../../utils')
|
|
6
12
|
|
|
7
13
|
const safeGetSite = async (api, siteId) => {
|
|
8
14
|
try {
|
|
@@ -51,6 +57,37 @@ const envMigrate = async (options, command) => {
|
|
|
51
57
|
return false
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
// determine if siteFrom and/or siteTo is on Envelope
|
|
61
|
+
let method
|
|
62
|
+
if (!siteFrom.use_envelope && !siteTo.use_envelope) {
|
|
63
|
+
method = mongoToMongo
|
|
64
|
+
} else if (!siteFrom.use_envelope && siteTo.use_envelope) {
|
|
65
|
+
method = mongoToEnvelope
|
|
66
|
+
} else if (siteFrom.use_envelope && !siteTo.use_envelope) {
|
|
67
|
+
method = envelopeToMongo
|
|
68
|
+
} else {
|
|
69
|
+
method = envelopeToEnvelope
|
|
70
|
+
}
|
|
71
|
+
const success = await method({ api, siteFrom, siteTo })
|
|
72
|
+
|
|
73
|
+
if (!success) {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
log(
|
|
78
|
+
`Successfully migrated environment variables from ${chalk.greenBright(siteFrom.name)} to ${chalk.greenBright(
|
|
79
|
+
siteTo.name,
|
|
80
|
+
)}`,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Copies the env from a site not configured with Envelope to a different site not configured with Envelope
|
|
88
|
+
* @returns {Promise<boolean>}
|
|
89
|
+
*/
|
|
90
|
+
const mongoToMongo = async ({ api, siteFrom, siteTo }) => {
|
|
54
91
|
const [
|
|
55
92
|
{
|
|
56
93
|
build_settings: { env: envFrom = {} },
|
|
@@ -73,7 +110,7 @@ const envMigrate = async (options, command) => {
|
|
|
73
110
|
|
|
74
111
|
// Apply environment variable updates
|
|
75
112
|
await api.updateSite({
|
|
76
|
-
siteId:
|
|
113
|
+
siteId: siteTo.id,
|
|
77
114
|
body: {
|
|
78
115
|
build_settings: {
|
|
79
116
|
env: mergedEnv,
|
|
@@ -81,11 +118,107 @@ const envMigrate = async (options, command) => {
|
|
|
81
118
|
},
|
|
82
119
|
})
|
|
83
120
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Copies the env from a site not configured with Envelope to a site configured with Envelope
|
|
126
|
+
* @returns {Promise<boolean>}
|
|
127
|
+
*/
|
|
128
|
+
const mongoToEnvelope = async ({ api, siteFrom, siteTo }) => {
|
|
129
|
+
const envFrom = siteFrom.build_settings.env || {}
|
|
130
|
+
const keysFrom = Object.keys(envFrom)
|
|
131
|
+
|
|
132
|
+
if (isEmpty(envFrom)) {
|
|
133
|
+
log(`${chalk.greenBright(siteFrom.name)} has no environment variables, nothing to migrate`)
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const accountId = siteTo.account_slug
|
|
138
|
+
const siteId = siteTo.id
|
|
139
|
+
|
|
140
|
+
const envelopeTo = await api.getEnvVars({ accountId, siteId })
|
|
141
|
+
|
|
142
|
+
const envVarsToDelete = envelopeTo.filter(({ key }) => keysFrom.includes(key))
|
|
143
|
+
// delete marked env vars in parallel
|
|
144
|
+
await Promise.all(envVarsToDelete.map(({ key }) => api.deleteEnvVar({ accountId, siteId, key })))
|
|
145
|
+
|
|
146
|
+
// hit create endpoint
|
|
147
|
+
const body = translateFromMongoToEnvelope(envFrom)
|
|
148
|
+
try {
|
|
149
|
+
await api.createEnvVars({ accountId, siteId, body })
|
|
150
|
+
} catch (error) {
|
|
151
|
+
throw error.json ? error.json.msg : error
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return true
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Copies the env from a site configured with Envelope to a site not configured with Envelope
|
|
159
|
+
* @returns {Promise<boolean>}
|
|
160
|
+
*/
|
|
161
|
+
const envelopeToMongo = async ({ api, siteFrom, siteTo }) => {
|
|
162
|
+
const envelopeVariables = await api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id })
|
|
163
|
+
const envFrom = translateFromEnvelopeToMongo(envelopeVariables)
|
|
164
|
+
|
|
165
|
+
if (isEmpty(envFrom)) {
|
|
166
|
+
log(`${chalk.greenBright(siteFrom.name)} has no environment variables, nothing to migrate`)
|
|
167
|
+
return false
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const envTo = siteTo.build_settings.env || {}
|
|
171
|
+
|
|
172
|
+
// Merge from site A to site B
|
|
173
|
+
const mergedEnv = {
|
|
174
|
+
...envTo,
|
|
175
|
+
...envFrom,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Apply environment variable updates
|
|
179
|
+
await api.updateSite({
|
|
180
|
+
siteId: siteTo.id,
|
|
181
|
+
body: {
|
|
182
|
+
build_settings: {
|
|
183
|
+
env: mergedEnv,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Copies the env from a site configured with Envelope to a different site configured with Envelope
|
|
193
|
+
* @returns {Promise<boolean>}
|
|
194
|
+
*/
|
|
195
|
+
const envelopeToEnvelope = async ({ api, siteFrom, siteTo }) => {
|
|
196
|
+
const [envelopeFrom, envelopeTo] = await Promise.all([
|
|
197
|
+
api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }),
|
|
198
|
+
api.getEnvVars({ accountId: siteTo.account_slug, siteId: siteTo.id }),
|
|
199
|
+
])
|
|
200
|
+
|
|
201
|
+
const keysFrom = envelopeFrom.map(({ key }) => key)
|
|
202
|
+
|
|
203
|
+
if (isEmpty(keysFrom)) {
|
|
204
|
+
log(`${chalk.greenBright(siteFrom.name)} has no environment variables, nothing to migrate`)
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const accountId = siteTo.account_slug
|
|
209
|
+
const siteId = siteTo.id
|
|
210
|
+
const envVarsToDelete = envelopeTo.filter(({ key }) => keysFrom.includes(key))
|
|
211
|
+
// delete marked env vars in parallel
|
|
212
|
+
await Promise.all(envVarsToDelete.map(({ key }) => api.deleteEnvVar({ accountId, siteId, key })))
|
|
213
|
+
|
|
214
|
+
// hit create endpoint
|
|
215
|
+
try {
|
|
216
|
+
await api.createEnvVars({ accountId, siteId, body: envelopeFrom })
|
|
217
|
+
} catch (error) {
|
|
218
|
+
throw error.json ? error.json.msg : error
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true
|
|
89
222
|
}
|
|
90
223
|
|
|
91
224
|
/**
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
const { log, logJson } = require('../../utils')
|
|
2
|
+
const { log, logJson, translateFromEnvelopeToMongo } = require('../../utils')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The env:set command
|
|
6
|
-
* @param {string}
|
|
6
|
+
* @param {string} key Environment variable key
|
|
7
7
|
* @param {string} value Value to set to
|
|
8
8
|
* @param {import('commander').OptionValues} options
|
|
9
9
|
* @param {import('../base-command').BaseCommand} command
|
|
10
10
|
* @returns {Promise<boolean>}
|
|
11
11
|
*/
|
|
12
|
-
const envSet = async (
|
|
12
|
+
const envSet = async (key, value, options, command) => {
|
|
13
13
|
const { api, site } = command.netlify
|
|
14
14
|
const siteId = site.id
|
|
15
15
|
|
|
@@ -21,32 +21,67 @@ const envSet = async (name, value, options, command) => {
|
|
|
21
21
|
const siteData = await api.getSite({ siteId })
|
|
22
22
|
|
|
23
23
|
// Get current environment variables set in the UI
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
} = siteData
|
|
24
|
+
const setInService = siteData.use_envelope ? setInEnvelope : setInMongo
|
|
25
|
+
const finalEnv = await setInService({ api, siteData, key, value })
|
|
27
26
|
|
|
27
|
+
// Return new environment variables of site if using json flag
|
|
28
|
+
if (options.json) {
|
|
29
|
+
logJson(finalEnv)
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
log(`Set environment variable ${key}=${value} for site ${siteData.name}`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Updates the env for a site record with a new key/value pair
|
|
38
|
+
* @returns {Promise<object>}
|
|
39
|
+
*/
|
|
40
|
+
const setInMongo = async ({ api, key, siteData, value }) => {
|
|
41
|
+
const { env = {} } = siteData.build_settings
|
|
28
42
|
const newEnv = {
|
|
29
43
|
...env,
|
|
30
|
-
[
|
|
44
|
+
[key]: value,
|
|
31
45
|
}
|
|
32
|
-
|
|
33
46
|
// Apply environment variable updates
|
|
34
|
-
|
|
35
|
-
siteId,
|
|
47
|
+
await api.updateSite({
|
|
48
|
+
siteId: siteData.id,
|
|
36
49
|
body: {
|
|
37
50
|
build_settings: {
|
|
38
51
|
env: newEnv,
|
|
39
52
|
},
|
|
40
53
|
},
|
|
41
54
|
})
|
|
55
|
+
return newEnv
|
|
56
|
+
}
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Updates the env for a site configured with Envelope with a new key/value pair
|
|
60
|
+
* @returns {Promise<object>}
|
|
61
|
+
*/
|
|
62
|
+
const setInEnvelope = async ({ api, key, siteData, value }) => {
|
|
63
|
+
const accountId = siteData.account_slug
|
|
64
|
+
const siteId = siteData.id
|
|
65
|
+
// fetch envelope env vars
|
|
66
|
+
const envelopeVariables = await api.getEnvVars({ accountId, siteId })
|
|
67
|
+
const scopes = ['builds', 'functions', 'runtime', 'post_processing']
|
|
68
|
+
const values = [{ context: 'all', value }]
|
|
69
|
+
// check if we need to create or update
|
|
70
|
+
const exists = envelopeVariables.some((envVar) => envVar.key === key)
|
|
71
|
+
const method = exists ? api.updateEnvVar : api.createEnvVars
|
|
72
|
+
const body = exists ? { key, scopes, values } : [{ key, scopes, values }]
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await method({ accountId, siteId, key, body })
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw error.json ? error.json.msg : error
|
|
47
78
|
}
|
|
48
79
|
|
|
49
|
-
|
|
80
|
+
const env = translateFromEnvelopeToMongo(envelopeVariables)
|
|
81
|
+
return {
|
|
82
|
+
...env,
|
|
83
|
+
[key]: value,
|
|
84
|
+
}
|
|
50
85
|
}
|
|
51
86
|
|
|
52
87
|
/**
|
|
@@ -57,11 +92,11 @@ const envSet = async (name, value, options, command) => {
|
|
|
57
92
|
const createEnvSetCommand = (program) =>
|
|
58
93
|
program
|
|
59
94
|
.command('env:set')
|
|
60
|
-
.argument('<
|
|
95
|
+
.argument('<key>', 'Environment variable key')
|
|
61
96
|
.argument('[value]', 'Value to set to', '')
|
|
62
97
|
.description('Set value of environment variable')
|
|
63
|
-
.action(async (
|
|
64
|
-
await envSet(
|
|
98
|
+
.action(async (key, value, options, command) => {
|
|
99
|
+
await envSet(key, value, options, command)
|
|
65
100
|
})
|
|
66
101
|
|
|
67
102
|
module.exports = { createEnvSetCommand }
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
const { log, logJson } = require('../../utils')
|
|
2
|
+
const { log, logJson, translateFromEnvelopeToMongo } = require('../../utils')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The env:unset command
|
|
6
|
-
* @param {string}
|
|
6
|
+
* @param {string} key Environment variable key
|
|
7
7
|
* @param {import('commander').OptionValues} options
|
|
8
8
|
* @param {import('../base-command').BaseCommand} command
|
|
9
9
|
* @returns {Promise<boolean>}
|
|
10
10
|
*/
|
|
11
|
-
const envUnset = async (
|
|
11
|
+
const envUnset = async (key, options, command) => {
|
|
12
12
|
const { api, site } = command.netlify
|
|
13
13
|
const siteId = site.id
|
|
14
14
|
|
|
@@ -19,6 +19,23 @@ const envUnset = async (name, options, command) => {
|
|
|
19
19
|
|
|
20
20
|
const siteData = await api.getSite({ siteId })
|
|
21
21
|
|
|
22
|
+
const unsetInService = siteData.use_envelope ? unsetInEnvelope : unsetInMongo
|
|
23
|
+
const finalEnv = await unsetInService({ api, siteData, key })
|
|
24
|
+
|
|
25
|
+
// Return new environment variables of site if using json flag
|
|
26
|
+
if (options.json) {
|
|
27
|
+
logJson(finalEnv)
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
log(`Unset environment variable ${key} for site ${siteData.name}`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Deletes a given key from the env of a site record
|
|
36
|
+
* @returns {Promise<object>}
|
|
37
|
+
*/
|
|
38
|
+
const unsetInMongo = async ({ api, key, siteData }) => {
|
|
22
39
|
// Get current environment variables set in the UI
|
|
23
40
|
const {
|
|
24
41
|
build_settings: { env = {} },
|
|
@@ -27,11 +44,11 @@ const envUnset = async (name, options, command) => {
|
|
|
27
44
|
const newEnv = env
|
|
28
45
|
|
|
29
46
|
// Delete environment variable from current variables
|
|
30
|
-
delete newEnv[
|
|
47
|
+
delete newEnv[key]
|
|
31
48
|
|
|
32
49
|
// Apply environment variable updates
|
|
33
|
-
|
|
34
|
-
siteId,
|
|
50
|
+
await api.updateSite({
|
|
51
|
+
siteId: siteData.id,
|
|
35
52
|
body: {
|
|
36
53
|
build_settings: {
|
|
37
54
|
env: newEnv,
|
|
@@ -39,13 +56,36 @@ const envUnset = async (name, options, command) => {
|
|
|
39
56
|
},
|
|
40
57
|
})
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
59
|
+
return newEnv
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Deletes a given key from the env of a site configured with Envelope
|
|
64
|
+
* @returns {Promise<object>}
|
|
65
|
+
*/
|
|
66
|
+
const unsetInEnvelope = async ({ api, key, siteData }) => {
|
|
67
|
+
const accountId = siteData.account_slug
|
|
68
|
+
const siteId = siteData.id
|
|
69
|
+
// fetch envelope env vars
|
|
70
|
+
const envelopeVariables = await api.getEnvVars({ accountId, siteId })
|
|
71
|
+
|
|
72
|
+
// check if the given key exists
|
|
73
|
+
const env = translateFromEnvelopeToMongo(envelopeVariables)
|
|
74
|
+
if (!env[key]) {
|
|
75
|
+
// if not, no need to call delete; return early
|
|
76
|
+
return env
|
|
46
77
|
}
|
|
47
78
|
|
|
48
|
-
|
|
79
|
+
// delete the given key
|
|
80
|
+
try {
|
|
81
|
+
await api.deleteEnvVar({ accountId, siteId, key })
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw error.json ? error.json.msg : error
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
delete env[key]
|
|
87
|
+
|
|
88
|
+
return env
|
|
49
89
|
}
|
|
50
90
|
|
|
51
91
|
/**
|
|
@@ -57,10 +97,10 @@ const createEnvUnsetCommand = (program) =>
|
|
|
57
97
|
program
|
|
58
98
|
.command('env:unset')
|
|
59
99
|
.aliases(['env:delete', 'env:remove'])
|
|
60
|
-
.argument('<
|
|
100
|
+
.argument('<key>', 'Environment variable key')
|
|
61
101
|
.description('Unset an environment variable which removes it from the UI')
|
|
62
|
-
.action(async (
|
|
63
|
-
await envUnset(
|
|
102
|
+
.action(async (key, options, command) => {
|
|
103
|
+
await envUnset(key, options, command)
|
|
64
104
|
})
|
|
65
105
|
|
|
66
106
|
module.exports = { createEnvUnsetCommand }
|
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
const { NETLIFYDEVERR, chalk, error, log } = require('../../utils')
|
|
12
12
|
const { openBrowser } = require('../../utils/open-browser')
|
|
13
13
|
|
|
14
|
-
const {
|
|
14
|
+
const { ensureAppForSite, executeCreatePersistedQueryMutation } = OneGraphCliClient
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Creates the `netlify graph:edit` command
|
|
@@ -50,12 +50,26 @@ const graphEdit = async (options, command) => {
|
|
|
50
50
|
})
|
|
51
51
|
|
|
52
52
|
const { branch } = gitRepoInfo()
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
const persistedResult = await executeCreatePersistedQueryMutation(
|
|
54
|
+
{
|
|
55
|
+
nfToken: netlifyToken,
|
|
56
|
+
appId: siteId,
|
|
57
|
+
description: 'Temporary snapshot of local queries',
|
|
58
|
+
query: graphqlDocument,
|
|
59
|
+
tags: ['netlify-cli', `session:${oneGraphSessionId}`, `git-branch:${branch}`],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
accessToken: netlifyToken,
|
|
63
|
+
|
|
64
|
+
siteId,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
const persistedDoc =
|
|
69
|
+
persistedResult.data &&
|
|
70
|
+
persistedResult.data.oneGraph &&
|
|
71
|
+
persistedResult.data.oneGraph.createPersistedQuery &&
|
|
72
|
+
persistedResult.data.oneGraph.createPersistedQuery.persistedQuery
|
|
59
73
|
|
|
60
74
|
const newMetadata = { docId: persistedDoc.id }
|
|
61
75
|
|
|
@@ -102,7 +102,7 @@ const graphOperations = async (options, command) => {
|
|
|
102
102
|
* @param {import('../base-command').BaseCommand} program
|
|
103
103
|
* @returns
|
|
104
104
|
*/
|
|
105
|
-
const
|
|
105
|
+
const createGraphOperationsCommand = (program) =>
|
|
106
106
|
program
|
|
107
107
|
.command('graph:operations')
|
|
108
108
|
.description('List all of the locally available operations')
|
|
@@ -110,4 +110,4 @@ const createGraphOperationCommand = (program) =>
|
|
|
110
110
|
await graphOperations(options, command)
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
-
module.exports = {
|
|
113
|
+
module.exports = { createGraphOperationsCommand }
|
|
@@ -3,7 +3,7 @@ const { createGraphConfigWriteCommand } = require('./graph-config-write')
|
|
|
3
3
|
const { createGraphEditCommand } = require('./graph-edit')
|
|
4
4
|
const { createGraphHandlerCommand } = require('./graph-handler')
|
|
5
5
|
const { createGraphLibraryCommand } = require('./graph-library')
|
|
6
|
-
const {
|
|
6
|
+
const { createGraphOperationsCommand } = require('./graph-operations')
|
|
7
7
|
const { createGraphPullCommand } = require('./graph-pull')
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -25,7 +25,7 @@ const createGraphCommand = (program) => {
|
|
|
25
25
|
createGraphEditCommand(program)
|
|
26
26
|
createGraphHandlerCommand(program)
|
|
27
27
|
createGraphLibraryCommand(program)
|
|
28
|
-
|
|
28
|
+
createGraphOperationsCommand(program)
|
|
29
29
|
createGraphPullCommand(program)
|
|
30
30
|
|
|
31
31
|
return program
|
|
@@ -128,8 +128,14 @@ const monitorCLISessionEvents = (input) => {
|
|
|
128
128
|
const { events } = next
|
|
129
129
|
|
|
130
130
|
if (events.length !== 0) {
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
let ackIds = []
|
|
132
|
+
try {
|
|
133
|
+
ackIds = await onEvents(events)
|
|
134
|
+
} catch (eventHandlerError) {
|
|
135
|
+
warn(`Error handling event: ${eventHandlerError}`)
|
|
136
|
+
} finally {
|
|
137
|
+
await OneGraphClient.ackCLISessionEvents({ appId, authToken: netlifyToken, sessionId, eventIds: ackIds })
|
|
138
|
+
}
|
|
133
139
|
}
|
|
134
140
|
|
|
135
141
|
await enabledServiceWatcher(netlifyToken, appId)
|
|
@@ -474,14 +480,31 @@ const persistNewOperationsDocForSession = async ({
|
|
|
474
480
|
siteRoot,
|
|
475
481
|
}) => {
|
|
476
482
|
const { branch } = gitRepoInfo()
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
483
|
+
const persistedResult = await executeCreatePersistedQueryMutation(
|
|
484
|
+
{
|
|
485
|
+
nfToken: netlifyToken,
|
|
486
|
+
appId: siteId,
|
|
487
|
+
description: 'Temporary snapshot of local queries',
|
|
488
|
+
query: operationsDoc,
|
|
489
|
+
tags: ['netlify-cli', `session:${oneGraphSessionId}`, `git-branch:${branch}`, `local-change`],
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
accessToken: netlifyToken,
|
|
493
|
+
siteId,
|
|
494
|
+
},
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
const persistedDoc =
|
|
498
|
+
persistedResult.data &&
|
|
499
|
+
persistedResult.data.oneGraph &&
|
|
500
|
+
persistedResult.data.oneGraph.createPersistedQuery &&
|
|
501
|
+
persistedResult.data.oneGraph.createPersistedQuery.persistedQuery
|
|
502
|
+
|
|
503
|
+
if (!persistedDoc) {
|
|
504
|
+
warn(`Failed to create persisted query for editing, ${JSON.stringify(persistedResult, null, 2)}`)
|
|
482
505
|
}
|
|
483
|
-
|
|
484
|
-
const newMetadata =
|
|
506
|
+
|
|
507
|
+
const newMetadata = { docId: persistedDoc.id }
|
|
485
508
|
const result = await upsertMergeCLISessionMetadata({
|
|
486
509
|
netlifyGraphConfig,
|
|
487
510
|
netlifyToken,
|
|
@@ -697,7 +720,8 @@ const ensureCLISession = async ({ metadata, netlifyToken, site, state }) => {
|
|
|
697
720
|
|
|
698
721
|
const OneGraphCliClient = {
|
|
699
722
|
ackCLISessionEvents: OneGraphClient.ackCLISessionEvents,
|
|
700
|
-
|
|
723
|
+
executeCreatePersistedQueryMutation: OneGraphClient.executeCreatePersistedQueryMutation,
|
|
724
|
+
executeCreateApiTokenMutation: OneGraphClient.executeCreateApiTokenMutation,
|
|
701
725
|
fetchCliSessionEvents: OneGraphClient.fetchCliSessionEvents,
|
|
702
726
|
ensureAppForSite,
|
|
703
727
|
updateCLISessionMetadata,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translates a Mongo env into an Envelope env
|
|
3
|
+
* @param {object} env - The site's env as it exists in Mongo
|
|
4
|
+
* @returns {Array<object>} The array of Envelope env vars
|
|
5
|
+
*/
|
|
6
|
+
const translateFromMongoToEnvelope = (env = {}) => {
|
|
7
|
+
const envVars = Object.entries(env).map(([key, value]) => ({
|
|
8
|
+
key,
|
|
9
|
+
scopes: ['builds', 'functions', 'runtime', 'post_processing'],
|
|
10
|
+
values: [
|
|
11
|
+
{
|
|
12
|
+
context: 'all',
|
|
13
|
+
value,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
return envVars
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Translates an Envelope env into a Mongo env
|
|
23
|
+
* @param {Array<object>} envVars - The array of Envelope env vars
|
|
24
|
+
* @returns {object} The env object as compatible with Mongo
|
|
25
|
+
*/
|
|
26
|
+
const translateFromEnvelopeToMongo = (envVars = []) =>
|
|
27
|
+
envVars
|
|
28
|
+
.sort((left, right) => (left.key.toLowerCase() < right.key.toLowerCase() ? -1 : 1))
|
|
29
|
+
.reduce((acc, cur) => {
|
|
30
|
+
const envVar = cur.values.find((val) => ['dev', 'all'].includes(val.context))
|
|
31
|
+
if (envVar && envVar.value) {
|
|
32
|
+
return {
|
|
33
|
+
...acc,
|
|
34
|
+
[cur.key]: envVar.value,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return acc
|
|
38
|
+
}, {})
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
translateFromMongoToEnvelope,
|
|
42
|
+
translateFromEnvelopeToMongo,
|
|
43
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const createStreamPromise = require('./create-stream-promise')
|
|
|
4
4
|
const deploy = require('./deploy')
|
|
5
5
|
const detectServerSettings = require('./detect-server-settings')
|
|
6
6
|
const dev = require('./dev')
|
|
7
|
+
const env = require('./env')
|
|
7
8
|
const execa = require('./execa')
|
|
8
9
|
const functions = require('./functions')
|
|
9
10
|
const getGlobalConfig = require('./get-global-config')
|
|
@@ -24,6 +25,7 @@ module.exports = {
|
|
|
24
25
|
...deploy,
|
|
25
26
|
...detectServerSettings,
|
|
26
27
|
...dev,
|
|
28
|
+
...env,
|
|
27
29
|
...functions,
|
|
28
30
|
...getRepoData,
|
|
29
31
|
...ghAuth,
|