Skip to content

Commit 43e88a7

Browse files
author
Phillip
committed
replaced all raw unix commands with NodeJS fs api calls
1 parent 74608a7 commit 43e88a7

2 files changed

Lines changed: 78 additions & 48 deletions

File tree

‎backend/src/services/file-operations.ts‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,38 @@ export async function listDirectory(dirPath: string): Promise<Array<{
121121
throw new Error(`Failed to list directory ${dirPath}: ${error}`)
122122
}
123123
}
124+
125+
export async function directoryExists(dirPath: string): Promise<boolean> {
126+
try {
127+
const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(getReposPath(), dirPath)
128+
const stats = await fs.stat(fullPath)
129+
return stats.isDirectory()
130+
} catch {
131+
return false
132+
}
133+
}
134+
135+
export async function removeDirectory(dirPath: string): Promise<void> {
136+
try {
137+
const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(getReposPath(), dirPath)
138+
await fs.rm(fullPath, { recursive: true, force: true })
139+
} catch (error) {
140+
throw new Error(`Failed to remove directory ${dirPath}: ${error}`)
141+
}
142+
}
143+
144+
export async function listDirectoryNames(dirPath: string): Promise<string[]> {
145+
try {
146+
const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(getReposPath(), dirPath)
147+
const entries = await fs.readdir(fullPath, { withFileTypes: true })
148+
const directories: string[] = []
149+
for (const entry of entries) {
150+
if (entry.isDirectory()) {
151+
directories.push(entry.name)
152+
}
153+
}
154+
return directories
155+
} catch {
156+
return []
157+
}
158+
}

‎backend/src/services/repo.ts‎

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { executeCommand } from '../utils/process'
2-
import { ensureDirectoryExists } from './file-operations'
2+
import { ensureDirectoryExists, directoryExists, removeDirectory, listDirectoryNames } from './file-operations'
33
import * as db from '../db/queries'
44
import type { Database } from 'bun:sqlite'
55
import type { Repo, CreateRepoInput } from '../types/repo'
@@ -88,12 +88,7 @@ async function isValidGitRepo(repoPath: string): Promise<boolean> {
8888
async function checkRepoNameAvailable(name: string): Promise<boolean> {
8989
const reposPath = getReposPath()
9090
const targetPath = path.join(reposPath, name)
91-
try {
92-
await executeCommand(['test', '-e', targetPath], { silent: true })
93-
return false
94-
} catch {
95-
return true
96-
}
91+
return !(await directoryExists(targetPath))
9792
}
9893

9994
async function copyRepoToWorkspace(sourcePath: string, targetName: string): Promise<void> {
@@ -186,9 +181,7 @@ export async function initLocalRepo(
186181
logger.info(`Absolute path detected: ${normalizedInputPath}`)
187182

188183
try {
189-
const exists = await executeCommand(['test', '-d', normalizedInputPath], { silent: true })
190-
.then(() => true)
191-
.catch(() => false)
184+
const exists = await directoryExists(normalizedInputPath)
192185

193186
if (!exists) {
194187
throw new Error(`No such file or directory: '${normalizedInputPath}'`)
@@ -296,14 +289,14 @@ export async function initLocalRepo(
296289

297290
if (directoryCreated && !sourceWasGitRepo) {
298291
try {
299-
await executeCommand(['rm', '-rf', repoLocalPath], getReposPath())
292+
await removeDirectory(path.join(getReposPath(), repoLocalPath))
300293
logger.info(`Rolled back directory: ${repoLocalPath}`)
301294
} catch (fsError: any) {
302295
logger.error(`Failed to rollback directory ${repoLocalPath}:`, fsError)
303296
}
304297
} else if (sourceWasGitRepo) {
305298
try {
306-
await executeCommand(['rm', '-rf', repoLocalPath], getReposPath())
299+
await removeDirectory(path.join(getReposPath(), repoLocalPath))
307300
logger.info(`Cleaned up copied directory: ${repoLocalPath}`)
308301
} catch (fsError: any) {
309302
logger.error(`Failed to clean up copied directory ${repoLocalPath}:`, fsError)
@@ -333,9 +326,9 @@ export async function cloneRepo(
333326
}
334327

335328
await ensureDirectoryExists(getReposPath())
336-
const baseRepoExists = await executeCommand(['bash', '-c', `test -d ${baseRepoDirName} && echo exists || echo missing`], path.resolve(getReposPath()))
329+
const baseRepoExists = await directoryExists(path.join(getReposPath(), baseRepoDirName))
337330

338-
const shouldUseWorktree = useWorktree && branch && baseRepoExists.trim() === 'exists'
331+
const shouldUseWorktree = useWorktree && branch && baseRepoExists
339332

340333
const createRepoInput: CreateRepoInput = {
341334
repoUrl: normalizedRepoUrl,
@@ -366,26 +359,24 @@ export async function cloneRepo(
366359

367360
await createWorktreeSafely(baseRepoPath, worktreePath, branch)
368361

369-
const worktreeVerified = await executeCommand(['test', '-d', worktreePath])
370-
.then(() => true)
371-
.catch(() => false)
362+
const worktreeVerified = await directoryExists(worktreePath)
372363

373364
if (!worktreeVerified) {
374365
throw new Error(`Worktree directory was not created at: ${worktreePath}`)
375366
}
376367

377368
logger.info(`Worktree verified at: ${worktreePath}`)
378369

379-
} else if (branch && baseRepoExists.trim() === 'exists' && useWorktree) {
370+
} else if (branch && baseRepoExists && useWorktree) {
380371
logger.info(`Base repo exists but worktree creation failed, cloning branch separately`)
381372

382-
const worktreeExists = await executeCommand(['bash', '-c', `test -d ${worktreeDirName} && echo exists || echo missing`], path.resolve(getReposPath()))
383-
if (worktreeExists.trim() === 'exists') {
373+
const worktreeExists = await directoryExists(path.join(getReposPath(), worktreeDirName))
374+
if (worktreeExists) {
384375
logger.info(`Workspace directory exists, removing it: ${worktreeDirName}`)
385376
try {
386-
await executeCommand(['rm', '-rf', worktreeDirName], getReposPath())
387-
const verifyRemoved = await executeCommand(['bash', '-c', `test -d ${worktreeDirName} && echo exists || echo removed`], getReposPath())
388-
if (verifyRemoved.trim() === 'exists') {
377+
await removeDirectory(path.join(getReposPath(), worktreeDirName))
378+
const verifyRemoved = !(await directoryExists(path.join(getReposPath(), worktreeDirName)))
379+
if (!verifyRemoved) {
389380
throw new Error(`Failed to remove existing directory: ${worktreeDirName}`)
390381
}
391382
} catch (cleanupError: any) {
@@ -402,27 +393,32 @@ export async function cloneRepo(
402393
throw new Error(`Workspace directory ${worktreeDirName} already exists. Please delete it manually or contact support.`)
403394
}
404395

405-
logger.info(`Branch '${branch}' not found during clone, cloning default branch and creating branch locally`)
406-
await executeGitWithFallback(['git', 'clone', normalizedRepoUrl, worktreeDirName], { cwd: getReposPath(), env })
407-
let localBranchExists = 'missing'
408-
try {
409-
await executeCommand(['git', '-C', path.resolve(getReposPath(), worktreeDirName), 'rev-parse', '--verify', `refs/heads/${branch}`])
410-
localBranchExists = 'exists'
411-
} catch {
412-
localBranchExists = 'missing'
413-
}
414-
if (localBranchExists.trim() === 'missing') {
396+
if (branch && (error.message.includes('Remote branch') || error.message.includes('not found'))) {
397+
logger.info(`Branch '${branch}' not found, cloning default branch and creating branch locally`)
398+
await executeGitWithFallback(['git', 'clone', normalizedRepoUrl, worktreeDirName], { cwd: getReposPath(), env })
399+
let localBranchExists = 'missing'
400+
try {
401+
await executeCommand(['git', '-C', path.resolve(getReposPath(), worktreeDirName), 'rev-parse', '--verify', `refs/heads/${branch}`])
402+
localBranchExists = 'exists'
403+
} catch {
404+
localBranchExists = 'missing'
405+
}
406+
407+
if (localBranchExists === 'missing') {
415408
await executeCommand(['git', '-C', path.resolve(getReposPath(), worktreeDirName), 'checkout', '-b', branch])
416409
} else {
417410
await executeCommand(['git', '-C', path.resolve(getReposPath(), worktreeDirName), 'checkout', branch])
418411
}
412+
} else {
413+
throw error
414+
}
419415
}
420416
} else {
421-
if (baseRepoExists.trim() === 'exists') {
417+
if (baseRepoExists) {
422418
logger.info(`Repository directory already exists, verifying it's a valid git repo: ${baseRepoDirName}`)
423419
const isValidRepo = await executeCommand(['git', '-C', path.resolve(getReposPath(), baseRepoDirName), 'rev-parse', '--git-dir'], path.resolve(getReposPath())).then(() => 'valid').catch(() => 'invalid')
424420

425-
if (isValidRepo.trim() === 'valid') {
421+
if (isValidRepo === 'valid') {
426422
logger.info(`Valid repository found: ${normalizedRepoUrl}`)
427423

428424
if (branch) {
@@ -462,19 +458,19 @@ export async function cloneRepo(
462458
return { ...repo, cloneStatus: 'ready' }
463459
} else {
464460
logger.warn(`Invalid repository directory found, removing and recloning: ${baseRepoDirName}`)
465-
await executeCommand(['rm', '-rf', baseRepoDirName], getReposPath())
461+
await removeDirectory(path.join(getReposPath(), baseRepoDirName))
466462
}
467463
}
468464

469465
logger.info(`Cloning repo: ${normalizedRepoUrl}${branch ? ` to branch ${branch}` : ''}`)
470466

471-
const worktreeExists = await executeCommand(['bash', '-c', `test -d ${worktreeDirName} && echo exists || echo missing`], getReposPath())
472-
if (worktreeExists.trim() === 'exists') {
467+
const worktreeExists = await directoryExists(path.join(getReposPath(), worktreeDirName))
468+
if (worktreeExists) {
473469
logger.info(`Workspace directory exists, removing it: ${worktreeDirName}`)
474470
try {
475-
await executeCommand(['rm', '-rf', worktreeDirName], getReposPath())
476-
const verifyRemoved = await executeCommand(['bash', '-c', `test -d ${worktreeDirName} && echo exists || echo removed`], getReposPath())
477-
if (verifyRemoved.trim() === 'exists') {
471+
await removeDirectory(path.join(getReposPath(), worktreeDirName))
472+
const verifyRemoved = !(await directoryExists(path.join(getReposPath(), worktreeDirName)))
473+
if (!verifyRemoved) {
478474
throw new Error(`Failed to remove existing directory: ${worktreeDirName}`)
479475
}
480476
} catch (cleanupError: any) {
@@ -506,7 +502,7 @@ export async function cloneRepo(
506502
localBranchExists = 'missing'
507503
}
508504

509-
if (localBranchExists.trim() === 'missing') {
505+
if (localBranchExists === 'missing') {
510506
await executeCommand(['git', '-C', path.resolve(getReposPath(), worktreeDirName), 'checkout', '-b', branch])
511507
} else {
512508
await executeCommand(['git', '-C', path.resolve(getReposPath(), worktreeDirName), 'checkout', branch])
@@ -704,10 +700,10 @@ export async function deleteRepoFiles(database: Database, repoId: number): Promi
704700

705701
// Remove the directory
706702
logger.info(`Removing directory: ${dirName} from ${getReposPath()}`)
707-
await executeCommand(['rm', '-rf', dirName], getReposPath())
703+
await removeDirectory(path.join(getReposPath(), dirName))
708704

709-
const checkExists = await executeCommand(['bash', '-c', `test -d ${dirName} && echo exists || echo deleted`], getReposPath())
710-
if (checkExists.trim() === 'exists') {
705+
const checkExists = await directoryExists(path.join(getReposPath(), dirName))
706+
if (checkExists) {
711707
logger.error(`Directory still exists after deletion: ${dirName}`)
712708
throw new Error(`Failed to delete workspace directory: ${dirName}`)
713709
}
@@ -764,8 +760,7 @@ export async function cleanupOrphanedDirectories(database: Database): Promise<vo
764760
const reposPath = getReposPath()
765761
await ensureDirectoryExists(reposPath)
766762

767-
const dirResult = await executeCommand(['ls', '-1'], reposPath).catch(() => '')
768-
const directories = dirResult.split('\n').filter(d => d.trim())
763+
const directories = await listDirectoryNames(reposPath)
769764

770765
if (directories.length === 0) {
771766
return
@@ -782,7 +777,7 @@ export async function cleanupOrphanedDirectories(database: Database): Promise<vo
782777
for (const dir of orphanedDirs) {
783778
try {
784779
logger.info(`Removing orphaned directory: ${dir}`)
785-
await executeCommand(['rm', '-rf', dir], reposPath)
780+
await removeDirectory(path.join(reposPath, dir))
786781
} catch (error) {
787782
logger.warn(`Failed to remove orphaned directory ${dir}:`, error)
788783
}

0 commit comments

Comments
 (0)