Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@aws-sdk/client-cloudwatch-logs": "^3.1107.0",
"@aws-sdk/client-lambda": "^3.1107.0",
"@datadog/datadog-ci-base": "^5.22.0",
"simple-git": "^3.36.0"
"simple-git": "^4.0.2"
},
"peerDependencies": {
"serverless": "4.x || 3.x || 2.x || 1.x"
Expand All @@ -44,6 +44,9 @@
"js-yaml": "^4.3.2",
"lodash": "^4.18.1",
"brace-expansion": "^5.0.9",
"undici": "^7.29.1"
"undici": "^7.29.1",
"simple-git": "^4.0.2",
"basic-ftp": "^6.2.2",
"oxfmt/tinypool": "2.1.2"
}
}
75 changes: 75 additions & 0 deletions src/git.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { getGitCommitInfo } from "@datadog/datadog-ci-base/commands/git-metadata/library";
import { newSimpleGit } from "./git";

describe("Git integration", () => {
let temporaryDirectory: string;
let repositoryDirectory: string;
let nestedDirectory: string;
let commitHash: string;

beforeEach(() => {
temporaryDirectory = realpathSync(mkdtempSync(join(tmpdir(), "datadog-git-")));
repositoryDirectory = join(temporaryDirectory, "repository");
nestedDirectory = join(repositoryDirectory, "nested");
mkdirSync(nestedDirectory, { recursive: true });

// Datadog may add safe.directory in CI. simple-git filters GIT_CONFIG_GLOBAL,
// so isolate the home directories as well to contain global Git config writes.
vi.stubEnv("HOME", temporaryDirectory);
vi.stubEnv("XDG_CONFIG_HOME", join(temporaryDirectory, "config"));
vi.stubEnv("GIT_CONFIG_GLOBAL", join(temporaryDirectory, "gitconfig"));
vi.stubEnv("GIT_CONFIG_NOSYSTEM", "1");

const git = (...args: string[]) => execFileSync("git", args, { cwd: repositoryDirectory, encoding: "utf8" });
git("init", "--quiet", "--initial-branch=main");
git("config", "user.name", "Git Integration Test");
git("config", "user.email", "git-test@example.com");
writeFileSync(join(repositoryDirectory, "root.ts"), "export const root = true;\n");
writeFileSync(join(nestedDirectory, "nested.ts"), "export const nested = true;\n");
git("add", ".");
git("commit", "--quiet", "--no-gpg-sign", "-m", "Initial test commit");
git("remote", "add", "origin", "https://github.com/example/security-upgrade.git");
commitHash = git("rev-parse", "HEAD").trim();
});

afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllEnvs();
rmSync(temporaryDirectory, { recursive: true, force: true });
});

it("detects a Git repository and reads its current commit", async () => {
vi.spyOn(process, "cwd").mockReturnValue(repositoryDirectory);

const git = await newSimpleGit();

expect(git).toBeDefined();
expect(await git!.checkIsRepo()).toBe(true);
expect(await git!.revparse("HEAD")).toBe(commitHash);
});

it("uses the repository root when called from a nested directory", async () => {
vi.spyOn(process, "cwd").mockReturnValue(nestedDirectory);

const git = await newSimpleGit();

expect(git).toBeDefined();
expect(await git!.raw("ls-files")).toBe("nested/nested.ts\nroot.ts\n");
});

it("returns undefined outside a Git repository", async () => {
vi.spyOn(process, "cwd").mockReturnValue(temporaryDirectory);

expect(await newSimpleGit()).toBeUndefined();
});

it("reads Datadog commit metadata from a nested directory", async () => {
vi.spyOn(process, "cwd").mockReturnValue(nestedDirectory);

expect(await getGitCommitInfo()).toEqual(["github.com/example/security-upgrade.git", commitHash]);
});
});
8 changes: 4 additions & 4 deletions src/git.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as simpleGit from "simple-git";
import { simpleGit, type SimpleGit } from "simple-git";

// Returns a configured SimpleGit.
export const newSimpleGit = async (): Promise<simpleGit.SimpleGit | undefined> => {
export const newSimpleGit = async (): Promise<SimpleGit | undefined> => {
const options = {
baseDir: process.cwd(),
binary: "git",
Expand All @@ -10,12 +10,12 @@ export const newSimpleGit = async (): Promise<simpleGit.SimpleGit | undefined> =
try {
// Attempt to set the baseDir to the root of the repository so the 'git ls-files' command
// returns the tracked files paths relative to the root of the repository.
const git = simpleGit.gitP(options);
const git = simpleGit(options);
const root = await git.revparse("--show-toplevel");
options.baseDir = root;
} catch {
return undefined;
}

return simpleGit.gitP(options);
return simpleGit(options);
};
Loading