@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 +28 -118
- package/commands/accountManageauth.d.ts +1 -0
- package/commands/accountManageauth.js +22 -2
- package/commands/fileImport.js +17 -14
- package/commands/fileUpload.d.ts +3 -1
- package/commands/fileUpload.js +54 -33
- package/lib/compileHelper.d.ts +2 -1
- package/lib/compileHelper.js +9 -10
- package/package.json +1 -1
- package/templates/customizations.projectroot.d.ts +1 -1
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
|
|
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
|
-

|
|
17
|
-
|
|
18
|
-
## `project:create`: New Account
|
|
19
|
-
Create project to work on a case
|
|
20
|
-

|
|
21
|
-
|
|
22
|
-
## `project:create`: New Case on Account
|
|
23
|
-

|
|
24
|
-
|
|
25
|
-
## `file:upload`: Globbing that filters out irrelevant files
|
|
26
|
-

|
|
27
|
-
|
|
28
|
-
## `file:upload`: Upload all files from a deploy.xml file
|
|
29
|
-

|
|
30
|
-
|
|
31
|
-
## `file:upload`: Compile and / or run tests on files you are uploading
|
|
32
|
-

|
|
33
|
-
|
|
34
|
-
## `file:import`: Compare your current file with what is in the account
|
|
35
|
-

|
|
36
|
-
|
|
37
|
-
## `file:import`: Import files that match a glob pattern
|
|
38
|
-

|
|
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
|
-

|
|
48
|
-
|
|
49
|
-
## `object:import`: import all objects that are listed in a chosen deploy.xml file
|
|
50
|
-

|
|
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
|
-

|
|
56
|
-
|
|
57
|
-
## Compile and test typescript as you write
|
|
58
|
-
;
|
|
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
|
-
|
|
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
|
-
#➜
|
|
105
|
-
export
|
|
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
|
-
|
|
116
|
-
|
|
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
|
|
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
|
-
|
|
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 `
|
|
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 `
|
|
100
|
+
It is recommended that you set your `sdf.config.js` file to be the following:
|
|
196
101
|
|
|
197
102
|
```js
|
|
198
|
-
const
|
|
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
|
|
204
|
-
|
|
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
|
|
227
|
-
Regardless of what weird branching strategies
|
|
228
|
-
|
|
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
|
|
142
|
+
In all of the above you can be end-user or a partner
|
|
233
143
|
|
|
234
|
-
So we have one folder for
|
|
144
|
+
So we have one folder for an account.
|
|
235
145
|
|
|
236
|
-
If
|
|
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.
|
|
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
|
}
|
package/commands/fileImport.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
179
|
+
return answer !== choices['OOPS'];
|
|
177
180
|
}, Promise.resolve(true));
|
|
178
181
|
}
|
|
179
182
|
catch (e) {
|
package/commands/fileUpload.d.ts
CHANGED
|
@@ -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: (
|
|
52
|
+
export declare const testFilesPrompt: (opts?: {
|
|
53
|
+
nameOfOptionsFlag: "runjest" | string;
|
|
54
|
+
}) => SdfCommand;
|
package/commands/fileUpload.js
CHANGED
|
@@ -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
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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 = { '
|
|
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 ?
|
|
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 === '
|
|
358
|
+
if (answer2 === 'UPLOAD_ANYWAY')
|
|
338
359
|
return options;
|
|
339
360
|
throw errorHelper.makeError(origin, new Error('User Chose to Quit'));
|
|
340
361
|
}
|
package/lib/compileHelper.d.ts
CHANGED
|
@@ -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>;
|
package/lib/compileHelper.js
CHANGED
|
@@ -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
|
@@ -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.
|
|
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
|
|