@suitegeezus/suitecloud-stacker 25.2.128 → 25.2.129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,62 +1,12 @@
1
1
  Contains helpers for SDF (Node CLI edition)
2
2
 
3
3
  # Features
4
- - Collection of pre-configured hooks (commands) to manage ACS problems withing customer account
4
+ - Collection of pre-configured hooks (commands) to manage customer accounts
5
5
  - Collection of pre-configured hooks to manage the outer project
6
6
  - Pre-defined handlers that you can pick and choose from for a given "command"
7
7
  - Types for SDF commands
8
8
  - command line enhancements
9
9
 
10
- Missing: Collection of hooks to manage an entire instance -- Cuz we (ACS) don't do this but if you have your own instance / demo account then feel free to create your own suitecloud.config file and use the stackable structure. Note that most customers don't really do this either -- they often have outside devs who contribute at least sometimes
11
-
12
- # Demos
13
-
14
- ## `account:setup:ci` : Token Auth Memory
15
- Setup token auth for an account from existing token auths that you have with detection for conflicts and valid naming
16
- ![example](./img/accountsetupci.example1.gif)
17
-
18
- ## `project:create`: New Account
19
- Create project to work on a case
20
- ![example](./img/projectcreate.example1.gif)
21
-
22
- ## `project:create`: New Case on Account
23
- ![example](./img/projectcreate.example2.gif)
24
-
25
- ## `file:upload`: Globbing that filters out irrelevant files
26
- ![example](./img/fileupload.example1.gif)
27
-
28
- ## `file:upload`: Upload all files from a deploy.xml file
29
- ![example](./img/fileupload.example3.gif)
30
-
31
- ## `file:upload`: Compile and / or run tests on files you are uploading
32
- ![example](./img/fileupload.example4.gif)
33
-
34
- ## `file:import`: Compare your current file with what is in the account
35
- ![example](./img/fileimport.example3.gif)
36
-
37
- ## `file:import`: Import files that match a glob pattern
38
- ![example](./img/fileimport.example2.gif)
39
-
40
- ## `file:import`: Import all files from a deploy.xml file
41
- ```shell
42
- cd /SuiteScripts/ACS/casefolder
43
- suitecloud file:import --paths ./deploy/deploy.xml
44
- ```
45
-
46
- ## `object:import`: import objects that match a glob pattern
47
- ![example](./img/objectimport.example1.gif)
48
-
49
- ## `object:import`: import all objects that are listed in a chosen deploy.xml file
50
- ![example](./img/objectimport.example2.gif)
51
-
52
- ## `project:deploy`: Choose which project definition to deploy
53
-
54
- ## `object:import`: Import Objects will organize them into folders by type automatically
55
- ![example](./img/objectOrganization.png)
56
-
57
- ## Compile and test typescript as you write
58
- ![example](./img/nodemon.example1.gif);
59
-
60
10
  # How SDF works in this project
61
11
 
62
12
  SDF by default has a certain structure that makes the following (annoying?) assumptions:
@@ -89,20 +39,10 @@ npm install -g nodemon
89
39
  4. Optional: Be able to sync repositories with ALM via OCNA
90
40
  - These are entitlements that will help you stay on the latest version of this repo and use customer repos
91
41
  - If you can't then you can still use this for your testdrive accounts -- just ask me how
92
- 5. Optional: define `oraclegit` in your ssh **config** (highly recommended)
93
- ```txt
94
- HOST oraclegit
95
- User gerald.gillespie@fullscript.com
96
- HostName alm.oraclecorp.com
97
- TCPKeepAlive yes
98
- AddKeysToAgent yes
99
- UseKeyChain yes
100
- IdentityFile ~/.ssh/ed25519ggoracle2
101
- ```
102
- 6. Optional: Create an environment variable for DIFF_TOOL
42
+ 5Optional: Create an environment variable for SUITECLOUD_DIFF_TOOL
103
43
  ```shell
104
- #➜ nsaccounts git:(main) ✗ set | grep 'DIFF'
105
- export DIFF_TOOL='/Applications/./IntelliJ\ IDEA.app/Contents/MacOS/idea'
44
+ #➜ set | grep 'DIFF'
45
+ export SUITECLOUD_DIFF_TOOL='/Applications/./IntelliJ\ IDEA.app/Contents/MacOS/idea'
106
46
  ```
107
47
 
108
48
  ### NetSuite pre-requisites
@@ -112,13 +52,10 @@ You will need to have:
112
52
  - SuiteScript, etc enabled
113
53
 
114
54
  ## Install on Mac / Linux / Windows
115
- 1. clone the repo (not the weird extra slash `:/` ALM needs)
116
- ```shell
117
- git clone oraclegit:/nscs_suitecloud-stacker_114170/suitecloud-stacker-root.git
118
- ```
119
- 2. Run npm install
55
+
56
+ Run npm install
120
57
  ```shell
121
- npm install
58
+ npm install @suitegeezus/suitecloud-stacker
122
59
  ```
123
60
 
124
61
  # Usage: Hello World Example
@@ -128,29 +65,8 @@ npm install
128
65
 
129
66
 
130
67
 
131
- ## Pre-Defined Stacks for the Common scenario
132
- - Collection of hooks meant for ACP subprojects (e.g. a specific case/ project)
133
- ```js
134
- const safeCommands = require('suitecloud-stacker/safeCommands.js');
135
- module.exports = {
136
- ...projectJson,
137
- commands: {...safeCommands.caseCommands},
138
- },
139
- {debug: false, block: false},
140
- module
141
-
142
- ```
143
-
144
- ### Spin up a new SDF project for a case
145
- You can use this command to create your own case
146
- ```bash
147
- cd ~/code/projectRoot
148
- suitecloud project:create
149
- # follow-the prompts
150
- ```
151
-
152
68
  ## Stackable / chaining structure for SDF command hooks
153
- This means your suitecloud.config.js can specify or replace commands something like this:
69
+ This means your sdf.config.js can specify or replace commands something like this:
154
70
 
155
71
  ```js
156
72
  const commands = {
@@ -175,33 +91,29 @@ Also, don't bother with the IDE plugins. not only do they have their own limitat
175
91
 
176
92
  This has the following usage
177
93
 
178
- ### `--authid`
179
- supports a format like this `2452352:wut` where the value before the `:` is the authid and the value after the colon is the folder name.
180
-
181
- if you only specify the first part without any colon then the same value is used for both. i.e. `2452352` is equivalent to `2452352:2452352`
182
-
183
- you can create this anywhere. Wherever you create it you should invoke it from there.
184
- e.g.
185
- ```bash
186
- cd FileCabinet/SuiteScripts/folder
187
- suitecloud
188
- ```
189
94
 
190
95
  ## working directory
191
- native `suitecloud` must be called from a directory that has a `suitecloud.config.js` file.
96
+ `sdf` must be called from a directory that has a `sdf.config.js` file.
192
97
 
193
- As such if you setup a project using native command and run it then it will work because that process will create a `suitecloud.config.js`.
98
+ As such if you setup a project using native command and run it then it will work because that process will create a `sdf.config.js`.
194
99
 
195
- It is recommended that you set your `suitecloud.config.js` file to be the following:
100
+ It is recommended that you set your `sdf.config.js` file to be the following:
196
101
 
197
102
  ```js
198
- const sdfAcs = require('./sdf/safeCommands');
103
+ const safeCommands = require('@suitegeezus/suitecloud-stacker/safeCommands');
199
104
 
200
105
  // create the commands that you want
201
106
  const commands = {};
202
107
 
203
- // pass them through the sdfAcs layer
204
- module.exports = sdfAcs.makeSuiteCloudConfig({commands});
108
+ // pass them through the stacker
109
+ safeCommands.makeSafeExports(
110
+ {
111
+ defaultProjectFolder: 'src',
112
+ commands: stackerCommands,
113
+ },
114
+ {debug: false, block: false, autoCreateProjectJson: false,},
115
+ module
116
+ );
205
117
  ```
206
118
 
207
119
  # Errors
@@ -223,20 +135,18 @@ This should also be true of objects.
223
135
 
224
136
  We can use SDF for both code (suitescripts) and objects.
225
137
 
226
- Regardless of how many sandboxen the customer has...
227
- Regardless of what weird branching strategies they might have... or don't have.
228
- Regardless of whether we are writing code for the customer's use or writing code the helps us create the solution for the customer...
229
- Regardless of how many cases are concurrently being worked on for a customer...
230
- When we write code or create objects it is hella convenient and powerful to have both under source control and to use merging strategies that support the work we are doing for the customer.
138
+ Regardless of how many sandboxen you have...
139
+ Regardless of what weird branching strategies you might have... or don't have.
140
+ When we write code or create objects it is hella convenient and powerful to have both under source control and to use merging strategies that support the work we are doing...
231
141
 
232
- In all of the above you can have a real "customer" or it could simply be a concept "customer"
142
+ In all of the above you can be end-user or a partner
233
143
 
234
- So we have one folder for a customer.
144
+ So we have one folder for an account.
235
145
 
236
- If a customer has multiple sandboxes then we probably have multiple branches but it doesn't matter. Our "master" branch is not the customer's master branch. It is our statement that this code is production worthy and ready to be vetted.
146
+ If you have sandboxes then we probably have multiple branches, but it doesn't matter. A "master" branch is our statement that this code is production worthy and ready to be vetted.
237
147
 
238
148
  ## But I don't want my customer to be a sub-folder in this project!
239
149
  You don't have to.
240
- There is nothing stopping you in git from (in fact it supports) having a subfolder be for a different repository. That repository can have different users. ESS member Bob can have a different set of accounts from Raffi and they can share any overlapping number of them. But when they want to collaborate on something in source control related to an account they can `pull` from a common view and still work independantly on separate branches and yet have full visibility into the other work.
150
+ There is nothing stopping you in git from (in fact it supports) having a subfolder be for a different repository.
241
151
 
242
152
  Further, the ability to `pull` from source control does not mean that you will be able to `file:deploy` to the customer account. You still need to have an authenticated token to do that. And that probably means you'll need to have login privileges to the customer's account (production, sandbox or whatever).
@@ -75,6 +75,7 @@ type BetterAccountList = {
75
75
  */
76
76
  export declare const fetchAccountList: () => Promise<BetterAccountList[]>;
77
77
  export declare const betterList: () => SdfCommand;
78
+ export declare const getEnvironment: (authid: string) => Promise<"PRODUCTION" | "SANDBOX" | "RELEASE_PREVIEW" | "TESTDRIVE" | "UNKNOWN">;
78
79
  /**
79
80
  * @description - Prompts for account choice. This provides both verification and literal choice. If the choice does not match the current
80
81
  * context then an error is forced -- this is because SDF does not recognize a change in the account after launching.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateList = exports.promptForAccountChoice = exports.betterList = exports.fetchAccountList = exports.saveList = void 0;
3
+ exports.validateList = exports.promptForAccountChoice = exports.getEnvironment = exports.betterList = exports.fetchAccountList = exports.saveList = void 0;
4
4
  const pathHelpers = require("../lib/pathHelpers");
5
5
  const promptHelpers_1 = require("../lib/promptHelpers");
6
6
  const projectJsonHelpers = require("../lib/projectJsonHelpers");
@@ -102,6 +102,26 @@ const betterList = () => {
102
102
  };
103
103
  };
104
104
  exports.betterList = betterList;
105
+ const getEnvironment = async (authid) => {
106
+ const list = await (0, exports.fetchAccountList)();
107
+ const match = list.find((item) => (item.authid === authid));
108
+ const companyId = match?.accountInfo?.companyId;
109
+ switch (true) {
110
+ case typeof companyId === 'undefined':
111
+ return 'UNKNOWN';
112
+ case /^td/.test(companyId):
113
+ return 'TESTDRIVE';
114
+ case /^\d+$/.test(companyId):
115
+ return 'PRODUCTION';
116
+ case /_SB\d+$/.test(companyId):
117
+ return 'SANDBOX';
118
+ case /_RP\d*$/.test(companyId):
119
+ return 'RELEASE_PREVIEW';
120
+ default:
121
+ return 'UNKNOWN';
122
+ }
123
+ };
124
+ exports.getEnvironment = getEnvironment;
105
125
  /**
106
126
  * @description - Prompts for account choice. This provides both verification and literal choice. If the choice does not match the current
107
127
  * context then an error is forced -- this is because SDF does not recognize a change in the account after launching.
@@ -112,9 +132,9 @@ const promptForAccountChoice = () => {
112
132
  return {
113
133
  _origin: origin,
114
134
  beforeExecuting: async (options) => {
135
+ process.stdout.write((0, promptHelpers_1.goColor)('HIGHLIGHT', '\n', options));
115
136
  if (options?.arguments?.runhooks === 'quiet')
116
137
  return options;
117
- process.stdout.write((0, promptHelpers_1.goColor)('HIGHLIGHT', '\n', options));
118
138
  if (!Reflect.has({ ...options?.arguments }, 'authid')) {
119
139
  throw onErrorHelper.makeError(origin, new Error(`${options.command} is missing authid. Perhaps you should not stack ${origin} here`));
120
140
  }
@@ -82,14 +82,12 @@ const downloadForDiff = () => {
82
82
  const tempLocations = [];
83
83
  const auths = [options.authId, secondAuth].filter(Boolean);
84
84
  // the temp import can happen all at once
85
- await auths.reduce(async (linkedPromises, auth) => {
86
- await linkedPromises;
85
+ await Promise.all(auths.map(async (auth) => {
87
86
  const rootFolder = auth;
88
87
  tempLocations.push(await (0, pathHelpers_1.makeTempDirForSdf)({ root: rootFolder }));
89
88
  const childArgs = [
90
89
  '--paths', ...candidates,
91
90
  '--calledfromcomparefiles',
92
- '--runhooks none',
93
91
  '--excludeproperties',
94
92
  '--project', rootFolder,
95
93
  // run with noconfig to disconnect from existing project
@@ -113,8 +111,7 @@ const downloadForDiff = () => {
113
111
  if (errorCode !== 0)
114
112
  process.exit(errorCode);
115
113
  // the file location is the
116
- return linkedPromises;
117
- }, Promise.resolve(true));
114
+ }));
118
115
  let diffTool = process.env.SUITECLOUD_DIFF_TOOL;
119
116
  {
120
117
  // console.log(process.env);
@@ -133,8 +130,10 @@ const downloadForDiff = () => {
133
130
  return (0, pathHelpers_1.joinPaths)(location, 'FileCabinet', file);
134
131
  }).filter(Boolean).map((f) => `"${f}"`);
135
132
  });
136
- await diffCombos.reduce(async (linked, combo) => {
137
- await linked;
133
+ await diffCombos.reduce(async (linked, combo, currentIndex) => {
134
+ const keepGoing = await linked;
135
+ if (!keepGoing)
136
+ return keepGoing;
138
137
  const diffChildProc = cp.spawn(diffTool, ['diff', ...combo], {
139
138
  detached: true, // Allows the child to run independently
140
139
  stdio: 'inherit',
@@ -155,25 +154,29 @@ const downloadForDiff = () => {
155
154
  await diffPromise;
156
155
  diffChildProc.unref();
157
156
  const toGet = combo.pop();
157
+ // do not prompt when in quiet mode
158
+ const choices = {
159
+ 'Y': 'Yes',
160
+ 'N': 'No - skip this one',
161
+ 'OOPS': `No, stop diffing and refuse all remaining ${diffCombos.length - currentIndex} choices`
162
+ };
163
+ let answer = choices['N'];
158
164
  if (toGet) {
159
- const fixedToGet = toGet.replace(/^.*(.\bSuiteScripts.*)$/, '$1');
160
- // do not prompt when in quiet mode
161
- const choices = { 'Y': 'Yes', 'N': 'No' };
162
- let answer = choices['N'];
165
+ const fixedToGet = toGet.replace(/^.*(.\bSuiteScripts.*?)"*$/, '"$1"');
163
166
  options.arguments.paths = [];
164
167
  if (!isQuiet) {
165
168
  answer = await (0, promptHelpers_1.promptChoices)({
166
169
  choices,
167
170
  displayChoices: choices,
168
- premessage: fixedToGet,
169
- question: 'Would you like to continue with the import?',
171
+ premessage: `${currentIndex} of ${diffCombos.length}\nInclude: ${fixedToGet}?`,
172
+ question: 'Would you like to include this file ?',
170
173
  default: choices['N'],
171
174
  });
172
175
  }
173
176
  if (answer === choices['Y'])
174
177
  options.arguments.paths.push(fixedToGet);
175
178
  }
176
- return linked;
179
+ return answer !== choices['OOPS'];
177
180
  }, Promise.resolve(true));
178
181
  }
179
182
  catch (e) {
@@ -49,4 +49,6 @@ export declare const compileFilesPrompt: () => SdfCommand;
49
49
  * @description - calls jest for the files involved
50
50
  * ⚠️ - make sure paths are fixed first
51
51
  */
52
- export declare const testFilesPrompt: () => SdfCommand;
52
+ export declare const testFilesPrompt: (opts?: {
53
+ nameOfOptionsFlag: "runjest" | string;
54
+ }) => SdfCommand;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.testFilesPrompt = exports.compileFilesPrompt = exports.fixFileUploadPaths = exports.confirmFileList = exports.getFilesFromDeployXml = exports.promptForPath = void 0;
4
4
  const nodePath = require("node:path");
5
+ const accountManageauth_1 = require("./accountManageauth");
5
6
  const errorHelper = require("../lib/onErrorHelper");
6
7
  const compileHelper = require("../lib/compileHelper");
7
8
  const promptHelpers = require("../lib/promptHelpers");
@@ -285,7 +286,7 @@ exports.compileFilesPrompt = compileFilesPrompt;
285
286
  * @description - calls jest for the files involved
286
287
  * ⚠️ - make sure paths are fixed first
287
288
  */
288
- const testFilesPrompt = () => {
289
+ const testFilesPrompt = (opts) => {
289
290
  const origin = 'testFilesPrompt';
290
291
  let command;
291
292
  return {
@@ -293,48 +294,68 @@ const testFilesPrompt = () => {
293
294
  isStackable: true,
294
295
  beforeExecuting: async (options) => {
295
296
  command = options.command || 'unknown';
296
- const choices = { 'Y': 'Yes', 'N': 'No' };
297
- // remove the "N" choice for prod
298
- if (options._isProd)
299
- delete choices['N'];
300
- let answer;
301
- if (spawnSuitecloud.isChildProcess() || options.arguments.runhooks === 'quiet') {
302
- answer = choices['Y'];
303
- }
304
- else {
305
- answer = await promptHelpers.promptChoices({
306
- choices: choices,
307
- displayChoices: choices,
308
- premessage: options._isProd ? 'This is PROD' : '',
309
- question: `Would you like to run relatedTests?`,
310
- default: choices['Y']
311
- });
312
- }
313
- if (answer === choices['N'])
314
- return options;
315
- // make sure paths are correct for jest
316
- const didTheyPass = await compileHelper.runJestTest(options.arguments.paths.map((p) => {
297
+ const jestFlag = opts?.nameOfOptionsFlag || 'runjest';
298
+ const isQuiet = options.arguments.runhooks === 'quiet';
299
+ const environment = await (0, accountManageauth_1.getEnvironment)(options.arguments.authid);
300
+ const forceYes = (options.arguments.customoptions || []).includes(jestFlag);
301
+ const longTest = !spawnSuitecloud.isChildProcess() && options.arguments.runhooks !== 'quiet';
302
+ const statesToConsider = ['go'];
303
+ const filesToTest = options.arguments.paths.map((p) => {
317
304
  return nodePath.join(options.projectPath.replace(/^(.*)(.\bFileCabinet\b.\bSuiteScripts\b.*)?$/, '$1/FileCabinet'), p.replace(/^.*FileCabinet/, ''));
318
- }), Boolean(!options._isProd))
319
- .then(() => true)
320
- .catch((e) => {
321
- // TODO turn this on
322
- // if (options._isProd) throw errorHelper.makeError(origin,new Error('Tests must exist AND pass for PROD ' + options.command));
323
- return false;
324
305
  });
325
- if (didTheyPass)
306
+ const allGood = await statesToConsider.reduce(async (bailPromise, state) => {
307
+ const bail = await bailPromise;
308
+ const choices = { 'Y': 'Yes', 'N': 'No' };
309
+ // remove the "N" choice for prod
310
+ if (environment === 'PRODUCTION')
311
+ delete choices['N'];
312
+ let answer;
313
+ switch (true) {
314
+ case (spawnSuitecloud.isChildProcess()):
315
+ case forceYes:
316
+ answer = choices['Y'];
317
+ break;
318
+ case !isQuiet:
319
+ answer = await promptHelpers.promptChoices({
320
+ choices: choices,
321
+ displayChoices: choices,
322
+ premessage: `This is ${environment} (${options.arguments.authid})`,
323
+ question: `Would you like to run relatedTests?`,
324
+ default: choices['Y']
325
+ });
326
+ break;
327
+ case environment === 'PRODUCTION':
328
+ answer = choices['Y'];
329
+ break;
330
+ default:
331
+ answer = choices['N'];
332
+ break;
333
+ }
334
+ if (answer === choices['N'])
335
+ return Promise.resolve(true);
336
+ // make sure paths are correct for jest
337
+ const didTheyPass = await compileHelper.runJestTest(filesToTest, environment !== 'PRODUCTION' && !forceYes, bail)
338
+ .then(() => true)
339
+ .catch((e) => {
340
+ if (environment === 'PRODUCTION')
341
+ throw errorHelper.makeError(origin, new Error('Tests must exist AND pass for PROD ' + options.command));
342
+ return false;
343
+ });
344
+ return Promise.resolve(didTheyPass);
345
+ }, Promise.resolve(true));
346
+ if (allGood)
326
347
  return options;
327
348
  if (!spawnSuitecloud.isChildProcess() && options.arguments.runhooks !== 'quiet') {
328
349
  // remove the "N" choice for prod
329
- const choices2 = { 'Y': 'Yes' };
350
+ const choices2 = { 'UPLOAD_ANYWAY': 'Yes' };
330
351
  const answer2 = await promptHelpers.promptChoices({
331
352
  choices: { ...choices2, 'N': 'No' },
332
353
  displayChoices: choices2,
333
- premessage: options._isProd ? 'This is PROD' : '',
334
- question: `Tests failed. Would you like to upload anyway?`,
354
+ premessage: options._isProd ? `This is PROD ${options.authId}` : options.authId,
355
+ question: `Tests failed. Would you like to upload to ${options.authId} anyway? (You must spell it out)`,
335
356
  default: null,
336
357
  });
337
- if (answer2 === 'Yes')
358
+ if (answer2 === 'UPLOAD_ANYWAY')
338
359
  return options;
339
360
  throw errorHelper.makeError(origin, new Error('User Chose to Quit'));
340
361
  }
@@ -40,5 +40,6 @@ export declare const getAllTestFiles: () => Promise<string[]>;
40
40
  * @description - we might be running tests OR we might be testing files
41
41
  * @param {string[]} jsFilesToTest - Get the list of files from a dryrun
42
42
  * @param {boolean} allowMissing - whether to require tests or not
43
+ * @param {boolean} bail - bail after one failure
43
44
  */
44
- export declare const runJestTest: (jsFilesToTest: string[], allowMissing?: boolean) => Promise<number>;
45
+ export declare const runJestTest: (jsFilesToTest: string[], allowMissing?: boolean, bail?: boolean) => Promise<number>;
@@ -142,8 +142,9 @@ exports.getAllTestFiles = getAllTestFiles;
142
142
  * @description - we might be running tests OR we might be testing files
143
143
  * @param {string[]} jsFilesToTest - Get the list of files from a dryrun
144
144
  * @param {boolean} allowMissing - whether to require tests or not
145
+ * @param {boolean} bail - bail after one failure
145
146
  */
146
- const runJestTest = async (jsFilesToTest, allowMissing) => {
147
+ const runJestTest = async (jsFilesToTest, allowMissing, bail) => {
147
148
  const jsOnly = jsFilesToTest.filter((js) => /\.js$/.test(js));
148
149
  if (jsOnly.length === 0)
149
150
  return 0;
@@ -153,21 +154,19 @@ const runJestTest = async (jsFilesToTest, allowMissing) => {
153
154
  // if the list are just files
154
155
  jestArgs.push('--findRelatedTests');
155
156
  jestArgs.push(...jsOnly);
156
- if (allowMissing)
157
- jestArgs.push('--passWithNoTests');
158
- }
159
- else {
160
- if (allowMissing)
161
- jestArgs.push('--passWithNoTests');
162
- // jestArgs.push('./**/*.js');
163
- // the list is test.js files
164
157
  }
158
+ if (allowMissing)
159
+ jestArgs.push('--passWithNoTests');
160
+ // jestArgs.push('./**/*.js');
161
+ // the list is test.js files
162
+ if (bail)
163
+ jestArgs.push('--bail');
165
164
  process.stdout.write(promptHelpers.goColor('WARN', '\nRunning tests', jestArgs, '\n'));
166
165
  const p = process;
167
166
  return new Promise((ok, no) => {
168
167
  const jest = cp.spawn('npx', ['jest',
169
168
  //'--showConfig'
170
- ...jestArgs
169
+ ...jestArgs,
171
170
  ], {
172
171
  cwd: process.cwd(),
173
172
  // combination of getting all the data without tripping up the parent error state
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suitegeezus/suitecloud-stacker",
3
- "version": "25.2.128",
3
+ "version": "25.2.129",
4
4
  "description": "SuiteCloud Stacker",
5
5
  "main": "safeCommands.js",
6
6
  "scripts": {
@@ -4,7 +4,7 @@
4
4
  /**
5
5
  * @file {1}
6
6
  * - only use this once per account
7
- * - this is auto-generated file based on a search of the account. See ESS query runner
7
+ * - this is auto-generated file based on a search of the account.
8
8
  * @author Gerald Gillespie <gerald.gillespie@fullscript.com>
9
9
  */
10
10