@salesforce/source-tracking 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +0 -26
- package/lib/index.d.ts +1 -1
- package/lib/index.js +3 -1
- package/lib/shared/conflicts.d.ts +16 -0
- package/lib/shared/conflicts.js +84 -0
- package/lib/shared/expectedSourceMembers.d.ts +2 -0
- package/lib/shared/expectedSourceMembers.js +76 -0
- package/lib/shared/functions.d.ts +2 -0
- package/lib/shared/functions.js +5 -1
- package/lib/shared/localComponentSetArray.d.ts +15 -0
- package/lib/shared/localComponentSetArray.js +84 -0
- package/lib/shared/localShadowRepo.js +1 -1
- package/lib/shared/metadataKeys.d.ts +1 -0
- package/lib/shared/metadataKeys.js +17 -1
- package/lib/shared/populateFilePaths.d.ts +9 -0
- package/lib/shared/populateFilePaths.js +77 -0
- package/lib/shared/populateTypesAndNames.d.ts +18 -0
- package/lib/shared/populateTypesAndNames.js +80 -0
- package/lib/shared/remoteSourceTrackingService.d.ts +0 -5
- package/lib/shared/remoteSourceTrackingService.js +3 -72
- package/lib/shared/types.d.ts +12 -4
- package/lib/shared/types.js +5 -0
- package/lib/sourceTracking.d.ts +81 -17
- package/lib/sourceTracking.js +167 -271
- package/package.json +7 -7
package/lib/sourceTracking.js
CHANGED
|
@@ -9,17 +9,20 @@ exports.SourceTracking = void 0;
|
|
|
9
9
|
*/
|
|
10
10
|
const fs = require("fs");
|
|
11
11
|
const path_1 = require("path");
|
|
12
|
-
const os_1 = require("os");
|
|
13
12
|
const core_1 = require("@salesforce/core");
|
|
14
13
|
const kit_1 = require("@salesforce/kit");
|
|
15
14
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
16
15
|
const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
|
|
17
16
|
const remoteSourceTrackingService_1 = require("./shared/remoteSourceTrackingService");
|
|
18
17
|
const localShadowRepo_1 = require("./shared/localShadowRepo");
|
|
18
|
+
const conflicts_1 = require("./shared/conflicts");
|
|
19
19
|
const guards_1 = require("./shared/guards");
|
|
20
20
|
const functions_1 = require("./shared/functions");
|
|
21
21
|
const metadataKeys_1 = require("./shared/metadataKeys");
|
|
22
22
|
const compatibility_1 = require("./compatibility");
|
|
23
|
+
const populateFilePaths_1 = require("./shared/populateFilePaths");
|
|
24
|
+
const populateTypesAndNames_1 = require("./shared/populateTypesAndNames");
|
|
25
|
+
const localComponentSetArray_1 = require("./shared/localComponentSetArray");
|
|
23
26
|
/**
|
|
24
27
|
* Manages source tracking files (remote and local)
|
|
25
28
|
*
|
|
@@ -28,95 +31,48 @@ const compatibility_1 = require("./compatibility");
|
|
|
28
31
|
*/
|
|
29
32
|
class SourceTracking extends kit_1.AsyncCreatable {
|
|
30
33
|
constructor(options) {
|
|
34
|
+
var _a, _b, _c;
|
|
31
35
|
super(options);
|
|
32
|
-
this.registry = new source_deploy_retrieve_1.RegistryAccess();
|
|
33
36
|
this.org = options.org;
|
|
37
|
+
this.orgId = this.org.getOrgId();
|
|
34
38
|
this.projectPath = options.project.getPath();
|
|
35
39
|
this.packagesDirs = options.project.getPackageDirectories();
|
|
36
40
|
this.logger = core_1.Logger.childFromRoot('SourceTracking');
|
|
37
41
|
this.project = options.project;
|
|
38
|
-
this.
|
|
42
|
+
this.ignoreConflicts = (_a = options.ignoreConflicts) !== null && _a !== void 0 ? _a : false;
|
|
43
|
+
this.ignoreLocalCache = (_b = options.ignoreLocalCache) !== null && _b !== void 0 ? _b : false;
|
|
44
|
+
this.subscribeSDREvents = (_c = options.subscribeSDREvents) !== null && _c !== void 0 ? _c : false;
|
|
45
|
+
this.hasSfdxTrackingFiles = (0, compatibility_1.hasSfdxTrackingFiles)(this.orgId, this.projectPath);
|
|
39
46
|
}
|
|
40
47
|
// eslint-disable-next-line class-methods-use-this
|
|
41
48
|
async init() {
|
|
42
|
-
|
|
49
|
+
await this.maybeSubscribeLifecycleEvents();
|
|
43
50
|
}
|
|
44
51
|
/**
|
|
45
52
|
*
|
|
46
|
-
* @param byPackageDir if true, returns
|
|
53
|
+
* @param byPackageDir if true, returns a ComponentSet for each packageDir that has any changes
|
|
54
|
+
* * if false, returns an array containing one ComponentSet with all changes
|
|
55
|
+
* * if not specified, this method will follow what sfdx-project.json says
|
|
47
56
|
* @returns ComponentSet[]
|
|
48
57
|
*/
|
|
49
|
-
async localChangesAsComponentSet(byPackageDir
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// remove the forceIgnored items early
|
|
60
|
-
.map((group) => group.filter((item) => this.forceIgnore.accepts(item)));
|
|
58
|
+
async localChangesAsComponentSet(byPackageDir) {
|
|
59
|
+
const [projectConfig] = await Promise.all([
|
|
60
|
+
this.project.resolveProjectConfig(),
|
|
61
|
+
this.ensureLocalTracking(),
|
|
62
|
+
]);
|
|
63
|
+
const sourceApiVersion = projectConfig.sourceApiVersion;
|
|
64
|
+
const [nonDeletes, deletes] = await Promise.all([
|
|
65
|
+
this.localRepo.getNonDeleteFilenames(),
|
|
66
|
+
this.localRepo.getDeleteFilenames(),
|
|
67
|
+
]);
|
|
61
68
|
// it'll be easier to filter filenames and work with smaller component sets than to filter SourceComponents
|
|
62
|
-
const groupings = (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}))
|
|
68
|
-
: [
|
|
69
|
-
{
|
|
70
|
-
nonDeletes: allNonDeletes,
|
|
71
|
-
deletes: allDeletes,
|
|
72
|
-
path: this.packagesDirs.map((dir) => dir.name).join(';'),
|
|
73
|
-
},
|
|
74
|
-
]).filter((group) => group.deletes.length || group.nonDeletes.length);
|
|
69
|
+
const groupings = (0, localComponentSetArray_1.getGroupedFiles)({
|
|
70
|
+
packageDirs: this.packagesDirs,
|
|
71
|
+
nonDeletes,
|
|
72
|
+
deletes,
|
|
73
|
+
}, byPackageDir !== null && byPackageDir !== void 0 ? byPackageDir : Boolean(projectConfig.pushPackageDirectoriesSequentially)); // if the users specified true or false for the param, that overrides the project config
|
|
75
74
|
this.logger.debug(`will build array of ${groupings.length} componentSet(s)`);
|
|
76
|
-
return groupings
|
|
77
|
-
.map((grouping) => {
|
|
78
|
-
this.logger.debug(`building componentSet for ${grouping.path} (deletes: ${grouping.deletes.length} nonDeletes: ${grouping.nonDeletes.length})`);
|
|
79
|
-
const componentSet = new source_deploy_retrieve_1.ComponentSet();
|
|
80
|
-
if (sourceApiVersion) {
|
|
81
|
-
componentSet.sourceApiVersion = sourceApiVersion;
|
|
82
|
-
}
|
|
83
|
-
const resolverForDeletes = new source_deploy_retrieve_1.MetadataResolver(undefined, source_deploy_retrieve_1.VirtualTreeContainer.fromFilePaths(grouping.deletes));
|
|
84
|
-
grouping.deletes
|
|
85
|
-
.flatMap((filename) => resolverForDeletes.getComponentsFromPath(filename))
|
|
86
|
-
.filter(guards_1.sourceComponentGuard)
|
|
87
|
-
.map((component) => {
|
|
88
|
-
// if the component is a file in a bundle type AND there are files from the bundle that are not deleted, set the bundle for deploy, not for delete
|
|
89
|
-
if ((0, functions_1.isBundle)(component) && component.content && fs.existsSync(component.content)) {
|
|
90
|
-
// all bundle types have a directory name
|
|
91
|
-
try {
|
|
92
|
-
resolverForNonDeletes
|
|
93
|
-
.getComponentsFromPath((0, path_1.resolve)(component.content))
|
|
94
|
-
.filter(guards_1.sourceComponentGuard)
|
|
95
|
-
.map((nonDeletedComponent) => componentSet.add(nonDeletedComponent));
|
|
96
|
-
}
|
|
97
|
-
catch (e) {
|
|
98
|
-
this.logger.warn(`unable to find component at ${component.content}. That's ok if it was supposed to be deleted`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
componentSet.add(component, source_deploy_retrieve_1.DestructiveChangesType.POST);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
grouping.nonDeletes
|
|
106
|
-
.flatMap((filename) => {
|
|
107
|
-
try {
|
|
108
|
-
return resolverForNonDeletes.getComponentsFromPath((0, path_1.resolve)(filename));
|
|
109
|
-
}
|
|
110
|
-
catch (e) {
|
|
111
|
-
this.logger.warn(`unable to resolve ${filename}`);
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
.filter(guards_1.sourceComponentGuard)
|
|
116
|
-
.map((component) => componentSet.add(component));
|
|
117
|
-
return componentSet;
|
|
118
|
-
})
|
|
119
|
-
.filter((componentSet) => componentSet.size > 0);
|
|
75
|
+
return (0, localComponentSetArray_1.getComponentSets)(groupings, sourceApiVersion);
|
|
120
76
|
}
|
|
121
77
|
async remoteNonDeletesAsComponentSet() {
|
|
122
78
|
const [changeResults, sourceBackedComponents] = await Promise.all([
|
|
@@ -176,12 +132,6 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
176
132
|
}
|
|
177
133
|
return results;
|
|
178
134
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Get metadata changes made locally and in the org.
|
|
181
|
-
*
|
|
182
|
-
* @returns local and remote changed metadata
|
|
183
|
-
*
|
|
184
|
-
*/
|
|
185
135
|
async getChanges(options) {
|
|
186
136
|
if ((options === null || options === void 0 ? void 0 : options.origin) === 'local') {
|
|
187
137
|
await this.ensureLocalTracking();
|
|
@@ -218,13 +168,13 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
218
168
|
this.logger.debug('remoteChanges', remoteChanges);
|
|
219
169
|
const filteredChanges = remoteChanges
|
|
220
170
|
.filter(remoteFilterByState[options.state])
|
|
221
|
-
// skip any remote types not in the registry. Will emit
|
|
222
|
-
.filter((rce) =>
|
|
171
|
+
// skip any remote types not in the registry. Will emit warnings
|
|
172
|
+
.filter((rce) => (0, metadataKeys_1.registrySupportsType)(rce.type));
|
|
223
173
|
if (options.format === 'ChangeResult') {
|
|
224
174
|
return filteredChanges.map((change) => (0, remoteSourceTrackingService_1.remoteChangeElementToChangeResult)(change));
|
|
225
175
|
}
|
|
226
176
|
if (options.format === 'ChangeResultWithPaths') {
|
|
227
|
-
return
|
|
177
|
+
return (0, populateFilePaths_1.populateFilePaths)(filteredChanges.map((change) => (0, remoteSourceTrackingService_1.remoteChangeElementToChangeResult)(change)), this.project.getPackageDirectories().map((pkgDir) => pkgDir.path));
|
|
228
178
|
}
|
|
229
179
|
// turn it into a componentSet to resolve filenames
|
|
230
180
|
const remoteChangesAsComponentSet = new source_deploy_retrieve_1.ComponentSet(filteredChanges.map((element) => ({
|
|
@@ -247,6 +197,19 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
247
197
|
}
|
|
248
198
|
throw new Error(`unsupported options: ${JSON.stringify(options)}`);
|
|
249
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* Convenience method to reduce duplicated steps required to do a fka pull
|
|
203
|
+
* It's full of side effects: retrieving remote deletes, deleting those files locall, and then updating tracking files
|
|
204
|
+
* Most bizarrely, it then returns a ComponentSet of the remote nonDeletes.
|
|
205
|
+
*
|
|
206
|
+
* @returns the ComponentSet for what you would retrieve now that the deletes are done
|
|
207
|
+
*/
|
|
208
|
+
async maybeApplyRemoteDeletesToLocal() {
|
|
209
|
+
const changesToDelete = await this.getChanges({ origin: 'remote', state: 'delete', format: 'SourceComponent' });
|
|
210
|
+
await this.deleteFilesAndUpdateTracking(changesToDelete);
|
|
211
|
+
return this.remoteNonDeletesAsComponentSet();
|
|
212
|
+
}
|
|
250
213
|
/**
|
|
251
214
|
*
|
|
252
215
|
* returns immediately if there are no changesToDelete
|
|
@@ -294,8 +257,8 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
294
257
|
await this.ensureLocalTracking();
|
|
295
258
|
// relative paths make smaller trees AND isogit wants them relative
|
|
296
259
|
const relativeOptions = {
|
|
297
|
-
files: ((_a = options.files) !== null && _a !== void 0 ? _a : []).map((file) =>
|
|
298
|
-
deletedFiles: ((_b = options.deletedFiles) !== null && _b !== void 0 ? _b : []).map((file) =>
|
|
260
|
+
files: ((_a = options.files) !== null && _a !== void 0 ? _a : []).map((file) => (0, functions_1.ensureRelative)(file, this.projectPath)),
|
|
261
|
+
deletedFiles: ((_b = options.deletedFiles) !== null && _b !== void 0 ? _b : []).map((file) => (0, functions_1.ensureRelative)(file, this.projectPath)),
|
|
299
262
|
};
|
|
300
263
|
// plot twist: if you delete a member of a bundle (ex: lwc/foo/foo.css) and push, it'll not be in the fileResponses (deployedFiles) or deletedFiles
|
|
301
264
|
// what got deleted? Any local changes NOT in the fileResponses but part of a successfully deployed bundle
|
|
@@ -329,17 +292,23 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
329
292
|
}
|
|
330
293
|
await this.remoteSourceTrackingService.syncSpecifiedElements(fileResponses);
|
|
331
294
|
}
|
|
295
|
+
async reReadLocalTrackingCache() {
|
|
296
|
+
await this.localRepo.getStatus(true);
|
|
297
|
+
}
|
|
332
298
|
/**
|
|
333
299
|
* If the local tracking shadowRepo doesn't exist, it will be created.
|
|
334
|
-
* Does nothing if it already exists
|
|
300
|
+
* Does nothing if it already exists, unless you've instantiate SourceTracking to not cache local status, in which case it'll re-read your files
|
|
335
301
|
* Useful before parallel operations
|
|
336
302
|
*/
|
|
337
303
|
async ensureLocalTracking() {
|
|
338
304
|
if (this.localRepo) {
|
|
305
|
+
if (this.ignoreLocalCache) {
|
|
306
|
+
await this.localRepo.getStatus(true);
|
|
307
|
+
}
|
|
339
308
|
return;
|
|
340
309
|
}
|
|
341
310
|
this.localRepo = await localShadowRepo_1.ShadowRepo.getInstance({
|
|
342
|
-
orgId: this.
|
|
311
|
+
orgId: this.orgId,
|
|
343
312
|
projectPath: (0, path_1.normalize)(this.projectPath),
|
|
344
313
|
packageDirs: this.packagesDirs,
|
|
345
314
|
hasSfdxTrackingFiles: this.hasSfdxTrackingFiles,
|
|
@@ -395,7 +364,7 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
395
364
|
* Deletes the remote tracking files
|
|
396
365
|
*/
|
|
397
366
|
async clearRemoteTracking() {
|
|
398
|
-
return remoteSourceTrackingService_1.RemoteSourceTrackingService.delete(this.
|
|
367
|
+
return remoteSourceTrackingService_1.RemoteSourceTrackingService.delete(this.orgId, this.hasSfdxTrackingFiles);
|
|
399
368
|
}
|
|
400
369
|
/**
|
|
401
370
|
* Sets the files to max revision so that no changes appear
|
|
@@ -409,6 +378,7 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
409
378
|
* Compares local and remote changes to detect conflicts
|
|
410
379
|
*/
|
|
411
380
|
async getConflicts() {
|
|
381
|
+
var _a;
|
|
412
382
|
// we're going to need have both initialized
|
|
413
383
|
await Promise.all([this.ensureRemoteTracking(), this.ensureLocalTracking()]);
|
|
414
384
|
// Strategy: check local changes first (since it'll be faster) to avoid callout
|
|
@@ -430,201 +400,128 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
430
400
|
if (remoteChanges.length === 0) {
|
|
431
401
|
return [];
|
|
432
402
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
metadataKeyIndex.set((0, functions_1.getMetadataKey)(change.name, change.type), change);
|
|
440
|
-
}
|
|
441
|
-
(_a = change.filenames) === null || _a === void 0 ? void 0 : _a.map((filename) => {
|
|
442
|
-
fileNameIndex.set(filename, change);
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
const conflicts = new Set();
|
|
446
|
-
this.populateTypesAndNames({ elements: localChanges, excludeUnresolvable: true }).map((change) => {
|
|
447
|
-
var _a;
|
|
448
|
-
const metadataKey = (0, functions_1.getMetadataKey)(change.name, change.type);
|
|
449
|
-
// option 1: name and type match
|
|
450
|
-
if (metadataKeyIndex.has(metadataKey)) {
|
|
451
|
-
conflicts.add({ ...metadataKeyIndex.get(metadataKey) });
|
|
452
|
-
}
|
|
453
|
-
else {
|
|
454
|
-
// option 2: some of the filenames match
|
|
455
|
-
(_a = change.filenames) === null || _a === void 0 ? void 0 : _a.map((filename) => {
|
|
456
|
-
if (fileNameIndex.has(filename)) {
|
|
457
|
-
conflicts.add({ ...fileNameIndex.get(filename) });
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
}
|
|
403
|
+
(_a = this.forceIgnore) !== null && _a !== void 0 ? _a : (this.forceIgnore = source_deploy_retrieve_1.ForceIgnore.findAndCreate(this.project.getDefaultPackage().path));
|
|
404
|
+
return (0, conflicts_1.getDedupedConflictsFromChanges)({
|
|
405
|
+
localChanges,
|
|
406
|
+
remoteChanges,
|
|
407
|
+
projectPath: this.projectPath,
|
|
408
|
+
forceIgnore: this.forceIgnore,
|
|
461
409
|
});
|
|
462
|
-
// deeply de-dupe
|
|
463
|
-
return Array.from(conflicts);
|
|
464
410
|
}
|
|
465
411
|
/**
|
|
466
|
-
*
|
|
412
|
+
* handles both remote and local tracking
|
|
467
413
|
*
|
|
468
|
-
* @
|
|
469
|
-
* @input excludeUnresolvable: boolean Filter out components where you can't get the name and type (that is, it's probably not a valid source component)
|
|
470
|
-
* @input resolveDeleted: constructs a virtualTree instead of the actual filesystem--useful when the files no longer exist
|
|
471
|
-
* @input useFsForceIgnore: (default behavior) use forceIgnore from the filesystem. If false, uses the base forceIgnore from SDR
|
|
414
|
+
* @param result FileResponse[]
|
|
472
415
|
*/
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
416
|
+
async updateTrackingFromDeploy(deployResult) {
|
|
417
|
+
const successes = deployResult
|
|
418
|
+
.getFileResponses()
|
|
419
|
+
.filter((fileResponse) => fileResponse.state !== source_deploy_retrieve_1.ComponentStatus.Failed && fileResponse.filePath);
|
|
420
|
+
if (!successes.length) {
|
|
421
|
+
return;
|
|
476
422
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
423
|
+
await Promise.all([
|
|
424
|
+
this.updateLocalTracking({
|
|
425
|
+
// assertions allowed because filtered above
|
|
426
|
+
files: successes
|
|
427
|
+
.filter((fileResponse) => fileResponse.state !== source_deploy_retrieve_1.ComponentStatus.Deleted)
|
|
428
|
+
.map((fileResponse) => fileResponse.filePath),
|
|
429
|
+
deletedFiles: successes
|
|
430
|
+
.filter((fileResponse) => fileResponse.state === source_deploy_retrieve_1.ComponentStatus.Deleted)
|
|
431
|
+
.map((fileResponse) => fileResponse.filePath),
|
|
432
|
+
}),
|
|
433
|
+
this.updateRemoteTracking(successes.map(({ state, fullName, type, filePath }) => ({ state, fullName, type, filePath }))),
|
|
434
|
+
]);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* handles both remote and local tracking
|
|
438
|
+
*
|
|
439
|
+
* @param result FileResponse[]
|
|
440
|
+
*/
|
|
441
|
+
async updateTrackingFromRetrieve(retrieveResult) {
|
|
442
|
+
const successes = retrieveResult
|
|
443
|
+
.getFileResponses()
|
|
444
|
+
.filter((fileResponse) => fileResponse.state !== source_deploy_retrieve_1.ComponentStatus.Failed);
|
|
445
|
+
if (!successes.length) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
await Promise.all([
|
|
449
|
+
this.updateLocalTracking({
|
|
450
|
+
// assertion allowed because it's filtering out undefined
|
|
451
|
+
files: successes.map((fileResponse) => fileResponse.filePath).filter(Boolean),
|
|
452
|
+
}),
|
|
453
|
+
this.updateRemoteTracking(successes.map(({ state, fullName, type, filePath }) => ({ state, fullName, type, filePath })), true // retrieves don't need to poll for SourceMembers
|
|
454
|
+
),
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* If you've already got an instance of STL, but need to change the conflicts setting
|
|
459
|
+
* normally you set this on instantiation
|
|
460
|
+
*
|
|
461
|
+
* @param value true/false
|
|
462
|
+
*/
|
|
463
|
+
setIgnoreConflicts(value) {
|
|
464
|
+
this.ignoreConflicts = value;
|
|
465
|
+
}
|
|
466
|
+
async maybeSubscribeLifecycleEvents() {
|
|
467
|
+
if (this.subscribeSDREvents && (await this.org.tracksSource())) {
|
|
468
|
+
const lifecycle = core_1.Lifecycle.getInstance();
|
|
469
|
+
// the only thing STL uses pre events for is to check conflicts. So if you don't care about conflicts, don't listen!
|
|
470
|
+
if (!this.ignoreConflicts) {
|
|
471
|
+
this.logger.debug('subscribing to predeploy/retrieve events');
|
|
472
|
+
// subscribe to SDR `pre` events to handle conflicts before deploy/retrieve
|
|
473
|
+
lifecycle.on('scopedPreDeploy', async (e) => {
|
|
474
|
+
this.logger.debug('received scopedPreDeploy event');
|
|
475
|
+
if (e.orgId === this.orgId) {
|
|
476
|
+
(0, conflicts_1.throwIfConflicts)((0, conflicts_1.findConflictsInComponentSet)(e.componentSet, await this.getConflicts()));
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
lifecycle.on('scopedPreRetrieve', async (e) => {
|
|
480
|
+
this.logger.debug('received scopedPreRetrieve event');
|
|
481
|
+
if (e.orgId === this.orgId) {
|
|
482
|
+
(0, conflicts_1.throwIfConflicts)((0, conflicts_1.findConflictsInComponentSet)(e.componentSet, await this.getConflicts()));
|
|
522
483
|
}
|
|
523
484
|
});
|
|
524
485
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
486
|
+
// subscribe to SDR post-deploy event
|
|
487
|
+
this.logger.debug('subscribing to postdeploy/retrieve events');
|
|
488
|
+
// yes, the post hooks really have different payloads!
|
|
489
|
+
lifecycle.on('scopedPostDeploy', async (e) => {
|
|
490
|
+
this.logger.debug('received scopedPostDeploy event');
|
|
491
|
+
if (e.orgId === this.orgId) {
|
|
492
|
+
await this.updateTrackingFromDeploy(e.deployResult);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
lifecycle.on('scopedPostRetrieve', async (e) => {
|
|
496
|
+
this.logger.debug('received scopedPostRetrieve event');
|
|
497
|
+
if (e.orgId === this.orgId) {
|
|
498
|
+
await this.updateTrackingFromRetrieve(e.retrieveResult);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
529
502
|
}
|
|
530
503
|
async getLocalStatusRows() {
|
|
531
504
|
await this.ensureLocalTracking();
|
|
532
505
|
let results = [];
|
|
533
|
-
const localDeletes =
|
|
506
|
+
const localDeletes = (0, populateTypesAndNames_1.populateTypesAndNames)({
|
|
534
507
|
elements: await this.getChanges({ origin: 'local', state: 'delete', format: 'ChangeResult' }),
|
|
535
508
|
excludeUnresolvable: true,
|
|
536
509
|
resolveDeleted: true,
|
|
537
|
-
|
|
510
|
+
projectPath: this.projectPath,
|
|
538
511
|
});
|
|
539
|
-
const localAdds =
|
|
512
|
+
const localAdds = (0, populateTypesAndNames_1.populateTypesAndNames)({
|
|
540
513
|
elements: await this.getChanges({ origin: 'local', state: 'add', format: 'ChangeResult' }),
|
|
541
514
|
excludeUnresolvable: true,
|
|
542
|
-
|
|
515
|
+
projectPath: this.projectPath,
|
|
543
516
|
});
|
|
544
|
-
const localModifies =
|
|
517
|
+
const localModifies = (0, populateTypesAndNames_1.populateTypesAndNames)({
|
|
545
518
|
elements: await this.getChanges({ origin: 'local', state: 'modify', format: 'ChangeResult' }),
|
|
546
519
|
excludeUnresolvable: true,
|
|
547
|
-
|
|
520
|
+
projectPath: this.projectPath,
|
|
548
521
|
});
|
|
549
522
|
results = results.concat(localAdds.flatMap((item) => this.localChangesToOutputRow(item, 'add')), localModifies.flatMap((item) => this.localChangesToOutputRow(item, 'modify')), localDeletes.flatMap((item) => this.localChangesToOutputRow(item, 'delete')));
|
|
550
523
|
return results;
|
|
551
524
|
}
|
|
552
|
-
registrySupportsType(type) {
|
|
553
|
-
try {
|
|
554
|
-
if (metadataKeys_1.mappingsForSourceMemberTypesToMetadataType.has(type)) {
|
|
555
|
-
return true;
|
|
556
|
-
}
|
|
557
|
-
// this must use getTypeByName because findType doesn't support addressable child types (ex: customField!)
|
|
558
|
-
this.registry.getTypeByName(type);
|
|
559
|
-
return true;
|
|
560
|
-
}
|
|
561
|
-
catch (e) {
|
|
562
|
-
process.emitWarning(`Unable to find type ${type} in registry`);
|
|
563
|
-
return false;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* uses SDR to translate remote metadata records into local file paths
|
|
568
|
-
*/
|
|
569
|
-
populateFilePaths(elements) {
|
|
570
|
-
if (elements.length === 0) {
|
|
571
|
-
return [];
|
|
572
|
-
}
|
|
573
|
-
this.logger.debug('populateFilePaths for change elements', elements);
|
|
574
|
-
// component set generated from an array of MetadataMember from all the remote changes
|
|
575
|
-
// but exclude the ones that aren't in the registry
|
|
576
|
-
const remoteChangesAsMetadataMember = elements
|
|
577
|
-
.map((element) => {
|
|
578
|
-
if (typeof element.type === 'string' && typeof element.name === 'string') {
|
|
579
|
-
return {
|
|
580
|
-
type: element.type,
|
|
581
|
-
fullName: element.name,
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
})
|
|
585
|
-
.filter(guards_1.metadataMemberGuard);
|
|
586
|
-
const remoteChangesAsComponentSet = new source_deploy_retrieve_1.ComponentSet(remoteChangesAsMetadataMember);
|
|
587
|
-
this.logger.debug(` the generated component set has ${remoteChangesAsComponentSet.size.toString()} items`);
|
|
588
|
-
if (remoteChangesAsComponentSet.size < elements.length) {
|
|
589
|
-
// there *could* be something missing
|
|
590
|
-
// some types (ex: LWC) show up as multiple files in the remote changes, but only one in the component set
|
|
591
|
-
// iterate the elements to see which ones didn't make it into the component set
|
|
592
|
-
const missingComponents = elements.filter((element) => !remoteChangesAsComponentSet.has({ type: element === null || element === void 0 ? void 0 : element.type, fullName: element === null || element === void 0 ? void 0 : element.name }));
|
|
593
|
-
// Throw if anything was actually missing
|
|
594
|
-
if (missingComponents.length > 0) {
|
|
595
|
-
throw new Error(`unable to generate complete component set for ${elements
|
|
596
|
-
.map((element) => `${element.name} (${element.type})`)
|
|
597
|
-
.join(os_1.EOL)}`);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
const matchingLocalSourceComponentsSet = source_deploy_retrieve_1.ComponentSet.fromSource({
|
|
601
|
-
fsPaths: this.packagesDirs.map((dir) => dir.path),
|
|
602
|
-
include: remoteChangesAsComponentSet,
|
|
603
|
-
});
|
|
604
|
-
this.logger.debug(` local source-backed component set has ${matchingLocalSourceComponentsSet.size.toString()} items from remote`);
|
|
605
|
-
// make it simpler to find things later
|
|
606
|
-
const elementMap = new Map();
|
|
607
|
-
elements.map((element) => {
|
|
608
|
-
elementMap.set((0, functions_1.getKeyFromObject)(element), element);
|
|
609
|
-
});
|
|
610
|
-
// iterates the local components and sets their filenames
|
|
611
|
-
for (const matchingComponent of matchingLocalSourceComponentsSet.getSourceComponents().toArray()) {
|
|
612
|
-
if (matchingComponent.fullName && matchingComponent.type.name) {
|
|
613
|
-
this.logger.debug(`${matchingComponent.fullName}|${matchingComponent.type.name} matches ${matchingComponent.xml} and maybe ${matchingComponent.walkContent().toString()}`);
|
|
614
|
-
const key = (0, functions_1.getMetadataKey)(matchingComponent.type.name, matchingComponent.fullName);
|
|
615
|
-
elementMap.set(key, {
|
|
616
|
-
...elementMap.get(key),
|
|
617
|
-
modified: true,
|
|
618
|
-
origin: 'remote',
|
|
619
|
-
filenames: [matchingComponent.xml, ...matchingComponent.walkContent()].filter((filename) => filename),
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return Array.from(elementMap.values());
|
|
624
|
-
}
|
|
625
|
-
ensureRelative(filePath) {
|
|
626
|
-
return (0, path_1.isAbsolute)(filePath) ? (0, path_1.relative)(this.projectPath, filePath) : filePath;
|
|
627
|
-
}
|
|
628
525
|
async getLocalChangesAsFilenames(state) {
|
|
629
526
|
if (state === 'modify') {
|
|
630
527
|
return this.localRepo.getModifyFilenames();
|
|
@@ -641,22 +538,21 @@ class SourceTracking extends kit_1.AsyncCreatable {
|
|
|
641
538
|
throw new Error(`unable to get local changes for state ${state}`);
|
|
642
539
|
}
|
|
643
540
|
localChangesToOutputRow(input, localType) {
|
|
644
|
-
var _a
|
|
541
|
+
var _a;
|
|
645
542
|
this.logger.debug('converting ChangeResult to a row', input);
|
|
646
|
-
|
|
647
|
-
type: (_a = input.type) !== null && _a !== void 0 ? _a : '',
|
|
648
|
-
origin: 'local',
|
|
649
|
-
state: localType,
|
|
650
|
-
fullName: (_b = input.name) !== null && _b !== void 0 ? _b : '',
|
|
651
|
-
// ignored property will be set in populateTypesAndNames
|
|
652
|
-
ignored: (_c = input.ignored) !== null && _c !== void 0 ? _c : false,
|
|
653
|
-
};
|
|
543
|
+
(_a = this.forceIgnore) !== null && _a !== void 0 ? _a : (this.forceIgnore = source_deploy_retrieve_1.ForceIgnore.findAndCreate(this.project.getDefaultPackage().path));
|
|
654
544
|
if (input.filenames) {
|
|
655
|
-
return input.filenames.map((filename) =>
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
545
|
+
return input.filenames.map((filename) => {
|
|
546
|
+
var _a, _b;
|
|
547
|
+
return ({
|
|
548
|
+
type: (_a = input.type) !== null && _a !== void 0 ? _a : '',
|
|
549
|
+
state: localType,
|
|
550
|
+
fullName: (_b = input.name) !== null && _b !== void 0 ? _b : '',
|
|
551
|
+
filePath: filename,
|
|
552
|
+
origin: 'local',
|
|
553
|
+
ignored: this.forceIgnore.denies(filename),
|
|
554
|
+
});
|
|
555
|
+
});
|
|
660
556
|
}
|
|
661
557
|
throw new Error('no filenames found for local ChangeResult');
|
|
662
558
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/source-tracking",
|
|
3
3
|
"description": "API for tracking local and remote Salesforce metadata changes",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"/oclif.manifest.json"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@salesforce/core": "^3.
|
|
47
|
-
"@salesforce/kit": "^1.5.
|
|
48
|
-
"@salesforce/source-deploy-retrieve": "^6.0
|
|
46
|
+
"@salesforce/core": "^3.22.1",
|
|
47
|
+
"@salesforce/kit": "^1.5.42",
|
|
48
|
+
"@salesforce/source-deploy-retrieve": "^6.1.0",
|
|
49
49
|
"graceful-fs": "^4.2.9",
|
|
50
50
|
"isomorphic-git": "1.17.0",
|
|
51
51
|
"ts-retry-promise": "^0.6.0"
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"cz-conventional-changelog": "^3.3.0",
|
|
64
64
|
"eslint": "^7.32.0",
|
|
65
65
|
"eslint-config-prettier": "^8.3.0",
|
|
66
|
-
"eslint-config-salesforce": "^0.1
|
|
66
|
+
"eslint-config-salesforce": "^1.0.1",
|
|
67
67
|
"eslint-config-salesforce-license": "^0.1.6",
|
|
68
|
-
"eslint-config-salesforce-typescript": "^0.
|
|
68
|
+
"eslint-config-salesforce-typescript": "^1.0.0",
|
|
69
69
|
"eslint-plugin-header": "^3.1.1",
|
|
70
70
|
"eslint-plugin-import": "2.25.4",
|
|
71
71
|
"eslint-plugin-jsdoc": "^37.0.1",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"sinon": "^10.0.0",
|
|
81
81
|
"ts-node": "^10.1.0",
|
|
82
82
|
"ts-prune": "^0.10.0",
|
|
83
|
-
"typescript": "^4.
|
|
83
|
+
"typescript": "^4.7.4"
|
|
84
84
|
},
|
|
85
85
|
"config": {
|
|
86
86
|
"commitizen": {
|