Skip to content

Commit

Permalink
Cody: Add support for more git clone urls and remove reload on codeba…
Browse files Browse the repository at this point in the history
…se update (#51274)
  • Loading branch information
abeatrix committed May 2, 2023
1 parent bfe3f7b commit 306cce6
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 139 deletions.
4 changes: 4 additions & 0 deletions client/cody/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ All notable changes to Sourcegraph Cody will be documented in this file.

### Added

- Updating `cody.codebase` does not require reloading VS Code

### Fixed

- Fixes an issue where code blocks were unexpectedly escaped [pull/51247](https://github.com/sourcegraph/sourcegraph/pull/51247)

### Changed

- Replace `Cody: Set Access Token` command with `Cody: Sign in`

## [0.0.9]

### Added
Expand Down
54 changes: 24 additions & 30 deletions client/cody/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,84 +99,69 @@
},
{
"command": "cody.recipe.explain-code",
"title": "Ask Cody: Explain Code in Detail",
"when": "cody.activated"
"title": "Ask Cody: Explain Code in Detail"
},
{
"command": "cody.recipe.explain-code-high-level",
"title": "Ask Cody: Explain Code at a High Level",
"when": "cody.activated"
"title": "Ask Cody: Explain Code at a High Level"
},
{
"command": "cody.recipe.generate-unit-test",
"title": "Ask Cody: Generate Unit Test",
"when": "cody.activated"
"title": "Ask Cody: Generate Unit Test"
},
{
"command": "cody.recipe.generate-docstring",
"title": "Ask Cody: Generate Docstring",
"when": "cody.activated"
"title": "Ask Cody: Generate Docstring"
},
{
"command": "cody.recipe.translate-to-language",
"title": "Ask Cody: Translate to Language",
"when": "cody.activated"
"title": "Ask Cody: Translate to Language"
},
{
"command": "cody.recipe.git-history",
"title": "Ask Cody: Summarize Recent Code Changes",
"when": "cody.activated"
"title": "Ask Cody: Summarize Recent Code Changes"
},
{
"command": "cody.recipe.improve-variable-names",
"title": "Ask Cody: Improve Variable Names",
"when": "cody.activated"
"title": "Ask Cody: Improve Variable Names"
},
{
"command": "cody.recipe.fixup",
"title": "Cody: Fixup",
"when": "cody.activated"
"title": "Cody: Fixup"
},
{
"command": "cody.set-access-token",
"title": "Cody: Set Access Token",
"when": "!cody.activated"
"title": "Cody: Set Access Token"
},
{
"command": "cody.delete-access-token",
"title": "Cody: Delete Access Token",
"when": "cody.activated"
"title": "Cody: Sign out"
},
{
"command": "cody.experimental.suggest",
"title": "Cody: View Suggestions",
"when": "cody.activated"
"title": "Cody: View Suggestions"
},
{
"command": "cody.settings",
"title": "Cody: Settings",
"group": "Cody",
"icon": "$(gear)",
"when": "cody.activated"
"icon": "$(gear)"
},
{
"command": "cody.focus",
"title": "Cody: Sign In to Use Cody",
"when": "!cody.activated"
"title": "Cody: Sign In"
},
{
"command": "cody.interactive.clear",
"title": "Cody: Clear & Restart Chat Session",
"group": "Cody",
"icon": "$(clear-all)",
"when": "cody.activated"
"icon": "$(clear-all)"
},
{
"command": "cody.history",
"title": "Cody: Chat History",
"group": "Cody",
"icon": "$(history)",
"when": "cody.activated"
"icon": "$(history)"
}
],
"keybindings": [
Expand Down Expand Up @@ -227,6 +212,15 @@
{
"command": "cody.recipe.fixup",
"when": "cody.activated"
},
{
"command": "cody.set-access-token",
"when": "false"
},
{
"command": "cody.focus",
"title": "Cody: Sign In",
"when": "!cody.activated"
}
],
"editor/context": [
Expand Down
30 changes: 30 additions & 0 deletions client/cody/src/chat/ChatViewProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ describe('convertGitCloneURLToCodebaseName', () => {
)
})

test('converts Bitbucket HTTPS URL', () => {
expect(convertGitCloneURLToCodebaseName('https://username@bitbucket.org/sourcegraph/sourcegraph.git')).toEqual(
'bitbucket.org/sourcegraph/sourcegraph'
)
})

test('converts Bitbucket SSH URL', () => {
expect(convertGitCloneURLToCodebaseName('git@bitbucket.sgdev.org:sourcegraph/sourcegraph.git')).toEqual(
'bitbucket.sgdev.org/sourcegraph/sourcegraph'
)
})

test('converts GitLab SSH URL', () => {
expect(convertGitCloneURLToCodebaseName('git@gitlab.com:sourcegraph/sourcegraph.git')).toEqual(
'gitlab.com/sourcegraph/sourcegraph'
Expand All @@ -31,6 +43,24 @@ describe('convertGitCloneURLToCodebaseName', () => {
)
})

test('converts GitHub SSH URL with Git', () => {
expect(convertGitCloneURLToCodebaseName('git@github.com:sourcegraph/sourcegraph.git')).toEqual(
'github.com/sourcegraph/sourcegraph'
)
})

test('converts Eriks SSH Alias URL', () => {
expect(convertGitCloneURLToCodebaseName('github:sourcegraph/sourcegraph')).toEqual(
'github.com/sourcegraph/sourcegraph'
)
})

test('converts HTTP URL', () => {
expect(convertGitCloneURLToCodebaseName('http://github.com/sourcegraph/sourcegraph')).toEqual(
'github.com/sourcegraph/sourcegraph'
)
})

test('returns null for invalid URL', () => {
expect(convertGitCloneURLToCodebaseName('invalid')).toEqual(null)
})
Expand Down
95 changes: 45 additions & 50 deletions client/cody/src/chat/ChatViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { isError } from '@sourcegraph/cody-shared/src/utils'

import { View } from '../../webviews/NavBar'
import { LocalStorage } from '../command/LocalStorageProvider'
import { updateConfiguration } from '../configuration'
import { getFullConfig, updateConfiguration } from '../configuration'
import { logEvent } from '../event-logger'
import { LocalKeywordContextFetcher } from '../keyword-context/local-keyword-context-fetcher'
import { CODY_ACCESS_TOKEN_SECRET, SecretStorage } from '../secret-storage'
Expand Down Expand Up @@ -97,6 +97,13 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
this.disposables.push(
vscode.window.onDidChangeActiveTextEditor(async () => {
await this.updateCodebaseContext()
}),
vscode.workspace.onDidChangeConfiguration(async () => {
this.config = await getFullConfig(this.secretStorage)
const newCodebaseContext = await getCodebaseContext(this.config, this.rgPath, this.editor)
if (newCodebaseContext) {
this.codebaseContext = newCodebaseContext
}
})
)
}
Expand Down Expand Up @@ -378,20 +385,10 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
return Promise.resolve()
},
onTurnComplete: () => {
console.log({ text })
console.log(text.split('\n'))
console.log(text.split('\n').slice(0, 3))
console.log(
text
.split('\n')
.slice(3)
.map(line => line.trim().replace(/^-/, '').trim())
)
const suggestions = text
.split('\n')
.slice(0, 3)
.map(line => line.trim().replace(/^-/, '').trim())
console.log({ suggestions })
this.sendSuggestions(suggestions)
return Promise.resolve()
},
Expand Down Expand Up @@ -516,6 +513,8 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp
*/
private publishConfig(): void {
const send = async (): Promise<void> => {
// update codebase context on configuration change
void this.updateCodebaseContext()
// check if new configuration change is valid or not
// log user out if config is invalid
const isAuthed = await isValidLogin({
Expand Down Expand Up @@ -677,42 +676,46 @@ async function fileExists(filePath: string): Promise<boolean> {
}

// Converts a git clone URL to the codebase name that includes the slash-separated code host, owner, and repository name
// This should captures:
// - "github:sourcegraph/sourcegraph" a common SSH host alias
// - "https://github.com/sourcegraph/deploy-sourcegraph-k8s.git"
// - "git@github.com:sourcegraph/sourcegraph.git"
export function convertGitCloneURLToCodebaseName(cloneURL: string): string | null {
let parsed: URL | null = null
if (cloneURL.startsWith('git@')) {
// Handle Git SSH URL format
if (!cloneURL) {
console.error(`Unable to determine the git clone URL for this workspace.\ngit output: ${cloneURL}`)
return null
}
try {
const uri = new URL(cloneURL.replace('git@', ''))
// Handle common Git SSH URL format
const match = cloneURL.match(/git@([^:]+):([\w-]+)\/([\w-]+)(\.git)?/)
if (match) {
if (cloneURL.startsWith('git@') && match) {
const host = match[1]
const owner = match[2]
const repo = match[3]
return `${host}/${owner}/${repo}`
}
} else {
// Handle HTTP/HTTPS URL format
try {
parsed = new URL(cloneURL)
} catch {
return null
// Handle GitHub URLs
if (uri.protocol.startsWith('github') || uri.href.startsWith('github')) {
return `github.com/${uri.pathname.replace('.git', '')}`
}
// Handle GitLab URLs
if (uri.protocol.startsWith('gitlab') || uri.href.startsWith('gitlab')) {
return `gitlab.com/${uri.pathname.replace('.git', '')}`
}
// Handle HTTPS URLs
if (uri.protocol.startsWith('http') && uri.hostname && uri.pathname) {
return `${uri.hostname}${uri.pathname.replace('.git', '')}`
}
// Generic URL
if (uri.hostname && uri.pathname) {
return `${uri.hostname}${uri.pathname.replace('.git', '')}`
}
}

if (!parsed) {
return null
} catch (error) {
console.error(`Cody could not extract repo name from clone URL ${cloneURL}:`, error)
return null
}

const host = parsed.host
const path = parsed.pathname

// Handle GitHub/GitLab URL format
if (host.includes('gitlab.com') || host.includes('github.com')) {
const [owner, repo] = path.slice(1).split('/')
return `${host}/${owner}/${repo}`.replace(/.git$/, '')
}

// Generic fallback - assume URL path contains owner/repo
const [owner, repo] = path.slice(1).split('/')
return `${host}/${owner}/${repo}`
}

async function getCodebaseContext(config: Config, rgPath: string, editor: Editor): Promise<CodebaseContext | null> {
Expand All @@ -721,26 +724,18 @@ async function getCodebaseContext(config: Config, rgPath: string, editor: Editor
if (!workspaceRoot) {
return null
}

const gitCommand = spawnSync('git', ['remote', 'get-url', 'origin'], { cwd: workspaceRoot })
const gitOutput = gitCommand.stdout.toString().trim()
if (!gitOutput) {
void vscode.window.showErrorMessage(
`Unable to determine the git clone URL for this workspace.\ngit output: ${gitOutput}`
)
return null
}

// Get repository name from git clone URL
const codebase = convertGitCloneURLToCodebaseName(gitOutput)
// Get codebase from config or fallback to getting repository name from git clone URL
const codebase = config.codebase || convertGitCloneURLToCodebaseName(gitOutput)
if (!codebase) {
void vscode.window.showErrorMessage(`could not extract repo name from clone URL ${gitOutput}`)
return null
}
// Check if repo is embedded in endpoint
const repoId = await client.getRepoIdIfEmbeddingExists(codebase)
if (isError(repoId)) {
const errorMessage = `Cody could not find embeddings for '${codebase}' on your Sourcegraph instance.\n`
void vscode.window.showErrorMessage(errorMessage)
const infoMessage = `Cody could not find embeddings for '${codebase}' on your Sourcegraph instance.\n`
console.info(infoMessage)
return null
}

Expand Down
13 changes: 12 additions & 1 deletion client/cody/src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import * as vscode from 'vscode'

import type { ConfigurationUseContext, Configuration } from '@sourcegraph/cody-shared/src/configuration'
import type {
ConfigurationUseContext,
Configuration,
ConfigurationWithAccessToken,
} from '@sourcegraph/cody-shared/src/configuration'

import { SecretStorage, getAccessToken } from './secret-storage'

/**
* All configuration values, with some sanitization performed.
Expand Down Expand Up @@ -39,3 +45,8 @@ const codyConfiguration = vscode.workspace.getConfiguration('cody')
export async function updateConfiguration(configKey: string, configValue: string): Promise<void> {
await codyConfiguration.update(configKey, configValue, vscode.ConfigurationTarget.Global)
}

export const getFullConfig = async (secretStorage: SecretStorage): Promise<ConfigurationWithAccessToken> => {
const config = getConfiguration(vscode.workspace.getConfiguration())
return { ...config, accessToken: await getAccessToken(secretStorage) }
}
7 changes: 2 additions & 5 deletions client/cody/src/external-services.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { window } from 'vscode'

import { ChatClient } from '@sourcegraph/cody-shared/src/chat/chat'
import { CodebaseContext } from '@sourcegraph/cody-shared/src/codebase-context'
import { ConfigurationWithAccessToken } from '@sourcegraph/cody-shared/src/configuration'
Expand Down Expand Up @@ -39,11 +37,10 @@ export async function configureExternalServices(

const repoId = initialConfig.codebase ? await client.getRepoId(initialConfig.codebase) : null
if (isError(repoId)) {
const errorMessage =
const infoMessage =
`Cody could not find the '${initialConfig.codebase}' repository on your Sourcegraph instance.\n` +
'Please check that the repository exists and is entered correctly in the cody.codebase setting.'
console.error(errorMessage)
void window.showErrorMessage(errorMessage)
console.info(infoMessage)
}
const embeddingsSearch = repoId && !isError(repoId) ? new SourcegraphEmbeddingsSearchClient(client, repoId) : null

Expand Down
Loading

0 comments on commit 306cce6

Please sign in to comment.