Skip to content

Commit 4a88db8

Browse files
committed
Improve git error handling to prevent false bug reports in Observe
1 parent a43ed51 commit 4a88db8

File tree

1 file changed

+60
-51
lines changed
  • packages/cli-kit/src/public/node

1 file changed

+60
-51
lines changed

packages/cli-kit/src/public/node/git.ts

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ export async function initializeGitRepository(directory: string, initialBranch =
2727
outputDebug(outputContent`Initializing git repository at ${outputToken.path(directory)}...`)
2828
await ensureGitIsPresentOrAbort()
2929
// We use init and checkout instead of `init --initial-branch` because the latter is only supported in git 2.28+
30-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
31-
// @ts-ignore
32-
const repo = git(directory)
33-
await repo.init()
34-
await repo.checkoutLocalBranch(initialBranch)
30+
await withGit({
31+
directory,
32+
callback: async (repo) => {
33+
await repo.init()
34+
await repo.checkoutLocalBranch(initialBranch)
35+
},
36+
})
3537
}
3638

3739
/**
@@ -44,11 +46,7 @@ export async function initializeGitRepository(directory: string, initialBranch =
4446
* @returns Files ignored by the lockfile.
4547
*/
4648
export async function checkIfIgnoredInGitRepository(directory: string, files: string[]): Promise<string[]> {
47-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
48-
// @ts-ignore
49-
const repo = git(directory)
50-
const ignoredLockfile = await repo.checkIgnore(files)
51-
return ignoredLockfile
49+
return withGit({directory, callback: (repo) => repo.checkIgnore(files)})
5250
}
5351

5452
export interface GitIgnoreTemplate {
@@ -199,11 +197,13 @@ export async function downloadGitRepository(cloneOptions: GitCloneOptions): Prom
199197
await git(simpleGitOptions).clone(repository!, destination, options)
200198

201199
if (latestTag) {
202-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
203-
// @ts-ignore
204-
const localGitRepository = git(destination)
205-
const latestTag = await getLocalLatestTag(localGitRepository, repoUrl)
206-
await localGitRepository.checkout(latestTag)
200+
await withGit({
201+
directory: destination,
202+
callback: async (localGitRepository) => {
203+
const latestTag = await getLocalLatestTag(localGitRepository, repoUrl)
204+
await localGitRepository.checkout(latestTag)
205+
},
206+
})
207207
}
208208
} catch (err) {
209209
if (err instanceof Error) {
@@ -240,10 +240,9 @@ async function getLocalLatestTag(repository: SimpleGit, repoUrl: string): Promis
240240
* @returns The latest commit of the repository.
241241
*/
242242
export async function getLatestGitCommit(directory?: string): Promise<DefaultLogFields & ListLogLine> {
243-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
244-
// @ts-ignore
245-
const logs = await git({baseDir: directory}).log({
246-
maxCount: 1,
243+
const logs = await withGit({
244+
directory,
245+
callback: (repo) => repo.log({maxCount: 1}),
247246
})
248247
if (!logs.latest) {
249248
throw new AbortError(
@@ -263,10 +262,7 @@ export async function getLatestGitCommit(directory?: string): Promise<DefaultLog
263262
* @returns A promise that resolves when the files are added to the index.
264263
*/
265264
export async function addAllToGitFromDirectory(directory?: string): Promise<void> {
266-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
267-
// @ts-ignore
268-
const simpleGit = git({baseDir: directory})
269-
await simpleGit.raw('add', '--all')
265+
await withGit({directory, callback: (repo) => repo.raw('add', '--all')})
270266
}
271267

272268
export interface CreateGitCommitOptions {
@@ -282,13 +278,11 @@ export interface CreateGitCommitOptions {
282278
* @returns The hash of the created commit.
283279
*/
284280
export async function createGitCommit(message: string, options?: CreateGitCommitOptions): Promise<string> {
285-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
286-
// @ts-ignore
287-
const simpleGit = git({baseDir: options?.directory})
288-
289281
const commitOptions = options?.author ? {'--author': options.author} : undefined
290-
const result = await simpleGit.commit(message, commitOptions)
291-
282+
const result = await withGit({
283+
directory: options?.directory,
284+
callback: (repo) => repo.commit(message, commitOptions),
285+
})
292286
return result.commit
293287
}
294288

@@ -299,9 +293,7 @@ export async function createGitCommit(message: string, options?: CreateGitCommit
299293
* @returns The HEAD symbolic reference of the repository.
300294
*/
301295
export async function getHeadSymbolicRef(directory?: string): Promise<string> {
302-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
303-
// @ts-ignore
304-
const ref = await git({baseDir: directory}).raw('symbolic-ref', '-q', 'HEAD')
296+
const ref = await withGit({directory, callback: (repo) => repo.raw('symbolic-ref', '-q', 'HEAD')})
305297
if (!ref) {
306298
throw new AbortError(
307299
"Git HEAD can't be detached to run command",
@@ -354,9 +346,7 @@ export async function ensureInsideGitDirectory(directory?: string): Promise<void
354346
* @returns True if the directory is inside a .git directory tree.
355347
*/
356348
export async function insideGitDirectory(directory?: string): Promise<boolean> {
357-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
358-
// @ts-ignore
359-
return git({baseDir: directory}).checkIsRepo()
349+
return withGit({directory, callback: (repo) => repo.checkIsRepo()})
360350
}
361351

362352
export class GitDirectoryNotCleanError extends AbortError {}
@@ -379,9 +369,29 @@ export async function ensureIsClean(directory?: string): Promise<void> {
379369
* @returns True is the .git directory is clean.
380370
*/
381371
export async function isClean(directory?: string): Promise<boolean> {
372+
return (await withGit({directory, callback: (git: SimpleGit) => git.status()})).isClean()
373+
}
374+
375+
async function withGit<T>({
376+
directory,
377+
callback,
378+
}: {
379+
directory?: string
380+
callback: (git: SimpleGit) => Promise<T>
381+
}): Promise<T> {
382382
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
383383
// @ts-ignore
384-
return (await git({baseDir: directory}).status()).isClean()
384+
const git = git({baseDir: directory})
385+
try {
386+
return await callback(git)
387+
} catch (err) {
388+
if (err instanceof Error) {
389+
const abortError = new AbortError(err.message)
390+
abortError.stack = err.stack
391+
throw abortError
392+
}
393+
throw err
394+
}
385395
}
386396

387397
/**
@@ -391,9 +401,7 @@ export async function isClean(directory?: string): Promise<boolean> {
391401
* @returns String with the latest tag or undefined if no tags are found.
392402
*/
393403
export async function getLatestTag(directory?: string): Promise<string | undefined> {
394-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
395-
// @ts-ignore
396-
const tags = await git({baseDir: directory}).tags()
404+
const tags = await withGit({directory, callback: (repo) => repo.tags()})
397405
return tags.latest
398406
}
399407

@@ -408,18 +416,19 @@ export async function removeGitRemote(directory: string, remoteName = 'origin'):
408416
outputDebug(outputContent`Removing git remote ${remoteName} from ${outputToken.path(directory)}...`)
409417
await ensureGitIsPresentOrAbort()
410418

411-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
412-
// @ts-ignore
413-
const repo = git(directory)
414-
415-
// Check if remote exists first
416-
const remotes = await repo.getRemotes()
417-
const remoteExists = remotes.some((remote: {name: string}) => remote.name === remoteName)
419+
await withGit({
420+
directory,
421+
callback: async (repo) => {
422+
// Check if remote exists first
423+
const remotes = await repo.getRemotes()
424+
const remoteExists = remotes.some((remote: {name: string}) => remote.name === remoteName)
418425

419-
if (!remoteExists) {
420-
outputDebug(outputContent`Remote ${remoteName} does not exist, no action needed`)
421-
return
422-
}
426+
if (!remoteExists) {
427+
outputDebug(outputContent`Remote ${remoteName} does not exist, no action needed`)
428+
return
429+
}
423430

424-
await repo.removeRemote(remoteName)
431+
await repo.removeRemote(remoteName)
432+
},
433+
})
425434
}

0 commit comments

Comments
 (0)