Browse Source

Git Repo URL telemetry link fix: only Https (#5202)

* https parsing in URL sanatizing for git repo

* fix: remove duplicate JSDoc and add SSH to HTTPS conversion test

- Remove duplicate JSDoc comment from convertGitUrlToHttps function
- Add test case to verify getGitRepositoryInfo converts SSH URLs to HTTPS format

---------

Co-authored-by: Daniel Riccio <[email protected]>
Will Li 6 months ago
parent
commit
dc44e7a40b
2 changed files with 124 additions and 1 deletions
  1. 84 0
      src/utils/__tests__/git.spec.ts
  2. 40 1
      src/utils/git.ts

+ 84 - 0
src/utils/__tests__/git.spec.ts

@@ -12,6 +12,7 @@ import {
 	extractRepositoryName,
 	getWorkspaceGitInfo,
 	GitRepositoryInfo,
+	convertGitUrlToHttps,
 } from "../git"
 import { truncateOutput } from "../../integrations/misc/extract-text"
 
@@ -560,6 +561,89 @@ describe("getGitRepositoryInfo", () => {
 			repositoryName: "RooCodeInc/Roo-Code",
 		})
 	})
+
+	it("should convert SSH URLs to HTTPS format", async () => {
+		// Clear previous mocks
+		vitest.clearAllMocks()
+
+		// Create a spy to track the implementation
+		const gitSpy = vitest.spyOn(fs.promises, "readFile")
+
+		// Mock successful access to .git directory
+		vitest.mocked(fs.promises.access).mockResolvedValue(undefined)
+
+		// Mock git config file with SSH URL
+		const mockConfig = `
+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = false
+[remote "origin"]
+	url = [email protected]:RooCodeInc/Roo-Code.git
+	fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "main"]
+	remote = origin
+	merge = refs/heads/main
+`
+		// Mock HEAD file content
+		const mockHead = "ref: refs/heads/main"
+
+		// Setup the readFile mock to return different values based on the path
+		gitSpy.mockImplementation((path: any, encoding: any) => {
+			if (path === configPath) {
+				return Promise.resolve(mockConfig)
+			} else if (path === headPath) {
+				return Promise.resolve(mockHead)
+			}
+			return Promise.reject(new Error(`Unexpected path: ${path}`))
+		})
+
+		const result = await getGitRepositoryInfo(workspaceRoot)
+
+		// Verify that the SSH URL was converted to HTTPS
+		expect(result).toEqual({
+			repositoryUrl: "https://github.com/RooCodeInc/Roo-Code.git",
+			repositoryName: "RooCodeInc/Roo-Code",
+			defaultBranch: "main",
+		})
+	})
+})
+
+describe("convertGitUrlToHttps", () => {
+	it("should leave HTTPS URLs unchanged", () => {
+		const url = "https://github.com/RooCodeInc/Roo-Code.git"
+		const converted = convertGitUrlToHttps(url)
+
+		expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git")
+	})
+
+	it("should convert SSH URLs to HTTPS format", () => {
+		const url = "[email protected]:RooCodeInc/Roo-Code.git"
+		const converted = convertGitUrlToHttps(url)
+
+		expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git")
+	})
+
+	it("should convert SSH URLs with ssh:// prefix to HTTPS format", () => {
+		const url = "ssh://[email protected]/RooCodeInc/Roo-Code.git"
+		const converted = convertGitUrlToHttps(url)
+
+		expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git")
+	})
+
+	it("should handle URLs without git@ prefix", () => {
+		const url = "ssh://github.com/RooCodeInc/Roo-Code.git"
+		const converted = convertGitUrlToHttps(url)
+
+		expect(converted).toBe("https://github.com/RooCodeInc/Roo-Code.git")
+	})
+
+	it("should handle invalid URLs gracefully", () => {
+		const url = "not-a-valid-url"
+		const converted = convertGitUrlToHttps(url)
+
+		expect(converted).toBe("not-a-valid-url")
+	})
 })
 
 describe("sanitizeGitUrl", () => {

+ 40 - 1
src/utils/git.ts

@@ -51,7 +51,8 @@ export async function getGitRepositoryInfo(workspaceRoot: string): Promise<GitRe
 
 			if (urlMatch && urlMatch[1]) {
 				const url = urlMatch[1].trim()
-				gitInfo.repositoryUrl = sanitizeGitUrl(url)
+				// Sanitize the URL and convert to HTTPS format for telemetry
+				gitInfo.repositoryUrl = convertGitUrlToHttps(sanitizeGitUrl(url))
 				const repositoryName = extractRepositoryName(url)
 				if (repositoryName) {
 					gitInfo.repositoryName = repositoryName
@@ -88,6 +89,44 @@ export async function getGitRepositoryInfo(workspaceRoot: string): Promise<GitRe
 	}
 }
 
+/**
+ * Converts a git URL to HTTPS format
+ * @param url The git URL to convert
+ * @returns The URL in HTTPS format, or the original URL if conversion is not possible
+ */
+export function convertGitUrlToHttps(url: string): string {
+	try {
+		// Already HTTPS, just return it
+		if (url.startsWith("https://")) {
+			return url
+		}
+
+		// Handle SSH format: [email protected]:user/repo.git -> https://github.com/user/repo.git
+		if (url.startsWith("git@")) {
+			const match = url.match(/git@([^:]+):(.+)/)
+			if (match && match.length === 3) {
+				const [, host, path] = match
+				return `https://${host}/${path}`
+			}
+		}
+
+		// Handle SSH with protocol: ssh://[email protected]/user/repo.git -> https://github.com/user/repo.git
+		if (url.startsWith("ssh://")) {
+			const match = url.match(/ssh:\/\/(?:git@)?([^\/]+)\/(.+)/)
+			if (match && match.length === 3) {
+				const [, host, path] = match
+				return `https://${host}/${path}`
+			}
+		}
+
+		// Return original URL if we can't convert it
+		return url
+	} catch {
+		// If parsing fails, return original
+		return url
+	}
+}
+
 /**
  * Sanitizes a git URL to remove sensitive information like tokens
  * @param url The original git URL