Skip to content

Support commit SHA in --remote-branch option#212

Merged
yamadashy merged 16 commits intoyamadashy:mainfrom
huy-trn:main
Dec 31, 2024
Merged

Support commit SHA in --remote-branch option#212
yamadashy merged 16 commits intoyamadashy:mainfrom
huy-trn:main

Conversation

@huy-trn
Copy link
Contributor

@huy-trn huy-trn commented Dec 21, 2024

Related:

The older PR claims to support branch names, tags, and commit hashes, but commit hashes are not actually supported.

Checklist

  • Run npm run test
  • Run npm run lint

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2024

📝 Walkthrough

Walkthrough

The pull request modifies the execGitShallowClone function in the Git command module to enhance its handling of remote branches. The parameter branch has been renamed to remoteBranch, and the function's logic now includes a conditional check for this parameter. If remoteBranch is provided, the function performs a series of Git commands to initialize the repository, fetch the specified branch, and check it out. If no branch is specified, it defaults to a standard shallow clone. Related tests and function signatures have also been updated accordingly.

Changes

File Change Summary
src/core/file/gitCommand.ts Updated execGitShallowClone function to implement conditional logic for handling remoteBranch. Added imports for fs/promises and path.
tests/core/file/gitCommand.test.ts Adjusted tests to replace branch with remoteBranch, ensuring proper command execution based on the new logic. Added new test cases for branch-specific cloning and error handling.
src/cli/actions/remoteAction.ts Modified cloneRepository function to rename branch to remoteBranch, reflecting the changes in the execGitShallowClone function.

Possibly related PRs

  • refactor(core): Git Command Module Extraction #176: This PR modifies the cloneRepository function in src/cli/actions/remoteAction.ts, which directly calls the execGitShallowClone function, aligning with the changes made in the main PR regarding the remoteBranch parameter.
  • Add remote branch option #196: This PR adds a branch parameter to the cloneRepository function, which is relevant to the changes in the main PR that renamed the branch parameter to remoteBranch and altered the function's logic accordingly.
  • Update README.md #199: This PR updates the README to include guidance on the --remote-branch option, which is directly related to the changes made in the main PR regarding the handling of the remoteBranch parameter.

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Dec 21, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.16%. Comparing base (7470a93) to head (9213ad8).
Report is 17 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #212      +/-   ##
==========================================
+ Coverage   92.07%   92.16%   +0.08%     
==========================================
  Files          44       44              
  Lines        2058     2080      +22     
  Branches      450      458       +8     
==========================================
+ Hits         1895     1917      +22     
  Misses        163      163              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
tests/core/file/gitCommand.test.ts (1)

Line range hint 46-78: Add test cases for branch and commit SHA support

The current tests only cover the case where branch is undefined. We need additional test cases to verify the new functionality.

Add these test cases:

test('should execute git commands sequence when branch is specified', async () => {
  const mockExecAsync = vi.fn().mockResolvedValue({ stdout: '', stderr: '' });
  const url = 'https://github.com/user/repo.git';
  const directory = '/tmp/repo';
  const branch = 'feature-branch';

  await execGitShallowClone(url, directory, branch, { execAsync: mockExecAsync });

  expect(mockExecAsync).toHaveBeenNthCalledWith(1, `git -C ${directory} init`);
  expect(mockExecAsync).toHaveBeenNthCalledWith(2, `git -C ${directory} remote add origin ${url}`);
  expect(mockExecAsync).toHaveBeenNthCalledWith(3, `git -C ${directory} fetch --depth 1 origin ${branch}`);
  expect(mockExecAsync).toHaveBeenNthCalledWith(4, `git -C ${directory} checkout FETCH_HEAD`);
});

test('should support commit SHA', async () => {
  const mockExecAsync = vi.fn().mockResolvedValue({ stdout: '', stderr: '' });
  const url = 'https://github.com/user/repo.git';
  const directory = '/tmp/repo';
  const commitSha = 'abc123def456';

  await execGitShallowClone(url, directory, commitSha, { execAsync: mockExecAsync });

  expect(mockExecAsync).toHaveBeenNthCalledWith(3, `git -C ${directory} fetch --depth 1 origin ${commitSha}`);
});

test('should handle errors in git commands sequence', async () => {
  const mockExecAsync = vi.fn()
    .mockResolvedValueOnce({ stdout: '', stderr: '' }) // init succeeds
    .mockRejectedValueOnce(new Error('Remote add failed')); // remote add fails
  
  const url = 'https://github.com/user/repo.git';
  const directory = '/tmp/repo';
  const branch = 'feature-branch';

  await expect(execGitShallowClone(url, directory, branch, { execAsync: mockExecAsync }))
    .rejects.toThrow('Remote add failed');
});
🧹 Nitpick comments (1)
src/core/file/gitCommand.ts (1)

29-38: Enhance error handling and add logging

The implementation lacks proper error handling and logging which could make debugging issues difficult, especially for commit SHA support.

Add try-catch blocks and logging:

 if(branch){
+  try {
     await deps.execAsync(`git -C ${directory} init`);
     await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
     await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${branch}`);
     await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
     await deps.execAsync(`rm -rf ${directory}/.git 2>/dev/null || rmdir /s /q ${directory}\\.git`)
+  } catch (error) {
+    logger.error(`Failed to clone repository at branch/commit: ${branch}`, error);
+    throw error;
+  }
 }else{
+  try {
     await deps.execAsync(`git clone --depth 1 ${url} ${directory}`);
+  } catch (error) {
+    logger.error('Failed to shallow clone repository', error);
+    throw error;
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b263c87 and 08d7247.

📒 Files selected for processing (2)
  • src/core/file/gitCommand.ts (1 hunks)
  • tests/core/file/gitCommand.test.ts (2 hunks)
🔇 Additional comments (1)
src/core/file/gitCommand.ts (1)

29-38: Verify the implementation supports commit SHA as intended

The current implementation uses git fetch which should work with commit SHAs, but we should verify this behavior.

Let's verify the git commands work with commit SHAs:

Copy link

@standard-input standard-input bot left a comment

Choose a reason for hiding this comment

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

No issues flagged.
Standard Input can make mistakes. Check important info.

@huy-trn huy-trn marked this pull request as draft December 21, 2024 10:15
@huy-trn huy-trn marked this pull request as ready for review December 21, 2024 11:03
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/core/file/gitCommand.ts (1)

31-36: ⚠️ Potential issue

Add input validation for branch/commit SHA parameter

The branch parameter is directly interpolated into shell commands without validation, making it vulnerable to shell injection. This is especially important now that it accepts commit SHAs.

Add validation before using the branch parameter:

 if (branch) {
+  // Validate branch parameter to prevent shell injection
+  if (!/^[a-zA-Z0-9._-]+$/.test(branch)) {
+    throw new Error('Invalid branch name or commit SHA');
+  }
   await deps.execAsync(`git -C ${directory} init`);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caff31b and 2b78228.

📒 Files selected for processing (2)
  • src/core/file/gitCommand.ts (2 hunks)
  • tests/core/file/gitCommand.test.ts (2 hunks)
🔇 Additional comments (2)
src/core/file/gitCommand.ts (1)

2-3: LGTM: Appropriate imports added for file system operations

The new imports are correctly added to support the .git directory cleanup operations.

tests/core/file/gitCommand.test.ts (1)

47-51: LGTM: Test updated correctly for undefined branch case

The test correctly verifies the behavior when no branch is specified.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/cli/actions/remoteAction.ts (1)

76-76: Enhance error messaging for commit SHA scenarios

While the parameter rename is correct, the error handling could be more specific for commit SHA failures.

 export const cloneRepository = async (
   url: string,
   directory: string,
   remoteBranch?: string,
   deps = {
     execGitShallowClone: execGitShallowClone,
   },
 ): Promise<void> => {
-  logger.log(`Clone repository: ${url} to temporary directory. ${pc.dim(`path: ${directory}`)}`);
+  const isCommitSha = remoteBranch && /^[0-9a-f]{40}$/i.test(remoteBranch);
+  logger.log(
+    `Clone repository: ${url}${remoteBranch ? ` (${isCommitSha ? 'commit' : 'branch'}: ${remoteBranch})` : ''} to temporary directory. ${pc.dim(
+      `path: ${directory}`,
+    )}`,
+  );
   logger.log('');

   try {
     await deps.execGitShallowClone(url, directory, remoteBranch);
   } catch (error) {
-    throw new RepomixError(`Failed to clone repository: ${(error as Error).message}`);
+    throw new RepomixError(
+      `Failed to ${remoteBranch ? `fetch ${isCommitSha ? 'commit' : 'branch'}` : 'clone repository'}: ${
+        (error as Error).message
+      }`,
+    );
   }
 };

Also applies to: 85-85

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b78228 and e9fb3eb.

📒 Files selected for processing (3)
  • src/cli/actions/remoteAction.ts (2 hunks)
  • src/core/file/gitCommand.ts (2 hunks)
  • tests/core/file/gitCommand.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/core/file/gitCommand.test.ts
🔇 Additional comments (2)
src/core/file/gitCommand.ts (2)

31-39: 🛠️ Refactor suggestion

Add error handling for git operations

The git commands could fail for various reasons (network issues, invalid commit SHA, etc.) but there's no specific error handling.

 if (remoteBranch) {
+  try {
     await deps.execAsync(`git -C ${directory} init`);
     await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
     await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${remoteBranch}`);
     await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
     await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  } catch (error) {
+    // Cleanup partial clone on failure
+    await fs.rm(directory, { recursive: true, force: true });
+    throw new Error(`Git operation failed: ${(error as Error).message}`);
+  }
 } else {
+  try {
     await deps.execAsync(`git clone --depth 1 ${url} ${directory}`);
+  } catch (error) {
+    throw new Error(`Git clone failed: ${(error as Error).message}`);
+  }
 }

Likely invalid or redundant comment.


31-36: ⚠️ Potential issue

Modify implementation to properly support commit SHAs

The current implementation using fetch --depth 1 may fail for commit SHAs as they require the full history to be fetched. For proper commit SHA support:

 if (remoteBranch) {
+  // Validate input to prevent shell injection
+  if (!/^[a-zA-Z0-9._-]+$/.test(remoteBranch)) {
+    throw new Error('Invalid branch name or commit SHA');
+  }
+  
+  // Determine if remoteBranch is a commit SHA (40 chars hex)
+  const isCommitSha = /^[0-9a-f]{40}$/i.test(remoteBranch);
+
   await deps.execAsync(`git -C ${directory} init`);
   await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
-  await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${remoteBranch}`);
+  // Fetch without --depth for commit SHAs to ensure the commit is accessible
+  await deps.execAsync(`git -C ${directory} fetch ${isCommitSha ? '' : '--depth 1'} origin ${remoteBranch}`);
   await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
   await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/core/file/gitCommand.ts (1)

26-26: ⚠️ Potential issue

Add input validation for remoteBranch parameter

The remoteBranch parameter needs validation before being used in shell commands to prevent injection attacks.

export const execGitShallowClone = async (
  url: string,
  directory: string,
  remoteBranch?: string,
  deps = {
    execAsync,
  },
) => {
+  if (remoteBranch && !/^[a-zA-Z0-9._-]+$/.test(remoteBranch)) {
+    throw new Error('Invalid branch name or commit SHA');
+  }
🧹 Nitpick comments (1)
src/core/file/gitCommand.ts (1)

31-39: Validate the URL parameter

The URL parameter should be validated to ensure it's a valid git URL.

 export const execGitShallowClone = async (
   url: string,
   directory: string,
   remoteBranch?: string,
   deps = {
     execAsync,
   },
 ) => {
+  // Basic git URL validation
+  const gitUrlPattern = /^(https?:\/\/|git@|git:\/\/).+\.git$/;
+  if (!gitUrlPattern.test(url)) {
+    throw new Error('Invalid git URL format');
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9fb3eb and 02ad3c4.

📒 Files selected for processing (3)
  • src/cli/actions/remoteAction.ts (2 hunks)
  • src/core/file/gitCommand.ts (2 hunks)
  • tests/core/file/gitCommand.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/core/file/gitCommand.test.ts
  • src/cli/actions/remoteAction.ts
🔇 Additional comments (3)
src/core/file/gitCommand.ts (3)

2-3: LGTM: Appropriate imports added for file system operations

The new imports are correctly used for handling file system operations in a cross-platform manner.


31-36: 🛠️ Refactor suggestion

Improve git fetch strategy for commit SHAs

The current implementation uses --depth 1 which might not work reliably with commit SHAs if they're not in the latest commits.

   if (remoteBranch) {
     await deps.execAsync(`git -C ${directory} init`);
     await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
-    await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${remoteBranch}`);
+    // Fetch without depth limit if it looks like a SHA
+    const fetchCmd = /^[a-f0-9]{40}$/.test(remoteBranch)
+      ? `git -C ${directory} fetch origin ${remoteBranch}`
+      : `git -C ${directory} fetch --depth 1 origin ${remoteBranch}`;
+    await deps.execAsync(fetchCmd);
     await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
     await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });

Likely invalid or redundant comment.


31-39: 🛠️ Refactor suggestion

Add error handling and cleanup

The implementation should handle errors and clean up partial clones on failure.

+  try {
     if (remoteBranch) {
       await deps.execAsync(`git -C ${directory} init`);
       await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
       await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${remoteBranch}`);
       await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
       await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
     } else {
       await deps.execAsync(`git clone --depth 1 ${url} ${directory}`);
     }
+  } catch (error) {
+    // Clean up partial clone on failure
+    await fs.rm(directory, { recursive: true, force: true }).catch(() => {});
+    throw new Error(`Git clone failed: ${(error as Error).message}`);
+  }

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/core/file/gitCommand.ts (2)

26-26: ⚠️ Potential issue

Add type validation for remoteBranch parameter

While the rename to remoteBranch is good, the parameter still needs validation to prevent shell injection.

Add validation before using the parameter:

+type ValidRemoteBranch = string & { readonly __brand: unique symbol };
+
+function validateRemoteBranch(input: string): ValidRemoteBranch {
+  if (!/^[a-zA-Z0-9._/-]+$|^[0-9a-f]{4,40}$/i.test(input)) {
+    throw new Error('Invalid branch name or commit SHA');
+  }
+  return input as ValidRemoteBranch;
+}

export const execGitShallowClone = async (
  url: string,
  directory: string,
-  remoteBranch?: string,
+  remoteBranch?: ValidRemoteBranch,

31-48: 🛠️ Refactor suggestion

Add error handling for git operations

The git commands could fail for various reasons (network issues, invalid SHA, etc.) but lack specific error handling.

   if (remoteBranch) {
+    try {
       await deps.execAsync(`git -C ${directory} init`);
       await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
 
       // Short SHA detection - matches 4-40 character hex string
       const maybeShortSha = remoteBranch?.match(/^[0-9a-f]{4,39}$/i);
 
       if (maybeShortSha) {
         await deps.execAsync(`git -C ${directory} fetch origin`);
         await deps.execAsync(`git -C ${directory} checkout ${remoteBranch}`);
       } else {
         await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${remoteBranch}`);
         await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
       }
+    } catch (error) {
+      await fs.rm(directory, { recursive: true, force: true });
+      throw new Error(`Git operation failed: ${(error as Error).message}`);
+    }
   } else {
+    try {
       await deps.execAsync(`git clone --depth 1 ${url} ${directory}`);
+    } catch (error) {
+      throw new Error(`Shallow clone failed: ${(error as Error).message}`);
+    }
   }
🧹 Nitpick comments (2)
src/core/file/gitCommand.ts (2)

35-36: Improve SHA detection precision

The current regex allows 4-39 character hex strings, but Git SHAs are typically 7-40 characters.

-    const maybeShortSha = remoteBranch?.match(/^[0-9a-f]{4,39}$/i);
+    const maybeShortSha = remoteBranch?.match(/^[0-9a-f]{7,40}$/i);

50-51: Add error handling for cleanup operation

The cleanup operation should handle errors gracefully to ensure users are informed of any issues.

   // Clean up .git directory
-  await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  try {
+    await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  } catch (error) {
+    logger.warn(`Failed to cleanup .git directory: ${(error as Error).message}`);
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f481c97 and 96cd44d.

📒 Files selected for processing (1)
  • src/core/file/gitCommand.ts (2 hunks)
🔇 Additional comments (1)
src/core/file/gitCommand.ts (1)

2-3: LGTM: Improved cross-platform compatibility with Node.js built-ins

The addition of fs/promises and path imports enables better cross-platform file operations, addressing previous feedback about platform-specific commands.

@yamadashy
Copy link
Owner

Hi, @tranquochuy645 !
Thank you for the implementation!

Your changes look good, but I wanted to add support for short commit hashes, so I've added a commit to handle that. I've verified that all the following cases now work:

# branch
npm run cli-run -- --remote yamadashy/repomix --remote-branch main

# full hash
npm run cli-run -- --remote yamadashy/repomix --remote-branch bc4f6fd6eb1a680c6d88404da5eb0844ee795e6e

# short hash
npm run cli-run -- --remote yamadashy/repomix --remote-branch bc4f6f

How does this look to you?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/core/file/gitCommand.ts (1)

31-36: ⚠️ Potential issue

Add input validation for remoteBranch parameter

The remoteBranch parameter is directly interpolated into shell commands without sanitization, which could allow command injection.

Add validation before using the remoteBranch parameter:

 if (remoteBranch) {
+  // Validate remoteBranch parameter to prevent shell injection
+  if (!/^[a-zA-Z0-9._/-]+$/.test(remoteBranch)) {
+    throw new Error('Invalid branch name, tag, or commit SHA');
+  }
   await deps.execAsync(`git -C ${directory} init`);
🧹 Nitpick comments (2)
src/core/file/gitCommand.ts (2)

37-56: Enhance error messages for better debugging

The short SHA detection and handling logic is well-implemented. Consider improving error messages to help users understand and resolve issues more easily.

-        throw err;
+        const errorMessage = err instanceof Error ? err.message : 'Unknown error';
+        throw new Error(`Failed to fetch branch/tag '${remoteBranch}': ${errorMessage}`);
-        throw err;
+        const errorMessage = err instanceof Error ? err.message : 'Unknown error';
+        throw new Error(`Failed to fetch commit '${remoteBranch}': ${errorMessage}. Possible causes: network issues, authentication failure, or invalid reference.`);

61-62: Add error handling for cleanup operation

The cleanup implementation is cross-platform compatible, but should handle potential errors during .git directory removal.

   // Clean up .git directory
-  await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  try {
+    await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  } catch (error) {
+    logger.warn(`Failed to cleanup .git directory: ${(error as Error).message}`);
+    // Continue execution as this is not a critical failure
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd7925d and b4ffe45.

📒 Files selected for processing (1)
  • src/core/file/gitCommand.ts (2 hunks)
🔇 Additional comments (1)
src/core/file/gitCommand.ts (1)

1-3: LGTM! Clean imports and consistent parameter naming.

The changes improve cross-platform compatibility with fs/promises and follow Node.js best practices with the node: prefix. The parameter rename to 'remoteBranch' better reflects its purpose.

Also applies to: 26-26

await deps.execAsync(`git -C ${directory} remote add origin ${url}`);

// Short SHA detection - matches 4-40 character hex string
const maybeShortSha = remoteBranch?.match(/^[0-9a-f]{4,39}$/i);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @yamadashy,

Thanks for the update!

I tried the fix with short commit hashes, and it works great! However, there's one thing still on my mind. The regex [0-9a-f]{4,39} can match a wide range of branch names (e.g., add1).

In cases where a user has a branch name matching this regex, it could trigger unnecessary fetching of all branches in a large repository, potentially wasting hours. What's more, the user doesn't have an option to speed up the process in this scenario. While this may be rare, it's still a possibility.

I believe using git fetch origin (without --depth 1) as a fallback when git fetch --depth 1 origin ${remoteBranch} fails would be a better solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have pushed some changes implementing the "fallback" approach.
Let me know your thoughts!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/core/file/gitCommand.ts (1)

31-59: Enhance error handling messages and types

The implementation of the fallback approach for short SHA detection looks good. Consider these improvements:

  1. Add more descriptive error messages
  2. Use custom error types for better error handling
+class GitError extends Error {
+  constructor(message: string, public readonly command?: string) {
+    super(message);
+    this.name = 'GitError';
+  }
+}

 if (!maybeShortSha) {
-  throw err;
+  throw new GitError(
+    `Failed to fetch branch "${remoteBranch}": ${(err as Error).message}`,
+    'git fetch'
+  );
 }

 if (!isRefNotfoundError) {
-  throw err;
+  throw new GitError(
+    `Git operation failed: ${(err as Error).message}`,
+    'git fetch'
+  );
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 49-49: src/core/file/gitCommand.ts#L49
Added line #L49 was not covered by tests

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4ffe45 and eb8959b.

📒 Files selected for processing (2)
  • src/core/file/gitCommand.ts (2 hunks)
  • tests/core/file/gitCommand.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/core/file/gitCommand.test.ts
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/core/file/gitCommand.ts

[warning] 49-49: src/core/file/gitCommand.ts#L49
Added line #L49 was not covered by tests

🔇 Additional comments (4)
src/core/file/gitCommand.ts (4)

2-3: LGTM: Clean imports for fs operations

The added imports from Node.js built-in modules are appropriate for the new file system operations.


26-26: LGTM: Improved parameter naming

The rename from 'branch' to 'remoteBranch' better reflects the parameter's purpose and usage.


61-62: LGTM: Cross-platform cleanup implementation

The cleanup implementation uses modern Node.js APIs and is cross-platform compatible.


47-50: Add test coverage for non-ref-notfound errors

The error handling for non-ref-notfound errors (line 49) lacks test coverage.

Would you like me to generate test cases covering these error scenarios?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 49-49: src/core/file/gitCommand.ts#L49
Added line #L49 was not covered by tests

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/core/file/gitCommand.ts (1)

31-36: ⚠️ Potential issue

Add input validation to prevent shell injection

The code directly interpolates user input into shell commands without sanitization, which could allow command injection attacks.

Add validation before using the remoteBranch parameter:

 if (remoteBranch) {
+  // Validate remoteBranch parameter to prevent shell injection
+  if (!/^[0-9a-zA-Z._/-]+$/.test(remoteBranch)) {
+    throw new Error('Invalid branch name, tag, or commit SHA');
+  }
   await deps.execAsync(`git -C ${directory} init`);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb8959b and 766995c.

📒 Files selected for processing (1)
  • src/core/file/gitCommand.ts (2 hunks)
🔇 Additional comments (3)
src/core/file/gitCommand.ts (3)

2-3: LGTM: Clean imports using Node.js best practices

The new imports follow Node.js best practices by using the node: prefix for built-in modules.


26-26: LGTM: Clear parameter naming

The rename from branch to remoteBranch better reflects the parameter's purpose and aligns with the changes across the codebase.


63-64: LGTM: Cross-platform cleanup implementation

The cleanup implementation uses Node.js fs API instead of shell commands, ensuring cross-platform compatibility.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/core/file/gitCommand.ts (2)

26-26: ⚠️ Potential issue

Add input validation for remoteBranch parameter

The remoteBranch parameter is directly interpolated into shell commands without sanitization, which could allow shell injection attacks.

Add validation before using the parameter:

export const execGitShallowClone = async (
  url: string,
  directory: string,
  remoteBranch?: string,
  deps = {
    execAsync,
  },
) => {
+  // Validate remoteBranch parameter to prevent shell injection
+  if (remoteBranch && !/^[a-zA-Z0-9._/-]+$/.test(remoteBranch)) {
+    throw new Error('Invalid branch name or commit SHA');
+  }

47-61: 🛠️ Refactor suggestion

Improve SHA detection logic

The current SHA detection has several issues:

  1. The regex /^[0-9a-f]{4,39}$/i is too permissive (Git requires minimum 7 chars)
  2. The variable name isNotShortSHA is confusing due to double negative
  3. The fallback fetch could be expensive for large repositories

Apply these improvements:

-      // Short SHA detection - matches a hexadecimal string of 4 to 39 characters
-      // If the string matches this regex, it MIGHT be a short SHA
-      // If the string doesn't match, it is DEFINITELY NOT a short SHA
-      const isNotShortSHA = !remoteBranch.match(/^[0-9a-f]{4,39}$/i);
-
-      if (isNotShortSHA) {
+      // Short SHA detection - matches a hexadecimal string of 7 to 40 characters
+      // Full SHA is exactly 40 chars, short SHA is 7-39 chars
+      const isPotentialSHA = remoteBranch.match(/^[0-9a-f]{7,39}$|^[0-9a-f]{40}$/i);
+
+      if (!isPotentialSHA) {
         // Rethrow error as nothing else we can do
         throw err;
       }
🧹 Nitpick comments (1)
src/core/file/gitCommand.ts (1)

66-67: Add error handling for cleanup operation

The cleanup operation should handle errors gracefully, as the .git directory removal might fail.

Add error handling:

   // Clean up .git directory
-  await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  try {
+    await fs.rm(path.join(directory, '.git'), { recursive: true, force: true });
+  } catch (error) {
+    logger.warn('Failed to cleanup .git directory:', (error as Error).message);
+    // Continue as this is not a critical error
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 766995c and d64c1b0.

📒 Files selected for processing (2)
  • src/core/file/gitCommand.ts (2 hunks)
  • tests/core/file/gitCommand.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/core/file/gitCommand.test.ts

@yamadashy
Copy link
Owner

Sorry for the delay in reviewing this! I've rebased and force pushed to keep the branch up to date.
I'll start reviewing it now.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/core/file/gitCommand.ts (1)

34-45: Enhance error handling clarity

The error handling is robust but could be more explicit about the type of error encountered.

Consider adding more specific error messages:

     try {
       await deps.execAsync(`git -C ${directory} fetch --depth 1 origin ${remoteBranch}`);
       await deps.execAsync(`git -C ${directory} checkout FETCH_HEAD`);
     } catch (err: unknown) {
+      if (!(err instanceof Error)) {
+        throw new Error('Unknown error during git operations');
+      }
+
       // git fetch --depth 1 origin <short SHA> always throws "couldn't find remote ref" error
       const isRefNotfoundError =
-        err instanceof Error && err.message.includes(`couldn't find remote ref ${remoteBranch}`);
+        err.message.includes(`couldn't find remote ref ${remoteBranch}`);

       if (!isRefNotfoundError) {
-        // Rethrow error as nothing else we can do
+        // Rethrow with context for better debugging
+        throw new Error(`Git operation failed: ${err.message}`);
-        throw err;
       }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d64c1b0 and 9213ad8.

📒 Files selected for processing (3)
  • src/cli/actions/remoteAction.ts (2 hunks)
  • src/core/file/gitCommand.ts (2 hunks)
  • tests/core/file/gitCommand.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/cli/actions/remoteAction.ts
  • tests/core/file/gitCommand.test.ts
🔇 Additional comments (4)
src/core/file/gitCommand.ts (4)

2-3: LGTM: Proper imports added for filesystem operations

The new imports for fs/promises and path are correctly added to support the cleanup operations.


26-26: LGTM: Parameter name improved for clarity

Renaming from branch to remoteBranch better indicates the parameter's purpose.


66-67: LGTM: Clean implementation of .git directory cleanup

The cleanup uses modern Node.js APIs (fs.rm) with proper path joining and error handling options.


31-33: ⚠️ Potential issue

Add input validation for URL and directory parameters

The parameters are directly interpolated into shell commands without sanitization, making them vulnerable to command injection.

Add validation before executing shell commands:

+  // Validate URL format
+  try {
+    new URL(url);
+  } catch {
+    throw new Error('Invalid Git URL format');
+  }
+
+  // Validate and resolve directory path
+  const resolvedPath = path.resolve(directory);
+  if (resolvedPath.includes('..')) {
+    throw new Error('Directory path cannot contain path traversal');
+  }
+
   if (remoteBranch) {
-    await deps.execAsync(`git -C ${directory} init`);
-    await deps.execAsync(`git -C ${directory} remote add origin ${url}`);
+    await deps.execAsync(`git -C ${resolvedPath} init`);
+    await deps.execAsync(`git -C ${resolvedPath} remote add origin ${url}`);

Likely invalid or redundant comment.

@yamadashy
Copy link
Owner

@tranquochuy645
The code looks perfect! I've tested it out and everything seems to be working as expected.

Thanks for your improvements. I'll merge this now.

@yamadashy yamadashy merged commit 935b695 into yamadashy:main Dec 31, 2024
@yamadashy
Copy link
Owner

I added an example of using a commit hash with the --remote-branch flag to make it clearer that this flag supports not just branches but also tags and commit hashes. #232

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants