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

Implement internal Ruby activation mechanism #923

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

vinistock
Copy link
Member

@vinistock vinistock commented Dec 1, 2023

Motivation

Closes Shopify/ruby-lsp#1776, closes Shopify/ruby-lsp#1784, closes Shopify/ruby-lsp#1794, closes Shopify/ruby-lsp#1216, closes Shopify/ruby-lsp#1762

Currently, we activate the Ruby environment using the user's version manager invoked through a shell. This is necessary because most version managers are shell scripts loaded in config files like ~/.zshrc or ~/.bashrc.

Unfortunately, this has been causing headache because some shell plugins or configurations may accidentally block or interact with our activation script, which leads to difficult to debug issues.

I believe we can provide a better experience if we provide a minimal Ruby environment activation mechanism implemented directly inside TypeScript so that we can avoid interacting with the shell altogether.

Possible drawbacks

While this will fix the issues related to interacting with the shell, it's not free of drawbacks. Certain version managers provide more complex functionality that may not be supported by our internal mechanism.

For example, rtx supports adding extra environment variables in its .rtx.toml file. Other version managers allow users to change the default GEM_HOME where user gems are installed.

Moving forward with this approach means that we might need to add some extra complexity to handle this scenarios or at least make certain things configurable.

Release strategy

We will need to have a long preview release cycle for this one to ensure that activation is working fine for common setups.

Implementation

The bulk of the changes are in Ruby. All other changes are just putting pieces together.

The idea of the activation mechanism is that we

  1. Try to determine the Ruby version by looking into common config files (e.g.: .ruby-version, dev.yml, .tools-versions, .rtx.toml)
  2. We then try to find a Ruby installation for that version in the directories common used by version managers
  3. If we can't find a Ruby version or can't find the directory for the installation, we prompt users to manually select their Ruby binary
  4. We then run a script that basically just returns GEM_HOME and GEM_ROOT to us
  5. Finally, we modify the environment to include these
    • GEM_HOME -> must point to the user directory for gem installations
    • GEM_ROOT -> must point to the directory for default gem installations
    • GEM_PATH -> a concatenation of GEM_HOME and GEM_ROOT
    • RUBY_VERSION - the Ruby version
    • PATH -> must contain the executable paths for user gems, default gems and for the Ruby bin folder

Automated Tests

Added tests for all different config files we support.

Manual Tests

We need to ensure that Ruby environment activation works properly for chruby, rbenv, rvm, shadowenv, rtx and asdf.

For all of them, the manual tests are analogous:

  1. Write the configuration file with a Ruby version (e.g.: .ruby-version)
  2. Start the Ruby LSP on that directory
  3. Verify that the right Ruby version was picked
  4. Verify that the LSP boots properly

@vinistock vinistock added the enhancement New feature or request label Dec 1, 2023
@vinistock vinistock self-assigned this Dec 1, 2023
@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch 9 times, most recently from dff7eb1 to dc787b5 Compare December 4, 2023 19:47
Base automatically changed from vs/refactor_client to main December 4, 2023 21:25
@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch from dc787b5 to 7f64d9f Compare December 4, 2023 22:03
@vinistock vinistock marked this pull request as ready for review December 4, 2023 22:05
@vinistock vinistock requested a review from a team as a code owner December 4, 2023 22:05
@vinistock vinistock mentioned this pull request Dec 5, 2023
1 task
@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch 10 times, most recently from 21831bf to 32f6521 Compare December 6, 2023 19:51
@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch from ee24448 to 456f287 Compare January 19, 2024 17:43
@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch from 2b65350 to 78a842e Compare January 30, 2024 14:25
@Nowaker
Copy link

Nowaker commented Feb 3, 2024

FYI, I switched from release version of Ruby LSP to pre-release 0.6.7 and Ruby version detection fails.

.ruby-version says 3.3.0. Using RVM to switch between versions.

Output:

2024-02-03 14:08:53.454 [info] (worldmodern) Discovered Ruby version 3.3.0

And error message:
image

So it appears RVM doesn't work on Ruby LSP 0.6.7.

@DuncSmith
Copy link

Hi, I'm seeing similar issue to @Nowaker, only I'm using mise.

macos
Ruby 3.3.0
ruby-lsp 0.13.4
vscode extension 0.6.7
mise 2024.2.2

I have a .local.mise.toml with

[tools]
ruby = "3.3.0"

I get the same Automatic: Ruby detection failed... message, and in the output
2024-02-05 09:23:27.163 [info] (reponame) Discovered Ruby version 3.3.0

So far the only solution I've found is to use the release version (which works fine with this) or hardcode the path, which is not ideal.

"rubyLsp.rubyExecutablePath": "/Users/<username>/.local/share/mise/installs/ruby/3.3.0/bin/ruby",

@vinistock
Copy link
Member Author

@Nowaker are your rubies installed under ~/.rvm/rubies? And if so, what's the right path to the Ruby executable? Is it ~/.rvm/rubies/3.3.0/bin/ruby?

@DuncSmith what's the purpose of the .local.mise.toml file? When do you use that over .mise.toml? Does it interact with other configuration files in any way? And where do you place it (inside the project or in your home dir)?

@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch from b781d2b to a98af41 Compare February 9, 2024 22:04
@DuncSmith
Copy link

@DuncSmith what's the purpose of the .local.mise.toml file? When do you use that over .mise.toml? Does it interact with other configuration files in any way? And where do you place it (inside the project or in your home dir)?

I'd add the local version to my gitignore and use it to override any default config. But I think though this was a bad example on my part, apologies. I do see the same issue with a project specific .mise.toml or without, and fall back to the global config, in my case I have this in ~/.config/mise/config.toml

@Nowaker
Copy link

Nowaker commented Feb 13, 2024

@Nowaker are your rubies installed under ~/.rvm/rubies? And if so, what's the right path to the Ruby executable? Is it ~/.rvm/rubies/3.3.0/bin/ruby?

@vinistock Yes to both. A standard RVM installation.

@vinistock
Copy link
Member Author

@Nowaker And you don't have any other logs after the line you already mentioned? It discovered the right version and then stopped printing?

2024-02-03 14:08:53.454 [info] (worldmodern) Discovered Ruby version 3.3.0

@vinistock
Copy link
Member Author

@DuncSmith I'm not a user of Mise, so I'm going to need more information to get the implementation right.

  • In which directory does each configuration file go? Are both .mise.toml and .local.mise.toml intended to be placed inside projects?
  • For the global config, that looks shell specific. Is it always under ~/.config/mise/config.toml or is that because you are using fish? Is there a list of the other directories where this global configuration might go for other shells and other operating systems?
  • How do the files interact? I assume that the project-specific ones take precedence over the global one, but what's the relationship between .mise.toml and .local.mise.toml?

@Nowaker
Copy link

Nowaker commented Feb 15, 2024

@vinistock Correct, no more logs than what I provided. Happy to provide more information if I'm told where and what exactly to look up. Thanks :)

@vinistock vinistock force-pushed the vs/switch_to_manual_ruby_activation branch from 3486b11 to 1b1bd56 Compare February 15, 2024 21:41
@vinistock
Copy link
Member Author

@Nowaker if the directories are the default ones, then I suspect it's some permission issue. We were using fs.access with R_OK to check if the directory is readable.

I changed the code to use F_OK, which checks if the directory is visible. Then before trying to boot anything, I also added a check to see if we have permission to execute, since we run exec for the Ruby executable.

Please let me know what happens when you upgrade.

@Nowaker
Copy link

Nowaker commented Feb 15, 2024

@vinistock Aye aye. But how do I check - did you release a new pre-release version to the VS Code Marketplace? As that's how I tested the thing.

@vinistock
Copy link
Member Author

Yup, I just shipped v0.6.10.

@medwards1771
Copy link

Hi @vinistock 👋

i'm using the rubyLsp extension in a vscode devcontainer and having trouble getting past the error: Failed to activate Ruby environment. [see screenshot below].

my setup

  • vscode v1.87.2
  • rubyLsp v0.6.10
  • devcontainer details:
    • the container environment does not use a ruby version manager. the value of which ruby while successfully (using rubyLsp v0.5.17) connected to the devcontainer is usr/local/bin/ruby. the devcontainer doesn't install ruby; instead it relies on a docker image with ruby pre-built into it
    • devcontainer.json contents:
{
  "name": "<redacted>",
  "dockerComposeFile": "../docker-compose.yml",
  "service": "devcontainer",
  "runServices": ["postgres", "redis"],
  "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
  "remoteEnv": {
    "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}",
    "GIT_EDITOR": "code --wait"
  },
  "features": {
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
      "version": "latest",
      "enableNonRootDocker": "false",
      "moby": "true"
    },
    "ghcr.io/devcontainers/features/common-utils:2": {
      "installZsh": "true",
      "configureZshAsDefaultShell": "true"
    }
  },
 ...
  "customizations": {
    "vscode": {
      "extensions": [
        "Shopify.ruby-lsp@prerelease"
      ],
      "settings": {
        "terminal.integrated.defaultProfile.linux": "zsh",
      }
    }
  }
}

debugging steps

  1. i tried setting the value of rubyLsp.rubyExecutablePath in devcontainer.json's customizations.settings to usr/local/bin/ruby, but this failed with the same error, Failed to activate Ruby environment: Command failed: <see screenshot for full command> /bin/sh: 1: /workspaces/forem/ruby: not found

[screenshot]
Screen Shot 2024-04-03 at 2 47 28 PM

@medwards1771
Copy link

Hi @vinistock 👋

i'm using the rubyLsp extension in a vscode devcontainer and having trouble getting past the error: Failed to activate Ruby environment. [see screenshot below].

I was able to resolve this by setting "rubyLsp.rubyVersionManager": "auto" in .vscode/settings.json and uninstalling asdf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
8 participants