Skip to content

Commit

Permalink
Merge pull request #3 from mcarvin8/fix/simple-git
Browse files Browse the repository at this point in the history
fix: use simple-git for git commands
  • Loading branch information
mcarvin8 authored Apr 7, 2024
2 parents 5797a55 + f1e50ce commit bd4ab7a
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 239 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

The `apex-tests-git-delta` is a simple Salesforce CLI plugin to take 2 commit SHAs in a Salesforce Git repository and return the delta Apex tests to run against when executing a delta deployment.

This plugin requires Git Bash to be installed in your environment.
This plugin requires [git](https://git-scm.com/downloads) to be installed and that it can be called using the command `git`.

The tests are determined by looking at all commit messages in the commit range and extracting them with a regular expression defined in a text file.

Expand Down Expand Up @@ -42,7 +42,7 @@ sf project deploy start -x manifest/package.xml -l RunSpecifiedTests -t $testcla

## Why another plugin to determine delta tests?

The [SFDX Git Delta ](https://github.com/scolladon/sfdx-git-delta) is an amazing tool that generates packages and directories for delta deployments. It could also be used to generate a comma-separated list of added/modified Apex classes, which could be used to run only the tests which were modified.
The [SFDX Git Delta](https://github.com/scolladon/sfdx-git-delta) is an amazing tool that generates packages and directories for delta deployments. It could also be used to generate a comma-separated list of added/modified Apex classes, which could be used to run only the tests which were modified.

However, depending on your testing strategy and other dependencies, running tests against only the Apex classes which changed may not be enough. You may need to declare additional Apex Tests to run against which will not be modified in this commit range.

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"dependencies": {
"@oclif/core": "^3.18.1",
"@salesforce/core": "^6.4.7",
"@salesforce/sf-plugins-core": "^7.1.3"
"@salesforce/sf-plugins-core": "^7.1.3",
"simple-git": "^3.24.0"
},
"devDependencies": {
"@commitlint/cli": "^18.6.0",
Expand Down
2 changes: 1 addition & 1 deletion src/service/extractTestClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function extractTestClasses(
sfdxConfigFile: string
): Promise<{ validatedClasses: string; warnings: string[] }> {
const testClasses: Set<string> = new Set();
const matchedMessages = retrieveCommitMessages(fromRef, toRef, regex);
const matchedMessages = await retrieveCommitMessages(fromRef, toRef, regex);

matchedMessages.forEach((message: string) => {
// Split the commit message by commas or spaces
Expand Down
27 changes: 16 additions & 11 deletions src/service/retrieveCommitMessages.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
'use strict';
import { execSync } from 'node:child_process';
import * as fs from 'node:fs';
import { readFileSync } from 'node:fs';
import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git';

export function retrieveCommitMessages(fromCommit: string, toCommit: string, regexFilePath: string): string[] {
const gitLogCommand = `git log --format=%s ${fromCommit}..${toCommit}`;
let commitMessages: string;
try {
commitMessages = execSync(gitLogCommand, { encoding: 'utf-8' });
} catch (err) {
throw Error('The git diff failed to run due to the above error.');
}
export async function retrieveCommitMessages(
fromCommit: string,
toCommit: string,
regexFilePath: string
): Promise<string[]> {
const options: Partial<SimpleGitOptions> = {
baseDir: process.cwd(),
binary: 'git',
maxConcurrentProcesses: 6,
trimmed: false,
};
const git: SimpleGit = simpleGit(options);
const commitMessages = await git.raw('log', '--format=%s', `${fromCommit}..${toCommit}`);

let regex: RegExp;
let regexPattern = '';
try {
regexPattern = fs.readFileSync(regexFilePath, 'utf-8').trim();
regexPattern = readFileSync(regexFilePath, 'utf-8').trim();
regex = new RegExp(regexPattern, 'g');
} catch (err) {
throw Error(`The regular expression in '${regexFilePath}' is invalid.`);
Expand Down
15 changes: 10 additions & 5 deletions test/commands/delta/createTemporaryCommit.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
'use strict';

import * as promises from 'node:fs/promises';
import { execSync } from 'node:child_process';
import { SimpleGit } from 'simple-git';

export async function createTemporaryCommit(message: string, filePath: string, content: string): Promise<string> {
export async function createTemporaryCommit(
message: string,
filePath: string,
content: string,
git: SimpleGit
): Promise<string> {
await promises.writeFile(filePath, content);

// Stage the file
execSync(`git add "${filePath}"`);
await git.add(filePath);

// Commit with the provided message
execSync(`git commit -m "${message}"`);
await git.commit(message);

// Return the commit hash of the newly created commit
const commitHash = execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim();
const commitHash = (await git.revparse('HEAD')).trim();

return commitHash;
}
33 changes: 20 additions & 13 deletions test/commands/delta/empty.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

import * as fs from 'node:fs';
import { execSync } from 'node:child_process';
import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git';

import { TestContext } from '@salesforce/core/lib/testSetup.js';
import { expect } from 'chai';
Expand All @@ -20,33 +20,40 @@ describe('scan commit messages without the regex and return an empty string.', (

before(async () => {
process.chdir(tempDir);
const options: Partial<SimpleGitOptions> = {
baseDir: process.cwd(),
binary: 'git',
maxConcurrentProcesses: 6,
trimmed: false,
};
const git: SimpleGit = simpleGit(options);
fs.mkdirSync('force-app/main/default/classes', { recursive: true });
fs.mkdirSync('packaged/classes', { recursive: true });
execSync('git init', { cwd: tempDir });
execSync('git branch -m main');
await git.init();
fs.writeFileSync(regExFile, regExFileContents);
fs.writeFileSync(sfdxConfigFile, sfdxConfigJsonString);
let userName = '';
let userEmail = '';
let userName;
let userEmail;

try {
userName = execSync('git config --global user.name', { encoding: 'utf-8' }).trim();
userEmail = execSync('git config --global user.email', { encoding: 'utf-8' }).trim();
userName = await git.getConfig('user.name');
userEmail = await git.getConfig('user.email');
} catch (error) {
// Ignore errors if the git config values are not set
}

if (userName === '' && userEmail === '') {
execSync('git config --global user.name "CI Bot"');
execSync('git config --global user.email "90224411+mcarvin8@users.noreply.github.com"');
if (!userName?.value && !userEmail?.value) {
await git.addConfig('user.name', 'CI Bot', false, 'global');
await git.addConfig('user.email', '90224411+mcarvin8@users.noreply.github.com', false, 'global');
}
fromSha = await createTemporaryCommit(
'chore: initial commit',
'force-app/main/default/classes/SandboxTest.cls',
'dummy 1'
'dummy 1',
git
);
await createTemporaryCommit('chore: add a class', 'force-app/main/default/classes/TestClass3.cls', 'dummy 11');
toSha = await createTemporaryCommit('chore: add some tests', 'packaged/classes/TestClass4.cls', 'dummy 2');
await createTemporaryCommit('chore: add a class', 'force-app/main/default/classes/TestClass3.cls', 'dummy 11', git);
toSha = await createTemporaryCommit('chore: add some tests', 'packaged/classes/TestClass4.cls', 'dummy 2', git);
});

beforeEach(() => {
Expand Down
35 changes: 22 additions & 13 deletions test/commands/delta/unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

import * as fs from 'node:fs';
import { execSync } from 'node:child_process';
import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git';

import { TestContext } from '@salesforce/core/lib/testSetup.js';
import { expect } from 'chai';
Expand All @@ -20,40 +20,49 @@ describe('return the delta tests between git commits', () => {

before(async () => {
process.chdir(tempDir);
const options: Partial<SimpleGitOptions> = {
baseDir: process.cwd(),
binary: 'git',
maxConcurrentProcesses: 6,
trimmed: false,
};
const git: SimpleGit = simpleGit(options);
fs.mkdirSync('force-app/main/default/classes', { recursive: true });
fs.mkdirSync('packaged/classes', { recursive: true });
execSync('git init', { cwd: tempDir });
execSync('git branch -m main');
await git.init();
fs.writeFileSync(regExFile, regExFileContents);
fs.writeFileSync(sfdxConfigFile, sfdxConfigJsonString);
let userName = '';
let userEmail = '';
let userName;
let userEmail;

try {
userName = execSync('git config --global user.name', { encoding: 'utf-8' }).trim();
userEmail = execSync('git config --global user.email', { encoding: 'utf-8' }).trim();
userName = await git.getConfig('user.name');
userEmail = await git.getConfig('user.email');
} catch (error) {
// Ignore errors if the git config values are not set
}

if (userName === '' && userEmail === '') {
execSync('git config --global user.name "CI Bot"');
execSync('git config --global user.email "90224411+mcarvin8@users.noreply.github.com"');
if (!userName?.value && !userEmail?.value) {
await git.addConfig('user.name', 'CI Bot', false, 'global');
await git.addConfig('user.email', '90224411+mcarvin8@users.noreply.github.com', false, 'global');
}
fromSha = await createTemporaryCommit(
'chore: initial commit with Apex::TestClass00::Apex',
'force-app/main/default/classes/SandboxTest.cls',
'dummy 1'
'dummy 1',
git
);
await createTemporaryCommit(
'chore: initial commit with Apex::SandboxTest::Apex',
'force-app/main/default/classes/TestClass3.cls',
'dummy 11'
'dummy 11',
git
);
toSha = await createTemporaryCommit(
'chore: adding new tests Apex::TestClass3 TestClass4::Apex',
'packaged/classes/TestClass4.cls',
'dummy 2'
'dummy 2',
git
);
});

Expand Down
40 changes: 26 additions & 14 deletions test/commands/delta/warnings.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

import * as fs from 'node:fs';
import { execSync } from 'node:child_process';
import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git';

import { TestContext } from '@salesforce/core/lib/testSetup.js';
import { expect } from 'chai';
Expand All @@ -20,36 +20,48 @@ describe('confirm warnings are generated when files cannot be found in a package

before(async () => {
process.chdir(tempDir);
const options: Partial<SimpleGitOptions> = {
baseDir: process.cwd(),
binary: 'git',
maxConcurrentProcesses: 6,
trimmed: false,
};
const git: SimpleGit = simpleGit(options);
fs.mkdirSync('force-app/main/default/classes', { recursive: true });
fs.mkdirSync('packaged/classes', { recursive: true });
execSync('git init', { cwd: tempDir });
execSync('git branch -m main');
await git.init();
fs.writeFileSync(regExFile, regExFileContents);
fs.writeFileSync(sfdxConfigFile, sfdxConfigJsonString);
let userName = '';
let userEmail = '';
let userName;
let userEmail;

try {
userName = execSync('git config --global user.name', { encoding: 'utf-8' }).trim();
userEmail = execSync('git config --global user.email', { encoding: 'utf-8' }).trim();
userName = await git.getConfig('user.name');
userEmail = await git.getConfig('user.email');
} catch (error) {
// Ignore errors if the git config values are not set
}

if (userName === '' && userEmail === '') {
execSync('git config --global user.name "CI Bot"');
execSync('git config --global user.email "90224411+mcarvin8@users.noreply.github.com"');
if (!userName?.value && !userEmail?.value) {
await git.addConfig('user.name', 'CI Bot', false, 'global');
await git.addConfig('user.email', '90224411+mcarvin8@users.noreply.github.com', false, 'global');
}
fromSha = await createTemporaryCommit(
'chore: initial commit with Apex::TestClass00::Apex',
'SandboxTest.cls',
'dummy 1'
'dummy 1',
git
);
await createTemporaryCommit(
'chore: initial commit with Apex::SandboxTest::Apex',
'TestClass3.cls',
'dummy 11',
git
);
await createTemporaryCommit('chore: initial commit with Apex::SandboxTest::Apex', 'TestClass3.cls', 'dummy 11');
toSha = await createTemporaryCommit(
'chore: adding new tests Apex::TestClass3 TestClass4::Apex',
'TestClass4.cls',
'dummy 2'
'dummy 2',
git
);
});

Expand Down
Loading

0 comments on commit bd4ab7a

Please sign in to comment.