@ghl-ai/aw 0.1.39 → 0.1.40-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/git.mjs +101 -19
- package/package.json +1 -1
package/git.mjs
CHANGED
|
@@ -9,6 +9,49 @@ import { REGISTRY_BASE_BRANCH, REGISTRY_DIR, DOCS_SOURCE_DIR, RULES_SOURCE_DIR }
|
|
|
9
9
|
|
|
10
10
|
const exec = promisify(execCb);
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Env vars applied to every git command that touches the network.
|
|
14
|
+
* GIT_TERMINAL_PROMPT=0 prevents git from hanging when it would otherwise
|
|
15
|
+
* prompt for credentials (e.g. HTTPS URL with SSH-only auth configured).
|
|
16
|
+
* Instead, git exits immediately with a non-zero code that we can catch.
|
|
17
|
+
*/
|
|
18
|
+
const GIT_NET_ENV = { ...process.env, GIT_TERMINAL_PROMPT: '0' };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Convert an HTTPS GitHub URL to its SSH equivalent.
|
|
22
|
+
* e.g. https://github.com/Org/Repo.git → git@github.com:Org/Repo.git
|
|
23
|
+
*/
|
|
24
|
+
function toSshUrl(httpsUrl) {
|
|
25
|
+
const m = httpsUrl.match(/^https:\/\/github\.com\/(.+)$/);
|
|
26
|
+
return m ? `git@github.com:${m[1]}` : httpsUrl;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detect whether the user's git is configured to prefer SSH for github.com.
|
|
31
|
+
* Checks: 1) git insteadOf config 2) gh CLI auth status
|
|
32
|
+
* Returns true if SSH is preferred.
|
|
33
|
+
*/
|
|
34
|
+
function prefersSsh() {
|
|
35
|
+
// Check git url."git@github.com:".insteadOf
|
|
36
|
+
try {
|
|
37
|
+
const out = execSync(
|
|
38
|
+
'git config --global --get-regexp "url\\.git@github\\.com.*\\.insteadOf"',
|
|
39
|
+
{ stdio: 'pipe', encoding: 'utf8', env: GIT_NET_ENV },
|
|
40
|
+
).trim();
|
|
41
|
+
if (out.includes('https://github.com')) return true;
|
|
42
|
+
} catch { /* not configured */ }
|
|
43
|
+
|
|
44
|
+
// Check gh auth — if protocol is ssh, prefer SSH
|
|
45
|
+
try {
|
|
46
|
+
const out = execSync('gh auth status 2>&1', {
|
|
47
|
+
stdio: 'pipe', encoding: 'utf8', env: GIT_NET_ENV, timeout: 5000,
|
|
48
|
+
});
|
|
49
|
+
if (/git protocol:\s*ssh/i.test(out)) return true;
|
|
50
|
+
} catch { /* gh not installed or not authed */ }
|
|
51
|
+
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
12
55
|
// ── Backward-compat: temp-dir sparse checkout (used by search.mjs) ────────────
|
|
13
56
|
|
|
14
57
|
/**
|
|
@@ -18,12 +61,23 @@ const exec = promisify(execCb);
|
|
|
18
61
|
export function sparseCheckout(repo, paths) {
|
|
19
62
|
const tempDir = mkdtempSync(join(tmpdir(), 'aw-'));
|
|
20
63
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
64
|
+
const httpsUrl = repo.startsWith('http') ? repo : `https://github.com/${repo}.git`;
|
|
65
|
+
const urls = prefersSsh() ? [toSshUrl(httpsUrl), httpsUrl] : [httpsUrl, toSshUrl(httpsUrl)];
|
|
66
|
+
|
|
67
|
+
let cloned = false;
|
|
68
|
+
for (const url of urls) {
|
|
69
|
+
try {
|
|
70
|
+
execSync(`git clone --filter=blob:none --no-checkout "${url}" "${tempDir}"`, {
|
|
71
|
+
stdio: 'pipe', env: GIT_NET_ENV,
|
|
72
|
+
});
|
|
73
|
+
cloned = true;
|
|
74
|
+
break;
|
|
75
|
+
} catch {
|
|
76
|
+
// Clean up partial clone so next attempt can use the same tempDir
|
|
77
|
+
try { rmSync(join(tempDir, '.git'), { recursive: true, force: true }); } catch {}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!cloned) {
|
|
27
81
|
throw new Error(`Failed to clone ${repo}. Check your git credentials and repo access.`);
|
|
28
82
|
}
|
|
29
83
|
|
|
@@ -47,10 +101,20 @@ export function sparseCheckout(repo, paths) {
|
|
|
47
101
|
export async function sparseCheckoutAsync(repo, paths) {
|
|
48
102
|
const tempDir = mkdtempSync(join(tmpdir(), 'aw-'));
|
|
49
103
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
104
|
+
const httpsUrl = repo.startsWith('http') ? repo : `https://github.com/${repo}.git`;
|
|
105
|
+
const urls = prefersSsh() ? [toSshUrl(httpsUrl), httpsUrl] : [httpsUrl, toSshUrl(httpsUrl)];
|
|
106
|
+
|
|
107
|
+
let cloned = false;
|
|
108
|
+
for (const url of urls) {
|
|
109
|
+
try {
|
|
110
|
+
await exec(`git clone --filter=blob:none --no-checkout "${url}" "${tempDir}"`, { env: GIT_NET_ENV });
|
|
111
|
+
cloned = true;
|
|
112
|
+
break;
|
|
113
|
+
} catch {
|
|
114
|
+
try { rmSync(join(tempDir, '.git'), { recursive: true, force: true }); } catch {}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!cloned) {
|
|
54
118
|
throw new Error(`Failed to clone ${repo}. Check your git credentials and repo access.`);
|
|
55
119
|
}
|
|
56
120
|
|
|
@@ -106,7 +170,9 @@ export function isValidClone(awHome, repoUrl) {
|
|
|
106
170
|
if (!existsSync(join(awHome, '.git'))) return false;
|
|
107
171
|
try {
|
|
108
172
|
const remote = execSync('git remote get-url origin', { cwd: awHome, stdio: 'pipe', encoding: 'utf8' }).trim();
|
|
109
|
-
|
|
173
|
+
// Normalize both sides to bare repo path for comparison (handles HTTPS ↔ SSH)
|
|
174
|
+
const normalize = (u) => u.replace(/\.git$/, '').replace(/^git@github\.com:/, 'https://github.com/');
|
|
175
|
+
return normalize(remote) === normalize(repoUrl);
|
|
110
176
|
} catch {
|
|
111
177
|
return false;
|
|
112
178
|
}
|
|
@@ -123,10 +189,26 @@ export async function initPersistentClone(repoUrl, awHome, sparsePaths) {
|
|
|
123
189
|
try { execSync(`rm -rf "${awHome}"`, { stdio: 'pipe' }); } catch {}
|
|
124
190
|
}
|
|
125
191
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
192
|
+
const urls = prefersSsh()
|
|
193
|
+
? [toSshUrl(repoUrl), repoUrl]
|
|
194
|
+
: [repoUrl, toSshUrl(repoUrl)];
|
|
195
|
+
|
|
196
|
+
let cloned = false;
|
|
197
|
+
for (const url of urls) {
|
|
198
|
+
// Clean up any partial clone from a previous attempt
|
|
199
|
+
if (existsSync(awHome) && !isValidClone(awHome, url)) {
|
|
200
|
+
try { execSync(`rm -rf "${awHome}"`, { stdio: 'pipe' }); } catch {}
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
await exec(`git clone --filter=blob:none --no-checkout "${url}" "${awHome}"`, { env: GIT_NET_ENV });
|
|
204
|
+
cloned = true;
|
|
205
|
+
break;
|
|
206
|
+
} catch {
|
|
207
|
+
// try next URL
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (!cloned) {
|
|
211
|
+
throw new Error(`Failed to clone ${repoUrl}. Check your git credentials and repo access (HTTPS and SSH both failed).`);
|
|
130
212
|
}
|
|
131
213
|
|
|
132
214
|
try {
|
|
@@ -286,7 +368,7 @@ export async function fetchAndMerge(awHome, { silent = true } = {}) {
|
|
|
286
368
|
|
|
287
369
|
// ── 2. Fetch ──────────────────────────────────────────────────────────────
|
|
288
370
|
try {
|
|
289
|
-
await exec(`git -C "${awHome}" fetch origin ${REGISTRY_BASE_BRANCH}
|
|
371
|
+
await exec(`git -C "${awHome}" fetch origin ${REGISTRY_BASE_BRANCH}`, { env: GIT_NET_ENV });
|
|
290
372
|
} catch (e) {
|
|
291
373
|
throw new Error(`Failed to fetch from origin: ${e.message}`);
|
|
292
374
|
}
|
|
@@ -315,7 +397,7 @@ export async function fetchAndMerge(awHome, { silent = true } = {}) {
|
|
|
315
397
|
// someone else pushed to the remote tracking branch since our last fetch.
|
|
316
398
|
if (isPushBranch) {
|
|
317
399
|
try {
|
|
318
|
-
await exec(`git -C "${awHome}" push --force-with-lease origin "${currentBranch}"
|
|
400
|
+
await exec(`git -C "${awHome}" push --force-with-lease origin "${currentBranch}"`, { env: GIT_NET_ENV });
|
|
319
401
|
} catch { /* non-blocking — divergence will be resolved on next aw push */ }
|
|
320
402
|
}
|
|
321
403
|
} catch {
|
|
@@ -442,7 +524,7 @@ export function updatePushBranch(awHome, pushBranchName) {
|
|
|
442
524
|
}
|
|
443
525
|
|
|
444
526
|
try {
|
|
445
|
-
execSync(`git -C "${awHome}" push origin "${pushBranchName}" --force`, { stdio: 'pipe' });
|
|
527
|
+
execSync(`git -C "${awHome}" push origin "${pushBranchName}" --force`, { stdio: 'pipe', env: GIT_NET_ENV });
|
|
446
528
|
} catch (e) {
|
|
447
529
|
throw new Error(`Failed to push branch: ${e.message}`);
|
|
448
530
|
}
|
|
@@ -485,7 +567,7 @@ export async function createPushBranch(awHome, branchName, files, commitMsg, pre
|
|
|
485
567
|
}
|
|
486
568
|
|
|
487
569
|
try {
|
|
488
|
-
await exec(`git -C "${awHome}" push -u origin "${branchName}"
|
|
570
|
+
await exec(`git -C "${awHome}" push -u origin "${branchName}"`, { env: GIT_NET_ENV });
|
|
489
571
|
} catch (e) {
|
|
490
572
|
throw new Error(`Failed to push branch: ${e.message}`);
|
|
491
573
|
}
|