feat(codex): auto-install git bash via winget on windows
This commit is contained in:
@@ -2,8 +2,10 @@ import { execFileSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
|
||||
const GIT_BASH_ENV_KEY = "OMO_CODEX_GIT_BASH_PATH";
|
||||
const SKIP_GIT_BASH_AUTO_INSTALL_ENV_KEY = "OMO_CODEX_SKIP_GIT_BASH_AUTO_INSTALL";
|
||||
const PROGRAM_FILES_GIT_BASH = "C:\\Program Files\\Git\\bin\\bash.exe";
|
||||
const PROGRAM_FILES_X86_GIT_BASH = "C:\\Program Files (x86)\\Git\\bin\\bash.exe";
|
||||
const WINGET_INSTALL_ARGS = ["install", "--id", "Git.Git", "-e", "--source", "winget"];
|
||||
|
||||
export function resolveGitBash({ platform, env, exists, where }) {
|
||||
if (platform !== "win32") return { found: true, path: null, source: "not-required" };
|
||||
@@ -43,6 +45,23 @@ export function resolveGitBashForCurrentProcess(options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function prepareGitBashForInstall(options) {
|
||||
const resolveGitBashWithDefaults = options.resolveGitBash
|
||||
?? (() => resolveGitBashForCurrentProcess({ platform: options.platform, env: options.env }));
|
||||
const initialResolution = resolveGitBashWithDefaults();
|
||||
if (options.platform !== "win32" || initialResolution.found) return initialResolution;
|
||||
if (options.env[SKIP_GIT_BASH_AUTO_INSTALL_ENV_KEY] === "1") return initialResolution;
|
||||
|
||||
try {
|
||||
await options.runCommand("winget", WINGET_INSTALL_ARGS, { cwd: options.cwd });
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) throw error;
|
||||
return initialResolution;
|
||||
}
|
||||
|
||||
return resolveGitBashWithDefaults();
|
||||
}
|
||||
|
||||
function missingGitBash(checkedPaths) {
|
||||
return {
|
||||
found: false,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { resolveGitBash } from "./git-bash.mjs";
|
||||
import { prepareGitBashForInstall, resolveGitBash } from "./git-bash.mjs";
|
||||
|
||||
const programFilesGitBash = "C:\\Program Files\\Git\\bin\\bash.exe";
|
||||
const programFilesX86GitBash = "C:\\Program Files (x86)\\Git\\bin\\bash.exe";
|
||||
@@ -68,3 +68,63 @@ test("#given Windows without Git Bash #when resolving #then returns install guid
|
||||
assert.match(result.installHint, /winget install --id Git\.Git -e --source winget/);
|
||||
assert.match(result.installHint, /rerun `bunx omo install --platform=codex`/);
|
||||
});
|
||||
|
||||
test("#given Windows without Git Bash and winget is allowed #when preparing #then winget runs and resolver retries", async () => {
|
||||
const runCalls = [];
|
||||
const resolutions = [
|
||||
{ found: false, checkedPaths: [programFilesGitBash], installHint: "install hint" },
|
||||
{ found: true, path: programFilesGitBash, source: "program-files" },
|
||||
];
|
||||
let resolveCallCount = 0;
|
||||
|
||||
const result = await prepareGitBashForInstall({
|
||||
platform: "win32",
|
||||
env: {},
|
||||
cwd: "C:\\repo",
|
||||
resolveGitBash: () => resolutions[resolveCallCount++] ?? resolutions[resolutions.length - 1],
|
||||
runCommand: async (command, args, options) => {
|
||||
runCalls.push([command, ...args, options.cwd].join(" "));
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(runCalls, ["winget install --id Git.Git -e --source winget C:\\repo"]);
|
||||
assert.equal(resolveCallCount, 2);
|
||||
assert.deepEqual(result, { found: true, path: programFilesGitBash, source: "program-files" });
|
||||
});
|
||||
|
||||
test("#given Windows without Git Bash and skip env is set #when preparing #then winget is not run and install hint remains", async () => {
|
||||
const runCalls = [];
|
||||
const missingResolution = {
|
||||
found: false,
|
||||
checkedPaths: [programFilesGitBash, programFilesX86GitBash],
|
||||
installHint: "install hint",
|
||||
};
|
||||
|
||||
const result = await prepareGitBashForInstall({
|
||||
platform: "win32",
|
||||
env: { OMO_CODEX_SKIP_GIT_BASH_AUTO_INSTALL: "1" },
|
||||
cwd: "C:\\repo",
|
||||
resolveGitBash: () => missingResolution,
|
||||
runCommand: async (command, args, options) => {
|
||||
runCalls.push([command, ...args, options.cwd].join(" "));
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(runCalls, []);
|
||||
assert.deepEqual(result, missingResolution);
|
||||
});
|
||||
|
||||
test("#given non-Windows platform #when preparing #then winget is never called", async () => {
|
||||
const runCalls = [];
|
||||
const result = await prepareGitBashForInstall({
|
||||
platform: "linux",
|
||||
env: {},
|
||||
cwd: "/repo",
|
||||
runCommand: async (command, args, options) => {
|
||||
runCalls.push([command, ...args, options.cwd].join(" "));
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(runCalls, []);
|
||||
assert.deepEqual(result, { found: true, path: null, source: "not-required" });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user