@hubspot/cli 4.2.1-beta.1 → 4.2.1-beta.3
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/bin/cli.js +1 -1
- package/commands/accounts/clean.js +139 -0
- package/commands/accounts/list.js +1 -1
- package/commands/accounts.js +2 -0
- package/commands/project/add.js +1 -1
- package/commands/project/dev.js +84 -98
- package/commands/project/download.js +10 -19
- package/commands/project/listBuilds.js +4 -1
- package/commands/project/upload.js +3 -0
- package/commands/project/watch.js +3 -0
- package/commands/project.js +9 -6
- package/commands/sandbox/create.js +10 -1
- package/commands/sandbox/delete.js +8 -12
- package/commands/sandbox.js +3 -2
- package/lang/en.lyaml +55 -59
- package/lib/DevServerManager.js +82 -80
- package/lib/LocalDevManager.js +240 -561
- package/lib/SpinniesManager.js +2 -82
- package/lib/projectStructure.js +107 -0
- package/lib/projects.js +70 -22
- package/lib/prompts/downloadProjectPrompt.js +1 -4
- package/lib/prompts/projectDevTargetAccountPrompt.js +15 -0
- package/lib/ui.js +13 -1
- package/lib/usageTracking.js +57 -0
- package/package.json +5 -8
- package/lib/LocalDevManagerV2.js +0 -129
package/lib/LocalDevManager.js
CHANGED
|
@@ -1,40 +1,32 @@
|
|
|
1
|
-
const chokidar = require('chokidar');
|
|
2
1
|
const path = require('path');
|
|
3
|
-
const
|
|
2
|
+
const chokidar = require('chokidar');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
4
|
const { i18n } = require('./lang');
|
|
5
5
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
6
|
-
const {
|
|
7
|
-
isSpecifiedError,
|
|
8
|
-
} = require('@hubspot/cli-lib/errorHandlers/apiErrors');
|
|
9
6
|
const { handleKeypress } = require('@hubspot/cli-lib/lib/process');
|
|
10
7
|
const {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} = require('@hubspot/cli-lib/
|
|
14
|
-
const {
|
|
15
|
-
PROJECT_BUILD_TEXT,
|
|
16
|
-
PROJECT_DEPLOY_TEXT,
|
|
17
|
-
ERROR_TYPES,
|
|
18
|
-
} = require('@hubspot/cli-lib/lib/constants');
|
|
19
|
-
const { isAllowedExtension } = require('@hubspot/cli-lib/path');
|
|
20
|
-
const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
|
|
21
|
-
const {
|
|
22
|
-
cancelStagedBuild,
|
|
23
|
-
uploadFileToBuild,
|
|
24
|
-
deleteFileFromBuild,
|
|
25
|
-
provisionBuild,
|
|
26
|
-
queueBuild,
|
|
27
|
-
} = require('@hubspot/cli-lib/api/dfs');
|
|
8
|
+
getAccountId,
|
|
9
|
+
getConfigDefaultAccount,
|
|
10
|
+
} = require('@hubspot/cli-lib/lib/config');
|
|
11
|
+
const { PROJECT_CONFIG_FILE } = require('@hubspot/cli-lib/lib/constants');
|
|
28
12
|
const SpinniesManager = require('./SpinniesManager');
|
|
29
13
|
const DevServerManager = require('./DevServerManager');
|
|
30
14
|
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
31
|
-
const {
|
|
32
|
-
const {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
15
|
+
const { getProjectDetailUrl } = require('./projects');
|
|
16
|
+
const {
|
|
17
|
+
APP_COMPONENT_CONFIG,
|
|
18
|
+
COMPONENT_TYPES,
|
|
19
|
+
findProjectComponents,
|
|
20
|
+
getAppCardConfigs,
|
|
21
|
+
} = require('./projectStructure');
|
|
22
|
+
const {
|
|
23
|
+
UI_COLORS,
|
|
24
|
+
uiCommandReference,
|
|
25
|
+
uiAccountDescription,
|
|
26
|
+
uiBetaMessage,
|
|
27
|
+
uiLink,
|
|
28
|
+
uiLine,
|
|
29
|
+
} = require('./ui');
|
|
38
30
|
|
|
39
31
|
const WATCH_EVENTS = {
|
|
40
32
|
add: 'add',
|
|
@@ -43,30 +35,22 @@ const WATCH_EVENTS = {
|
|
|
43
35
|
unlinkDir: 'unlinkDir',
|
|
44
36
|
};
|
|
45
37
|
|
|
46
|
-
const
|
|
47
|
-
always: 'always',
|
|
48
|
-
manual: 'manual',
|
|
49
|
-
never: 'never',
|
|
50
|
-
};
|
|
38
|
+
const i18nKey = 'cli.lib.LocalDevManager';
|
|
51
39
|
|
|
52
40
|
class LocalDevManager {
|
|
53
41
|
constructor(options) {
|
|
54
42
|
this.targetAccountId = options.targetAccountId;
|
|
55
43
|
this.projectConfig = options.projectConfig;
|
|
56
44
|
this.projectDir = options.projectDir;
|
|
57
|
-
this.uploadPermission =
|
|
58
|
-
options.uploadPermission || UPLOAD_PERMISSIONS.always;
|
|
59
45
|
this.debug = options.debug || false;
|
|
46
|
+
this.deployedBuild = options.deployedBuild;
|
|
47
|
+
this.watcher = null;
|
|
48
|
+
this.uploadWarnings = {};
|
|
60
49
|
|
|
61
50
|
this.projectSourceDir = path.join(
|
|
62
51
|
this.projectDir,
|
|
63
52
|
this.projectConfig.srcDir
|
|
64
53
|
);
|
|
65
|
-
this.watcher = null;
|
|
66
|
-
this.uploadQueue = null;
|
|
67
|
-
this.standbyChanges = [];
|
|
68
|
-
this.debouncedBuild = null;
|
|
69
|
-
this.currentStagedBuildId = null;
|
|
70
54
|
|
|
71
55
|
if (!this.targetAccountId || !this.projectConfig || !this.projectDir) {
|
|
72
56
|
logger.log(i18n(`${i18nKey}.failedToInitialize`));
|
|
@@ -75,625 +59,320 @@ class LocalDevManager {
|
|
|
75
59
|
}
|
|
76
60
|
|
|
77
61
|
async start() {
|
|
62
|
+
SpinniesManager.stopAll();
|
|
78
63
|
SpinniesManager.init();
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
65
|
+
// Local dev currently relies on the existence of a deployed build in the target account
|
|
66
|
+
if (!this.deployedBuild) {
|
|
67
|
+
logger.log();
|
|
68
|
+
logger.error(
|
|
69
|
+
i18n(`${i18nKey}.noDeployedBuild`, {
|
|
70
|
+
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
74
|
+
}
|
|
84
75
|
|
|
85
|
-
|
|
76
|
+
const components = await findProjectComponents(this.projectSourceDir);
|
|
86
77
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
78
|
+
// The project is empty, there is nothing to run locally
|
|
79
|
+
if (!components.length) {
|
|
80
|
+
logger.log();
|
|
81
|
+
logger.error(i18n(`${i18nKey}.noComponents`));
|
|
82
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
91
83
|
}
|
|
92
84
|
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
const runnableComponents = components.filter(
|
|
86
|
+
component => component.runnable
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// The project does not contain any components that support local development
|
|
90
|
+
if (!runnableComponents.length) {
|
|
91
|
+
logger.log();
|
|
92
|
+
logger.error(i18n(`${i18nKey}.noRunnableComponents`));
|
|
93
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
94
|
+
}
|
|
95
95
|
|
|
96
|
-
logger.log(i18n(`${i18nKey}.header.betaMessage`));
|
|
97
96
|
logger.log();
|
|
97
|
+
const setupSucceeded = await this.devServerSetup(runnableComponents);
|
|
98
98
|
|
|
99
|
-
this.
|
|
99
|
+
if (setupSucceeded || !this.debug) {
|
|
100
|
+
console.clear();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
uiBetaMessage(i18n(`${i18nKey}.betaMessage`));
|
|
104
|
+
logger.log();
|
|
105
|
+
logger.log(
|
|
106
|
+
chalk.hex(UI_COLORS.SORBET)(
|
|
107
|
+
i18n(`${i18nKey}.running`, {
|
|
108
|
+
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
109
|
+
projectName: this.projectConfig.name,
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
logger.log(
|
|
114
|
+
uiLink(
|
|
115
|
+
i18n(`${i18nKey}.viewInHubSpotLink`),
|
|
116
|
+
getProjectDetailUrl(this.projectConfig.name, this.targetAccountId)
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
logger.log();
|
|
120
|
+
logger.log(i18n(`${i18nKey}.quitHelper`));
|
|
121
|
+
uiLine();
|
|
122
|
+
logger.log();
|
|
100
123
|
|
|
101
124
|
await this.devServerStart();
|
|
102
125
|
|
|
103
|
-
|
|
104
|
-
|
|
126
|
+
// Initialize project file watcher to detect configuration file changes
|
|
127
|
+
this.startWatching(runnableComponents);
|
|
105
128
|
|
|
106
129
|
this.updateKeypressListeners();
|
|
107
130
|
|
|
108
|
-
this.
|
|
131
|
+
this.monitorConsoleOutput();
|
|
132
|
+
|
|
133
|
+
// Verify that there are no mismatches between components in the local project
|
|
134
|
+
// and components in the deployed build of the project.
|
|
135
|
+
this.compareLocalProjectToDeployed(runnableComponents);
|
|
109
136
|
}
|
|
110
137
|
|
|
111
138
|
async stop() {
|
|
112
|
-
this.clearConsoleContent();
|
|
113
|
-
|
|
114
139
|
SpinniesManager.add('cleanupMessage', {
|
|
115
140
|
text: i18n(`${i18nKey}.exitingStart`),
|
|
116
141
|
});
|
|
117
142
|
|
|
118
143
|
await this.stopWatching();
|
|
119
|
-
await this.devServerCleanup();
|
|
120
144
|
|
|
121
|
-
|
|
145
|
+
const cleanupSucceeded = await this.devServerCleanup();
|
|
122
146
|
|
|
123
|
-
if (
|
|
124
|
-
try {
|
|
125
|
-
await cancelStagedBuild(this.targetAccountId, this.projectConfig.name);
|
|
126
|
-
} catch (err) {
|
|
127
|
-
if (
|
|
128
|
-
!isSpecifiedError(err, {
|
|
129
|
-
subCategory: ERROR_TYPES.BUILD_NOT_IN_PROGRESS,
|
|
130
|
-
})
|
|
131
|
-
) {
|
|
132
|
-
logApiErrorInstance(
|
|
133
|
-
err,
|
|
134
|
-
new ApiErrorContext({
|
|
135
|
-
accountId: this.targetAccountId,
|
|
136
|
-
projectName: this.projectConfig.name,
|
|
137
|
-
})
|
|
138
|
-
);
|
|
139
|
-
exitCode = EXIT_CODES.ERROR;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (exitCode === EXIT_CODES.SUCCESS) {
|
|
145
|
-
SpinniesManager.succeed('cleanupMessage', {
|
|
146
|
-
text: i18n(`${i18nKey}.exitingSucceed`),
|
|
147
|
-
});
|
|
148
|
-
} else {
|
|
147
|
+
if (!cleanupSucceeded) {
|
|
149
148
|
SpinniesManager.fail('cleanupMessage', {
|
|
150
149
|
text: i18n(`${i18nKey}.exitingFail`),
|
|
151
150
|
});
|
|
151
|
+
process.exit(EXIT_CODES.ERROR);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
updateConsoleHeader() {
|
|
158
|
-
SpinniesManager.addOrUpdate('devModeRunning', {
|
|
159
|
-
text: i18n(`${i18nKey}.header.running`, {
|
|
160
|
-
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
161
|
-
projectName: this.projectConfig.name,
|
|
162
|
-
}),
|
|
163
|
-
isParent: true,
|
|
164
|
-
category: 'header',
|
|
154
|
+
SpinniesManager.succeed('cleanupMessage', {
|
|
155
|
+
text: i18n(`${i18nKey}.exitingSucceed`),
|
|
165
156
|
});
|
|
166
|
-
|
|
167
|
-
text: i18n(`${i18nKey}.header.status.clean`),
|
|
168
|
-
status: 'non-spinnable',
|
|
169
|
-
indent: 1,
|
|
170
|
-
category: 'header',
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const viewText = DevServerManager.initialized
|
|
174
|
-
? uiLink(
|
|
175
|
-
i18n(`${i18nKey}.header.viewInHubSpotLink`),
|
|
176
|
-
DevServerManager.generateURL(`hs/project`),
|
|
177
|
-
{
|
|
178
|
-
inSpinnies: true,
|
|
179
|
-
}
|
|
180
|
-
)
|
|
181
|
-
: ' ';
|
|
182
|
-
|
|
183
|
-
SpinniesManager.addOrUpdate('viewInHubSpotLink', {
|
|
184
|
-
text: viewText,
|
|
185
|
-
status: 'non-spinnable',
|
|
186
|
-
indent: 1,
|
|
187
|
-
category: 'header',
|
|
188
|
-
});
|
|
189
|
-
SpinniesManager.addOrUpdate('spacer-1', {
|
|
190
|
-
text: ' ',
|
|
191
|
-
status: 'non-spinnable',
|
|
192
|
-
category: 'header',
|
|
193
|
-
});
|
|
194
|
-
SpinniesManager.addOrUpdate('quitHelper', {
|
|
195
|
-
text: i18n(`${i18nKey}.header.quitHelper`),
|
|
196
|
-
status: 'non-spinnable',
|
|
197
|
-
indent: 1,
|
|
198
|
-
category: 'header',
|
|
199
|
-
});
|
|
200
|
-
SpinniesManager.addOrUpdate('lineSeparator', {
|
|
201
|
-
text: '-'.repeat(50),
|
|
202
|
-
status: 'non-spinnable',
|
|
203
|
-
noIndent: true,
|
|
204
|
-
category: 'header',
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
clearConsoleContent() {
|
|
209
|
-
SpinniesManager.removeAll({ preserveCategory: 'header' });
|
|
157
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
210
158
|
}
|
|
211
159
|
|
|
212
160
|
updateKeypressListeners() {
|
|
213
161
|
handleKeypress(async key => {
|
|
214
162
|
if ((key.ctrl && key.name === 'c') || key.name === 'q') {
|
|
215
163
|
this.stop();
|
|
216
|
-
} else if (
|
|
217
|
-
(key.name === 'y' || key.name === 'n') &&
|
|
218
|
-
this.uploadPermission === UPLOAD_PERMISSIONS.manual &&
|
|
219
|
-
this.hasAnyUnsupportedStandbyChanges()
|
|
220
|
-
) {
|
|
221
|
-
SpinniesManager.remove('manualUploadRequired');
|
|
222
|
-
SpinniesManager.remove('manualUploadExplanation1');
|
|
223
|
-
SpinniesManager.remove('manualUploadExplanation2');
|
|
224
|
-
SpinniesManager.remove('manualUploadPrompt');
|
|
225
|
-
|
|
226
|
-
if (key.name === 'y') {
|
|
227
|
-
SpinniesManager.add(null, {
|
|
228
|
-
text: i18n(`${i18nKey}.upload.manualUploadConfirmed`),
|
|
229
|
-
status: 'succeed',
|
|
230
|
-
succeedColor: 'white',
|
|
231
|
-
noIndent: true,
|
|
232
|
-
});
|
|
233
|
-
this.updateDevModeStatus('manualUpload');
|
|
234
|
-
await this.createNewStagingBuild();
|
|
235
|
-
this.flushStandbyChanges();
|
|
236
|
-
await this.queueBuild();
|
|
237
|
-
} else if (key.name === 'n') {
|
|
238
|
-
SpinniesManager.add(null, {
|
|
239
|
-
text: i18n(`${i18nKey}.upload.manualUploadSkipped`),
|
|
240
|
-
status: 'fail',
|
|
241
|
-
failColor: 'white',
|
|
242
|
-
noIndent: true,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
164
|
}
|
|
246
165
|
});
|
|
247
166
|
}
|
|
248
167
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
logDeployError(deployStatus = {}) {
|
|
268
|
-
const subTasks = deployStatus[PROJECT_DEPLOY_TEXT.SUBTASK_KEY] || [];
|
|
269
|
-
const failedSubTasks = subTasks.filter(task => task.status === 'FAILURE');
|
|
270
|
-
|
|
271
|
-
if (failedSubTasks.length) {
|
|
272
|
-
this.updateDevModeStatus('deployError');
|
|
273
|
-
|
|
274
|
-
failedSubTasks.forEach(failedSubTask => {
|
|
275
|
-
SpinniesManager.add(null, {
|
|
276
|
-
text: failedSubTask.errorMessage,
|
|
277
|
-
status: 'fail',
|
|
278
|
-
failColor: 'white',
|
|
279
|
-
indent: 1,
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
updateDevModeStatus(langKey) {
|
|
286
|
-
SpinniesManager.update('devModeStatus', {
|
|
287
|
-
text: i18n(`${i18nKey}.header.status.${langKey}`),
|
|
288
|
-
status: 'non-spinnable',
|
|
289
|
-
noIndent: true,
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async pauseUploadQueue() {
|
|
294
|
-
this.uploadQueue.pause();
|
|
295
|
-
await this.uploadQueue.onIdle();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
hasAnyUnsupportedStandbyChanges() {
|
|
299
|
-
return this.standbyChanges.some(({ supported }) => !supported);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
async createNewStagingBuild() {
|
|
303
|
-
try {
|
|
304
|
-
const { buildId } = await provisionBuild(
|
|
305
|
-
this.targetAccountId,
|
|
306
|
-
this.projectConfig.name
|
|
168
|
+
logUploadWarning(reason) {
|
|
169
|
+
// Avoid logging the warning to the console if it is currently the most
|
|
170
|
+
// recently logged warning. We do not want to spam the console with the same message.
|
|
171
|
+
if (!this.uploadWarnings[reason]) {
|
|
172
|
+
const currentDefaultAccount = getConfigDefaultAccount();
|
|
173
|
+
const defaultAccountId = getAccountId(currentDefaultAccount);
|
|
174
|
+
|
|
175
|
+
logger.log();
|
|
176
|
+
logger.warn(i18n(`${i18nKey}.uploadWarning.header`, { reason }));
|
|
177
|
+
logger.log(
|
|
178
|
+
i18n(`${i18nKey}.uploadWarning.stopDev`, {
|
|
179
|
+
command: uiCommandReference('hs project dev'),
|
|
180
|
+
})
|
|
307
181
|
);
|
|
308
|
-
this.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
182
|
+
if (this.targetAccountId !== defaultAccountId) {
|
|
183
|
+
logger.log(
|
|
184
|
+
i18n(`${i18nKey}.uploadWarning.runUploadWithAccount`, {
|
|
185
|
+
command: uiCommandReference(
|
|
186
|
+
`hs project upload --account=${this.targetAccountId}`
|
|
187
|
+
),
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
logger.log(
|
|
192
|
+
i18n(`${i18nKey}.uploadWarning.runUpload`, {
|
|
193
|
+
command: uiCommandReference('hs project upload'),
|
|
194
|
+
})
|
|
195
|
+
);
|
|
317
196
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
197
|
+
logger.log(
|
|
198
|
+
i18n(`${i18nKey}.uploadWarning.restartDev`, {
|
|
199
|
+
command: uiCommandReference('hs project dev'),
|
|
200
|
+
})
|
|
201
|
+
);
|
|
321
202
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
await this.createNewStagingBuild();
|
|
203
|
+
this.mostRecentUploadWarning = reason;
|
|
204
|
+
this.uploadWarnings[reason] = true;
|
|
325
205
|
}
|
|
326
|
-
|
|
327
|
-
this.watcher.on('add', async filePath => {
|
|
328
|
-
this.handleWatchEvent(filePath, WATCH_EVENTS.add);
|
|
329
|
-
});
|
|
330
|
-
this.watcher.on('change', async filePath => {
|
|
331
|
-
this.handleWatchEvent(filePath, WATCH_EVENTS.change);
|
|
332
|
-
});
|
|
333
|
-
this.watcher.on('unlink', async filePath => {
|
|
334
|
-
this.handleWatchEvent(filePath, WATCH_EVENTS.unlink);
|
|
335
|
-
});
|
|
336
|
-
this.watcher.on('unlinkDir', async filePath => {
|
|
337
|
-
this.handleWatchEvent(filePath, WATCH_EVENTS.unlinkDir);
|
|
338
|
-
});
|
|
339
206
|
}
|
|
340
207
|
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
event,
|
|
344
|
-
filePath,
|
|
345
|
-
remotePath: path.relative(this.projectSourceDir, filePath),
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
if (changeInfo.filePath.includes('dist')) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
208
|
+
monitorConsoleOutput() {
|
|
209
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
351
210
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
211
|
+
process.stdout.write = function(chunk, encoding, callback) {
|
|
212
|
+
// Reset the most recently logged warning
|
|
213
|
+
if (
|
|
214
|
+
this.mostRecentUploadWarning &&
|
|
215
|
+
this.uploadWarnings[this.mostRecentUploadWarning]
|
|
216
|
+
) {
|
|
217
|
+
delete this.uploadWarnings[this.mostRecentUploadWarning];
|
|
218
|
+
}
|
|
358
219
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
220
|
+
return originalStdoutWrite(chunk, encoding, callback);
|
|
221
|
+
}.bind(this);
|
|
362
222
|
}
|
|
363
223
|
|
|
364
|
-
|
|
365
|
-
const
|
|
224
|
+
compareLocalProjectToDeployed(runnableComponents) {
|
|
225
|
+
const deployedComponentNames = this.deployedBuild.subbuildStatuses.map(
|
|
226
|
+
subbuildStatus => subbuildStatus.buildName
|
|
227
|
+
);
|
|
366
228
|
|
|
367
|
-
|
|
368
|
-
this.updateDevModeStatus('noUploadsAllowed');
|
|
229
|
+
let missingComponents = [];
|
|
369
230
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}),
|
|
374
|
-
status: 'fail',
|
|
375
|
-
failColor: 'white',
|
|
376
|
-
noIndent: true,
|
|
377
|
-
});
|
|
378
|
-
} else {
|
|
379
|
-
this.updateDevModeStatus('manualUploadRequired');
|
|
231
|
+
runnableComponents.forEach(({ type, config, path }) => {
|
|
232
|
+
if (type === COMPONENT_TYPES.app) {
|
|
233
|
+
const cardConfigs = getAppCardConfigs(config, path);
|
|
380
234
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
235
|
+
if (!deployedComponentNames.includes(config.name)) {
|
|
236
|
+
missingComponents.push(
|
|
237
|
+
`${i18n(`${i18nKey}.uploadWarning.appLabel`)} ${config.name}`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
385
240
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
SpinniesManager.add('manualUploadExplanation2', {
|
|
399
|
-
text: i18n(`${i18nKey}.upload.manualUploadExplanation2`),
|
|
400
|
-
status: 'non-spinnable',
|
|
401
|
-
indent: 1,
|
|
402
|
-
});
|
|
403
|
-
SpinniesManager.add('manualUploadPrompt', {
|
|
404
|
-
text: i18n(`${i18nKey}.upload.manualUploadPrompt`),
|
|
405
|
-
status: 'non-spinnable',
|
|
406
|
-
indent: 1,
|
|
241
|
+
cardConfigs.forEach(cardConfig => {
|
|
242
|
+
if (
|
|
243
|
+
cardConfig.data &&
|
|
244
|
+
cardConfig.data.title &&
|
|
245
|
+
!deployedComponentNames.includes(cardConfig.data.title)
|
|
246
|
+
) {
|
|
247
|
+
missingComponents.push(
|
|
248
|
+
`${i18n(`${i18nKey}.uploadWarning.appLabel`)} ${
|
|
249
|
+
cardConfig.data.title
|
|
250
|
+
}`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
407
253
|
});
|
|
408
254
|
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
if (missingComponents.length) {
|
|
258
|
+
this.logUploadWarning(
|
|
259
|
+
i18n(`${i18nKey}.uploadWarning.missingComponents`, {
|
|
260
|
+
missingComponents: missingComponents.join(', '),
|
|
261
|
+
})
|
|
262
|
+
);
|
|
409
263
|
}
|
|
410
264
|
}
|
|
411
265
|
|
|
412
|
-
|
|
413
|
-
|
|
266
|
+
startWatching(runnableComponents) {
|
|
267
|
+
this.watcher = chokidar.watch(this.projectDir, {
|
|
268
|
+
ignoreInitial: true,
|
|
269
|
+
});
|
|
414
270
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}),
|
|
421
|
-
status: 'non-spinnable',
|
|
422
|
-
});
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
if (shouldIgnoreFile(filePath, true)) {
|
|
427
|
-
SpinniesManager.add(null, {
|
|
428
|
-
text: i18n(`${i18nKey}.upload.fileIgnored`, {
|
|
429
|
-
filePath,
|
|
430
|
-
}),
|
|
431
|
-
status: 'non-spinnable',
|
|
271
|
+
const configPaths = runnableComponents
|
|
272
|
+
.filter(({ type }) => type === COMPONENT_TYPES.app)
|
|
273
|
+
.map(component => {
|
|
274
|
+
const appConfigPath = path.join(component.path, APP_COMPONENT_CONFIG);
|
|
275
|
+
return appConfigPath;
|
|
432
276
|
});
|
|
433
|
-
return false;
|
|
434
|
-
}
|
|
435
277
|
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
);
|
|
278
|
+
const projectConfigPath = path.join(this.projectDir, PROJECT_CONFIG_FILE);
|
|
279
|
+
configPaths.push(projectConfigPath);
|
|
439
280
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
this.
|
|
445
|
-
}
|
|
446
|
-
|
|
281
|
+
this.watcher.on('add', filePath => {
|
|
282
|
+
this.handleWatchEvent(filePath, WATCH_EVENTS.add, configPaths);
|
|
283
|
+
});
|
|
284
|
+
this.watcher.on('change', filePath => {
|
|
285
|
+
this.handleWatchEvent(filePath, WATCH_EVENTS.change, configPaths);
|
|
286
|
+
});
|
|
287
|
+
this.watcher.on('unlink', filePath => {
|
|
288
|
+
this.handleWatchEvent(filePath, WATCH_EVENTS.unlink, configPaths);
|
|
289
|
+
});
|
|
290
|
+
this.watcher.on('unlinkDir', filePath => {
|
|
291
|
+
this.handleWatchEvent(filePath, WATCH_EVENTS.unlinkDir, configPaths);
|
|
292
|
+
});
|
|
447
293
|
}
|
|
448
294
|
|
|
449
|
-
async
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
try {
|
|
453
|
-
if (event === WATCH_EVENTS.add || event === WATCH_EVENTS.change) {
|
|
454
|
-
const { name: spinnerName } = SpinniesManager.add(null, {
|
|
455
|
-
text: i18n(`${i18nKey}.upload.uploadingAddChange`, {
|
|
456
|
-
filePath: remotePath,
|
|
457
|
-
}),
|
|
458
|
-
status: 'non-spinnable',
|
|
459
|
-
});
|
|
460
|
-
await uploadFileToBuild(
|
|
461
|
-
this.targetAccountId,
|
|
462
|
-
this.projectConfig.name,
|
|
463
|
-
filePath,
|
|
464
|
-
remotePath
|
|
465
|
-
);
|
|
466
|
-
SpinniesManager.update(spinnerName, {
|
|
467
|
-
text: i18n(`${i18nKey}.upload.uploadedAddChange`, {
|
|
468
|
-
filePath: remotePath,
|
|
469
|
-
}),
|
|
470
|
-
status: 'non-spinnable',
|
|
471
|
-
});
|
|
472
|
-
} else if (
|
|
473
|
-
event === WATCH_EVENTS.unlink ||
|
|
474
|
-
event === WATCH_EVENTS.unlinkDir
|
|
475
|
-
) {
|
|
476
|
-
const { name: spinnerName } = SpinniesManager.add(null, {
|
|
477
|
-
text: i18n(`${i18nKey}.upload.uploadingRemoveChange`, {
|
|
478
|
-
filePath: remotePath,
|
|
479
|
-
}),
|
|
480
|
-
status: 'non-spinnable',
|
|
481
|
-
});
|
|
482
|
-
const path =
|
|
483
|
-
event === WATCH_EVENTS.unlinkDir ? `${remotePath}/` : remotePath;
|
|
484
|
-
await deleteFileFromBuild(
|
|
485
|
-
this.targetAccountId,
|
|
486
|
-
this.projectConfig.name,
|
|
487
|
-
path
|
|
488
|
-
);
|
|
489
|
-
SpinniesManager.update(spinnerName, {
|
|
490
|
-
text: i18n(`${i18nKey}.upload.uploadedRemoveChange`, {
|
|
491
|
-
filePath: remotePath,
|
|
492
|
-
}),
|
|
493
|
-
status: 'non-spinnable',
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
} catch (err) {
|
|
497
|
-
logger.debug(err);
|
|
498
|
-
}
|
|
295
|
+
async stopWatching() {
|
|
296
|
+
await this.watcher.close();
|
|
499
297
|
}
|
|
500
298
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
299
|
+
handleWatchEvent(filePath, event, configPaths) {
|
|
300
|
+
if (configPaths.includes(filePath)) {
|
|
301
|
+
this.logUploadWarning(
|
|
302
|
+
i18n(`${i18nKey}.uploadWarning.configEdit`, {
|
|
303
|
+
path: path.relative(this.projectDir, filePath),
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
} else {
|
|
307
|
+
this.devServerFileChange(filePath, event);
|
|
510
308
|
}
|
|
511
|
-
|
|
512
|
-
const debounceWaitTime =
|
|
513
|
-
event === WATCH_EVENTS.add
|
|
514
|
-
? BUILD_DEBOUNCE_TIME_LONG
|
|
515
|
-
: BUILD_DEBOUNCE_TIME_SHORT;
|
|
516
|
-
|
|
517
|
-
this.debouncedBuild = setTimeout(
|
|
518
|
-
this.queueBuild.bind(this),
|
|
519
|
-
debounceWaitTime
|
|
520
|
-
);
|
|
521
309
|
}
|
|
522
310
|
|
|
523
|
-
async
|
|
524
|
-
SpinniesManager.add(null, { text: ' ', status: 'non-spinnable' });
|
|
525
|
-
|
|
526
|
-
const { name: spinnerName } = SpinniesManager.add(null, {
|
|
527
|
-
text: i18n(`${i18nKey}.upload.uploadingChanges`, {
|
|
528
|
-
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
529
|
-
buildId: this.currentStagedBuildId,
|
|
530
|
-
}),
|
|
531
|
-
noIndent: true,
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
await this.pauseUploadQueue();
|
|
535
|
-
|
|
536
|
-
let queueBuildError;
|
|
537
|
-
|
|
311
|
+
async devServerSetup(components) {
|
|
538
312
|
try {
|
|
539
|
-
await
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
if (queueBuildError) {
|
|
545
|
-
this.updateDevModeStatus('buildError');
|
|
546
|
-
|
|
547
|
-
logger.debug(queueBuildError);
|
|
548
|
-
|
|
549
|
-
SpinniesManager.fail(spinnerName, {
|
|
550
|
-
text: i18n(`${i18nKey}.upload.uploadedChangesFailed`, {
|
|
551
|
-
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
552
|
-
buildId: this.currentStagedBuildId,
|
|
553
|
-
}),
|
|
554
|
-
failColor: 'white',
|
|
555
|
-
noIndent: true,
|
|
313
|
+
await DevServerManager.setup({
|
|
314
|
+
components,
|
|
315
|
+
debug: this.debug,
|
|
316
|
+
onUploadRequired: this.logUploadWarning.bind(this),
|
|
556
317
|
});
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
})
|
|
562
|
-
) {
|
|
563
|
-
SpinniesManager.add(null, {
|
|
564
|
-
text: i18n(`${i18nKey}.cancelledFromUI`),
|
|
565
|
-
status: 'non-spinnable',
|
|
566
|
-
indent: 1,
|
|
567
|
-
});
|
|
568
|
-
this.stop();
|
|
569
|
-
} else if (
|
|
570
|
-
queueBuildError &&
|
|
571
|
-
queueBuildError.error &&
|
|
572
|
-
queueBuildError.error.message
|
|
573
|
-
) {
|
|
574
|
-
SpinniesManager.add(null, {
|
|
575
|
-
text: queueBuildError.error.message,
|
|
576
|
-
status: 'non-spinnable',
|
|
577
|
-
indent: 1,
|
|
578
|
-
});
|
|
318
|
+
return true;
|
|
319
|
+
} catch (e) {
|
|
320
|
+
if (this.debug) {
|
|
321
|
+
logger.error(e);
|
|
579
322
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
this.targetAccountId,
|
|
583
|
-
this.projectConfig,
|
|
584
|
-
null,
|
|
585
|
-
this.currentStagedBuildId,
|
|
586
|
-
true
|
|
323
|
+
logger.error(
|
|
324
|
+
i18n(`${i18nKey}.devServer.setupError`, { message: e.message })
|
|
587
325
|
);
|
|
588
|
-
|
|
589
|
-
if (result.succeeded) {
|
|
590
|
-
this.updateDevModeStatus('clean');
|
|
591
|
-
|
|
592
|
-
SpinniesManager.succeed(spinnerName, {
|
|
593
|
-
text: i18n(`${i18nKey}.upload.uploadedChangesSucceeded`, {
|
|
594
|
-
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
595
|
-
buildId: result.buildId,
|
|
596
|
-
}),
|
|
597
|
-
succeedColor: 'white',
|
|
598
|
-
noIndent: true,
|
|
599
|
-
});
|
|
600
|
-
} else {
|
|
601
|
-
SpinniesManager.fail(spinnerName, {
|
|
602
|
-
text: i18n(`${i18nKey}.upload.uploadedChangesFailed`, {
|
|
603
|
-
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
604
|
-
buildId: result.buildId,
|
|
605
|
-
}),
|
|
606
|
-
failColor: 'white',
|
|
607
|
-
noIndent: true,
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
if (result.buildResult.status === 'FAILURE') {
|
|
611
|
-
this.logBuildError(result.buildResult);
|
|
612
|
-
} else if (result.deployResult.status === 'FAILURE') {
|
|
613
|
-
this.logDeployError(result.deployResult);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
SpinniesManager.removeAll({ targetCategory: 'projectPollStatus' });
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
!queueBuildError &&
|
|
622
|
-
this.uploadPermission === UPLOAD_PERMISSIONS.always
|
|
623
|
-
) {
|
|
624
|
-
await this.createNewStagingBuild();
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
this.uploadQueue.start();
|
|
628
|
-
|
|
629
|
-
if (this.hasAnyUnsupportedStandbyChanges()) {
|
|
630
|
-
this.flushStandbyChanges();
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
flushStandbyChanges() {
|
|
635
|
-
if (this.standbyChanges.length) {
|
|
636
|
-
this.uploadQueue.addAll(
|
|
637
|
-
this.standbyChanges.map(changeInfo => {
|
|
638
|
-
return async () => {
|
|
639
|
-
if (
|
|
640
|
-
this.uploadPermission === UPLOAD_PERMISSIONS.always &&
|
|
641
|
-
!this.uploadQueue.isPaused
|
|
642
|
-
) {
|
|
643
|
-
this.debounceQueueBuild(changeInfo);
|
|
644
|
-
}
|
|
645
|
-
await this.sendChanges(changeInfo);
|
|
646
|
-
};
|
|
647
|
-
})
|
|
648
|
-
);
|
|
649
|
-
this.standbyChanges = [];
|
|
326
|
+
return false;
|
|
650
327
|
}
|
|
651
328
|
}
|
|
652
329
|
|
|
653
|
-
async stopWatching() {
|
|
654
|
-
await this.watcher.close();
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
handleServerLog(serverKey, ...args) {
|
|
658
|
-
SpinniesManager.add(null, {
|
|
659
|
-
text: `${args.join('')}`,
|
|
660
|
-
status: 'non-spinnable',
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
|
|
664
330
|
async devServerStart() {
|
|
665
331
|
try {
|
|
666
332
|
await DevServerManager.start({
|
|
667
333
|
accountId: this.targetAccountId,
|
|
668
|
-
debug: this.debug,
|
|
669
|
-
spinniesLogger: this.handleServerLog,
|
|
670
334
|
projectConfig: this.projectConfig,
|
|
671
|
-
projectSourceDir: this.projectSourceDir,
|
|
672
335
|
});
|
|
673
336
|
} catch (e) {
|
|
674
337
|
if (this.debug) {
|
|
675
338
|
logger.error(e);
|
|
676
339
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
340
|
+
logger.error(
|
|
341
|
+
i18n(`${i18nKey}.devServer.startError`, { message: e.message })
|
|
342
|
+
);
|
|
343
|
+
process.exit(EXIT_CODES.ERROR);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
devServerFileChange(filePath, event) {
|
|
348
|
+
try {
|
|
349
|
+
DevServerManager.fileChange({ filePath, event });
|
|
350
|
+
} catch (e) {
|
|
351
|
+
if (this.debug) {
|
|
352
|
+
logger.error(e);
|
|
353
|
+
}
|
|
354
|
+
logger.error(
|
|
355
|
+
i18n(`${i18nKey}.devServer.fileChangeError`, {
|
|
356
|
+
message: e.message,
|
|
357
|
+
})
|
|
358
|
+
);
|
|
681
359
|
}
|
|
682
360
|
}
|
|
683
361
|
|
|
684
362
|
async devServerCleanup() {
|
|
685
363
|
try {
|
|
686
364
|
await DevServerManager.cleanup();
|
|
365
|
+
return true;
|
|
687
366
|
} catch (e) {
|
|
688
367
|
if (this.debug) {
|
|
689
368
|
logger.error(e);
|
|
690
369
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
370
|
+
logger.error(
|
|
371
|
+
i18n(`${i18nKey}.devServer.cleanupError`, { message: e.message })
|
|
372
|
+
);
|
|
373
|
+
return false;
|
|
695
374
|
}
|
|
696
375
|
}
|
|
697
376
|
}
|
|
698
377
|
|
|
699
|
-
module.exports =
|
|
378
|
+
module.exports = LocalDevManager;
|