diff options
Diffstat (limited to 'extension/src')
| -rw-r--r-- | extension/src/README.md | 3 | ||||
| -rw-r--r-- | extension/src/activation/environmentSetup.ts | 446 | 
2 files changed, 60 insertions, 389 deletions
diff --git a/extension/src/README.md b/extension/src/README.md index 4969890f..ef90c31b 100644 --- a/extension/src/README.md +++ b/extension/src/README.md @@ -12,7 +12,7 @@  5. From `continue/extension`, run `npm install` -6. Run `npm run full-package` +6. Run `npm run package`  7. Open `src/activation/activate.ts` file (or any TypeScript file) @@ -22,7 +22,6 @@  10. Every time you make changes to the code, you need to run `npm run esbuild` unless you make changes inside of `react-app` and then you need to run `npm run build` from there -  ## Alternative: Install from source  Update: directions to root README diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 50a2783a..01fd2e5c 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -10,65 +10,6 @@ import * as vscode from "vscode";  import * as os from "os";  import fkill from "fkill"; -const WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR = -  "A Python virtual enviroment cannot be activated because running scripts is disabled for this user. In order to use Continue, please enable signed scripts to run with this command in PowerShell: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`, reload VS Code, and then try again."; - -const MAX_RETRIES = 3; -async function retryThenFail( -  fn: () => Promise<any>, -  retries: number = MAX_RETRIES -): Promise<any> { -  try { -    if (retries < MAX_RETRIES && process.platform === "win32") { -      let [stdout, stderr] = await runCommand("Get-ExecutionPolicy"); -      if (!stdout.includes("RemoteSigned")) { -        [stdout, stderr] = await runCommand( -          "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser" -        ); -        console.log("Execution policy stdout: ", stdout); -        console.log("Execution policy stderr: ", stderr); -      } -    } - -    return await fn(); -  } catch (e: any) { -    if (retries > 0) { -      return await retryThenFail(fn, retries - 1); -    } - -    // Show corresponding error message depending on the platform -    let msg = -      "Failed to set up Continue extension. Please email hi@continue.dev and we'll get this fixed ASAP!"; -    try { -      switch (process.platform) { -        case "win32": -          msg = WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR; -          break; -        case "darwin": -          break; -        case "linux": -          const [pythonCmd] = await getPythonPipCommands(); -          msg = await getLinuxAptInstallError(pythonCmd); -          break; -      } -    } finally { -      console.log("After retries, failed to set up Continue extension", msg); -      vscode.window -        .showErrorMessage(msg, "View Logs", "Retry") -        .then((selection) => { -          if (selection === "View Logs") { -            vscode.commands.executeCommand("continue.viewLogs"); -          } else if (selection === "Retry") { -            // Reload VS Code window -            vscode.commands.executeCommand("workbench.action.reloadWindow"); -          } -        }); -    } - -    throw e; -  } -} -  async function runCommand(cmd: string): Promise<[string, string | undefined]> {    console.log("Running command: ", cmd);    var stdout: any = ""; @@ -147,216 +88,6 @@ export async function getPythonPipCommands() {    return [pythonCmd, pipCmd];  } -function getActivateUpgradeCommands(pythonCmd: string, pipCmd: string) { -  let activateCmd = ". env/bin/activate"; -  let pipUpgradeCmd = `${pipCmd} install --upgrade pip`; -  if (process.platform == "win32") { -    activateCmd = ".\\env\\Scripts\\activate"; -    pipUpgradeCmd = `${pythonCmd} -m pip install --upgrade pip`; -  } -  return [activateCmd, pipUpgradeCmd]; -} - -function checkEnvExists() { -  const envBinPath = path.join( -    serverPath(), -    "env", -    process.platform == "win32" ? "Scripts" : "bin" -  ); -  return ( -    fs.existsSync(path.join(envBinPath, "activate")) && -    fs.existsSync( -      path.join(envBinPath, process.platform == "win32" ? "pip.exe" : "pip") -    ) -  ); -} - -async function checkRequirementsInstalled() { -  // First, check if the requirements have been installed most recently for a later version of the extension -  if (fs.existsSync(requirementsVersionPath())) { -    const requirementsVersion = fs.readFileSync( -      requirementsVersionPath(), -      "utf8" -    ); -    if (requirementsVersion !== getExtensionVersion()) { -      // Remove the old version of continuedev from site-packages -      const [pythonCmd, pipCmd] = await getPythonPipCommands(); -      const [activateCmd] = getActivateUpgradeCommands(pythonCmd, pipCmd); -      const removeOldVersionCommand = [ -        `cd "${serverPath()}"`, -        activateCmd, -        `${pipCmd} uninstall -y continuedev`, -      ].join(" ; "); -      await runCommand(removeOldVersionCommand); -      return false; -    } -  } - -  let envLibsPath = path.join( -    serverPath(), -    "env", -    process.platform == "win32" ? "Lib" : "lib" -  ); -  // If site-packages is directly under env, use that -  if (fs.existsSync(path.join(envLibsPath, "site-packages"))) { -    envLibsPath = path.join(envLibsPath, "site-packages"); -  } else { -    // Get the python version folder name -    const pythonVersions = fs.readdirSync(envLibsPath).filter((f: string) => { -      return f.startsWith("python"); -    }); -    if (pythonVersions.length == 0) { -      return false; -    } -    const pythonVersion = pythonVersions[0]; -    envLibsPath = path.join(envLibsPath, pythonVersion, "site-packages"); -  } - -  const continuePath = path.join(envLibsPath, "continuedev"); - -  return fs.existsSync(continuePath); -} - -async function getLinuxAptInstallError(pythonCmd: string) { -  // First, try to run the command to install python3-venv -  let [stdout, stderr] = await runCommand(`${pythonCmd} --version`); -  if (stderr) { -    await vscode.window.showErrorMessage( -      "Python3 is not installed. Please install from https://www.python.org/downloads, reload VS Code, and try again." -    ); -    throw new Error(stderr); -  } -  const version = stdout.split(" ")[1].split(".")[1]; -  const installVenvCommand = `apt-get install python3.${version}-venv`; -  await runCommand("apt-get update"); -  return `[Important] Continue needs to create a Python virtual environment, but python3.${version}-venv is not installed. Please run this command in your terminal: \`${installVenvCommand}\`, reload VS Code, and then try again.`; -} - -async function createPythonVenv(pythonCmd: string) { -  if (checkEnvExists()) { -    console.log("Python env already exists, skipping..."); -  } else { -    // Assemble the command to create the env -    const createEnvCommand = [ -      `cd "${serverPath()}"`, -      `${pythonCmd} -m venv env`, -    ].join(" ; "); - -    const [stdout, stderr] = await runCommand(createEnvCommand); -    if ( -      stderr && -      stderr.includes("running scripts is disabled on this system") -    ) { -      console.log("Scripts disabled error when trying to create env"); -      await vscode.window.showErrorMessage(WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR); -      throw new Error(stderr); -    } else if ( -      stderr?.includes("On Debian/Ubuntu systems") || -      stdout?.includes("On Debian/Ubuntu systems") -    ) { -      const msg = await getLinuxAptInstallError(pythonCmd); -      console.log(msg); -      await vscode.window.showErrorMessage(msg); -    } else if (checkEnvExists()) { -      console.log("Successfully set up python env at ", `${serverPath()}/env`); -    } else if ( -      stderr?.includes("Permission denied") && -      stderr?.includes("python.exe") -    ) { -      // This might mean that another window is currently using the python.exe file to install requirements -      // So we want to wait and try again -      let i = 0; -      await new Promise((resolve, reject) => -        setInterval(() => { -          if (i > 5) { -            reject("Timed out waiting for other window to create env..."); -          } -          if (checkEnvExists()) { -            resolve(null); -          } else { -            console.log("Waiting for other window to create env..."); -          } -          i++; -        }, 5000) -      ); -    } else { -      const msg = [ -        "Python environment not successfully created. Trying again. Here was the stdout + stderr: ", -        `stdout: ${stdout}`, -        `stderr: ${stderr}`, -      ].join("\n\n"); -      console.log(msg); -      throw new Error(msg); -    } -  } -} - -async function setupPythonEnv() { -  console.log("Setting up python env for Continue extension..."); - -  const [pythonCmd, pipCmd] = await getPythonPipCommands(); -  const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands( -    pythonCmd, -    pipCmd -  ); - -  await retryThenFail(async () => { -    // First, create the virtual environment -    await createPythonVenv(pythonCmd); - -    // Install the requirements -    if (await checkRequirementsInstalled()) { -      console.log("Python requirements already installed, skipping..."); -    } else { -      const installRequirementsCommand = [ -        `cd "${serverPath()}"`, -        activateCmd, -        pipUpgradeCmd, -        `${pipCmd} install -r requirements.txt`, -      ].join(" ; "); -      const [, stderr] = await runCommand(installRequirementsCommand); -      if (stderr) { -        throw new Error(stderr); -      } -      // Write the version number for which requirements were installed -      fs.writeFileSync(requirementsVersionPath(), getExtensionVersion()); -    } -  }); -} - -function readEnvFile(path: string) { -  if (!fs.existsSync(path)) { -    return {}; -  } -  let envFile = fs.readFileSync(path, "utf8"); - -  let env: { [key: string]: string } = {}; -  envFile.split("\n").forEach((line) => { -    let [key, value] = line.split("="); -    if (typeof key === "undefined" || typeof value === "undefined") { -      return; -    } -    env[key] = value.replace(/"/g, ""); -  }); -  return env; -} - -function writeEnvFile(path: string, key: string, value: string) { -  if (!fs.existsSync(path)) { -    fs.writeFileSync(path, `${key}="${value}"`); -    return; -  } - -  let env = readEnvFile(path); -  env[key] = value; - -  let newEnvFile = ""; -  for (let key in env) { -    newEnvFile += `${key}="${env[key]}"\n`; -  } -  fs.writeFileSync(path, newEnvFile); -} -  async function checkServerRunning(serverUrl: string): Promise<boolean> {    // Check if already running by calling /health    try { @@ -381,16 +112,6 @@ export function getContinueGlobalPath(): string {    return continuePath;  } -function setupServerPath() { -  const sPath = serverPath(); -  const extensionServerPath = path.join(getExtensionUri().fsPath, "server"); -  const files = fs.readdirSync(extensionServerPath); -  files.forEach((file) => { -    const filePath = path.join(extensionServerPath, file); -    fs.copyFileSync(filePath, path.join(sPath, file)); -  }); -} -  function serverPath(): string {    const sPath = path.join(getContinueGlobalPath(), "server");    if (!fs.existsSync(sPath)) { @@ -411,15 +132,36 @@ function serverVersionPath(): string {    return path.join(serverPath(), "server_version.txt");  } -function requirementsVersionPath(): string { -  return path.join(serverPath(), "requirements_version.txt"); -} -  export function getExtensionVersion() {    const extension = vscode.extensions.getExtension("continue.continue");    return extension?.packageJSON.version || "";  } +// Returns whether a server of the current version is already running +async function checkOrKillRunningServer(serverUrl: string): Promise<boolean> { +  console.log("Checking if server is old version"); +  // Kill the server if it is running an old version +  if (fs.existsSync(serverVersionPath())) { +    const serverVersion = fs.readFileSync(serverVersionPath(), "utf8"); +    if ( +      serverVersion === getExtensionVersion() && +      (await checkServerRunning(serverUrl)) +    ) { +      // The current version is already up and running, no need to continue +      return true; +    } +  } +  console.log("Killing old server..."); +  try { +    await fkill(":65432"); +  } catch (e: any) { +    if (!e.message.includes("Process doesn't exist")) { +      console.log("Failed to kill old server:", e); +    } +  } +  return false; +} +  export async function startContinuePythonServer() {    // Check vscode settings    const serverUrl = getContinueServerUrl(); @@ -427,115 +169,45 @@ export async function startContinuePythonServer() {      return;    } -  setupServerPath(); - -  return await retryThenFail(async () => { -    console.log("Checking if server is old version"); -    // Kill the server if it is running an old version -    if (fs.existsSync(serverVersionPath())) { -      const serverVersion = fs.readFileSync(serverVersionPath(), "utf8"); -      if ( -        serverVersion === getExtensionVersion() && -        (await checkServerRunning(serverUrl)) -      ) { -        // The current version is already up and running, no need to continue -        return; -      } -    } -    console.log("Killing old server..."); -    try { -      await fkill(":65432"); -    } catch (e: any) { -      if (!e.message.includes("Process doesn't exist")) { -        console.log("Failed to kill old server:", e); -      } -    } - -    // Do this after above check so we don't have to waste time setting up the env -    await setupPythonEnv(); - -    // Spawn the server process on port 65432 -    const [pythonCmd] = await getPythonPipCommands(); -    const activateCmd = -      process.platform == "win32" -        ? ".\\env\\Scripts\\activate" -        : ". env/bin/activate"; - -    const command = `cd "${serverPath()}" && ${activateCmd} && cd .. && ${pythonCmd} -m server.run_continue_server`; - -    return new Promise(async (resolve, reject) => { -      console.log("Starting Continue python server..."); -      try { -        const child = spawn(command, { -          shell: true, -        }); -        child.stderr.on("data", (data: any) => { -          if ( -            data.includes("Uvicorn running on") || // Successfully started the server -            data.includes("only one usage of each socket address") || // [windows] The server is already running (probably a simultaneously opened VS Code window) -            data.includes("address already in use") // [mac/linux] The server is already running (probably a simultaneously opened VS Code window) -          ) { -            console.log("Successfully started Continue python server"); -            resolve(null); -          } else if (data.includes("ERROR") || data.includes("Traceback")) { -            console.log("Error starting Continue python server: ", data); -          } else { -            console.log(`stdout: ${data}`); -          } -        }); -        child.on("error", (error: any) => { -          console.log(`error: ${error.message}`); -        }); - -        child.on("close", (code: any) => { -          console.log(`child process exited with code ${code}`); -        }); +  // Check if server is already running +  if (await checkOrKillRunningServer(serverUrl)) { +    return; +  } -        child.stdout.on("data", (data: any) => { -          console.log(`stdout: ${data}`); -        }); +  // Get name of the corresponding executable for platform +  const exeDir = path.join(getExtensionUri().fsPath, "server", "exe"); +  let exePath: string; +  if (os.platform() === "win32") { +    exePath = path.join(exeDir, "windows", "run.exe"); +  } else if (os.platform() === "darwin") { +    exePath = path.join(exeDir, "mac", "run"); +    // Add permissions +    await runCommand(`chmod +x ${exePath}`); +    await runCommand(`xattr -dr com.apple.quarantine ${exePath}`); +  } else { +    exePath = path.join(exeDir, "linux", "run"); +  } -        // Write the current version of vscode to a file called server_version.txt -        fs.writeFileSync(serverVersionPath(), getExtensionVersion()); -      } catch (e) { -        console.log("Failed to start Continue python server", e); -        // If failed, check if it's because the server is already running (might have happened just after we checked above) -        if (await checkServerRunning(serverUrl)) { -          resolve(null); -        } else { -          reject(); -        } -      } -    }); +  // Run the executable +  const child = spawn(exePath, { +    shell: true, +  }); +  child.stderr.on("data", (data: any) => { +    console.log(data.toString());    }); -} -export function isPythonEnvSetup(): boolean { -  const pathToEnvCfg = path.join(serverPath(), "env", "pyvenv.cfg"); -  return fs.existsSync(pathToEnvCfg); -} +  child.on("error", (error: any) => { +    console.log(`error: ${error.message}`); +  }); -export async function downloadPython3() { -  // Download python3 and return the command to run it (python or python3) -  let os = process.platform; -  let command: string = ""; -  let pythonCmd = "python3"; -  if (os === "darwin") { -    throw new Error("python3 not found"); -  } else if (os === "linux") { -    command = -      "sudo apt update ; upgrade ; sudo apt install python3 python3-pip"; -  } else if (os === "win32") { -    command = -      "wget -O python_installer.exe https://www.python.org/ftp/python/3.11.3/python-3.11.3-amd64.exe ; python_installer.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0"; -    pythonCmd = "python"; -  } +  child.on("close", (code: any) => { +    console.log(`child process exited with code ${code}`); +  }); -  var [stdout, stderr] = await runCommand(command); -  if (stderr) { -    throw new Error(stderr); -  } -  console.log("Successfully downloaded python3"); +  child.stdout.on("data", (data: any) => { +    console.log(`stdout: ${data.toString()}`); +  }); -  return pythonCmd; +  // Write the current version of vscode extension to a file called server_version.txt +  fs.writeFileSync(serverVersionPath(), getExtensionVersion());  }  | 
