Skip to content

Conversation

@Harsh9485
Copy link
Contributor

@Harsh9485 Harsh9485 commented Dec 2, 2025

Description

This PR detects the IDE folder and uses the IDE’s CLI tool to install the plugins provided by the user. It’s a prototype, and for now I just want to show the idea. I’ll improve the script later

pycharm64.exe installPlugins <plugin-id ...>

Type of Change

  • New module
  • New template
  • Bug fix
  • Feature/enhancement
  • Documentation
  • Other

Module Information

Path: registry/coder/modules/jetbrains
New version: v1.1.0
Breaking change: [ ] Yes [x] No

Testing & Validation

  • Tests pass (bun test)
  • Code formatted (bun fmt)
  • Changes tested locally

image's

Screenshot 2025-12-02 145817 Screenshot 2025-12-02 145902

/claim #208

@DevelopmentCats
Copy link
Contributor

@Harsh9485 You will need to run bun fmt on this for the CI to pass

@Harsh9485
Copy link
Contributor Author

I’ll do that, but if you like the idea, I’ll push the full PR. I’ll update the README and improve the script. Let me know if you want anything changed.

@Harsh9485
Copy link
Contributor Author

hey @DevelopmentCats @matifali, can you review the PR?

@matifali matifali requested a review from Copilot December 3, 2025 13:44
Copilot finished reviewing on behalf of matifali December 3, 2025 13:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for automatic JetBrains plugin installation by detecting installed IDEs via JetBrains Toolbox and using each IDE's CLI to install user-specified plugins. The feature introduces a new jetbrains_plugins variable that maps IDE product codes to lists of plugin IDs, creating two coder_script resources: one to store the plugin configuration and another to execute the installation script.

Key Changes:

  • New jetbrains_plugins variable for specifying plugins per IDE
  • Bash script that polls for IDE installation and installs plugins via CLI
  • Two coder_script resources for storing configuration and running installation
  • Documentation with usage examples

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 13 comments.

File Description
registry/coder/modules/jetbrains/script/install_plugins.sh New bash script that waits for Toolbox IDEs to install, then uses each IDE's CLI to install specified plugins
registry/coder/modules/jetbrains/main.tf Adds jetbrains_plugins variable and two coder_script resources to store plugin config and run installation
registry/coder/modules/jetbrains/README.md Documents the new plugin auto-installer feature with examples and instructions
bun.lock Automatic lockfile update adding configVersion field

run_on_start = true

script = <<-EOT
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

There's an unnecessary blank line at the beginning of the heredoc. This will be included in the script output. Consider removing it:

script = <<-EOT
  ${file("${path.module}/script/install_plugins.sh")}
EOT
Suggested change

Copilot uses AI. Check for mistakes.

variable "jetbrains_plugins" {
type = map(list(string))
description = "Map of IDE product codes to plugin ID lists. Example: { IU = [\"com.foo\"], GO = [\"org.bar\"] }."
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The variable description should clarify what valid product codes are and provide a reference. Consider improving the description:

variable "jetbrains_plugins" {
  type        = map(list(string))
  description = "Map of IDE product codes to plugin ID lists. Valid codes: CL, GO, IU, PS, PY, RD, RM, RR, WS. Example: { IU = [\"com.foo.bar\"], PY = [\"org.example.plugin\"] }. Find plugin IDs at https://plugins.jetbrains.com/"
  default     = {}
}
Suggested change
description = "Map of IDE product codes to plugin ID lists. Example: { IU = [\"com.foo\"], GO = [\"org.bar\"] }."
description = "Map of JetBrains IDE product codes to plugin ID lists. Valid codes: CL (CLion), GO (GoLand), IU (IntelliJ IDEA Ultimate), PS (PhpStorm), PY (PyCharm), RD (Rider), RM (RubyMine), RR (ReSharper), WS (WebStorm). Example: { IU = [\"com.foo\"], GO = [\"org.bar\"] }. Find plugin IDs at https://plugins.jetbrains.com/."

Copilot uses AI. Check for mistakes.
}
```

### Plugin Auto‑Installer
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The section title uses a non-breaking hyphen character (U+2011) instead of a standard hyphen. While this may render correctly in most contexts, it could cause issues with some text editors or search functionality. Consider using a standard hyphen:

### Plugin Auto-Installer
Suggested change
### Plugin AutoInstaller
### Plugin Auto-Installer

Copilot uses AI. Check for mistakes.
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.2.1"
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The version in the example should match the actual module version being released. The PR metadata indicates this is for version v1.1.0, but the example shows version = "1.2.1". Update to the correct version or use a placeholder that clearly indicates users should check for the latest version.

Copilot uses AI. Check for mistakes.
Comment on lines 94 to 137
# Loop until all plugins installed
while [ ${#pending_codes[@]} -gt 0 ]; do

for product_dir in "$TOOLBOX_BASE"/*; do
[ -d "$product_dir" ] || continue

product_name="$(basename "$product_dir")"
code="$(map_folder_to_code "$product_name")"

# Only process codes user requested
if [[ ! " ${pending_codes[*]} " =~ " $code " ]]; then
continue
fi

cli_launcher="$(find_cli_launcher "$code" "$product_dir")" || continue

log "Detected IDE $code at $product_dir"

plugins="$(get_plugins_for_code "$code")"
if [ -z "$plugins" ]; then
log "No plugins for $code"
continue
fi

while read -r plugin; do
install_plugin "$cli_launcher" "$plugin"
done <<< "$plugins"

# remove code from pending list after success
tmp=()
for c in "${pending_codes[@]}"; do
[ "$c" != "$code" ] && tmp+=("$c")
done
pending_codes=("${tmp[@]}")

log "Finished $code. Remaining: ${pending_codes[*]:-none}"

done

# If still pending, wait and retry
if [ ${#pending_codes[@]} -gt 0 ]; then
sleep 10
fi
done
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The infinite while loop continues polling every 10 seconds even if no IDEs will ever be installed. If an IDE in jetbrains_plugins is never installed via Toolbox, this script will run indefinitely. Consider adding:

  1. A maximum retry count or timeout
  2. A way to skip codes that will never be available (e.g., if user only installs a subset of requested IDEs)

Example:

MAX_ATTEMPTS=60  # 10 minutes
attempt=0
while [ ${#pending_codes[@]} -gt 0 ] && [ $attempt -lt $MAX_ATTEMPTS ]; do
  # ... existing loop logic ...
  if [ ${#pending_codes[@]} -gt 0 ]; then
    sleep 10
    ((attempt++))
  fi
done

if [ ${#pending_codes[@]} -gt 0 ]; then
  log "Timeout: IDEs not found: ${pending_codes[*]}"
fi

Copilot uses AI. Check for mistakes.
Comment on lines 256 to 265
resource "coder_script" "install_jetbrains_plugins" {
agent_id = var.agent_id
display_name = "Install JetBrains Plugins"
run_on_start = true

script = <<-EOT
${file("${path.module}/script/install_plugins.sh")}
EOT
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The install_jetbrains_plugins script depends on the plugins.json file created by store_plugins, but there's no explicit dependency between these resources. While both have run_on_start = true, Terraform doesn't guarantee execution order without an explicit dependency. Add a depends_on to ensure proper ordering:

resource "coder_script" "install_jetbrains_plugins" {
  agent_id     = var.agent_id
  display_name = "Install JetBrains Plugins"
  run_on_start = true
  depends_on   = [coder_script.store_plugins]

  script = <<-EOT
    ${file("${path.module}/script/install_plugins.sh")}
  EOT
}

Copilot uses AI. Check for mistakes.
PLUGIN_MAP_FILE="$HOME/.config/jetbrains/plugins.json"

sudo apt-get update
sudo apt-get install -y libfreetype6
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The script uses jq to parse JSON but doesn't install it or check if it's available before use. This will cause the script to fail silently when jq commands are executed (lines 21, 25).

Consider either:

  1. Installing jq alongside libfreetype6: sudo apt-get install -y libfreetype6 jq
  2. Adding a command check similar to other modules: if ! command -v jq > /dev/null; then echo "jq is not installed"; exit 1; fi
Suggested change
sudo apt-get install -y libfreetype6
sudo apt-get install -y libfreetype6 jq

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 9
sudo apt-get update
sudo apt-get install -y libfreetype6
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Running apt-get update and apt-get install with sudo assumes the script runs in a Debian/Ubuntu environment with sudo privileges. This may not work on other distributions (e.g., Alpine, Fedora) or systems without sudo access. Consider adding a check for the package manager or documenting this requirement in the module's README.

Suggested change
sudo apt-get update
sudo apt-get install -y libfreetype6
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y libfreetype6
else
echo "Warning: 'apt-get' not found. Please ensure 'libfreetype6' is installed manually for your distribution." >&2
fi

Copilot uses AI. Check for mistakes.

install_plugin() {
log "Installing plugin: $2"
"$1" installPlugins "$2"
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The install_plugin function doesn't check if the plugin installation succeeds. If the IDE CLI command fails (e.g., invalid plugin ID, network issues, or CLI not supporting installPlugins), the script continues without error. Consider capturing the exit status and logging failures:

install_plugin() {
  log "Installing plugin: $2"
  if "$1" installPlugins "$2"; then
    log "Successfully installed plugin: $2"
  else
    log "Failed to install plugin: $2"
    return 1
  fi
}
Suggested change
"$1" installPlugins "$2"
if "$1" installPlugins "$2"; then
log "Successfully installed plugin: $2"
else
log "Failed to install plugin: $2"
return 1
fi

Copilot uses AI. Check for mistakes.
continue
fi

cli_launcher="$(find_cli_launcher "$code" "$product_dir")" || continue
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The script looks for the CLI launcher in $product_dir/bin/$exe, but this path might not match the actual Toolbox installation structure. JetBrains Toolbox typically installs IDEs in versioned subdirectories like ~/.local/share/JetBrains/Toolbox/apps/PyCharm-P/ch-0/242.23726.102/bin/pycharm. The script needs to find the correct version directory. Consider:

find_cli_launcher() {
  local exe
  exe="$(launcher_for_code "$1")" || return 1
  
  # Look for the newest version directory
  local latest_version
  latest_version=$(find "$2" -maxdepth 2 -type d -name "ch-*" 2>/dev/null | sort -V | tail -1)
  
  if [ -n "$latest_version" ] && [ -f "$latest_version/bin/$exe" ]; then
    echo "$latest_version/bin/$exe"
  else
    return 1
  fi
}

Copilot uses AI. Check for mistakes.
@matifali
Copy link
Member

matifali commented Dec 3, 2025

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +29 to +33
map_folder_to_code() {
case "$1" in
*pycharm*) echo "PY" ;;
*idea*) echo "IU" ;;
*webstorm*) echo "WS" ;;

Choose a reason for hiding this comment

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

P1 Badge Make folder-to-code mapping case-insensitive

The folder mapping uses lowercase globs (*pycharm*, *idea*, etc.), but JetBrains Toolbox installs create uppercase product directories (e.g., IDEA-U, PyCharm-P) on Linux. Because Bash pattern matching is case-sensitive, these branches never match, leaving code empty, so the main loop never processes the detected IDEs and the installer spins forever without installing plugins. Normalizing the folder name or enabling case-insensitive matching is needed for Toolbox layouts.

Useful? React with 👍 / 👎.

Comment on lines 61 to 65
local exe
exe="$(launcher_for_code "$1")" || return 1

if [ -f "$2/bin/$exe" ]; then
echo "$2/bin/$exe"

Choose a reason for hiding this comment

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

P1 Badge Search JetBrains CLI inside channel/build directories

find_cli_launcher currently looks only for $product_dir/bin/<launcher> under ~/.local/share/JetBrains/Toolbox/apps, but Toolbox installs place binaries under channel/build folders such as <product>/ch-0/<build>/bin/idea.sh. For any normal install this check always fails, so cli_launcher stays empty, pending_codes is never reduced, and the installer loops forever without installing plugins. The search needs to descend into the channel/build subdirectories to locate the CLI.

Useful? React with 👍 / 👎.

Comment on lines +84 to +88
# Load list of IDE codes user actually needs
mapfile -t pending_codes < <(get_enabled_codes)

if [ ${#pending_codes[@]} -eq 0 ]; then
log "No plugin entries found. Exiting."

Choose a reason for hiding this comment

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

P2 Badge Filter plugins to selected IDEs before waiting

pending_codes is populated from every key in plugins.json, regardless of which IDEs were actually selected or installed. If a user supplies plugin lists for more products than they enable (e.g., the README example lists all codes while default = ["IU", "PY"]), the while loop never clears the extra entries and sleeps forever, so the script never finishes. pending_codes should be intersected with the selected/installed IDE set before entering the loop to avoid hanging.

Useful? React with 👍 / 👎.

@Harsh9485
Copy link
Contributor Author

I’ve implemented all the useful suggestions, @DevelopmentCats @matifali.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants