11import { CheckRepoActions , GitConfigScope , simpleGit , SimpleGitProgressEvent } from 'simple-git' ;
22import { mkdir } from 'node:fs/promises' ;
33import { env } from './env.js' ;
4+ import { dirname , resolve } from 'node:path' ;
5+ import { existsSync } from 'node:fs' ;
46
57type onProgressFn = ( event : SimpleGitProgressEvent ) => void ;
68
9+ /**
10+ * Creates a simple-git client that has it's working directory
11+ * set to the given path.
12+ */
13+ const createGitClientForPath = ( path : string , onProgress ?: onProgressFn ) => {
14+ if ( ! existsSync ( path ) ) {
15+ throw new Error ( `Path ${ path } does not exist` ) ;
16+ }
17+
18+ const parentPath = resolve ( dirname ( path ) ) ;
19+
20+ const git = simpleGit ( {
21+ progress : onProgress ,
22+ } )
23+ . env ( {
24+ ...process . env ,
25+ /**
26+ * @note on some inside-baseball on why this is necessary: The specific
27+ * issue we saw was that a `git clone` would fail without throwing, and
28+ * then a subsequent `git config` command would run, but since the clone
29+ * failed, it wouldn't be running in a git directory. Git would then walk
30+ * up the directory tree until it either found a git directory (in the case
31+ * of the development env) or it would hit a GIT_DISCOVERY_ACROSS_FILESYSTEM
32+ * error when trying to cross a filesystem boundary (in the prod case).
33+ * GIT_CEILING_DIRECTORIES ensures that this walk will be limited to the
34+ * parent directory.
35+ */
36+ GIT_CEILING_DIRECTORIES : parentPath ,
37+ } )
38+ . cwd ( {
39+ path,
40+ } ) ;
41+
42+ return git ;
43+ }
44+
745export const cloneRepository = async (
846 {
947 cloneUrl,
@@ -20,11 +58,7 @@ export const cloneRepository = async (
2058 try {
2159 await mkdir ( path , { recursive : true } ) ;
2260
23- const git = simpleGit ( {
24- progress : onProgress ,
25- } ) . cwd ( {
26- path,
27- } )
61+ const git = createGitClientForPath ( path , onProgress ) ;
2862
2963 const cloneArgs = [
3064 "--bare" ,
@@ -62,11 +96,7 @@ export const fetchRepository = async (
6296 }
6397) => {
6498 try {
65- const git = simpleGit ( {
66- progress : onProgress ,
67- } ) . cwd ( {
68- path : path ,
69- } )
99+ const git = createGitClientForPath ( path , onProgress ) ;
70100
71101 if ( authHeader ) {
72102 await git . addConfig ( "http.extraHeader" , authHeader ) ;
@@ -108,9 +138,7 @@ export const fetchRepository = async (
108138 * present in gitConfig.
109139 */
110140export const upsertGitConfig = async ( path : string , gitConfig : Record < string , string > , onProgress ?: onProgressFn ) => {
111- const git = simpleGit ( {
112- progress : onProgress ,
113- } ) . cwd ( path ) ;
141+ const git = createGitClientForPath ( path , onProgress ) ;
114142
115143 try {
116144 for ( const [ key , value ] of Object . entries ( gitConfig ) ) {
@@ -130,9 +158,7 @@ export const upsertGitConfig = async (path: string, gitConfig: Record<string, st
130158 * If a key is not set, this is a no-op.
131159 */
132160export const unsetGitConfig = async ( path : string , keys : string [ ] , onProgress ?: onProgressFn ) => {
133- const git = simpleGit ( {
134- progress : onProgress ,
135- } ) . cwd ( path ) ;
161+ const git = createGitClientForPath ( path , onProgress ) ;
136162
137163 try {
138164 const configList = await git . listConfig ( ) ;
@@ -156,9 +182,7 @@ export const unsetGitConfig = async (path: string, keys: string[], onProgress?:
156182 * Returns true if `path` is the _root_ of a git repository.
157183 */
158184export const isPathAValidGitRepoRoot = async ( path : string , onProgress ?: onProgressFn ) => {
159- const git = simpleGit ( {
160- progress : onProgress ,
161- } ) . cwd ( path ) ;
185+ const git = createGitClientForPath ( path , onProgress ) ;
162186
163187 try {
164188 return git . checkIsRepo ( CheckRepoActions . IS_REPO_ROOT ) ;
@@ -184,7 +208,7 @@ export const isUrlAValidGitRepo = async (url: string) => {
184208}
185209
186210export const getOriginUrl = async ( path : string ) => {
187- const git = simpleGit ( ) . cwd ( path ) ;
211+ const git = createGitClientForPath ( path ) ;
188212
189213 try {
190214 const remotes = await git . getConfig ( 'remote.origin.url' , GitConfigScope . local ) ;
@@ -199,18 +223,13 @@ export const getOriginUrl = async (path: string) => {
199223}
200224
201225export const getBranches = async ( path : string ) => {
202- const git = simpleGit ( ) ;
203- const branches = await git . cwd ( {
204- path,
205- } ) . branch ( ) ;
206-
226+ const git = createGitClientForPath ( path ) ;
227+ const branches = await git . branch ( ) ;
207228 return branches . all ;
208229}
209230
210231export const getTags = async ( path : string ) => {
211- const git = simpleGit ( ) ;
212- const tags = await git . cwd ( {
213- path,
214- } ) . tags ( ) ;
232+ const git = createGitClientForPath ( path ) ;
233+ const tags = await git . tags ( ) ;
215234 return tags . all ;
216235}
0 commit comments