Skip to content

Commit 4cedc72

Browse files
committed
Reduce duplicate code + fix wrong git path usage
Signed-off-by: paulober <[email protected]>
1 parent b879c0e commit 4cedc72

File tree

6 files changed

+120
-140
lines changed

6 files changed

+120
-140
lines changed

src/logger.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export enum LoggerSource {
4040
cmake = "cmakeUtil",
4141
downloadHelper = "downloadHelper",
4242
pythonHelper = "pythonHelper",
43+
gitUtil = "gitUtil",
4344
}
4445

4546
/**

src/utils/download.mts

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import { STATUS_CODES } from "http";
1515
import type { Dispatcher } from "undici";
1616
import { Client } from "undici";
1717
import type { SupportedToolchainVersion } from "./toolchainUtil.mjs";
18-
import { cloneRepository, initSubmodules, getGit } from "./gitUtil.mjs";
19-
import { checkForGit } from "./requirementsUtil.mjs";
18+
import { cloneRepository, initSubmodules, ensureGit } from "./gitUtil.mjs";
2019
import { HOME_VAR, SettingsKey } from "../settings.mjs";
2120
import Settings from "../settings.mjs";
2221
import which from "which";
@@ -467,8 +466,8 @@ export async function downloadAndInstallSDK(
467466
}
468467

469468
// TODO: this does take about 2s - may be reduced
470-
const requirementsCheck = await checkForGit(settings);
471-
if (!requirementsCheck) {
469+
const gitPath = await ensureGit(settings, { returnPath: true });
470+
if (typeof gitPath !== "string" || gitPath.length === 0) {
472471
return false;
473472
}
474473

@@ -492,15 +491,14 @@ export async function downloadAndInstallSDK(
492491
);
493492
// Constants taken from the SDK CMakeLists.txt files
494493
const TINYUSB_TEST_PATH = joinPosix(
495-
"lib/tinyusb", "src/portable/raspberrypi/rp2040"
494+
"lib/tinyusb",
495+
"src/portable/raspberrypi/rp2040"
496496
);
497497
const CYW43_DRIVER_TEST_FILE = joinPosix("lib/cyw43-driver", "src/cyw43.h");
498498
const LWIP_TEST_PATH = joinPosix("lib/lwip", "src/Filelists.cmake");
499499
const BTSTACK_TEST_PATH = joinPosix("lib/btstack", "src/bluetooth.h");
500-
const MBEDTLS_TEST_PATH = joinPosix("lib/mbedtls", "library/aes.c")
501-
const submoduleChecks = [
502-
TINYUSB_TEST_PATH
503-
]
500+
const MBEDTLS_TEST_PATH = joinPosix("lib/mbedtls", "library/aes.c");
501+
const submoduleChecks = [TINYUSB_TEST_PATH];
504502
if (compareGe(version, "1.4.0")) {
505503
submoduleChecks.push(CYW43_DRIVER_TEST_FILE);
506504
submoduleChecks.push(LWIP_TEST_PATH);
@@ -520,20 +518,13 @@ export async function downloadAndInstallSDK(
520518
LoggerSource.downloader,
521519
`SDK ${version} is missing submodules - installing them now.`
522520
);
523-
const gitPath = await getGit(settings);
524-
if (gitPath !== undefined) {
525-
return initSubmodules(targetDirectory, gitPath);
526-
} else {
527-
return false;
528-
}
521+
522+
return initSubmodules(targetDirectory, gitPath);
529523
} else {
530524
return true;
531525
}
532526
}
533527

534-
// Ensure the target directory exists
535-
//await mkdir(targetDirectory, { recursive: true });
536-
const gitPath = await getGit(settings);
537528
// using deferred execution to avoid git clone if git is not available
538529
if (
539530
gitPath !== undefined &&

src/utils/downloadGit.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const GIT_DOWNLOAD_URL_WIN_AMD64 =
1717
/**
1818
* Downloads and installs a portable version of Git.
1919
*
20-
* Supports Windows x64 and macOS x64 + amd64.
20+
* Supports Windows x64 and macOS x64 + arm64.
2121
* But currently only execution on Windows x64 is enabled.
2222
*
2323
* @returns The path to the installed Git executable or undefined if the installation failed

src/utils/examplesUtil.mts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ import Logger, { LoggerSource } from "../logger.mjs";
33
import { existsSync, readFileSync, rmSync } from "fs";
44
import { homedir } from "os";
55
import {
6-
getGit,
6+
ensureGit,
77
sparseCheckout,
88
sparseCloneRepository,
99
execAsync,
1010
} from "./gitUtil.mjs";
1111
import Settings from "../settings.mjs";
12-
import { checkForGit } from "./requirementsUtil.mjs";
1312
import { cp } from "fs/promises";
1413
import { get } from "https";
1514
import { isInternetConnected, getDataRoot } from "./downloadHelpers.mjs";
@@ -162,13 +161,17 @@ export async function setupExample(
162161
}
163162

164163
// TODO: this does take about 2s - may be reduced
165-
const requirementsCheck = await checkForGit(settings);
166-
if (!requirementsCheck) {
164+
const gitPath = await ensureGit(settings, { returnPath: true });
165+
if (
166+
gitPath === undefined ||
167+
typeof gitPath !== "string" ||
168+
gitPath.length === 0
169+
) {
170+
Logger.log("Error: Git not found.");
171+
167172
return false;
168173
}
169174

170-
const gitPath = await getGit(settings);
171-
172175
if (existsSync(joinPosix(examplesRepoPath, ".git"))) {
173176
try {
174177
const ref = await execAsync(
@@ -183,7 +186,7 @@ export async function setupExample(
183186
Logger.log(`Removing old examples repo\n`);
184187
rmSync(examplesRepoPath, { recursive: true, force: true });
185188
}
186-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
189+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
187190
} catch (error) {
188191
// Corrupted examples directory
189192
Logger.log(`Removing corrupted examples repo\n`);

src/utils/gitUtil.mts

Lines changed: 99 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,137 @@
11
import { promisify } from "util";
22
import { exec } from "child_process";
3-
import Logger from "../logger.mjs";
3+
import Logger, { LoggerSource } from "../logger.mjs";
44
import { unlink } from "fs/promises";
55
import type Settings from "../settings.mjs";
66
import { SettingsKey, HOME_VAR } from "../settings.mjs";
77
import { homedir } from "os";
88
import which from "which";
99
import { window } from "vscode";
1010
import { compareGe } from "./semverUtil.mjs";
11+
import { downloadGit } from "./downloadGit.mjs";
1112

1213
export const execAsync = promisify(exec);
1314

14-
export const MIN_GIT_VERSION="2.28.0";
15+
export const MIN_GIT_VERSION = "2.28.0";
16+
export const DEFAULT_GIT_EXE = "git";
1517

16-
export async function checkGitVersion(gitExecutable: string):
17-
Promise<[boolean, string]>
18-
{
19-
const versionCommand =
20-
`${
21-
process.env.ComSpec === "powershell.exe" ? "&" : ""
22-
}"${gitExecutable}" version`;
23-
const ret = await execAsync(versionCommand)
18+
export async function checkGitVersion(
19+
gitExecutable: string
20+
): Promise<[boolean, string]> {
21+
const versionCommand = `${
22+
process.env.ComSpec === "powershell.exe" ? "&" : ""
23+
}"${gitExecutable}" version`;
24+
console.debug(`Checking git version: ${versionCommand}`);
25+
const ret = await execAsync(versionCommand);
26+
console.debug(
27+
`Git version check result: ${ret.stderr} stdout: ${ret.stdout}`
28+
);
2429
const regex = /git version (\d+\.\d+(\.\d+)*)/;
2530
const match = regex.exec(ret.stdout);
2631
if (match && match[1]) {
2732
const gitVersion = match[1];
33+
console.debug(`Found git version: ${gitVersion}`);
2834

29-
return [compareGe(gitVersion, MIN_GIT_VERSION), gitVersion];
35+
return [compareGe(gitVersion, MIN_GIT_VERSION), gitVersion];
3036
} else {
37+
console.debug(
38+
`Error: Could not parse git version from output: ${ret.stdout}`
39+
);
40+
3141
return [false, "unknown"];
3242
}
3343
}
3444

3545
/**
36-
* Get installed version of git, and install it if it isn't already
46+
* Checks if a git executable is available or try to install if possible.
47+
*
48+
* @returns True if git is available, false otherwise or if
49+
* options.returnPath is true the path to the git executable.
3750
*/
38-
export async function getGit(settings: Settings): Promise<string | undefined> {
39-
let gitExecutable: string | undefined =
51+
export async function ensureGit(
52+
settings: Settings,
53+
options?: { returnPath?: boolean }
54+
): Promise<string | boolean | undefined> {
55+
const returnPath = options?.returnPath ?? false;
56+
57+
const resolveGitPath = (): string =>
4058
settings
4159
.getString(SettingsKey.gitPath)
42-
?.replace(HOME_VAR, homedir().replaceAll("\\", "/")) || "git";
43-
let gitPath = await which(gitExecutable, { nothrow: true });
60+
?.replace(HOME_VAR, homedir().replaceAll("\\", "/")) || DEFAULT_GIT_EXE;
61+
62+
let gitExe = resolveGitPath();
63+
let gitPath = await which(gitExe, { nothrow: true });
64+
65+
// If custom path is invalid, offer to reset to default
66+
if (gitExe !== DEFAULT_GIT_EXE && gitPath === null) {
67+
const selection = await window.showErrorMessage(
68+
"The path to the git executable is invalid. " +
69+
"Do you want to reset it and search in system PATH?",
70+
{ modal: true, detail: gitExe },
71+
"Yes",
72+
"No"
73+
);
74+
75+
if (selection === "Yes") {
76+
await settings.updateGlobal(SettingsKey.gitPath, undefined);
77+
settings.reload();
78+
gitExe = DEFAULT_GIT_EXE;
79+
gitPath = await which(DEFAULT_GIT_EXE, { nothrow: true });
80+
} else {
81+
return returnPath ? undefined : false;
82+
}
83+
}
84+
85+
let isGitInstalled = gitPath !== null;
4486
let gitVersion: string | undefined;
87+
4588
if (gitPath !== null) {
46-
const versionRet = await checkGitVersion(gitPath);
47-
if (!versionRet[0]) {
89+
const [isValid, version] = await checkGitVersion(gitPath);
90+
isGitInstalled = isValid;
91+
gitVersion = version;
92+
93+
if (!isValid) {
4894
gitPath = null;
4995
}
50-
gitVersion = versionRet[1];
5196
}
52-
if (gitPath === null) {
53-
// if git is not in path then checkForInstallationRequirements
54-
// maye downloaded it, so reload
55-
settings.reload();
56-
gitExecutable = settings
57-
.getString(SettingsKey.gitPath)
58-
?.replace(HOME_VAR, homedir().replaceAll("\\", "/"));
59-
if (gitExecutable === null || gitExecutable === undefined) {
60-
if (gitVersion !== undefined) {
61-
Logger.log(`Error: Found Git version ${gitVersion} - ` +
62-
`requires ${MIN_GIT_VERSION}.`);
63-
64-
await window.showErrorMessage(
65-
`Found Git version ${gitVersion}, but requires ${MIN_GIT_VERSION}. ` +
97+
98+
// Try download if invalid or not found
99+
if (!isGitInstalled) {
100+
// Win32 x64 only
101+
const downloadedGitPath = await downloadGit();
102+
103+
if (downloadedGitPath) {
104+
await settings.updateGlobal(SettingsKey.gitPath, downloadedGitPath);
105+
isGitInstalled = true;
106+
gitPath = downloadedGitPath;
107+
} else if (gitVersion) {
108+
Logger.error(
109+
LoggerSource.gitUtil,
110+
`Installed Git is too old (${gitVersion})` +
111+
"and a new version could not be downloaded."
112+
);
113+
void window.showErrorMessage(
114+
`Found Git version ${gitVersion}, but requires ${MIN_GIT_VERSION}. ` +
66115
"Please install and add to PATH or " +
67-
"set the path to the git executable in global settings."
68-
);
69-
} else {
70-
Logger.log("Error: Git not found.");
71-
72-
await window.showErrorMessage(
73-
"Git not found. Please install and add to PATH or " +
74-
"set the path to the git executable in global settings."
75-
);
76-
}
77-
78-
return undefined;
116+
"set the path to the git executable in global settings."
117+
);
79118
} else {
80-
gitPath = await which(gitExecutable, { nothrow: true });
119+
Logger.error(
120+
LoggerSource.gitUtil,
121+
"Git is not installed and could not be downloaded."
122+
);
123+
void window.showErrorMessage(
124+
"Git is not installed. Please install and add to PATH or " +
125+
"set the path to the git executable in global settings." +
126+
(process.platform === "darwin"
127+
? " You can install it by running " +
128+
"`xcode-select --install` in the terminal."
129+
: "")
130+
);
81131
}
82132
}
83133

84-
return gitPath || undefined;
134+
return returnPath ? gitPath || undefined : isGitInstalled;
85135
}
86136

87137
/**
@@ -168,7 +218,7 @@ export async function sparseCloneRepository(
168218
await execAsync(cloneCommand);
169219
await execAsync(
170220
`cd ${
171-
process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " "
221+
process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " "
172222
}"${targetDirectory}" && ${
173223
process.env.ComSpec === "powershell.exe" ? "&" : ""
174224
}"${gitExecutable}" sparse-checkout set --cone`
@@ -212,7 +262,7 @@ export async function sparseCheckout(
212262
try {
213263
await execAsync(
214264
`cd ${
215-
process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " "
265+
process.env.ComSpec?.endsWith("cmd.exe") ? "/d " : " "
216266
} "${repoDirectory}" && ${
217267
process.env.ComSpec === "powershell.exe" ? "&" : ""
218268
}"${gitExecutable}" sparse-checkout add ${checkoutPath}`

0 commit comments

Comments
 (0)