Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playwright V2 E2E Framework #4706

Merged
merged 12 commits into from
Jul 3, 2024
Merged

Playwright V2 E2E Framework #4706

merged 12 commits into from
Jul 3, 2024

Conversation

RXminuS
Copy link
Contributor

@RXminuS RXminuS commented Jun 27, 2024

Completely revamped E2E testing framework for E2E tests, Issue Replications, and faster TDD UI development.

  1. eliminates all flake
  2. fully isolated tests + fully configurable per-test overrides (vscode version, installed extensions, config, etc)
  3. runs faster, fully parallel and headless
  4. leverages Playwright tracing UI instead of video recordings
  5. allows for easy recording and mocking of network traffic
  6. allows for automatic recording of tests by just "using VSCode"
  7. "hot-reloadable" extensions & tests (so watch mode works properly)

Test plan

Not making part of CI and doesn't effect anyone else yet. Locally we test with pnpm run test:e2e2

30s is higher than the test timeout, which lead to hard to debug
failures. 10s means in the default case if we have issues the 'Could not
start code process' error will be thrown.
On linux I need my PATH and because of the environment I am on I also
need other environment variables to be able to run random binaries from
the internet (ie the vscode we download).
@keegancsmith
Copy link
Member

@RXminuS I just pushed some commits to make this work on my linux machine. Please take a look. Busy reviewing the code as well.

I did not test on windows. However, I inspected the tarball from the
download site and it follows the same pattern as linux
Copy link
Member

@keegancsmith keegancsmith left a comment

Choose a reason for hiding this comment

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

I like this a lot and really want to land it now so we can iterate on it more incrementally. However, I have a concern around the complexity introduced around how we fetch the symf and bfg assets. Is there maybe a way we can approach that differently?

Alright now some other comments:

Where do you expect people to put the tests in the current project hierarchy? Directly in vscode/e2e?

When the fixture fails due to timeout it is really hard to debug. For some reason it doesn't blame to the actual line that was running:

  1) [e2e] › e2e/example.test.ts:8:9 › Demonstrations › Show off v2 features ───────────────────────

    Fixture "vscodeUI" timeout of 21126ms exceeded during setup.

       at e2e/utils/vscody/fixture.ts:173

      171 | })
      172 |
    > 173 | const implFixture = _test.extend<TestContext, WorkerContext>({
          |                           ^
      174 |     serverRootDir: [
      175 |         // biome-ignore lint/correctness/noEmptyPattern: <explanation>
      176 |         async ({}, use, testInfo) => {

        at /home/keegan/src/github.com/sourcegraph/cody/vscode/e2e/utils/vscody/fixture.ts:173:27

@@ -12,5 +10,5 @@ interface BuildGraphQLUrlOptions {
export const buildGraphQLUrl = ({ request, baseUrl }: BuildGraphQLUrlOptions): string => {
const nameMatch = request ? request.match(/^\s*(?:query|mutation)\s+(\w+)/) : ''
const apiURL = `${GRAPHQL_URI}${nameMatch ? `?${nameMatch[1]}` : ''}`
return baseUrl ? new URL(trimEnd(baseUrl, '/') + apiURL).href : apiURL
return baseUrl ? new URL(apiURL, baseUrl).href : apiURL
Copy link
Member

Choose a reason for hiding this comment

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

FYI this doesn't mean the same as before since apiURL is an absolute path it will remove any path from baseUrl. What motivated this change? I do think this is safe though, since sourcegraph doesn't generally support not being the root of a domain.

> new URL('/foo', 'http://example.com/bar/').href
'http://example.com/foo'


export namespace SemverString {
const splitPrefixRegex = /^(?<prefix>.*)(?<version>\d+\.\d+\.\d+)$/
export function forcePrefix<P extends string>(prefix: P, value: string): SemverString<P> {
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure the version strings are always semver? What value do we have in enforcing this now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This type is used in the two locations where the only "guard" against mis-use was a comment left above it. Especially with the fact that one of them uses a 'v' and the other doesn't made that I had a very hard to debug issue as I simply misread the urls the whole time. This essentially just captures those two original comments as type-checks.

@@ -0,0 +1,28 @@
# Issue Tests
Copy link
Member

Choose a reason for hiding this comment

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

If it isn't run as part of CI, what is the point of keeping the test code around? It will just rot right?

@@ -0,0 +1,6 @@
TODO: This will be a nice guide

- `pnpm run test:e2e2:run --ui` to view trace UI locally
Copy link
Member

Choose a reason for hiding this comment

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

When I did that though it only had the symlink project selected, which was confused me until I realised I could select other projects. Any idea why this happened / any idea how we can make this better?

This UI is rad though :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I had the same, I'll have a look if I can make that nicer

- `pnpm run test:e2e2:run --ui` to view trace UI locally
- How to update network recordings
- How to record a test
- Issue tests
Copy link
Member

Choose a reason for hiding this comment

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

Can you also give guidance on how to debug? I couldn't work it out when trying to get this to work for linux and ended up just reading code + console.log :)

* Get the path to `bfg` binary. If possible it will be downloaded.
*/
export async function getBfgPath(context: vscode.ExtensionContext): Promise<string | null> {
// If user-specified symf path is set, use that
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// If user-specified symf path is set, use that
// If user-specified bfg path is set, use that

}
// this protects agains multiple async functions in the same node process from
// starting a download
const processDownloadLock = new Mutex()
Copy link
Member

Choose a reason for hiding this comment

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

the older code just used a promise to get the same behaviour which was quite easy to understand. What are the benefits of using an explicit mutex?

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 don't really have a preference, I just didn't think it worth having two entirely different methods in what is functionally the same code between BFG and SYMF downloads. I thought it looked more explicit with the Mutex what the aim was.

Comment on lines +77 to +80
if (await fileExists(bfgPath)) {
logDebug('CodyEngine', `using downloaded bfg path "${bfgPath}"`)
return bfgPath
}
Copy link
Member

Choose a reason for hiding this comment

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

you only grab the lock after this point, which means someone else could of come in and downloaded after this, but then you still will grab the lock afterwards and re-download. You probably need to check inside of the lock again for if the file exists.

Comment on lines 70 to 74
const age = Date.now() - fileStats.mtimeMs
if (age < maxMtimeMs) {
// this file has not been abandoned
return false
}
Copy link
Member

Choose a reason for hiding this comment

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

This feels very dodgy. You are trusting that mtime is regularly updated on all filesystems in a reasonably fast way. These are ways I can see this breaking:

  • the filesystem just doesn't respect mtime (rare but possible)
  • the regularity mtime is updated is more than your maxMtimeMs. eg networked filesystems or possibly filesystems that wait for fsync and have large buffers / slow downloads.
  • an attempt to download happened with the wrong clock time in the future and failed. NTP ran and the clock is now correct, leading to us refusing to ever download

I am sure there are more reasons.

It seems like a lot of the complexity added here was to help with e2e tests which can have multiple processes starting up at the same time. Is there a way we can only use the more complicated logic in those cases, and rely on simpler code for the normal user of cody?

Copy link
Contributor Author

@RXminuS RXminuS Jun 28, 2024

Choose a reason for hiding this comment

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

As mentioned below, this is actually not a problem just for the tests, it's a problem (although rare) in the extension in general. But I agree, I wasn't really happy with this either. I'll see if I can think of something simpler (as I did something similar but nicer in some test specific areas). For instance I can just add a timestamp to the name of the file, which would eliminate a bunch of logic & be more robust

// this file has not been abandoned
return false
}
logDebug('symf', `file ${filePath} is abandoned, removing it`)
Copy link
Member

Choose a reason for hiding this comment

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

this helper is also used by bfg now?

@RXminuS
Copy link
Contributor Author

RXminuS commented Jun 28, 2024

I think the BFG & Symf asset fetching is a problem in the extension in general, not just for these tests. I first noticed it while trying to fix the original E2E tests. The way to trigger it in VSCode is if for some reason you have two extension hosts and you reload the window with an updated extension it would break as two are concurrently trying to unzip to the same location.

I can definetly clean it up a bit into a cleaner and more isolated abstraction

@RXminuS
Copy link
Contributor Author

RXminuS commented Jun 28, 2024

When the fixture fails due to timeout it is really hard to debug. For some reason it doesn't blame to the actual line that was running:

I'll add it to the todo list because it's really annoying. I'm not sure if it's just something in Playwright though. You can actually see the original location further up in the logs and in the trace it does show correctly as well

@keegancsmith
Copy link
Member

keegancsmith commented Jun 28, 2024

Ok the complexity is worth it then. I think it is probably worth avoiding concurrent downloads. I did some research cause I dig systems programming and wondered whats best. Everyone seems to do it differently, sqlite relies on fcntl locks and git relies on lock files but then asks the user to clean up if they are left lying around. Given we are in node, I quite liked the creativity of relying on mkdir as a locking primitive. It also has the same issue around assuming mtime is never in the future, but from reading the code I don't think it has a lot of the other potential pitfalls. https://www.npmjs.com/package/proper-lockfile

Edit looking through there open issues I only saw one proper legit one and it ended up being from playwright (who use this package!). I would also use there version of the package since that seems like a very annoying bug moxystudio/node-proper-lockfile#111

@@ -437,6 +442,7 @@ const implFixture = _test.extend<TestContext, WorkerContext>({
'serve-web',
'--accept-server-license-terms',
'--port=0',
`--cli-data-dir=${serverExecutableDir.replace(/ /g, '\\ ')}`,
`--server-data-dir=${serverRootDir.replace(/ /g, '\\ ')}`,
`--extensions-dir=${extensionsDir.replace(/ /g, '\\ ')}`, // cli doesn't handle quotes properly so just escape spaces,
Copy link
Member

Choose a reason for hiding this comment

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

to be honest I'm surprised you need to escape these strings with that regex given you are using the spawn apis. Are you sure that is needed?

const config = { url: `http://127.0.0.1:${port}/`, token: token }

// we now need to wait for the server to be downloaded
Copy link
Member

Choose a reason for hiding this comment

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

is the server another component that isn't included in the tarball you download? I'm slightly concerned that we are just ignoring what we have put on disk because this cli doesn't see it and it instead is downloading the latest stable version of vscode here.

Is there a global before hook we could maybe be using for all of these? That might be cleaner from a test reporting perspective. But I guess this has some nice properties as well :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doing multiple version handling and switching I will get to later. Current setup aligns with how we already electron download latest version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reason for not doing global hook is I want to be able to on a test by test basis switch to specific vscode commit if I want to (especially for issue reproductions)

@keegancsmith
Copy link
Member

@RXminuS just tried this out on linux, it is failing. I don't think the waiting for server stuff is working. I can look into it tomorrow.

2) [e2e] › e2e/example.test.ts:8:9 › Demonstrations › Show off v2 features › Start VSCode Session 

    TimeoutError: locator.waitFor: Timeout 10000ms exceeded.
    Call log:
      - waiting for locator('iframe.web-worker-ext-host-iframe')


       at e2e/utils/vscody/uix/vscode.ts:165

      163 |
      164 |         // wait for the UI to be ready
    > 165 |         await page.locator('iframe.web-worker-ext-host-iframe').waitFor({
          |                                                                 ^
      166 |             state: 'attached',
      167 |             timeout: 10000,
      168 |         })

        at /home/keegan/src/github.com/sourcegraph/cody/vscode/e2e/utils/vscody/uix/vscode.ts:165:65
        at /home/keegan/src/github.com/sourcegraph/cody/vscode/e2e/example.test.ts:29:9

Another interesting thing I am noticing is this output, I don't know if we should be adding these sort of node specific APIs to cody/shared?

[0] All files were successful downloaded, check resources/wasm directory
[1] [plugin:vite:resolve] [plugin vite:resolve] Module "node:path" has been externalized for browser compatibility, imported by "/home/keegan/src/github.com/sourcegraph/cody/lib/shared/src/lockfile.ts". See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
[0] 
[0]   dist/post-uninstall.js      998.7kb
[0]   dist/post-uninstall.js.map    2.1mb
[0] 
[0] ⚡ Done in 195ms
[1] [plugin:vite:resolve] [plugin vite:resolve] Module "fs" has been externalized for browser compatibility, imported by "/home/keegan/src/github.com/sourcegraph/cody/node_modules/.pnpm/[email protected]/node_modules/graceful-fs/graceful-fs.js". See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
[1] [plugin:vite:resolve] [plugin vite:resolve] Module "util" has been externalized for browser compatibility, imported by "/home/keegan/src/github.com/sourcegraph/cody/node_modules/.pnpm/[email protected]/node_modules/graceful-fs/graceful-fs.js". See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
[1] [plugin:vite:resolve] [plugin vite:resolve] Module "assert" has been externalized for browser compatibility, imported by "/home/keegan/src/github.com/sourcegraph/cody/node_modules/.pnpm/[email protected]/node_modules/graceful-fs/graceful-fs.js". See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
[0] 
[0]   dist/extension.node.js      15.0mb ⚠️
[0]   dist/extension.node.js.map  23.5mb
[0] 
[0] ⚡ Done in 386ms
[0] pnpm run -s _build:esbuild:desktop exited with code 0
[1] [plugin:vite:resolve] [plugin vite:resolve] Module "stream" has been externalized for browser compatibility, imported by "/home/keegan/src/github.com/sourcegraph/cody/node_modules/.pnpm/[email protected]/node_modules/graceful-fs/legacy-streams.js". See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
[1] [plugin:vite:resolve] [plugin vite:resolve] Module "constants" has been externalized for browser compatibility, imported by "/home/keegan/src/github.com/sourcegraph/cody/node_modules/.pnpm/[email protected]/node_modules/graceful-fs/polyfills.js". See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
[1] pnpm run -s _build:webviews --mode development exited with code 0

@RXminuS
Copy link
Contributor Author

RXminuS commented Jul 1, 2024

Another interesting thing I am noticing is this output, I don't know if we should be adding these sort of node specific APIs to cody/shared?

Ah yeah my bad, oversight. Easy fix.

I'll also setup my machine so I can help with Linux and windows testing

@RXminuS
Copy link
Contributor Author

RXminuS commented Jul 1, 2024

Just tried this out on linux, it is failing. I don't think the waiting for server stuff is working. I can look into it tomorrow.

I forgot to await the downloading 🤦

Comment on lines 470 to 472
// inherit environment
// TODO: Check why this was necessary. Shouldn't be needed
// ...process.env,
Copy link
Member

Choose a reason for hiding this comment

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

its just good unix practice to do this. In particular on some distributions of linux you need special environment variables passed in to make things like the ld.so to work. EG on my version of linux (NixOS) I need it to be able to read LD_NIX* environment variables so it can run random binaries downloaded from the internet (like we are doing for chromium via playwright and vscode)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah sorry please ignore these; I more meant from the perspective of what specifically vscode was lacking given I thought it was running in portable mode.

But I've figured it out now. Will add this in again in a bit

`Could not find a vscode executable under ${path.dirname(electronExecutable)}`
)
})
const [cliPath, ...cliArgs] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutable)
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't work right now on linux due to the fact that vscodeExecutable is pointing to code-tunnel.exe not the code executable. I will push commits very soon if I get it to work.

@keegancsmith
Copy link
Member

@RXminuS vscode just drove me crazy here. Issues:

  • bin/code spawns the child ./code which spawns the child bin/code-tunnel
  • bin/code is a shell script which doesn't use exec, so pops up as just sh
  • due to the above f-show and some other bad behaviour when playwright stops running code-tunnel ignores that and keeps everything alive.
  • also due to the above f-show, code-tunnel is what is actually listening on the port, so the looking for listening port fails.

on linux if we want this to work we need to directly run code-tunnel. I think to move this forward we just need to have seperate code paths for starting vscode up per platform just to prevent regressions. Then maybe we can see how to make things fit together nicely.

Other stuff: I remove the inclusion of cliArgs since that specified extensionDir/etc which meant we ended up specifying it twice.

@RXminuS
Copy link
Contributor Author

RXminuS commented Jul 2, 2024

I have a commit coming that fixes some of this. Just tested on Mac and Linux. Just doing a windows run now

@RXminuS
Copy link
Contributor Author

RXminuS commented Jul 2, 2024

There was a whole bunch of other funk going on w.r.t. extensions

@keegancsmith
Copy link
Member

Latest commit works for me on linux if I just do pnpm run test:e2e2:run!

FYI the install deps thing fails since that only supports ubuntu which I don't use. I can live with that since CI uses ubuntu.

@keegancsmith
Copy link
Member

On windows this is the value for me for sharedExtensionDir is C:\Users\keegan\.vscode-server\extensions. I'm assuming we can use this based on the comments, so you just need to do

-                if (!sharedExtensionsDir.endsWith('.vscode-server/extensions')) {
+                if (
+                    !(
+                        sharedExtensionsDir.endsWith('.vscode-server/extensions') ||
+                        sharedExtensionsDir.endsWith('.vscode-server\\extensions')
+                    )
+                ) {

With that if I run one test at a time it works! However, when doing parallel runs I get this error

Error: EBUSY: resource busy or locked, unlink 'C:\Users\keegan\Code\cody\.test\runs\01J1T6FA83FRCKZYNG1B6H9Y7E\test-vscode-server-XXXXXX7U4YEC\data\logs\20240702T190446\network.log'
    at unlinkSync (node:fs:1877:11)
    at _unlinkSync (node:internal/fs/rimraf:214:14)
    at rimrafSync (node:internal/fs/rimraf:195:7)
    at node:internal/fs/rimraf:253:9
    at Array.forEach (<anonymous>)
    at _rmdirSync (node:internal/fs/rimraf:250:7)
    at rimrafSync (node:internal/fs/rimraf:193:7)
    at node:internal/fs/rimraf:253:9
    at Array.forEach (<anonymous>)
    at _rmdirSync (node:internal/fs/rimraf:250:7)
    at rimrafSync (node:internal/fs/rimraf:193:7)
    at node:internal/fs/rimraf:253:9
    at Array.forEach (<anonymous>)
    at _rmdirSync (node:internal/fs/rimraf:250:7)
    at rimrafSync (node:internal/fs/rimraf:193:7)
    at node:internal/fs/rimraf:253:9
    at Array.forEach (<anonymous>)
    at _rmdirSync (node:internal/fs/rimraf:250:7)
    at rimrafSync (node:internal/fs/rimraf:193:7)
    at node:internal/fs/rimraf:253:9
    at Array.forEach (<anonymous>)
    at _rmdirSync (node:internal/fs/rimraf:250:7)
    at rimrafSync (node:internal/fs/rimraf:193:7)
    at rmSync (node:fs:1264:10)
    at Object.<anonymous> (C:\Users\keegan\Code\cody\vscode\playwright.v2.config.ts:22:15)
    at Module._compile (node:internal/modules/cjs/loader:1358:14)
    at Module.f._compile (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\utilsBundleImpl.js:16:994)
    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
    at Object.i.<computed>.ut._extensions.<computed> (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\utilsBundleImpl.js:16:1010)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1024:12)
    at Module.require (node:internal/modules/cjs/loader:1233:19)
    at require (node:internal/modules/helpers:179:18)
    at requireOrImport (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\transform\transform.js:192:20)
    at loadUserConfig (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\common\configLoader.js:96:83)
    at loadConfig (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\common\configLoader.js:101:28)
    at loadConfigFromFileRestartIfNeeded (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\common\configLoader.js:258:16)
    at runTests (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\program.js:191:76)
    at t.<anonymous> (C:\Users\keegan\Code\cody\node_modules\.pnpm\[email protected]\node_modules\playwright\lib\program.js:54:7) {
  errno: -4082,
  code: 'EBUSY',
  syscall: 'unlink',
  path: 'C:\\Users\\keegan\\Code\\cody\\.test\\runs\\01J1T6FA83FRCKZYNG1B6H9Y7E\\test-vscode-server-XXXXXX7U4YEC\\data\\logs\\20240702T190446\\network.log'
}

@keegancsmith
Copy link
Member

The thing I mentioned earlier of not shutting down the process properly is the root cause of the EBUSY. Basically the process just keeps on running even once playwright is done. On windows you can't delete a file if a process still has it open (which we do on startup to clean up old runs). I just checked who had network.log open and it was this process

image

@RXminuS
Copy link
Contributor Author

RXminuS commented Jul 2, 2024

Yeah I was trying to figure that out. It seems that on windows orphaned processes are made root processes instead of being killed. There's a helper function in the vscode-server repo to work around this that we'll need to copy.

Copy link
Member

@keegancsmith keegancsmith left a comment

Choose a reason for hiding this comment

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

We are gonna land this now without the CI changes. So this code is not 100% ready for others, but it won't effect anyone and will allow us to work on this incrementally from this base.

@keegancsmith keegancsmith marked this pull request as ready for review July 3, 2024 11:21
@RXminuS RXminuS merged commit 4cea8a9 into main Jul 3, 2024
19 checks passed
@RXminuS RXminuS deleted the rnauta/feat/e2e-v2 branch July 3, 2024 14:16
mkondratek added a commit that referenced this pull request Jul 20, 2024
A recent change brok the urls. This PR reverts the original behaviour.

The recent change:
#4706 (comment)
Slack thread:
https://sourcegraph.slack.com/archives/C05AGQYD528/p1721382757890889

## Test plan
- chat context file items looks properly for remote files
pkukielka pushed a commit to sourcegraph/jetbrains that referenced this pull request Jul 22, 2024
This PR update's cody commit. The most important LLM selection dropdown
**with self hosted models** is expected to be visible for enterprise
users now.

The important PRs from Cody to be included:
- sourcegraph/cody#4913 (self hosted models)
- ~sourcegraph/cody#4706 ([a bug with
paths](sourcegraph/cody#4706 (comment))?
[slack
thread](https://sourcegraph.slack.com/archives/C05AGQYD528/p1721382757890889))~
FIXED BY sourcegraph/cody#4955

## Test plan
1. full QA
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