microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
164.43k stars 29.34k forks source link

[remote] Support `code --install-extension` on the remote #86232

Closed bradwilson closed 3 years ago

bradwilson commented 4 years ago

I am running WSL 2 (from the 20H1 Slow Ring, build 19033), and I have VS Code installed on the Windows side. I have noticed that adding Remote - WSL breaks some of the WSL-to-Windows communication paths, namely the ability to invoke code --install-extension from WSL and have it install the extension in Code for Windows.

Note that this functionality works fine without "Remote - WSL" installed; it's only the installation of the remote extension inside of VS Code that breaks the communication.

Without WSL - Remote: code --install-extension from WSL correctly installs the extension on the Windows side of VS Code (without any UI popup)

With WSL - Remote: code --install-extension incorrectly pops up the VS Code UI (does not open any folder, just pops up the UI) and the extension is not installed.

aeschli commented 4 years ago

With Remote WSL isntalled, there's comes the concept of remote hosts, where extensions can be installed either locally or/and on remote. Running code --install-extension in a WSL shell should install the extension on the remote side, which we don't support (yet) via CLI. To install a an extension on the windows side, you need to run code --install-extension in a windows command prompt.

vscodebot[bot] commented 4 years ago

(Experimental duplicate detection) Thanks for submitting this issue. Please also check if it is already covered by an existing one, like:

bradwilson commented 4 years ago

The fact that code --install-extension works just fine to install extensions to the Windows side before your extension is installed, means to me that code --install-extension should continue to install extensions to the Windows side even after your extension is added, for consistency purposes (it doesn't make sense that adding your extension would change the behavior).

Adding an additional flag to code --install-extension when "Remote - WSL" is installed to install Linux-side extensions would also be a nice to have feature, but that's not actually what I'm asking for. I just want you to un-break this behavior that my Ansible playbooks depend on. 😄

aeschli commented 3 years ago

@bradwilson To install extensions on Windows 'the old way', you need to call the WIndows code command (code.cmd), not code.sh.

I first thought you wanted the WSL code command to install extensions in WSL. That is possible when in the remote integrated terminal: https://github.com/microsoft/vscode-remote-release/issues/2589

bradwilson commented 3 years ago

@aeschli You misread my issue and then closed it based on your misunderstanding.

aeschli commented 3 years ago

@bradwilson Yes, I'm a bit confused. Can you clarify what your request is?

My suggestion was that installing an extension on the Windows side was what you want, run code.cmd. (from a WSL shell: cmd.exe /c "code.cmd --install-extension ...")

bradwilson commented 3 years ago

It's quite simple:

Something in this extension breaks the way code works such that I can no longer install extensions.

Your proposed workaround is "don't do that", except that's sort of irrelevant. You've broken something that works, and just because you didn't break something else (running code from Windows) isn't the point.


If what you're really asking is "why do you want to run code from WSL?", the answer is: I automate the standing up of my Linux-based development environment with Ansible scripts. Obviously these work fine on native Linux; they also work fine within WSL, right up to the moment where "Remote - WSL" is installed at which point my automation is now broken.

Here's an example of one such script:

- name: .NET Core (VS Code)
  hosts: 127.0.0.1
  connection: local

  tasks:
    - name: Install VS Code Extensions
      command: code --install-extension {{ item }}
      args:
        creates: ~/.vscode/extensions/{{ item }}-*/package.json
      loop:
        - formulahendry.dotnet-test-explorer    # .NET Core Test Explorer
        - k--kato.docomment                     # C# XML Documentation Comments
        - ms-dotnettools.csharp                 # C#

The end result is that once the Ansible script installs "Remote - WSL" (by way of installing ms-vscode-remote.vscode-remote-extensionpack), then Ansible is immediately no longer able to install any new extensions into VSCode. (There's a small amount of unintended irony that I install "Remote - WSL" with the feature which you then immediately break. 😂)

If you choose not to fix this, then that's your decision, but your suggested workaround is wholly unacceptable to my usage. I also don't think you should have the right to decide to arbitrarily break a built-in feature which was already working, but that's my personal interpretation.

phealy commented 3 years ago

In this particular case the problem is that code.cmd (the Windows command shell script) handles the functionality to install extensions. That functionality is still there and usable, even with Remote-WSL installed. However, once Remote-WSL is added, code from the Linux shell now gets code.sh instead of code.cmd, and code.sh doesn't have that functionality, because it's for launching via the Remote-WSL server instead of the Windows side.

Explicitly calling code.cmd, as was mentioned earlier, still works and is probably the workaround you need. However, you have to call it via cmd.exe, because otherwise bash tries to parse it as a shell script.

$ /mnt/c/Windows/System32/cmd.exe /C "C:\Program Files\Microsoft VS Code\bin\code.cmd" --install-extension ms-vscode.vscode-node-azure-pack
Installing extensions...

You could key off one of the environment variables WSL sets by default to have an ansible playbook that will work in either location:

- name: .NET Core (VS Code)
  hosts: 127.0.0.1
  connection: local

  tasks:
    - include_tasks: set_vscode_facts.yml

    - name: Install VS Code Extensions
      command: "{{ vscode_command }} --install-extension {{ item }}"
      args:
        creates: "{{ vscode_extensions }}/{{ item }}-*/package.json"
      loop:
        - formulahendry.dotnet-test-explorer    # .NET Core Test Explorer
        - k--kato.docomment                     # C# XML Documentation Comments
        - ms-dotnettools.csharp                 # C#

set_vscode_facts.yml:

- name: Set VS Code facts (Linux)
  set_fact:
    vscode_command: code
    vscode_extensions: ~/.vscode/extensions
  when: "'WSL_INTEROP' not in ansible_env"

- name: Set VS Code facts (WSL)
  set_fact:
    vscode_command: '/mnt/c/Windows/System32/cmd.exe /C "C:\Program Files\Microsoft VS Code\bin\code.cmd"'
    vscode_extensions: /mnt/c/Users/$USER/.vscode/extensions
  when: "'WSL_INTEROP' in ansible_env"
bradwilson commented 3 years ago

@phealy Attempting to validate this analysis, I feel like it's either incorrect (or the description is misleading, perhaps in an attempt at simplification). I need to get a Windows machine w/ VS Code on it (without any extensions installed yet) to validate my analysis, because I can't assume that the act of installing "Remote - WSL" didn't replace the default code Bash script with one that's remote-aware. But my initial swag through shows me that at no time does code in WSL ever invoke code.cmd, whether you have "Remote - WSL" installed or not. The shift point appears to be between either invoking a shell script that resides inside the "Remote - WSL" installation folder vs. directly invoking electron.exe to run the cli.js script that's part of VS Code. (This is also what code.cmd does on the Windows side, but it's much simpler since it just assumes you're running on Windows and doesn't do any WSL or Remote detection logic.)

So the differences come down the features that are implemented in cli.js but not in wslCode.sh. Those differences can be summarized as: installing "Remote - WSL" (which causes code to run wslCode.sh instead of cli.js) removes all the extension management commands. This is fundamentally my complaint: wslCode.sh could absolutely be updated to support the handful of command line switches simply by falling back to running cli.js (just like code does) when it detects that the user has asked to perform one of the extension management commands. This is fundamentally my ask: don't take away functionality that users might be depending on.

As for your suggested workaround, there are some assumptions in it:

Because of this, I would describe this more as a "temporary" fix, and not a legitimate long-term workaround.

phealy commented 3 years ago

I agree - this could certainly be cleaned up. In fact, you can probably fix a number of those assumptions by just assuming the user has a sane PATH, which you were doing anyway by just running code. However, this assumes that the user is using a WSL distribution that packages the WSL utilities.

  1. Remove the path from the call to cmd.exe, assuming that %WINDIR%\System32 is in the path.
  2. Set the path to the extensions folder by storing the result from stdout of calling cmd.exe /c "echo %APPDATA%"
  3. Find code.cmd by parsing the path returned from which code via wslpath: wslpath -w "$(which code)"

I'm not on the VS Code Remote team, I just happened to come across this and thought I could offer a suggestion for a fix that would let you keep going.

aeschli commented 3 years ago

In 1.53, code.sh with Remote-WSL installed now also supports install/uninstall-extension and list-extension. But, it will install/list extensions only the remote/WSL side.

I understand that this is not the direction you wanted. But we believe is what users that work in WSL with the Remote-WSL extension need and expect. We are thinking of adding a --local flag to enable installing extensions locally as well. If that's something that would like to have, lets create a separate request and continue the discussion there.

Sorry for hijacking your bug request and turning it into a feature request for something you didn't want.

bradwilson commented 3 years ago

Sounds like I should just work to adapt @phealy's workaround.