Fetching VS Code Extensions from marketplace.visualstudio.com

I’ve been migrating from VS Code, and some of my favorite Extensions are not available in IDX via https://open-vsx.org/, or they’re often outdated. So I devised a way in which I can just use https://marketplace.visualstudio.com/ to fetch my Extensions, in a roundabout manner. I am aware that I can freely download the .vsix files from the VS Marketplace, and I can freely install a .vsix in IDX, but… I’m wondering if this is possibly violating the licenses of the developers of the Extensions, or the terms of IDX. Please weigh in if you have some perspective on this. Thanks!

If you dont mind please share how to fetch those extentions

1 Like

I would like to do that too

1 Like

Well, I was hoping to feel better about the ethics of doing it before sharing the method, but…

First, let me say, it isn’t the best solution in the world, and it kind of hurts the intentions of Nix and IDX and creates headaches, and could easily break. And I wouldn’t use this in large scale production.

But here is how I’m doing it:

The following is a small bash script that, when given the argument of a package ID, including publisher, such as ms-python.vscode-pylance, will fetch the latest verison’s .vsix from Microsoft’s VS Marketplace CDN, and install it:

msget.sh


#!/bin/bash

# Check if an extension ID is provided as an argument
if [ -z "$1" ]; then
  echo "Usage: $0 <extension_id>"
  exit 1
fi

extension_id="$1"

# Extract the publisher name and extension name from the ID
publisher=$(echo "$extension_id" | cut -d'.' -f1)
package=$(echo "$extension_id" | cut -d'.' -f2)

# Create the msget directory if it doesn't exist
msget_dir="msget"
mkdir -p "$msget_dir"

# Create the vsix subdirectory if it doesn't exist
vsix_dir="$msget_dir/vsix"
mkdir -p "$vsix_dir"

# Construct the download URL using the new format
download_url="https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${package}/latest/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage"

# Construct the filename with the extension ID
vsix_filename="${extension_id}.vsix"
vsix_filepath="$vsix_dir/$vsix_filename"

# Download the VSIX file using the constructed URL, overwriting if it exists
curl -L -o "$vsix_filepath" "$download_url"

echo "Downloaded $vsix_filepath"

# Install the VSIX using the 'code' command
code --install-extension "$vsix_filepath"

echo "Installed $extension_name extension"

I save this script at ./msget/msget.sh.

Then, in your ./.idx/dev.nix file, you need to:

First, add pkgs.curl, if you don’t already have it.

Then, you need to add to onCreate/onStart something akin to the following:


      onCreate = {
        msget_executable = ''chmod +x ./msget/msget.sh'';
        msget_pylance = ''./msget/msget.sh ms-python.vscode-pylance'';
        msget_colorize = ''./msget/msget.sh kamikillerto.vscode-colorize'';
      };
      onStart = {
        msget_executable = ''chmod +x ./msget/msget.sh'';
        msget_pylance = ''./msget/msget.sh ms-python.vscode-pylance'';
        msget_colorize = ''./msget/msget.sh kamikillerto.vscode-colorize'';
      };

I prefer to put these commands in both onCreate and onStart, so that it installs them when creating a workspace, and then updates them every time I rebuild the environment. You could add in checks so it isn’t so wasteful and all that, but I was lazy at that point. :sweat_smile:

So what we are doing in those commands there, first we make our script executable, then we simply pass a package ID as an argument to the script for each Extension we want to add.

It’s very nice to have this option, IMO, but it seems like really bad form if you’re doing anything professional here, just FYI.

I hope that helps and you guys enjoy it! :green_heart:

1 Like

@Yellow_Lemon I saw you just liked this; I actually have updated this quite a bit in the last few hours. Let me get you a fresh script with a little better organization and version control. Just a few minutes! :+1:

Alright,
so the onCreate / onStart is now vastly simplified:

./.idx/dev.nix


    workspace = {
      onCreate = {
        msget = ''
            cd ./msget
            chmod +x ./msget.sh
            ./msget.sh extensions.txt
        '';
      };
      onStart = {
        msget = ''
            cd ./msget
            chmod +x ./msget.sh
            ./msget.sh extensions.txt
        '';
      };
    };

We provide a list of extensions we want to grab, listed one per line in ./msget/extensions.txt.

We can specify a version number we want to fetch by typing like so:

ms-python.vscode-pylance==2023.9.10

If we do not specify a version, it defaults to the latest available. (much like a requirements.txt)

(btw, that 2023.9.10 version seems to be the most recent Pylance version compatible with Code OSS 1.89.1, if you want it…)

The script is now also cleaning up the .vsix files after installation, although you can keep them still if you want them with the flag -k when running the script:

./msget.sh -k extensions.txt

Here is the updated script:

./msget/msget.sh


#!/bin/bash

# Check if an extension list file is provided as an argument
if [ -z "$1" ]; then
  echo "Usage: $0 [-k] <extensions.txt>"
  exit 1
fi

# Check for the -k flag
keep_files=false
if [ "$1" == "-k" ]; then
  keep_files=true
  shift # Shift the arguments to remove the flag
fi

extensions="$1"

# Check if the file exists
if [ ! -f "$extensions" ]; then
  echo "Error: Extension list file '$extensions' not found."
  exit 1
fi

# Create the msget directory if it doesn't exist
msget_dir="msget"
mkdir -p "$msget_dir"

# Create the vsix subdirectory if it doesn't exist
vsix_dir="$msget_dir/vsix"
mkdir -p "$vsix_dir"

# Read the extension IDs from the file line by line
while IFS= read -r line || [[ -n "$line" ]]; do
  # Split the line by '==' to separate extension ID and version (if any)
  extension_id=$(echo "$line" | awk -F'==' '{print $1}')
  version=$(echo "$line" | awk -F'==' '{print $2}')

  # If version is not specified, default to "latest"
  if [ -z "$version" ]; then
    version="latest"
  fi
  
  # Extract the publisher name and extension name from the ID
  publisher=$(echo "$extension_id" | cut -d'.' -f1)
  package=$(echo "$extension_id" | cut -d'.' -f2)

  # Construct the download URL using the version variable
  download_url="https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${package}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage"

  # Construct the filename with the extension ID and version (if specified)
  vsix_filename="${extension_id}"
  if [ "$version" != "latest" ]; then
    vsix_filename="${vsix_filename}-${version}"
  fi
  vsix_filename="${vsix_filename}.vsix"
  vsix_filepath="$vsix_dir/$vsix_filename"

  # Download the VSIX file using the constructed URL, overwriting if it exists
  curl -L -o "$vsix_filepath" "$download_url"

  echo "Download process completed for $vsix_filepath"

  # Install the VSIX using the 'code' command
  code --install-extension "$vsix_filepath"

  echo "Installation process completed for $package extension"

  # Delete the .vsix file if -k is not specified
  if ! $keep_files; then
    rm "$vsix_filepath"
    echo "Deleted $vsix_filepath"
  fi
done < "$extensions"

# Delete the directories if -k is not specified
if ! $keep_files; then
  rmdir "$vsix_dir" "$msget_dir"
  echo "Deleted directories $vsix_dir and $msget_dir"
fi

Cheers to more Extensions! :beers: :sparkles:

EDITS 1 - 3: Typo corrections and formatting adjustments; nothing meaningfully changed.

EDIT 4: I realized a slight error in which the final extension in extensions.txt would be ignored if there was not a trailing blank line. I have corrected it above now in the script (Line 33) by changing while IFS= read -r line; do to while IFS= read -r line || [[ -n "$line" ]]; do.

2 Likes

@symmetricalboy thank you so much!! I was able to install a color theme that was not available in Project IDX extensions

1 Like