backend-manager 5.0.30 → 5.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/bin/bem +1 -1
  2. package/firebase-debug.log +28 -0
  3. package/package.json +1 -1
  4. package/src/cli/cli-refactored.js +0 -21
  5. package/src/cli/cli.js +41 -204
  6. package/src/cli/commands/index.js +0 -1
  7. package/src/cli/commands/install.js +1 -1
  8. package/src/cli/commands/serve.js +1 -6
  9. package/src/cli/commands/setup-tests/backend-manager-tests-file.js +23 -0
  10. package/src/cli/commands/setup-tests/backend-manager.js +79 -0
  11. package/src/cli/commands/setup-tests/base-test.js +36 -0
  12. package/src/cli/commands/setup-tests/bem-config-id.js +25 -0
  13. package/src/cli/commands/setup-tests/bem-config.js +59 -0
  14. package/src/cli/commands/setup-tests/env-runtime-config.js +68 -0
  15. package/src/cli/commands/setup-tests/firebase-admin.js +71 -0
  16. package/src/cli/commands/setup-tests/firebase-cli.js +30 -0
  17. package/src/cli/commands/setup-tests/firebase-functions.js +71 -0
  18. package/src/cli/commands/setup-tests/firestore-indexes-file.js +34 -0
  19. package/src/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
  20. package/src/cli/commands/setup-tests/firestore-indexes-synced.js +69 -0
  21. package/src/cli/commands/setup-tests/firestore-rules-file.js +52 -0
  22. package/src/cli/commands/setup-tests/firestore-rules-in-json.js +20 -0
  23. package/src/cli/commands/setup-tests/functions-package.js +25 -0
  24. package/src/cli/commands/setup-tests/gitignore.js +40 -0
  25. package/src/cli/commands/setup-tests/helpers.js +24 -0
  26. package/src/cli/commands/setup-tests/hosting-folder.js +23 -0
  27. package/src/cli/commands/setup-tests/hosting-rewrites.js +34 -0
  28. package/src/cli/commands/setup-tests/index.js +82 -0
  29. package/src/cli/commands/setup-tests/is-firebase-project.js +21 -0
  30. package/src/cli/commands/setup-tests/node-version.js +34 -0
  31. package/src/cli/commands/setup-tests/npm-dist-script.js +20 -0
  32. package/src/cli/commands/setup-tests/npm-start-script.js +20 -0
  33. package/src/cli/commands/setup-tests/nvmrc-version.js +30 -0
  34. package/src/cli/commands/setup-tests/public-html-files.js +33 -0
  35. package/src/cli/commands/setup-tests/realtime-rules-file.js +52 -0
  36. package/src/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
  37. package/src/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
  38. package/src/cli/commands/setup-tests/remoteconfig-template-in-json.js +20 -0
  39. package/src/cli/commands/setup-tests/service-account.js +39 -0
  40. package/src/cli/commands/setup-tests/storage-lifecycle-policy.js +46 -0
  41. package/src/cli/commands/setup-tests/storage-rules-file.js +32 -0
  42. package/src/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
  43. package/src/cli/commands/setup.js +46 -69
  44. package/templates/runtimeconfig.json +3 -6
  45. package/src/cli/commands/config.js +0 -165
package/bin/bem CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- let Main = new (require('../src/cli/cli.js'))(process.argv);
2
+ let Main = new (require('../src/cli/cli-refactored.js'))(process.argv);
3
3
  (async function() {
4
4
  'use strict';
5
5
  await Main.process(process.argv);
@@ -22,3 +22,31 @@
22
22
  [debug] [2025-10-20T23:59:40.562Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
23
23
  [debug] [2025-10-20T23:59:40.563Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
24
24
  [debug] [2025-10-20T23:59:40.563Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
25
+ [debug] [2025-11-18T02:05:23.261Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
26
+ [debug] [2025-11-18T02:05:23.267Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
27
+ [debug] [2025-11-18T02:05:23.263Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
28
+ [debug] [2025-11-18T02:05:23.263Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
29
+ [debug] [2025-11-18T02:05:23.263Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
30
+ [debug] [2025-11-18T02:05:23.271Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
31
+ [debug] [2025-11-18T02:05:23.271Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
32
+ [debug] [2025-11-18T02:05:23.269Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
33
+ [debug] [2025-11-18T02:05:23.269Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
34
+ [debug] [2025-11-18T02:05:23.269Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
35
+ [debug] [2025-11-18T02:05:23.277Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
36
+ [debug] [2025-11-18T02:05:23.278Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
37
+ [debug] [2025-11-18T02:05:23.329Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
38
+ [debug] [2025-11-18T02:05:23.331Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
39
+ [debug] [2025-11-18T02:05:23.330Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
40
+ [debug] [2025-11-18T02:05:23.331Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
41
+ [debug] [2025-11-18T02:05:23.331Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
42
+ [debug] [2025-11-18T02:05:23.332Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
43
+ [debug] [2025-11-18T02:05:23.332Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
44
+ [debug] [2025-11-18T02:05:23.333Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
45
+ [debug] [2025-11-18T02:05:23.333Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
46
+ [debug] [2025-11-18T02:05:23.331Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
47
+ [debug] [2025-11-18T02:05:23.332Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
48
+ [debug] [2025-11-18T02:05:23.332Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
49
+ [debug] [2025-11-18T02:05:23.333Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
50
+ [debug] [2025-11-18T02:05:23.333Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
51
+ [debug] [2025-11-18T02:05:23.334Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
52
+ [debug] [2025-11-18T02:05:23.334Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.0.30",
3
+ "version": "5.0.31",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -12,7 +12,6 @@ const DeployCommand = require('./commands/deploy');
12
12
  const TestCommand = require('./commands/test');
13
13
  const CleanCommand = require('./commands/clean');
14
14
  const IndexesCommand = require('./commands/indexes');
15
- const ConfigCommand = require('./commands/config');
16
15
 
17
16
  function Main() {}
18
17
 
@@ -81,26 +80,6 @@ Main.prototype.process = async function (args) {
81
80
  return await cmd.get(undefined, true);
82
81
  }
83
82
 
84
- // Get config
85
- if (self.options['functions:config:get'] || self.options['config:get']) {
86
- const cmd = new ConfigCommand(self);
87
- return await cmd.get();
88
- }
89
-
90
- // Set config
91
- if (self.options['functions:config:set'] || self.options['config:set']) {
92
- const cmd = new ConfigCommand(self);
93
- await cmd.set();
94
- return await cmd.get();
95
- }
96
-
97
- // Unset config
98
- if (self.options['functions:config:unset'] || self.options['config:unset'] || self.options['config:delete'] || self.options['config:remove']) {
99
- const cmd = new ConfigCommand(self);
100
- await cmd.unset();
101
- return await cmd.get();
102
- }
103
-
104
83
  // Deploy
105
84
  if (self.options.deploy) {
106
85
  const cmd = new DeployCommand(self);
package/src/cli/cli.js CHANGED
@@ -52,7 +52,7 @@ let bem_allRulesBackupRegex = /({{\s*?backend-manager\s*?}})/sgm;
52
52
  let MOCHA_PKG_SCRIPT = 'mocha ../test/ --recursive --timeout=10000';
53
53
  let NPM_CLEAN_SCRIPT = 'rm -fr node_modules && rm -fr package-lock.json && npm cache clean --force && npm install && npm rb';
54
54
  let NOFIX_TEXT = chalk.red(`There is no automatic fix for this check.`);
55
- let runtimeconfigTemplate = loadJSON(`${__dirname}/../../templates/runtimeconfig.json`);
55
+ let envRuntimeTemplate = loadJSON(`${__dirname}/../../templates/runtimeconfig.json`);
56
56
  let bemConfigTemplate = loadJSON(`${__dirname}/../../templates/backend-manager-config.json`);
57
57
 
58
58
  function Main() {
@@ -98,7 +98,6 @@ Main.prototype.process = async function (args) {
98
98
 
99
99
  // Setup command
100
100
  if (self.options.setup) {
101
- await cmd_configGet(self).catch(e => log(chalk.red(`Failed to run config:get`)));
102
101
  await self.setup();
103
102
  }
104
103
 
@@ -118,7 +117,6 @@ Main.prototype.process = async function (args) {
118
117
  if (self.options.serve) {
119
118
  if (!self.options.quick && !self.options.q) {
120
119
  }
121
- await cmd_configGet(self);
122
120
  await self.setup();
123
121
 
124
122
  const port = self.argv.port || _.get(self.argv, '_', [])[1] || '5000';
@@ -133,26 +131,6 @@ Main.prototype.process = async function (args) {
133
131
  return await cmd.get(undefined, true);
134
132
  }
135
133
 
136
- // Get config
137
- if (self.options['functions:config:get'] || self.options['config:get']) {
138
- const cmd = new commands.ConfigCommand(self);
139
- return await cmd.get();
140
- }
141
-
142
- // Set config
143
- if (self.options['functions:config:set'] || self.options['config:set']) {
144
- const cmd = new commands.ConfigCommand(self);
145
- await cmd.set();
146
- return await cmd.get();
147
- }
148
-
149
- // Unset config
150
- if (self.options['functions:config:unset'] || self.options['config:unset'] || self.options['config:delete'] || self.options['config:remove']) {
151
- const cmd = new commands.ConfigCommand(self);
152
- await cmd.unset();
153
- return await cmd.get();
154
- }
155
-
156
134
  // Deploy
157
135
  if (self.options.deploy) {
158
136
  await self.setup();
@@ -204,11 +182,26 @@ Main.prototype.setup = async function () {
204
182
 
205
183
  log(chalk.green(`\n---- RUNNING SETUP v${self.default.version} ----`));
206
184
 
185
+ // Load environment variables from .env file
186
+ const envPath = `${self.firebaseProjectPath}/functions/.env`;
187
+ if (jetpack.exists(envPath)) {
188
+ require('dotenv').config({ path: envPath });
189
+ }
190
+
191
+ // Parse runtime config from .env
192
+ self.runtimeConfigJSON = {};
193
+ if (process.env.RUNTIME_CONFIG) {
194
+ try {
195
+ self.runtimeConfigJSON = JSON5.parse(process.env.RUNTIME_CONFIG);
196
+ } catch (e) {
197
+ // Will be caught in test
198
+ }
199
+ }
200
+
207
201
  // Load files
208
202
  self.package = loadJSON(`${self.firebaseProjectPath}/functions/package.json`);
209
203
  self.firebaseJSON = loadJSON(`${self.firebaseProjectPath}/firebase.json`);
210
204
  self.firebaseRC = loadJSON(`${self.firebaseProjectPath}/.firebaserc`);
211
- self.runtimeConfigJSON = loadJSON(`${self.firebaseProjectPath}/functions/.runtimeconfig.json`);
212
205
  self.remoteconfigJSON = loadJSON(`${self.firebaseProjectPath}/functions/remoteconfig.template.json`);
213
206
  self.projectPackage = loadJSON(`${self.firebaseProjectPath}/package.json`);
214
207
  self.bemConfigJSON = loadJSON(`${self.firebaseProjectPath}/functions/backend-manager-config.json`);
@@ -405,13 +398,29 @@ Main.prototype.setup = async function () {
405
398
  return self.package.scripts.dist
406
399
  }, fix_distScript);
407
400
 
408
- // Test: Is the project using a proper .runtimeconfig
409
- await self.test('using proper .runtimeconfig', async function () {
401
+ // Test: Is the project using a proper .env with RUNTIME_CONFIG
402
+ await self.test('using proper .env with RUNTIME_CONFIG', async function () {
403
+ // Check if .env file exists
404
+ const envPath = `${self.firebaseProjectPath}/functions/.env`;
405
+ if (!jetpack.exists(envPath)) {
406
+ return false;
407
+ }
408
+
409
+ // Check if RUNTIME_CONFIG exists in process.env
410
+ if (!process.env.RUNTIME_CONFIG) {
411
+ return false;
412
+ }
413
+
414
+ // Check if runtimeConfigJSON was parsed successfully
415
+ if (!hasContent(self.runtimeConfigJSON)) {
416
+ return false;
417
+ }
418
+
410
419
  // Set pass
411
420
  let pass = true;
412
421
 
413
422
  // Loop through all the keys in the template
414
- powertools.getKeys(runtimeconfigTemplate).forEach((key) => {
423
+ powertools.getKeys(envRuntimeTemplate).forEach((key) => {
415
424
  const userValue = _.get(self.runtimeConfigJSON, key, undefined);
416
425
 
417
426
  // If the user value is undefined, then we need to set pass to false
@@ -422,7 +431,7 @@ Main.prototype.setup = async function () {
422
431
 
423
432
  // Return result
424
433
  return pass;
425
- }, fix_runtimeConfig);
434
+ }, fix_envRuntimeConfig);
426
435
 
427
436
  // Test: Is the project using a proper backend-manager-config.json
428
437
  await self.test('using proper backend-manager-config.json', async function () {
@@ -754,14 +763,14 @@ function bemPackageVersionWarning(package, current, latest) {
754
763
  }
755
764
  }
756
765
 
757
- async function fix_runtimeConfig(self) {
766
+ async function fix_envRuntimeConfig(self) {
758
767
  return new Promise(function(resolve, reject) {
759
768
  // Log
760
769
  log(NOFIX_TEXT);
761
- log(chalk.red(`You need to run ${chalk.bold(`npx bm config:set`)} for each of these keys:`));
770
+ log(chalk.red(`You need to manually edit ${chalk.bold(`functions/.env`)} and ensure RUNTIME_CONFIG has these keys:`));
762
771
 
763
772
  // Log what keys are missing
764
- powertools.getKeys(runtimeconfigTemplate).forEach((key) => {
773
+ powertools.getKeys(envRuntimeTemplate).forEach((key) => {
765
774
  const userValue = _.get(self.runtimeConfigJSON, key, undefined);
766
775
 
767
776
  if (typeof userValue === 'undefined') {
@@ -1010,7 +1019,7 @@ function fix_hostingRewrites(self) {
1010
1019
 
1011
1020
  // Add to top
1012
1021
  hosting.rewrites.unshift({
1013
- source: '{/backend-manager}',
1022
+ source: '/backend-manager',
1014
1023
  function: 'bm_api',
1015
1024
  });
1016
1025
 
@@ -1287,179 +1296,7 @@ async function cmd_indexesGet(self, filePath, log) {
1287
1296
  });
1288
1297
  }
1289
1298
 
1290
- async function cmd_configGet(self, filePath) {
1291
- return new Promise(function(resolve, reject) {
1292
- const finalPath = `${self.firebaseProjectPath}/${filePath || 'functions/.runtimeconfig.json'}`;
1293
-
1294
- const max = 10;
1295
- let retries = 0;
1296
-
1297
- async function _attempt() {
1298
- try {
1299
- const output = await powertools.execute(`firebase functions:config:get > ${finalPath}`, { log: true });
1300
-
1301
- // Log success message
1302
- console.log(chalk.green(`Saving config to: ${finalPath}`));
1303
-
1304
- // Resolve with the required config
1305
- resolve(require(finalPath));
1306
- } catch (error) {
1307
- console.error(chalk.red(`Failed to get config: ${error}`));
1308
-
1309
- // Check if retries are exhausted
1310
- if (retries++ >= max) {
1311
- return reject(error);
1312
- }
1313
-
1314
- // Retry logic with delay
1315
- const delay = 2500 * retries;
1316
- console.error(chalk.yellow(`Retrying config:get ${retries}/${max} in ${delay}ms...`));
1317
- setTimeout(_attempt, delay);
1318
- }
1319
- }
1320
-
1321
- // Start the attempts
1322
- _attempt();
1323
- });
1324
- }
1325
-
1326
- async function cmd_configSet(self, newPath, newValue) {
1327
- return new Promise(async function(resolve, reject) {
1328
- // console.log(self.options);
1329
- // console.log(self.argv);
1330
- newPath = newPath || await inquirer.prompt([
1331
- {
1332
- type: 'input',
1333
- name: 'path',
1334
- default: 'service.key'
1335
- }
1336
- ]).then(answers => answers.path);
1337
-
1338
- let object = null;
1339
-
1340
- try {
1341
- object = JSON5.parse(newPath);
1342
- } catch (e) {
1343
- }
1344
-
1345
- const isObject = object && typeof object === 'object';
1346
-
1347
- // If it's a string, ensure some things
1348
- if (!isObject) {
1349
- // Validate path
1350
- if (!newPath.includes('.')) {
1351
- console.log(chalk.red(`Path needs 2 parts (one.two): ${newPath}`));
1352
- return reject();
1353
- }
1354
-
1355
- // Make sure it's only letters, numbers, periods, and underscores
1356
- if (newPath.match(/[^a-zA-Z0-9._]/)) {
1357
- console.log(chalk.red(`Path contains invalid characters: ${newPath}`));
1358
- return reject();
1359
- }
1360
- }
1361
-
1362
- try {
1363
- if (isObject) {
1364
- const keyify = (obj, prefix = '') =>
1365
- Object.keys(obj).reduce((res, el) => {
1366
- if( Array.isArray(obj[el]) ) {
1367
- return res;
1368
- } else if( typeof obj[el] === 'object' && obj[el] !== null ) {
1369
- return [...res, ...keyify(obj[el], prefix + el + '.')];
1370
- }
1371
- return [...res, prefix + el];
1372
- }, []);
1373
- const pathArray = keyify(object);
1374
- for (var i = 0; i < pathArray.length; i++) {
1375
- const pathName = pathArray[i];
1376
- const pathValue = _.get(object, pathName);
1377
- // console.log(chalk.blue(`Setting object: ${chalk.bold(pathName)} = ${chalk.bold(pathValue)}`));
1378
- console.log(chalk.blue(`Setting object: ${chalk.bold(pathName)}`));
1379
- await cmd_configSet(self, pathName, pathValue)
1380
- .catch(e => {
1381
- log(chalk.red(`Failed to save object path: ${e}`));
1382
- })
1383
- }
1384
- return resolve();
1385
- }
1386
- } catch (e) {
1387
- log(chalk.red(`Failed to save object: ${e}`));
1388
- return reject(e)
1389
- }
1390
-
1391
- newValue = newValue || await inquirer.prompt([
1392
- {
1393
- type: 'input',
1394
- name: 'value',
1395
- default: '123-abc'
1396
- }
1397
- ]).then(answers => answers.value)
1398
-
1399
- let isInvalid = false;
1400
- if (newPath !== newPath.toLowerCase()) {
1401
- isInvalid = true;
1402
- newPath = newPath.replace(/([A-Z])/g, '_$1').trim().toLowerCase();
1403
- }
1404
-
1405
- log(chalk.yellow(`Saving to ${chalk.bold(newPath)}...`));
1406
-
1407
- await powertools.execute(`firebase functions:config:set ${newPath}="${newValue}"`, { log: true })
1408
- .then((output) => {
1409
- // Check if it was invalid
1410
- if (isInvalid) {
1411
- log(chalk.red(`!!! Your path contained an invalid uppercase character`));
1412
- log(chalk.red(`!!! It was set to: ${chalk.bold(newPath)}`));
1413
- } else {
1414
- log(chalk.green(`Successfully saved to ${chalk.bold(newPath)}`));
1415
- }
1416
-
1417
- // Resolve the promise
1418
- resolve();
1419
- })
1420
- .catch((e) => {
1421
- log(chalk.red(`Failed to save ${chalk.bold(newPath)}: ${e}`));
1422
-
1423
- // Reject the promise with the error
1424
- reject(e);
1425
- });
1426
- });
1427
- }
1428
-
1429
- async function cmd_configUnset(self) {
1430
- return new Promise(async function(resolve, reject) {
1431
- // console.log(self.options);
1432
- // console.log(self.argv);
1433
- await inquirer
1434
- .prompt([
1435
- /* Pass your questions in here */
1436
- {
1437
- type: 'input',
1438
- name: 'path',
1439
- default: 'service.key'
1440
- }
1441
- ])
1442
- .then(async (answers) => {
1443
- // Use user feedback for... whatever!!
1444
- // console.log('answer', answers);
1445
- log(chalk.yellow(`Deleting ${chalk.bold(answers.path)}...`));
1446
-
1447
- await powertools.execute(`firebase functions:config:unset ${answers.path}`, { log: true })
1448
- .then((output) => {
1449
- log(chalk.green(`Successfully deleted ${chalk.bold(answers.path)}`));
1450
1299
 
1451
- // Resolve the promise
1452
- resolve();
1453
- })
1454
- .catch((e) => {
1455
- log(chalk.red(`Failed to delete ${chalk.bold(answers.path)}: ${e}`));
1456
-
1457
- // Reject the promise with the error
1458
- reject(e);
1459
- });
1460
- });
1461
- });
1462
- }
1463
1300
 
1464
1301
  async function cmd_iamImportExport(self) {
1465
1302
  return new Promise(async function(resolve, reject) {
@@ -10,5 +10,4 @@ module.exports = {
10
10
  TestCommand: require('./test'),
11
11
  CleanCommand: require('./clean'),
12
12
  IndexesCommand: require('./indexes'),
13
- ConfigCommand: require('./config'),
14
13
  };
@@ -14,7 +14,7 @@ class InstallCommand extends BaseCommand {
14
14
 
15
15
  async installLocal() {
16
16
  await this.uninstallPkg('backend-manager');
17
- await this.installPkg(`npm install ${os.homedir()}/Developer/Repositories/ITW-Creative-Works/backend-manager --save-dev`);
17
+ await this.installPkg(`npm install ${os.homedir()}/Developer/Repositories/ITW-Creative-Works/backend-manager`);
18
18
  }
19
19
 
20
20
  async installLive() {
@@ -5,12 +5,7 @@ const _ = require('lodash');
5
5
  class ServeCommand extends BaseCommand {
6
6
  async execute() {
7
7
  const self = this.main;
8
-
9
- // Get config
10
- const ConfigCommand = require('./config');
11
- const configCmd = new ConfigCommand(self);
12
- await configCmd.get();
13
-
8
+
14
9
  // Run setup
15
10
  const SetupCommand = require('./setup');
16
11
  const setupCmd = new SetupCommand(self);
@@ -0,0 +1,23 @@
1
+ const BaseTest = require('./base-test');
2
+ const jetpack = require('fs-jetpack');
3
+ const path = require('path');
4
+
5
+ class BackendManagerTestsFileTest extends BaseTest {
6
+ getName() {
7
+ return 'update backend-manager-tests.js';
8
+ }
9
+
10
+ async run() {
11
+ const self = this.self;
12
+ jetpack.write(`${self.firebaseProjectPath}/test/backend-manager-tests.js`,
13
+ (jetpack.read(path.resolve(`${__dirname}/../../../../templates/backend-manager-tests.js`)))
14
+ );
15
+ return true;
16
+ }
17
+
18
+ async fix() {
19
+ throw new Error('No automatic fix available for this test');
20
+ }
21
+ }
22
+
23
+ module.exports = BackendManagerTestsFileTest;
@@ -0,0 +1,79 @@
1
+ const BaseTest = require('./base-test');
2
+ const chalk = require('chalk');
3
+ const wonderfulVersion = require('wonderful-version');
4
+ const powertools = require('node-powertools');
5
+ const Npm = require('npm-api');
6
+ const helpers = require('./helpers');
7
+
8
+ class BackendManagerTest extends BaseTest {
9
+ getName() {
10
+ return 'using updated backend-manager';
11
+ }
12
+
13
+ async run() {
14
+ const pkg = 'backend-manager';
15
+ const latest = await this.getPkgVersion(pkg);
16
+ const mine = this.context.package.dependencies[pkg];
17
+
18
+ // Get level difference
19
+ const levelDifference = wonderfulVersion.levelDifference(latest, mine);
20
+
21
+ // Log if major version mismatch
22
+ if (!helpers.isLocal(mine) && levelDifference === 'major') {
23
+ console.log(chalk.red(`Version ${chalk.bold(latest)} of ${chalk.bold(pkg)} available but you must install this manually because it is a major update.`));
24
+ }
25
+
26
+ // Ensure the version is up to date
27
+ return helpers.isLocal(mine) || wonderfulVersion.is(mine, '>=', latest) || levelDifference === 'major';
28
+ }
29
+
30
+ async fix() {
31
+ await this.installPkg('backend-manager');
32
+
33
+ console.log(chalk.green(`Process has exited since a new version of backend-manager was installed. Run ${chalk.bold('npx bm setup')} again.`));
34
+ process.exit(0);
35
+ }
36
+
37
+ async getPkgVersion(packageName) {
38
+ const npm = new Npm();
39
+
40
+ return new Promise((resolve, reject) => {
41
+ npm.repo(packageName)
42
+ .package()
43
+ .then(function(pkg) {
44
+ resolve(pkg.version);
45
+ }, function(err) {
46
+ resolve('0.0.0');
47
+ });
48
+ });
49
+ }
50
+
51
+ async installPkg(name, version, type) {
52
+ let v;
53
+ let t;
54
+ if (name.indexOf('file:') > -1) {
55
+ v = '';
56
+ } else if (!version) {
57
+ v = '@latest';
58
+ } else {
59
+ v = version;
60
+ }
61
+
62
+ if (!type) {
63
+ t = '';
64
+ } else if (type === 'dev' || type === '--save-dev') {
65
+ t = ' --save-dev';
66
+ }
67
+
68
+ // Build the command
69
+ const command = `npm i ${name}${v}${t}`;
70
+
71
+ // Log
72
+ console.log('Running ', command);
73
+
74
+ // Execute
75
+ await powertools.execute(command, { log: true });
76
+ }
77
+ }
78
+
79
+ module.exports = BackendManagerTest;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Base class for all setup tests
3
+ * Each test should extend this class and implement the `run()` method
4
+ */
5
+ class BaseTest {
6
+ constructor(context) {
7
+ this.context = context;
8
+ this.self = context.main;
9
+ }
10
+
11
+ /**
12
+ * Override this method in each test
13
+ * @returns {Promise<boolean>} True if test passes, false if it fails
14
+ */
15
+ async run() {
16
+ throw new Error('Test must implement run() method');
17
+ }
18
+
19
+ /**
20
+ * Override this method to provide a fix for failed tests
21
+ * @returns {Promise<void>}
22
+ */
23
+ async fix() {
24
+ throw new Error('No automatic fix available for this test');
25
+ }
26
+
27
+ /**
28
+ * Get the test name (used for logging)
29
+ * @returns {string}
30
+ */
31
+ getName() {
32
+ return this.constructor.name.replace(/Test$/, '').replace(/([A-Z])/g, ' $1').trim().toLowerCase();
33
+ }
34
+ }
35
+
36
+ module.exports = BaseTest;
@@ -0,0 +1,25 @@
1
+ const BaseTest = require('./base-test');
2
+ const chalk = require('chalk');
3
+
4
+ class BemConfigIdTest extends BaseTest {
5
+ getName() {
6
+ return 'has correct ID in backend-manager-config.json';
7
+ }
8
+
9
+ async run() {
10
+ // Check if the project name matches the projectId
11
+ if (this.self.projectId !== this.self.bemConfigJSON?.firebaseConfig?.projectId) {
12
+ console.error(chalk.red('Mismatch between project name and firebaseConfig.projectId in backend-manager-config.json'));
13
+ return false;
14
+ }
15
+
16
+ // Return pass
17
+ return true;
18
+ }
19
+
20
+ async fix() {
21
+ throw new Error('No automatic fix available for this test');
22
+ }
23
+ }
24
+
25
+ module.exports = BemConfigIdTest;
@@ -0,0 +1,59 @@
1
+ const BaseTest = require('./base-test');
2
+ const jetpack = require('fs-jetpack');
3
+ const chalk = require('chalk');
4
+ const powertools = require('node-powertools');
5
+ const _ = require('lodash');
6
+ const helpers = require('./helpers');
7
+ const path = require('path');
8
+
9
+ // Load template
10
+ const bemConfigTemplate = helpers.loadJSON(path.resolve(__dirname, '../../../templates/backend-manager-config.json'));
11
+
12
+ class BemConfigTest extends BaseTest {
13
+ getName() {
14
+ return 'using proper backend-manager-config.json';
15
+ }
16
+
17
+ async run() {
18
+ // Set pass
19
+ let pass = true;
20
+
21
+ // Loop through all the keys in the template
22
+ powertools.getKeys(bemConfigTemplate).forEach((key) => {
23
+ const userValue = _.get(this.self.bemConfigJSON, key, undefined);
24
+
25
+ // If the user value is undefined, then we need to set pass to false
26
+ if (typeof userValue === 'undefined') {
27
+ pass = false;
28
+ }
29
+ });
30
+
31
+ // Return result
32
+ return pass;
33
+ }
34
+
35
+ async fix() {
36
+ console.log(chalk.red(`There is no automatic fix for this check.`));
37
+ console.log(chalk.red(`You need to open backend-manager-config.json and set each of these keys:`));
38
+
39
+ // Write if it doesn't exist
40
+ if (!this.context.hasContent(this.self.bemConfigJSON)) {
41
+ jetpack.write(`${this.self.firebaseProjectPath}/functions/backend-manager-config.json`, {});
42
+ }
43
+
44
+ // Log what keys are missing
45
+ powertools.getKeys(bemConfigTemplate).forEach((key) => {
46
+ const userValue = _.get(this.self.bemConfigJSON, key, undefined);
47
+
48
+ if (typeof userValue === 'undefined') {
49
+ console.log(chalk.red.bold(`${key}`));
50
+ } else {
51
+ console.log(chalk.red(`${key} (${userValue})`));
52
+ }
53
+ });
54
+
55
+ throw new Error('Missing required backend-manager-config.json keys');
56
+ }
57
+ }
58
+
59
+ module.exports = BemConfigTest;