@ds-sfdc/sfparty 1.2.6 → 1.3.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/.github/workflows/cicd.yaml +38 -0
- package/babel.config.cjs +3 -0
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/applicationVisibilities.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/fieldPermissions/Case.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/main.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AIRecordInsight.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Account.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentInvitation.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentTopicTimeSlot.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Asset.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationForm.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormDataUse.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormText.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/BusinessBrand.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Campaign.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscription.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionChannelType.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionTiming.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Contact.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointAddress.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointEmail.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointPhone.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointTypeConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactRequest.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContractLineItem.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Customer.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DandBCompany.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUseLegalBasis.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUsePurpose.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Document.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EngagementChannelType.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Entitlement.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EntitlementContact.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Individual.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Lead.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Macro.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/OperatingHours.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Opportunity.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/PartyConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Promotion.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/QuickText.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Seller.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceContract.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceTerritory.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Shift.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/SocialPersona.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Solution.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/StreamingChannel.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebCart.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebStore.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlan.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlanTemplate.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkStepTemplate.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/tabVisibilities.yaml +29 -29
- package/force-app-party/main/default/profiles/External Identity User/applicationVisibilities.yaml +1 -1
- package/force-app-party/main/default/profiles/External Identity User/main.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Account.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Asset.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/BusinessBrand.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Contact.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointEmail.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointPhone.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Customer.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Document.yaml +1 -1
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Individual.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/PushTopic.yaml +4 -4
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Seller.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/tabVisibilities.yaml +30 -30
- package/package.json +7 -5
- package/src/index.js +7 -45
- package/src/lib/checkVersion.js +42 -0
- package/src/lib/fileUtils.js +38 -38
- package/src/lib/gitUtils.js +87 -140
- package/test/lib/git/diff.spec.js +54 -0
- package/test/lib/git/lastCommit.spec.js +69 -0
- package/test/lib/git/log.spec.js +33 -0
- package/test/lib/git/updateLastCommit.spec.js +65 -0
- package/test/root.spec.js +7 -0
- package/test_mocha/lib/fileUtils.js +374 -0
- package/test_mocha/lib/git.js +82 -0
- package/test_mocha/lib/gitUtils.js +54 -0
- package/{tests/lib/versionCheck.spec.js → test_mocha/lib/versionCheck.js} +0 -0
- package/{tests/root.spec.js → test_mocha/root.js} +0 -0
- package/force-app-party/main/default/labels/CustomLabels/labels/Description.yaml +0 -7
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/SocialPersona.yaml +0 -8
- package/tests/lib/gitUtils.spec.js +0 -137
package/src/index.js
CHANGED
|
@@ -13,7 +13,6 @@ import { marked } from 'marked'
|
|
|
13
13
|
import markedTerminal from 'marked-terminal'
|
|
14
14
|
|
|
15
15
|
import * as fileUtils from './lib/fileUtils.js'
|
|
16
|
-
import * as pkgObj from '../package.json' assert { type: "json" }
|
|
17
16
|
import * as yargOptions from './meta/yargs.js'
|
|
18
17
|
import * as metadataSplit from './party/split.js'
|
|
19
18
|
import * as metadataCombine from './party/combine.js'
|
|
@@ -21,8 +20,10 @@ import * as labelDefinition from './meta/CustomLabels.js'
|
|
|
21
20
|
import * as profileDefinition from './meta/Profiles.js'
|
|
22
21
|
import * as permsetDefinition from './meta/PermissionSets.js'
|
|
23
22
|
import * as workflowDefinition from './meta/Workflows.js'
|
|
23
|
+
import { checkVersion } from './lib/checkVersion.js'
|
|
24
24
|
import * as git from './lib/gitUtils.js'
|
|
25
25
|
|
|
26
|
+
const pkgObj = fileUtils.readFile('package.json')
|
|
26
27
|
const processStartTime = process.hrtime.bigint()
|
|
27
28
|
|
|
28
29
|
marked.setOptions({
|
|
@@ -139,7 +140,7 @@ yargs(hideBin(process.argv))
|
|
|
139
140
|
.check(yargCheck)
|
|
140
141
|
},
|
|
141
142
|
handler: (argv) => {
|
|
142
|
-
checkVersion(pkgObj.
|
|
143
|
+
checkVersion(axios, exec, pkgObj.version, true)
|
|
143
144
|
}
|
|
144
145
|
})
|
|
145
146
|
.command({
|
|
@@ -154,7 +155,7 @@ yargs(hideBin(process.argv))
|
|
|
154
155
|
.check(yargCheck)
|
|
155
156
|
},
|
|
156
157
|
handler: (argv) => {
|
|
157
|
-
checkVersion(pkgObj.
|
|
158
|
+
checkVersion(axios, exec, pkgObj.version)
|
|
158
159
|
global.format = argv.format
|
|
159
160
|
splitHandler(argv, processStartTime)
|
|
160
161
|
}
|
|
@@ -171,7 +172,7 @@ yargs(hideBin(process.argv))
|
|
|
171
172
|
.check(yargCheck)
|
|
172
173
|
},
|
|
173
174
|
handler: (argv) => {
|
|
174
|
-
checkVersion(pkgObj.
|
|
175
|
+
checkVersion(axios, exec, pkgObj.version)
|
|
175
176
|
global.format = argv.format
|
|
176
177
|
const startProm = new Promise((resolve, reject) => {
|
|
177
178
|
if (argv.git !== undefined) {
|
|
@@ -551,7 +552,7 @@ function gitFiles(data) {
|
|
|
551
552
|
const pathArray = item.path.split(path.sep)
|
|
552
553
|
if (pathArray.length > 3) {
|
|
553
554
|
if (getDirectories().includes(pathArray[3])) {
|
|
554
|
-
switch (
|
|
555
|
+
switch (item.action) {
|
|
555
556
|
case 'add':
|
|
556
557
|
global.metaTypes[getKey(pathArray[3])].add.files.push(path.join(global.__basedir, item.path))
|
|
557
558
|
if (!global.metaTypes[getKey(pathArray[3])].add.directories.includes(pathArray[4])) {
|
|
@@ -602,7 +603,7 @@ function displayHeader() {
|
|
|
602
603
|
horizontal: '─',
|
|
603
604
|
vertical: '│',
|
|
604
605
|
}
|
|
605
|
-
let versionString = `sfparty v${pkgObj.
|
|
606
|
+
let versionString = `sfparty v${pkgObj.version}${(process.stdout.columns > pkgObj.description.length + 15) ? ' - ' + pkgObj.description : ''}`
|
|
606
607
|
let titleMessage = `${global.icons.party} ${chalk.yellowBright(versionString)} ${global.icons.party}`
|
|
607
608
|
titleMessage = titleMessage.padEnd((process.stdout.columns / 2) + versionString.length / 1.65)
|
|
608
609
|
titleMessage = titleMessage.padStart(process.stdout.columns)
|
|
@@ -640,42 +641,3 @@ function getRootPath(packageDir) {
|
|
|
640
641
|
|
|
641
642
|
return defaultDir
|
|
642
643
|
}
|
|
643
|
-
|
|
644
|
-
export async function checkVersion(currentVersion, update = false, test = false) {
|
|
645
|
-
try {
|
|
646
|
-
const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty')
|
|
647
|
-
const command = 'npm i -g @ds-sfdc/sfparty@latest'
|
|
648
|
-
if (currentVersion !== data['dist-tags'].latest) {
|
|
649
|
-
if (!test) console.log(`${(update) ? global.icons.working : global.icons.fail} A newer version ${chalk.bgCyanBright(data['dist-tags'].latest)} is available.`)
|
|
650
|
-
if (!update) {
|
|
651
|
-
if (test) return 'A newer version'
|
|
652
|
-
console.log(`Please upgrade by running ${chalk.cyanBright('sfparty update')}`)
|
|
653
|
-
} else {
|
|
654
|
-
if (!test) console.log(`Updating the application using ${chalk.cyanBright(command)}`)
|
|
655
|
-
exec('npm -v', (error, stdout, stderr) => {
|
|
656
|
-
if (error) {
|
|
657
|
-
if (test) 'npm is not installed'
|
|
658
|
-
global.logger.error("npm is not installed on this system. Please install npm and run the command again.")
|
|
659
|
-
return
|
|
660
|
-
} else {
|
|
661
|
-
exec(command, (error, stdout, stderr) => {
|
|
662
|
-
if (error) {
|
|
663
|
-
global.logger.error(error)
|
|
664
|
-
return
|
|
665
|
-
}
|
|
666
|
-
console.log(stdout)
|
|
667
|
-
console.log(stderr)
|
|
668
|
-
})
|
|
669
|
-
}
|
|
670
|
-
})
|
|
671
|
-
}
|
|
672
|
-
} else {
|
|
673
|
-
if (update) {
|
|
674
|
-
if (test) return 'You are on the latest version'
|
|
675
|
-
console.log(`${global.icons.success} You are on the latest version.`)
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
} catch (error) {
|
|
679
|
-
global.logger.error(error)
|
|
680
|
-
}
|
|
681
|
-
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export async function checkVersion(axios, exec, currentVersion, update = false) {
|
|
2
|
+
try {
|
|
3
|
+
const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty')
|
|
4
|
+
const command = 'npm i -g @ds-sfdc/sfparty@latest'
|
|
5
|
+
if (currentVersion !== data['dist-tags'].latest) {
|
|
6
|
+
console.log(`${(update) ? global.icons.working : global.icons.fail} A newer version ${chalk.bgCyanBright(data['dist-tags'].latest)} is available.`)
|
|
7
|
+
if (!update) {
|
|
8
|
+
console.log(`Please upgrade by running ${chalk.cyanBright('sfparty update')}`)
|
|
9
|
+
return 'A newer version'
|
|
10
|
+
} else {
|
|
11
|
+
console.log(`Updating the application using ${chalk.cyanBright(command)}`)
|
|
12
|
+
exec('npm -v', (error, stdout, stderr) => {
|
|
13
|
+
if (error) {
|
|
14
|
+
global.logger.error("npm is not installed on this system. Please install npm and run the command again.")
|
|
15
|
+
return 'npm is not installed'
|
|
16
|
+
} else {
|
|
17
|
+
exec(command, (error, stdout, stderr) => {
|
|
18
|
+
if (error) {
|
|
19
|
+
global.logger.error(error)
|
|
20
|
+
reject(error)
|
|
21
|
+
} else {
|
|
22
|
+
console.log(stdout)
|
|
23
|
+
console.log(stderr)
|
|
24
|
+
resolve(true)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
if (update) {
|
|
32
|
+
console.log(`${global.icons.success} You are on the latest version.`)
|
|
33
|
+
return 'You are on the latest version'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
global.logger.error(error)
|
|
38
|
+
throw error
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
package/src/lib/fileUtils.js
CHANGED
|
@@ -4,42 +4,42 @@ import * as path from 'path'
|
|
|
4
4
|
import * as yaml from 'js-yaml'
|
|
5
5
|
import { Parser } from 'xml2js'
|
|
6
6
|
|
|
7
|
-
export function directoryExists(dirPath) {
|
|
8
|
-
return
|
|
7
|
+
export function directoryExists(dirPath, fsTmp = fs) {
|
|
8
|
+
return fsTmp.existsSync(dirPath) && fsTmp.statSync(dirPath).isDirectory()
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function fileExists(filePath) {
|
|
12
|
-
return
|
|
11
|
+
export function fileExists(filePath, fsTmp = fs) {
|
|
12
|
+
return fsTmp.existsSync(filePath) && fsTmp.statSync(filePath).isFile()
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function createDirectory(dirPath) {
|
|
16
|
-
if (!
|
|
17
|
-
|
|
15
|
+
export function createDirectory(dirPath, fsTmp = fs) {
|
|
16
|
+
if (!fsTmp.existsSync(dirPath)) {
|
|
17
|
+
fsTmp.mkdirSync(dirPath, { recursive: true })
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function deleteDirectory(dirPath, recursive = false) {
|
|
22
|
-
if (!directoryExists(dirPath)) {
|
|
21
|
+
export function deleteDirectory(dirPath, recursive = false, fsTmp = fs) {
|
|
22
|
+
if (!directoryExists(dirPath, fsTmp)) {
|
|
23
23
|
return false
|
|
24
24
|
} else {
|
|
25
|
-
if (
|
|
26
|
-
|
|
25
|
+
if (fsTmp.existsSync(dirPath)) {
|
|
26
|
+
fsTmp.readdirSync(dirPath).forEach(function (file) {
|
|
27
27
|
var curPath = path.join(dirPath, file)
|
|
28
|
-
if (
|
|
29
|
-
deleteDirectory(curPath, recursive);
|
|
28
|
+
if (fsTmp.lstatSync(curPath).isDirectory() && recursive) { // recurse
|
|
29
|
+
deleteDirectory(curPath, recursive, fsTmp);
|
|
30
30
|
} else { // delete file
|
|
31
|
-
|
|
31
|
+
fsTmp.unlinkSync(curPath);
|
|
32
32
|
}
|
|
33
33
|
})
|
|
34
|
-
return
|
|
34
|
+
return fsTmp.rmdirSync(dirPath);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export function getFiles(dirPath, filter = undefined) {
|
|
39
|
+
export function getFiles(dirPath, filter = undefined, fsTmp = fs) {
|
|
40
40
|
const filesList = []
|
|
41
|
-
if (directoryExists(dirPath)) {
|
|
42
|
-
|
|
41
|
+
if (directoryExists(dirPath, fsTmp)) {
|
|
42
|
+
fsTmp.readdirSync(dirPath).forEach(file => {
|
|
43
43
|
if (!filter) {
|
|
44
44
|
filesList.push(file)
|
|
45
45
|
} else {
|
|
@@ -55,9 +55,9 @@ export function getFiles(dirPath, filter = undefined) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
export function getDirectories(dirPath) {
|
|
59
|
-
if (directoryExists(dirPath)) {
|
|
60
|
-
return
|
|
58
|
+
export function getDirectories(dirPath, fsTmp = fs) {
|
|
59
|
+
if (directoryExists(dirPath, fsTmp)) {
|
|
60
|
+
return fsTmp.readdirSync(dirPath, { withFileTypes: true })
|
|
61
61
|
.filter(dirent => dirent.isDirectory())
|
|
62
62
|
.map(dirent => dirent.name)
|
|
63
63
|
} else {
|
|
@@ -65,35 +65,35 @@ export function getDirectories(dirPath) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function deleteFile(filePath) {
|
|
69
|
-
if (!fileExists(filePath)) {
|
|
68
|
+
export function deleteFile(filePath, fsTmp = fs) {
|
|
69
|
+
if (!fileExists(filePath, fsTmp)) {
|
|
70
70
|
return false
|
|
71
71
|
} else {
|
|
72
|
-
return
|
|
72
|
+
return fsTmp.unlinkSync(filePath, { recursive: false, force: true });
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export function fileInfo(filePath) {
|
|
76
|
+
export function fileInfo(filePath, fsTmp = fs) {
|
|
77
77
|
return {
|
|
78
78
|
"dirname": path.join(path.dirname(filePath)), //something/folder/example
|
|
79
79
|
"basename": path.basename(filePath, path.extname(filePath)), //example
|
|
80
80
|
"filename": path.basename(filePath), //example.txt
|
|
81
81
|
"extname": path.extname(filePath), //txt
|
|
82
|
-
"exists":
|
|
83
|
-
"stats":
|
|
82
|
+
"exists": fsTmp.existsSync(filePath), //true if exists or false if not exists
|
|
83
|
+
"stats": fsTmp.existsSync(filePath) ? fsTmp.statSync(filePath) : undefined //stats object if exists or undefined if not exists
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
export function saveFile(json, fileName, format = path.extname(fileName).replace('.', '')) {
|
|
87
|
+
export function saveFile(json, fileName, format = path.extname(fileName).replace('.', ''), fsTmp = fs) {
|
|
88
88
|
try {
|
|
89
89
|
switch (format) {
|
|
90
90
|
case 'json':
|
|
91
91
|
let jsonString = JSON.stringify(json, null, '\t')
|
|
92
|
-
|
|
92
|
+
fsTmp.writeFileSync(fileName, jsonString)
|
|
93
93
|
break
|
|
94
94
|
case 'yaml':
|
|
95
95
|
let doc = yaml.dump(json)
|
|
96
|
-
|
|
96
|
+
fsTmp.writeFileSync(fileName, doc)
|
|
97
97
|
break
|
|
98
98
|
}
|
|
99
99
|
return true
|
|
@@ -103,11 +103,11 @@ export function saveFile(json, fileName, format = path.extname(fileName).replace
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
export function readFile(fileName, convert = true) {
|
|
106
|
+
export function readFile(fileName, convert = true, fsTmp = fs) {
|
|
107
107
|
try {
|
|
108
108
|
let result = undefined
|
|
109
|
-
if (fileExists(fileName)) {
|
|
110
|
-
const data =
|
|
109
|
+
if (fileExists(fileName, fsTmp)) {
|
|
110
|
+
const data = fsTmp.readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
|
|
111
111
|
if (convert && fileName.indexOf('.yaml') != -1) {
|
|
112
112
|
result = yaml.load(data)
|
|
113
113
|
} else if (convert && fileName.indexOf('.json') != -1) {
|
|
@@ -141,17 +141,17 @@ async function convertXML(data) {
|
|
|
141
141
|
})
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
export function writeFile(fileName, data, atime = new Date(), mtime = new Date()) {
|
|
144
|
+
export function writeFile(fileName, data, atime = new Date(), mtime = new Date(), fsTmp = fs) {
|
|
145
145
|
try {
|
|
146
146
|
// write data to the file
|
|
147
|
-
|
|
147
|
+
fsTmp.writeFileSync(fileName, data)
|
|
148
148
|
|
|
149
149
|
// if atime or mtime are undefined, use current date/time
|
|
150
150
|
if (atime === undefined) atime = new Date()
|
|
151
151
|
if (mtime === undefined) mtime = new Date()
|
|
152
152
|
|
|
153
153
|
// update XML file to match the latest atime and mtime of the files processed
|
|
154
|
-
|
|
154
|
+
fsTmp.utimesSync(fileName, atime, mtime)
|
|
155
155
|
|
|
156
156
|
} catch (error) {
|
|
157
157
|
global.logger.error(error)
|
|
@@ -159,7 +159,7 @@ export function writeFile(fileName, data, atime = new Date(), mtime = new Date()
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
export function find(filename, root) {
|
|
162
|
+
export function find(filename, root, fsTmp = fs) {
|
|
163
163
|
// code Copyright (c) 2014, Ben Gourley
|
|
164
164
|
// https://github.com/bengourley/find-nearest-file
|
|
165
165
|
root = root || process.cwd();
|
|
@@ -176,7 +176,7 @@ export function find(filename, root) {
|
|
|
176
176
|
var file = path.join(directory, filename)
|
|
177
177
|
|
|
178
178
|
try {
|
|
179
|
-
if (
|
|
179
|
+
if (fsTmp.statSync(file).isFile()) return file
|
|
180
180
|
// stat existed, but isFile() returned false
|
|
181
181
|
return nextLevelUp()
|
|
182
182
|
} catch (e) {
|
package/src/lib/gitUtils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import {
|
|
2
|
+
import { execSync } from 'node:child_process'
|
|
3
3
|
import * as os from 'node:os'
|
|
4
4
|
import { existsSync } from 'fs'
|
|
5
5
|
import * as fileUtils from './fileUtils.js'
|
|
@@ -15,169 +15,116 @@ const defaultDefinition = {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const status = {
|
|
18
|
-
A:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
A: {
|
|
19
|
+
type: 'add',
|
|
20
|
+
action: 'add'
|
|
21
|
+
},
|
|
22
|
+
C: {
|
|
23
|
+
type: 'copy',
|
|
24
|
+
action: 'add',
|
|
25
|
+
},
|
|
26
|
+
D: {
|
|
27
|
+
type: 'delete',
|
|
28
|
+
action: 'delete'
|
|
29
|
+
},
|
|
30
|
+
M: {
|
|
31
|
+
type: 'modify',
|
|
32
|
+
action: 'add'
|
|
33
|
+
},
|
|
34
|
+
R: {
|
|
35
|
+
type: 'rename',
|
|
36
|
+
action: 'add'
|
|
37
|
+
},
|
|
38
|
+
T: {
|
|
39
|
+
type: 'type change',
|
|
40
|
+
action: 'add'
|
|
41
|
+
},
|
|
42
|
+
U: {
|
|
43
|
+
type: 'unmerged',
|
|
44
|
+
action: 'ignore'
|
|
45
|
+
},
|
|
46
|
+
X: {
|
|
47
|
+
type: 'unknown',
|
|
48
|
+
action: 'ignore'
|
|
49
|
+
},
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
export function diff(dir, gitRef) {
|
|
52
|
+
export function diff(dir, gitRef = 'HEAD', existsSyncStub = existsSync, execSyncStub = execSync) {
|
|
40
53
|
return new Promise((resolve, reject) => {
|
|
41
|
-
if (!
|
|
42
|
-
reject(new Error(`The directory "${dir}" does not exist`))
|
|
43
|
-
}
|
|
44
|
-
if (!existsSync(`${dir}/.git`)) {
|
|
54
|
+
if (!existsSyncStub(dir) || !existsSyncStub(path.join(dir, '.git'))) {
|
|
45
55
|
reject(new Error(`The directory "${dir}" is not a git repository`))
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
let data = ''
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const gitString = result.toString()
|
|
53
|
-
const lastIndex = gitString.lastIndexOf(os.EOL)
|
|
54
|
-
let count = (gitString.match(/\n/g) || []).length;
|
|
55
|
-
data += gitString
|
|
56
|
-
const gitData = gitString.split(os.EOL)
|
|
57
|
-
let leftOver = ''
|
|
58
|
-
gitData.forEach((gitRow, index) => {
|
|
59
|
-
if (gitRow.indexOf('\t') !== -1 && (index < count || lastIndex + 1 == gitString)) {
|
|
60
|
-
const file = gitRow.split('\t')
|
|
61
|
-
if (file.slice(-1) !== '') {
|
|
62
|
-
files.push({
|
|
63
|
-
type: status[(file[0] === file.slice(-1)) ? 'A' : Array.from(file[0])[0]],
|
|
64
|
-
path: file.slice(-1)[0],
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
leftOver = gitRow
|
|
69
|
-
}
|
|
70
|
-
if (leftOver !== '') {
|
|
71
|
-
data = leftOver
|
|
72
|
-
} else {
|
|
73
|
-
data = ''
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
gitDiff.stderr.on("data", data => {
|
|
78
|
-
const errorMessage = 'git diff: ' + data.toString().split(os.EOL)[0]
|
|
79
|
-
|
|
80
|
-
reject(new Error(errorMessage))
|
|
81
|
-
})
|
|
82
|
-
gitDiff.on('error', (error) => {
|
|
83
|
-
if (error.message.indexOf('ENOENT')) {
|
|
84
|
-
error.message = 'git not installed or no entry found in path'
|
|
85
|
-
}
|
|
59
|
+
try {
|
|
60
|
+
data = execSyncStub(`git diff --name-status --oneline --relative ${gitRef}`, { cwd: dir }).toString()
|
|
61
|
+
} catch (error) {
|
|
86
62
|
reject(error)
|
|
87
|
-
}
|
|
88
|
-
gitDiff.on("close", code => {
|
|
89
|
-
if (data !== '') {
|
|
90
|
-
const gitData = data.toString().split(os.EOL)
|
|
91
|
-
gitData.forEach(gitRow => {
|
|
92
|
-
const file = gitRow.split('\t')
|
|
93
|
-
if (file.slice(-1) !== '') {
|
|
94
|
-
files.push({
|
|
95
|
-
type: status[(file[0] === file.slice(-1)) ? 'A' : Array.from(file[0])[0]],
|
|
96
|
-
path: file.slice(-1)[0],
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
resolve(files)
|
|
103
|
-
})
|
|
104
|
-
})
|
|
105
|
-
}
|
|
63
|
+
}
|
|
106
64
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
gitLog.on('error', (error) => {
|
|
120
|
-
if (error.message.indexOf('ENOENT')) {
|
|
121
|
-
error.message = 'git not installed or no entry found in path'
|
|
65
|
+
const gitData = data.toString().split(os.EOL)
|
|
66
|
+
const files = gitData.reduce((acc, gitRow) => {
|
|
67
|
+
if (gitRow.indexOf('\t') > 0) {
|
|
68
|
+
const file = gitRow.split('\t')
|
|
69
|
+
if (file.slice(-1) !== '') {
|
|
70
|
+
const statusType = status[(file[0] === file.slice(-1)) ? 'A' : Array.from(file[0])[0]];
|
|
71
|
+
acc.push({
|
|
72
|
+
type: statusType.type,
|
|
73
|
+
path: file.slice(-1)[0],
|
|
74
|
+
action: statusType.action
|
|
75
|
+
});
|
|
76
|
+
}
|
|
122
77
|
}
|
|
123
|
-
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
resolve(commits)
|
|
127
|
-
})
|
|
78
|
+
return acc;
|
|
79
|
+
}, []);
|
|
80
|
+
resolve(files);
|
|
128
81
|
})
|
|
129
82
|
}
|
|
130
83
|
|
|
131
|
-
export function
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
commit
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
84
|
+
export function log(dir, gitRef, execSyncStub = execSync) {
|
|
85
|
+
try {
|
|
86
|
+
const gitLog = execSyncStub(`git log --format=format:%H ${gitRef}`, { cwd: dir, encoding: 'utf-8' });
|
|
87
|
+
const commits = gitLog.split(os.EOL).filter(commit => commit)
|
|
88
|
+
return commits
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error.message.indexOf('ENOENT') > -1) {
|
|
91
|
+
error.message = 'git not installed or no entry found in path'
|
|
92
|
+
}
|
|
93
|
+
throw error
|
|
94
|
+
}
|
|
142
95
|
}
|
|
143
96
|
|
|
144
|
-
export function lastCommit(dir, fileName = 'index.yaml') {
|
|
145
|
-
|
|
146
|
-
const folder = path.
|
|
147
|
-
const filePath = path.
|
|
148
|
-
let
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
const data =
|
|
97
|
+
export function lastCommit(dir, fileName = 'index.yaml', existsSyncStub = existsSync, execSyncStub = execSync, fileUtilsStub = fileUtils) {
|
|
98
|
+
try {
|
|
99
|
+
const folder = path.resolve(dir, '.sfdx', 'sfparty')
|
|
100
|
+
const filePath = path.resolve(folder, fileName)
|
|
101
|
+
let lastCommit = undefined
|
|
102
|
+
|
|
103
|
+
fileUtilsStub.createDirectory(folder)
|
|
104
|
+
if (existsSyncStub(filePath)) {
|
|
105
|
+
const data = fileUtilsStub.readFile(filePath)
|
|
153
106
|
if (data.git.lastCommit !== undefined) {
|
|
154
|
-
|
|
107
|
+
lastCommit = data.git.lastCommit
|
|
155
108
|
}
|
|
156
109
|
}
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
})
|
|
166
|
-
.catch((error) => {
|
|
167
|
-
reject(error)
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
})
|
|
110
|
+
const latestCommit = execSyncStub(`git log --format=format:%H -1`, { cwd: dir, encoding: 'utf-8' })
|
|
111
|
+
return {
|
|
112
|
+
lastCommit: lastCommit,
|
|
113
|
+
latestCommit: latestCommit,
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw new Error(error)
|
|
117
|
+
}
|
|
171
118
|
}
|
|
172
119
|
|
|
173
|
-
export function updateLastCommit(dir, latest) {
|
|
120
|
+
export function updateLastCommit(dir, latest, fileUtilsStub = fileUtils) {
|
|
174
121
|
if (typeof latest !== 'string' && typeof latest !== 'undefined') throw new Error(`updateLastCommit received a ${typeof latest} instead of string`)
|
|
175
122
|
if (latest !== undefined) {
|
|
176
123
|
const folder = path.join(dir, '.sfdx', 'sfparty')
|
|
177
124
|
const fileName = path.join(folder, 'index.yaml')
|
|
178
125
|
let data = undefined
|
|
179
|
-
if (
|
|
180
|
-
data =
|
|
126
|
+
if (fileUtilsStub.fileExists(fileName)) {
|
|
127
|
+
data = fileUtilsStub.readFile(fileName)
|
|
181
128
|
}
|
|
182
129
|
|
|
183
130
|
if (data === undefined) {
|
|
@@ -185,6 +132,6 @@ export function updateLastCommit(dir, latest) {
|
|
|
185
132
|
}
|
|
186
133
|
|
|
187
134
|
data.git.lastCommit = latest
|
|
188
|
-
|
|
135
|
+
fileUtilsStub.saveFile(data, fileName)
|
|
189
136
|
}
|
|
190
137
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { execSync } from 'child_process'
|
|
2
|
+
import { existsSync } from 'fs'
|
|
3
|
+
import { diff } from '../../../src/lib/gitUtils'
|
|
4
|
+
|
|
5
|
+
jest.mock('fs', () => ({
|
|
6
|
+
existsSync: jest.fn()
|
|
7
|
+
}))
|
|
8
|
+
jest.mock('child_process', () => ({
|
|
9
|
+
execSync: jest.fn()
|
|
10
|
+
}))
|
|
11
|
+
const gitRef = "HEAD~1..HEAD"
|
|
12
|
+
|
|
13
|
+
describe('diff', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('rejects if directory is not a git repository', async () => {
|
|
19
|
+
existsSync.mockReturnValueOnce(false)
|
|
20
|
+
try {
|
|
21
|
+
await diff('/path/to/dir')
|
|
22
|
+
fail('Expected function to throw an error', gitRef, existsSync)
|
|
23
|
+
} catch (error) {
|
|
24
|
+
expect(error.message).toEqual('The directory "/path/to/dir" is not a git repository')
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('resolves with files when git diff command is successful', async () => {
|
|
29
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
30
|
+
execSync.mockReturnValueOnce(
|
|
31
|
+
`A\tfile1.txt
|
|
32
|
+
M\tfile2.txt
|
|
33
|
+
D\tfile3.txt`)
|
|
34
|
+
|
|
35
|
+
const files = await diff('/path/to/dir', gitRef, existsSync, execSync)
|
|
36
|
+
expect(files).toEqual([
|
|
37
|
+
{ type: 'add', path: 'file1.txt', action: 'add' },
|
|
38
|
+
{ type: 'modify', path: 'file2.txt', action: 'add' },
|
|
39
|
+
{ type: 'delete', path: 'file3.txt', action: 'delete' }
|
|
40
|
+
])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('rejects when git diff command fails', async () => {
|
|
44
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
45
|
+
execSync.mockImplementation(() => { throw new Error('Command failed') })
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await diff('/path/to/dir', gitRef, existsSync, execSync)
|
|
49
|
+
fail('Expected function to throw an error')
|
|
50
|
+
} catch (error) {
|
|
51
|
+
expect(error.message).toEqual('Command failed')
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
})
|