]> Git Repo - pico-vscode.git/blob - src/utils/gitUtil.mts
Merge branch 'main' into main
[pico-vscode.git] / src / utils / gitUtil.mts
1 import { promisify } from "util";
2 import { exec } from "child_process";
3 import Logger from "../logger.mjs";
4 import { unlink } from "fs/promises";
5 import type Settings from "../settings.mjs";
6 import { SettingsKey, HOME_VAR } from "../settings.mjs";
7 import { homedir } from "os";
8 import which from "which";
9 import { window } from "vscode";
10
11 export const execAsync = promisify(exec);
12
13 /**
14  * Get installed version of git, and install it if it isn't already
15  */
16 export async function getGit(settings: Settings): Promise<string | undefined> {
17   let gitExecutable: string | undefined =
18     settings
19       .getString(SettingsKey.gitPath)
20       ?.replace(HOME_VAR, homedir().replaceAll("\\", "/")) || "git";
21   let gitPath = await which(gitExecutable, { nothrow: true });
22   if (gitPath === null) {
23     // if git is not in path then checkForInstallationRequirements
24     // maye downloaded it, so reload
25     settings.reload();
26     gitExecutable = settings
27       .getString(SettingsKey.gitPath)
28       ?.replace(HOME_VAR, homedir().replaceAll("\\", "/"));
29     if (gitExecutable === null || gitExecutable === undefined) {
30       Logger.log("Error: Git not found.");
31
32       await window.showErrorMessage(
33         "Git not found. Please install and add to PATH or " +
34           "set the path to the git executable in global settings."
35       );
36
37       return undefined;
38     } else {
39       gitPath = await which(gitExecutable, { nothrow: true });
40     }
41   }
42
43   return gitPath || undefined;
44 }
45
46 /**
47  * Initialize git submodules in downloaded Pico-SDK.
48  *
49  * @param sdkDirectory The directory of the downloaded Pico-SDK.
50  * @returns True if the submodules were initialized successfully, false otherwise.
51  */
52 export async function initSubmodules(
53   sdkDirectory: string,
54   gitExecutable: string = "git"
55 ): Promise<boolean> {
56   try {
57     // Use the "git submodule update --init" command in the specified directory
58     // `cd` command need '/d' option on Windows. (Change drive "d:\" to "c:\")
59     const command =
60       `cd ${
61         process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " "
62       }"${sdkDirectory}" && ` +
63       `${
64         process.env.ComSpec === "powershell.exe" ? "&" : ""
65       }"${gitExecutable}" submodule update --init`;
66     await execAsync(command);
67
68     return true;
69   } catch (error) {
70     console.error(error);
71
72     return false;
73   }
74 }
75
76 export async function cloneRepository(
77   repository: string,
78   branch: string,
79   targetDirectory: string,
80   gitExecutable: string = "git"
81 ): Promise<boolean> {
82   // Clone the repository at the specified tag into the target directory
83   const cloneCommand =
84     `${
85       process.env.ComSpec === "powershell.exe" ? "&" : ""
86     }"${gitExecutable}" -c advice.detachedHead=false clone --branch ` +
87     `${branch} ${repository} "${targetDirectory}"`;
88
89   try {
90     await execAsync(cloneCommand);
91
92     Logger.log(`${repository} ${branch} has been cloned and installed.`);
93
94     return true;
95   } catch (error) {
96     try {
97       await unlink(targetDirectory);
98     } catch {
99       /* */
100     }
101
102     const err = error instanceof Error ? error.message : (error as string);
103     if (err.includes("already exists")) {
104       return true;
105     }
106     Logger.log(`Error while cloning repository: ${err}`);
107
108     return false;
109   }
110 }
111
112 export async function sparseCloneRepository(
113   repository: string,
114   branch: string,
115   targetDirectory: string,
116   gitExecutable: string = "git"
117 ): Promise<boolean> {
118   // Clone the repository at the specified tag into the target directory
119   const cloneCommand =
120     `${
121       process.env.ComSpec === "powershell.exe" ? "&" : ""
122     }"${gitExecutable}" -c advice.detachedHead=false clone ` +
123     "--filter=blob:none --sparse --branch " +
124     `${branch} ${repository} "${targetDirectory}"`;
125
126   try {
127     await execAsync(cloneCommand);
128     await execAsync(
129       `cd ${
130         process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " " 
131       }"${targetDirectory}" && ${
132         process.env.ComSpec === "powershell.exe" ? "&" : ""
133       }"${gitExecutable}" sparse-checkout set --cone`
134     );
135
136     Logger.log(
137       `${repository} ${branch} has been cloned with a sparse-checkout.`
138     );
139
140     return true;
141   } catch (error) {
142     try {
143       await unlink(targetDirectory);
144     } catch {
145       /* */
146     }
147
148     const err = error instanceof Error ? error.message : (error as string);
149     if (err.includes("already exists")) {
150       return true;
151     }
152     Logger.log(`Error while cloning repository: ${err}`);
153
154     return false;
155   }
156 }
157
158 /**
159  * Add a path to the sparse-checkout of a repository.
160  *
161  * @param repoDirectory The directory of the git repository (absolute).
162  * @param checkoutPath The repo-relative path to add to the sparse-checkout.
163  * @param gitExecutable The path to the git executable.
164  * @returns True if the path was added successfully, false otherwise.
165  */
166 export async function sparseCheckout(
167   repoDirectory: string,
168   checkoutPath: string,
169   gitExecutable: string = "git"
170 ): Promise<boolean> {
171   try {
172     await execAsync(
173       `cd ${
174         process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " " 
175       } "${repoDirectory}" && ${
176         process.env.ComSpec === "powershell.exe" ? "&" : ""
177       }"${gitExecutable}" sparse-checkout add ${checkoutPath}`
178     );
179
180     return true;
181   } catch (error) {
182     const err = error instanceof Error ? error.message : (error as string);
183     Logger.log(`Error while cloning repository: ${err}`);
184
185     return false;
186   }
187 }
This page took 0.03173 seconds and 4 git commands to generate.