Skip to content

Conversation

@jr
Copy link
Collaborator

@jr jr commented Sep 4, 2025

Important

Enhance shell path validation and caching in getShell() with async handling and update related tests and dependencies.

  • Behavior:
    • getShell() in shell.ts now validates shell paths using which and caches results for 5 minutes.
    • getSystemInfoSection() in system-info.ts updated to use async getShell().
    • generatePrompt() in system.ts updated to await getSystemInfoSection().
  • Dependencies:
    • Added which and @types/which to package.json.
  • Tests:
    • Updated shell.spec.ts to mock which and test async getShell() behavior across platforms.

This description was created by Ellipsis for 0423aa4. You can customize this summary. It will automatically update as commits are pushed.

@jr jr requested review from cte and mrubens as code owners September 4, 2025 20:59
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Sep 4, 2025
@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Sep 4, 2025
Copy link
Contributor

@roomote roomote bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution! I've reviewed the changes and found some issues that need attention. The implementation adds valuable shell path validation, but there are a few concerns around race conditions, error handling, and test coverage that should be addressed.

const shellPathCache = new Map<string, { path: string; timestamp: number }>()
const CACHE_TTL = 5 * 60 * 1000

async function validateShellPath(shellPath: string): Promise<string | null> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional? There's a potential race condition here. If multiple calls to validateShellPath happen simultaneously for the same path, between checking the cache (line 29-31) and setting it (line 37-40), another call could start validation, leading to duplicate which executions.

Consider using a Map of Promises to handle concurrent requests:

Suggested change
async function validateShellPath(shellPath: string): Promise<string | null> {
const shellPathCache = new Map<string, { path: string; timestamp: number }>()
const pendingValidations = new Map<string, Promise<string | null>>()
const CACHE_TTL = 5 * 60 * 1000
async function validateShellPath(shellPath: string): Promise<string | null> {
try {
const cached = shellPathCache.get(shellPath)
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.path
}
// Check if validation is already in progress
const pending = pendingValidations.get(shellPath)
if (pending) {
return pending
}
// Start new validation
const validationPromise = which(shellPath, { nothrow: true }).then(resolvedPath => {
if (resolvedPath) {
shellPathCache.set(shellPath, {
path: resolvedPath,
timestamp: Date.now(),
})
}
pendingValidations.delete(shellPath)
return resolvedPath
})
pendingValidations.set(shellPath, validationPromise)
return validationPromise
} catch {
return null
}
}

}

return null
} catch {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we improve error visibility here? The catch block silently swallows errors without any logging. This could make debugging difficult if which throws unexpected errors.

Consider adding error logging:

Suggested change
} catch {
} catch (error) {
// Log the error for debugging purposes
console.warn('Failed to validate shell path:', shellPath, error)
return null


// 4. Finally, fall back to a default
// 4. Finally, fall back to platform-specific defaults
if (process.platform === "win32") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional that these fallback paths are returned without validation? On some systems, even these default paths might not exist. Should we validate these as well, or at least document why we're confident they'll always exist?

Consider:

Suggested change
if (process.platform === "win32") {
// 4. Finally, fall back to platform-specific defaults
// Note: These defaults are returned without validation as they are
// considered system essentials that should always exist
if (process.platform === "win32") {
return SHELL_PATHS.CMD
} else {
return SHELL_PATHS.FALLBACK
}

/**
* Clears the shell path cache (useful for testing)
*/
export function clearShellPathCache(): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function intended for future use? It's exported but never called in production code, only in tests. If it's for testing only, should it be made internal or documented as a test utility?

vi.mocked(userInfo).mockReturnValue({ shell: null } as any)

// Mock which to always resolve paths successfully for tests
vi.mocked(which).mockImplementation(async (cmd: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be valuable to add test cases where which returns null to ensure the fallback logic works correctly? Currently, the mock always returns the command successfully.

Example test case:

Suggested change
vi.mocked(which).mockImplementation(async (cmd: string) => {
it("falls back through all options when validation fails", async () => {
// Mock which to return null for all paths
vi.mocked(which).mockResolvedValue(null)
mockVsCodeConfig("windows", "PowerShell", {
PowerShell: { path: "C:\Invalid\Path\pwsh.exe" },
})
vi.mocked(userInfo).mockReturnValue({ shell: "C:\Invalid\Shell.exe" } as any)
process.env.COMSPEC = "C:\Invalid\cmd.exe"
// Should fall back to the default CMD path
expect(await getShell()).toBe("C:\Windows\System32\cmd.exe")
})

@jr jr closed this Sep 4, 2025
@github-project-automation github-project-automation bot moved this from Triage to Done in Roo Code Roadmap Sep 4, 2025
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Sep 4, 2025
@jr jr deleted the jr/shell-validation branch September 4, 2025 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants