-
Notifications
You must be signed in to change notification settings - Fork 197
Expand file tree
/
Copy pathgit-clone.ts
More file actions
70 lines (62 loc) · 2 KB
/
Copy pathgit-clone.ts
File metadata and controls
70 lines (62 loc) · 2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* Git Clone Utility
*
* Clones a Git repository to a local directory using simple-git.
* Useful for integration tests that need a real repository as a workspace.
*/
import { mkdirSync } from "node:fs";
import { simpleGit, type SimpleGitOptions } from "simple-git";
interface CloneOptions {
/** Full repository URL (HTTPS or SSH). */
repoUrl: string;
/** Target directory to clone into. */
targetDir: string;
/** Clone only a single branch. */
branch?: string;
/** Shallow clone with the given depth (e.g. 1). */
depth?: number;
/** Only check out a specific path within the repo (sparse checkout). */
sparseCheckoutPath?: string;
}
/**
* Clone a Git repository.
*
* @example
* ```ts
* await cloneRepo({
* repoUrl: "https://github.com/Azure-Samples/todo-nodejs-mongo.git",
* targetDir: "/path/to/workspace",
* depth: 1,
* });
* ```
*/
export async function cloneRepo(options: CloneOptions): Promise<void> {
const { repoUrl, targetDir, branch, depth, sparseCheckoutPath } = options;
mkdirSync(targetDir, { recursive: true });
const gitOptions: Partial<SimpleGitOptions> = {
baseDir: targetDir,
binary: "git",
maxConcurrentProcesses: 1,
};
const git = simpleGit(gitOptions);
const cloneArgs: string[] = [];
if (branch) {
cloneArgs.push("--branch", branch, "--single-branch");
}
if (depth) {
cloneArgs.push("--depth", String(depth));
}
if (sparseCheckoutPath) {
// Validate to prevent path traversal or argument injection
if (/\.\.[/\\]/.test(sparseCheckoutPath) || /[^a-zA-Z0-9_\-/.]/.test(sparseCheckoutPath)) {
throw new Error(`Invalid sparse checkout path: ${sparseCheckoutPath}`);
}
// Clone without checking out files, then sparse-checkout the target path
cloneArgs.push("--filter=tree:0", "--no-checkout");
await git.clone(repoUrl, targetDir, cloneArgs);
await git.raw(["sparse-checkout", "set", sparseCheckoutPath]);
await git.checkout();
} else {
await git.clone(repoUrl, targetDir, cloneArgs);
}
}