Skip to content

feat(jetbrains): add plugin pre-installation support#718

Open
Rhan2020 wants to merge 3 commits intocoder:mainfrom
Rhan2020:feat/jetbrains-plugins-preinstall
Open

feat(jetbrains): add plugin pre-installation support#718
Rhan2020 wants to merge 3 commits intocoder:mainfrom
Rhan2020:feat/jetbrains-plugins-preinstall

Conversation

@Rhan2020
Copy link

Summary

Adds a plugins parameter to the JetBrains module that accepts a list of Marketplace plugin IDs and pre-installs them when the workspace starts.

/claim #208

Approach

Previous PRs attempted to use the JetBrains CLI (installPlugins) which doesn't work while the IDE is running — creating an unsolvable race condition. This PR takes a different approach:

  1. Direct download from Marketplace API — Downloads plugin ZIPs using https://plugins.jetbrains.com/pluginManager?action=download&id=<pluginXmlId>&build=<productCode>-<buildNumber>
  2. Extracts into the IDE's plugin directory — No CLI dependency, no race condition with running IDE
  3. Uses existing build numbers — The module already resolves build numbers per IDE, so we pass those to the Marketplace API for compatibility
  4. Background execution — Runs via nohup so workspace startup isn't blocked

Usage

module "jetbrains" {
  count    = data.coder_workspace.me.start_count
  source   = "registry.coder.com/coder/jetbrains/coder"
  version  = "1.4.0"
  agent_id = coder_agent.main.id
  folder   = "/home/coder/project"
  default  = ["PY", "IU"]

  plugins = [
    "com.koxudaxi.pydantic",
    "com.intellij.kubernetes",
  ]
}

Changes

  • main.tf: Added plugins variable (list(string)), plugin-related locals, and coder_script.jetbrains_plugins resource
  • scripts/install_plugins.sh: Shell script that downloads and extracts plugins from the Marketplace. Searches Toolbox, RemoteDev, and config-based plugin directories.
  • README.md: Documented the new plugins parameter with usage example
  • jetbrains.tftest.hcl: Added 3 tests for plugin functionality

Testing

All 20 tests pass (17 existing + 3 new):

  • no_plugin_script_when_plugins_empty — no script created when plugins=[]
  • plugin_script_created_when_plugins_set — script created when plugins specified
  • plugin_script_with_multiple_ides — single script handles multiple IDEs

Requirements

Workspace needs curl, jq, and unzip available (standard on most Coder workspace images).

Add a 'plugins' parameter to the JetBrains module that accepts a list of
Marketplace plugin IDs and automatically downloads and installs them when
the workspace starts.

Key design decisions:
- Downloads plugins directly from JetBrains Marketplace API instead of
  using the IDE CLI (which doesn't work while the IDE is running)
- Uses build numbers already available in the module to fetch compatible
  plugin versions
- Runs installation in background to avoid blocking workspace startup
- Searches multiple plugin directory locations (Toolbox, RemoteDev, config)

Closes coder#208
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.

Reviewed commit: 53cf1d9d88

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…onfig path

- Use POSIX [[:space:]] in grep regex for productCode detection so
  non-minified product-info.json files are matched correctly in
  RemoteDev dist directories.
- Derive versioned config directory (e.g. PyCharm2025.3) from the
  build number when synthesizing the fallback plugins path, matching
  the layout the IDE actually looks up.
@Rhan2020
Copy link
Author

Fixed both issues in the latest push:

  1. Switched the productCode grep to an extended regex with [[:space:]] so it matches both minified ("productCode":"PY") and pretty-printed ("productCode": "PY") JSON in product-info.json.

  2. The fallback plugins directory now derives a versioned path from the build number (e.g., build 253.xPyCharm2025.3/plugins) instead of using a bare ${config_pattern}/plugins path that the IDE wouldn't pick up.

@Rhan2020
Copy link
Author

Both P1 issues flagged by the Codex review were already addressed in commit 92dd0be:

  1. Whitespace-tolerant productCode parsing — Updated grep pattern to grep -oE '"productCode"[[:space:]]*:[[:space:]]*"[^"]*"' which handles both minified and pretty-printed JSON.

  2. Versioned fallback config path — The fallback branch now derives the IDE version from the build number (e.g., build 253.x → 2025.3) and creates a properly versioned path like ${config_pattern}${ide_version}/plugins.

Ready for review!

- Replace grep-based productCode extraction with jq for proper JSON
  parsing, handling any whitespace/formatting in product-info.json
- Validate build-number format (exactly 3 digits) before deriving
  the IDE version for the fallback config path, preventing bogus
  directory creation from unexpected build strings
@Harsh9485
Copy link
Contributor

Good idea!

When I was researching and working on this issue, I found suggestions online to use the IDE CLI tool to install plugins, so I started implementing that approach, but it failed 😅.

What I learned is that it’s always important to explore all possible approaches to solving the same issue.

@Rhan2020
Copy link
Author

Thanks for sharing that! Yeah I actually went down the same rabbit hole initially — the IDE CLI approach seems like the obvious choice, but it turns out the CLI isn't always available in headless/remote environments (which is exactly where Coder workspaces run). The direct download + extract to the plugins dir ended up being way more reliable since it doesn't depend on the IDE process being running or the CLI being in PATH.

Totally agree though, exploring multiple approaches is how you end up finding the one that actually works in all the edge cases. Appreciate the feedback 🙏

Copy link
Member

@matifali matifali left a comment

Choose a reason for hiding this comment

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

@Rhan2020, can you please attach a video demo as described in #213

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