Merge pull request #240521 from GenericNerdyUsername/jetbrains-plugins-overhauled

This commit is contained in:
Janik 2023-07-01 09:50:32 +02:00 committed by GitHub
commit 27e754677b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1101 additions and 95 deletions

View File

@ -10,27 +10,30 @@
, productShort ? product
, src
, version
, plugins ? [ ]
, buildNumber
, ...
}:
let
loname = lib.toLower productShort;
in
stdenvNoCC.mkDerivation {
inherit pname meta src version;
desktopName = product;
installPhase = ''
runHook preInstall
APP_DIR="$out/Applications/${product}.app"
mkdir -p "$APP_DIR"
cp -Tr "${product}.app" "$APP_DIR"
mkdir -p "$out/bin"
cat << EOF > "$out/bin/${loname}"
open -na '$APP_DIR' --args "\$@"
EOF
chmod +x "$out/bin/${loname}"
runHook postInstall
'';
nativeBuildInputs = [ undmg ];
sourceRoot = ".";
}
stdenvNoCC.mkDerivation {
inherit pname meta src version plugins;
passthru.buildNumber = buildNumber;
desktopName = product;
installPhase = ''
runHook preInstall
APP_DIR="$out/Applications/${product}.app"
mkdir -p "$APP_DIR"
cp -Tr "${product}.app" "$APP_DIR"
mkdir -p "$out/bin"
cat << EOF > "$out/bin/${loname}"
open -na '$APP_DIR' --args "\$@"
EOF
chmod +x "$out/bin/${loname}"
runHook postInstall
'';
nativeBuildInputs = [ undmg ];
sourceRoot = ".";
}

View File

@ -1,5 +1,13 @@
{ lib, stdenv, callPackage, fetchurl
, jdk, cmake, gdb, zlib, python3, icu
{ lib
, stdenv
, callPackage
, fetchurl
, jdk
, cmake
, gdb
, zlib
, python3
, icu
, lldb
, dotnet-sdk_6
, maven
@ -27,9 +35,9 @@ let
# Sorted alphabetically
buildClion = { pname, version, src, license, description, wmClass, ... }:
buildClion = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "CLion";
meta = with lib; {
homepage = "https://www.jetbrains.com/clion/";
@ -41,11 +49,10 @@ let
maintainers = with maintainers; [ edwtjo mic92 ];
};
}).overrideAttrs (attrs: {
nativeBuildInputs = (attrs.nativeBuildInputs or []) ++ lib.optionals (stdenv.isLinux) [
nativeBuildInputs = (attrs.nativeBuildInputs or [ ]) ++ lib.optionals (stdenv.isLinux) [
autoPatchelfHook
patchelf
];
buildInputs = (attrs.buildInputs or []) ++ lib.optionals (stdenv.isLinux) [
buildInputs = (attrs.buildInputs or [ ]) ++ lib.optionals (stdenv.isLinux) [
python3
stdenv.cc.cc
libdbusmenu
@ -57,12 +64,12 @@ let
postFixup = (attrs.postFixup or "") + lib.optionalString (stdenv.isLinux) ''
(
cd $out/clion
# bundled cmake does not find libc
rm -rf bin/cmake/linux
ln -s ${cmake} bin/cmake/linux
# bundled gdb does not find libcrypto 10
rm -rf bin/gdb/linux
ln -s ${gdb} bin/gdb/linux
# I think the included gdb has a couple of patches, so we patch it instead of replacing
ls -d $PWD/bin/gdb/linux/x64/lib/python3.8/lib-dynload/* |
xargs patchelf \
--replace-needed libssl.so.10 libssl.so \
--replace-needed libcrypto.so.10 libcrypto.so
ls -d $PWD/bin/lldb/linux/x64/lib/python3.8/lib-dynload/* |
xargs patchelf \
@ -70,16 +77,15 @@ let
--replace-needed libcrypto.so.10 libcrypto.so
autoPatchelf $PWD/bin
wrapProgram $out/bin/clion \
--set CL_JDK "${jdk}"
)
'';
});
buildDataGrip = { pname, version, src, license, description, wmClass, ... }:
buildDataGrip = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "DataGrip";
meta = with lib; {
homepage = "https://www.jetbrains.com/datagrip/";
@ -93,9 +99,9 @@ let
};
});
buildDataSpell = { pname, version, src, license, description, wmClass, ... }:
buildDataSpell = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "DataSpell";
meta = with lib; {
homepage = "https://www.jetbrains.com/dataspell/";
@ -108,9 +114,9 @@ let
};
});
buildGateway = { pname, version, src, license, description, wmClass, product, ... }:
buildGateway = { pname, version, src, license, description, wmClass, buildNumber, product, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk product;
inherit pname version src wmClass jdk buildNumber product;
productShort = "Gateway";
meta = with lib; {
homepage = "https://www.jetbrains.com/remote-development/gateway/";
@ -124,9 +130,9 @@ let
};
});
buildGoland = { pname, version, src, license, description, wmClass, ... }:
buildGoland = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "Goland";
meta = with lib; {
homepage = "https://www.jetbrains.com/go/";
@ -143,18 +149,16 @@ let
postFixup = (attrs.postFixup or "") + lib.optionalString stdenv.isLinux ''
interp="$(cat $NIX_CC/nix-support/dynamic-linker)"
patchelf --set-interpreter $interp $out/goland/plugins/go-plugin/lib/dlv/linux/dlv
chmod +x $out/goland/plugins/go-plugin/lib/dlv/linux/dlv
# fortify source breaks build since delve compiles with -O0
wrapProgram $out/bin/goland \
--prefix CGO_CPPFLAGS " " "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"
'';
});
buildIdea = { pname, version, src, license, description, wmClass, product, ... }:
buildIdea = { pname, version, src, license, description, wmClass, buildNumber, product, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk product;
inherit pname version src wmClass jdk buildNumber product;
productShort = "IDEA";
extraLdPath = [ zlib ];
extraWrapperArgs = [
@ -175,9 +179,9 @@ let
};
});
buildMps = { pname, version, src, license, description, wmClass, product, ... }:
buildMps = { pname, version, src, license, description, wmClass, product, buildNumber, ... }:
(mkJetBrainsProduct rec {
inherit pname version src wmClass jdk product;
inherit pname version src wmClass jdk buildNumber product;
productShort = "MPS";
meta = with lib; {
broken = (stdenv.isLinux && stdenv.isAarch64);
@ -193,9 +197,9 @@ let
};
});
buildPhpStorm = { pname, version, src, license, description, wmClass, ... }:
buildPhpStorm = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "PhpStorm";
meta = with lib; {
homepage = "https://www.jetbrains.com/phpstorm/";
@ -209,9 +213,9 @@ let
};
});
buildPycharm = { pname, version, src, license, description, wmClass, product, cythonSpeedup ? stdenv.isLinux, ... }:
buildPycharm = { pname, version, src, license, description, wmClass, buildNumber, product, cythonSpeedup ? stdenv.isLinux, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk product;
inherit pname version src wmClass jdk buildNumber product;
productShort = "PyCharm";
meta = with lib; {
broken = (stdenv.isLinux && stdenv.isAarch64);
@ -230,24 +234,24 @@ let
providing you almost everything you need for your comfortable
and productive development!
'';
maintainers = with maintainers; [ ];
maintainers = with maintainers; [ genericnerdyusername ];
};
}).overrideAttrs (finalAttrs: previousAttrs: lib.optionalAttrs cythonSpeedup {
buildInputs = with python3.pkgs; [ python3 setuptools ];
preInstall = ''
echo "compiling cython debug speedups"
if [[ -d plugins/python-ce ]]; then
${python3.interpreter} plugins/python-ce/helpers/pydev/setup_cython.py build_ext --inplace
else
${python3.interpreter} plugins/python/helpers/pydev/setup_cython.py build_ext --inplace
fi
echo "compiling cython debug speedups"
if [[ -d plugins/python-ce ]]; then
${python3.interpreter} plugins/python-ce/helpers/pydev/setup_cython.py build_ext --inplace
else
${python3.interpreter} plugins/python/helpers/pydev/setup_cython.py build_ext --inplace
fi
'';
# See https://www.jetbrains.com/help/pycharm/2022.1/cython-speedups.html
});
buildRider = { pname, version, src, license, description, wmClass, ... }:
buildRider = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "Rider";
# icu is required by Rider.Backend
extraLdPath = [ icu ];
@ -276,9 +280,9 @@ let
'');
});
buildRubyMine = { pname, version, src, license, description, wmClass, ... }:
buildRubyMine = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "RubyMine";
meta = with lib; {
homepage = "https://www.jetbrains.com/ruby/";
@ -288,9 +292,9 @@ let
};
});
buildWebStorm = { pname, version, src, license, description, wmClass, ... }:
buildWebStorm = { pname, version, src, license, description, wmClass, buildNumber, ... }:
(mkJetBrainsProduct {
inherit pname version src wmClass jdk;
inherit pname version src wmClass jdk buildNumber;
product = "WebStorm";
meta = with lib; {
homepage = "https://www.jetbrains.com/webstorm/";
@ -312,7 +316,8 @@ in
clion = buildClion rec {
pname = "clion";
version = products.clion.version;
description = "C/C++ IDE. New. Intelligent. Cross-platform";
buildNumber = products.clion.build_number;
description = "C/C++ IDE. New. Intelligent. Cross-platform";
license = lib.licenses.unfree;
src = fetchurl {
url = products.clion.url;
@ -325,6 +330,7 @@ in
datagrip = buildDataGrip rec {
pname = "datagrip";
version = products.datagrip.version;
buildNumber = products.datagrip.build_number;
description = "Your Swiss Army Knife for Databases and SQL";
license = lib.licenses.unfree;
src = fetchurl {
@ -338,6 +344,7 @@ in
dataspell = buildDataSpell rec {
pname = "dataspell";
version = products.dataspell.version;
buildNumber = products.dataspell.build_number;
description = "The IDE for Professional Data Scientists";
license = lib.licenses.unfree;
src = fetchurl {
@ -352,6 +359,7 @@ in
pname = "gateway";
product = "JetBrains Gateway";
version = products.gateway.version;
buildNumber = products.gateway.build_number;
description = "Your single entry point to all remote development environments";
license = lib.licenses.unfree;
src = fetchurl {
@ -365,6 +373,7 @@ in
goland = buildGoland rec {
pname = "goland";
version = products.goland.version;
buildNumber = products.goland.build_number;
description = "Up and Coming Go IDE";
license = lib.licenses.unfree;
src = fetchurl {
@ -379,6 +388,7 @@ in
pname = "idea-community";
product = "IntelliJ IDEA CE";
version = products.idea-community.version;
buildNumber = products.idea-community.build_number;
description = "Integrated Development Environment (IDE) by Jetbrains, community edition";
license = lib.licenses.asl20;
src = fetchurl {
@ -393,6 +403,7 @@ in
pname = "idea-ultimate";
product = "IntelliJ IDEA";
version = products.idea-ultimate.version;
buildNumber = products.idea-ultimate.build_number;
description = "Integrated Development Environment (IDE) by Jetbrains, requires paid license";
license = lib.licenses.unfree;
src = fetchurl {
@ -407,6 +418,7 @@ in
pname = "mps";
product = "MPS ${products.mps.version}";
version = products.mps.version;
buildNumber = products.mps.build_number;
description = "Create your own domain-specific language";
license = lib.licenses.asl20;
src = fetchurl {
@ -420,6 +432,7 @@ in
phpstorm = buildPhpStorm rec {
pname = "phpstorm";
version = products.phpstorm.version;
buildNumber = products.phpstorm.build_number;
description = "Professional IDE for Web and PHP developers";
license = lib.licenses.unfree;
src = fetchurl {
@ -434,6 +447,7 @@ in
pname = "pycharm-community";
product = "PyCharm CE";
version = products.pycharm-community.version;
buildNumber = products.pycharm-community.build_number;
description = "PyCharm Community Edition";
license = lib.licenses.asl20;
src = fetchurl {
@ -448,6 +462,7 @@ in
pname = "pycharm-professional";
product = "PyCharm";
version = products.pycharm-professional.version;
buildNumber = products.pycharm-community.build_number;
description = "PyCharm Professional Edition";
license = lib.licenses.unfree;
src = fetchurl {
@ -461,6 +476,7 @@ in
rider = buildRider rec {
pname = "rider";
version = products.rider.version;
buildNumber = products.rider.build_number;
description = "A cross-platform .NET IDE based on the IntelliJ platform and ReSharper";
license = lib.licenses.unfree;
src = fetchurl {
@ -474,6 +490,7 @@ in
ruby-mine = buildRubyMine rec {
pname = "ruby-mine";
version = products.ruby-mine.version;
buildNumber = products.ruby-mine.build_number;
description = "The Most Intelligent Ruby and Rails IDE";
license = lib.licenses.unfree;
src = fetchurl {
@ -487,6 +504,7 @@ in
webstorm = buildWebStorm rec {
pname = "webstorm";
version = products.webstorm.version;
buildNumber = products.webstorm.build_number;
description = "Professional IDE for Web and JavaScript development";
license = lib.licenses.unfree;
src = fetchurl {
@ -497,4 +515,6 @@ in
update-channel = products.webstorm.update-channel;
};
plugins = callPackage ./plugins { };
}

View File

@ -1,25 +1,51 @@
{ stdenv, lib, makeDesktopItem, makeWrapper, patchelf, writeText
, coreutils, gnugrep, which, git, unzip, libsecret, libnotify, e2fsprogs
, python3, vmopts ? null
{ stdenv
, lib
, makeDesktopItem
, makeWrapper
, patchelf
, writeText
, coreutils
, gnugrep
, which
, git
, unzip
, libsecret
, libnotify
, e2fsprogs
, python3
, vmopts ? null
}:
{ pname, product, productShort ? product, version, src, wmClass, jdk, meta, extraLdPath ? [], extraWrapperArgs ? [] }@args:
{ pname
, product
, productShort ? product
, version
, src
, wmClass
, buildNumber
, jdk
, meta
, extraLdPath ? [ ]
, extraWrapperArgs ? [ ]
}@args:
let loName = lib.toLower productShort;
hiName = lib.toUpper productShort;
vmoptsName = loName
+ lib.optionalString stdenv.hostPlatform.is64bit "64"
+ ".vmoptions";
let
loName = lib.toLower productShort;
hiName = lib.toUpper productShort;
vmoptsName = loName
+ lib.optionalString stdenv.hostPlatform.is64bit "64"
+ ".vmoptions";
in
with stdenv; lib.makeOverridable mkDerivation (rec {
inherit pname version src;
passthru.buildNumber = buildNumber;
meta = args.meta // { mainProgram = pname; };
desktopItem = makeDesktopItem {
name = pname;
exec = pname;
comment = lib.replaceStrings ["\n"] [" "] meta.longDescription;
comment = lib.replaceStrings [ "\n" ] [ " " ] meta.longDescription;
desktopName = product;
genericName = meta.description;
categories = [ "Development" ];
@ -32,30 +58,30 @@ with stdenv; lib.makeOverridable mkDerivation (rec {
nativeBuildInputs = [ makeWrapper patchelf unzip ];
postPatch = ''
get_file_size() {
local fname="$1"
echo $(ls -l $fname | cut -d ' ' -f5)
}
get_file_size() {
local fname="$1"
echo $(ls -l $fname | cut -d ' ' -f5)
}
munge_size_hack() {
local fname="$1"
local size="$2"
strip $fname
truncate --size=$size $fname
}
munge_size_hack() {
local fname="$1"
local size="$2"
strip $fname
truncate --size=$size $fname
}
rm -rf jbr
rm -rf jbr
interpreter=$(echo ${stdenv.cc.libc}/lib/ld-linux*.so.2)
if [[ "${stdenv.hostPlatform.system}" == "x86_64-linux" && -e bin/fsnotifier64 ]]; then
target_size=$(get_file_size bin/fsnotifier64)
patchelf --set-interpreter "$interpreter" bin/fsnotifier64
munge_size_hack bin/fsnotifier64 $target_size
else
target_size=$(get_file_size bin/fsnotifier)
patchelf --set-interpreter "$interpreter" bin/fsnotifier
munge_size_hack bin/fsnotifier $target_size
fi
interpreter=$(echo ${stdenv.cc.libc}/lib/ld-linux*.so.2)
if [[ "${stdenv.hostPlatform.system}" == "x86_64-linux" && -e bin/fsnotifier64 ]]; then
target_size=$(get_file_size bin/fsnotifier64)
patchelf --set-interpreter "$interpreter" bin/fsnotifier64
munge_size_hack bin/fsnotifier64 $target_size
else
target_size=$(get_file_size bin/fsnotifier)
patchelf --set-interpreter "$interpreter" bin/fsnotifier
munge_size_hack bin/fsnotifier $target_size
fi
'';
installPhase = ''

View File

@ -0,0 +1,122 @@
{ fetchurl
, fetchzip
, lib
, stdenv
, callPackage
, autoPatchelfHook
, glib
}:
let
pluginsJson = builtins.fromJSON (builtins.readFile ./plugins.json);
specialPluginsInfo = callPackage ./specialPlugins.nix { };
fetchPluginSrc = url: hash:
let
isJar = lib.hasSuffix ".jar" url;
fetcher = if isJar then fetchurl else fetchzip;
in
fetcher {
executable = isJar;
inherit url hash;
};
files = builtins.mapAttrs (key: value: fetchPluginSrc key value) pluginsJson.files;
ids = builtins.attrNames pluginsJson.plugins;
mkPlugin = id: file:
if !specialPluginsInfo ? "${id}"
then files."${file}"
else
stdenv.mkDerivation ({
name = "jetbrains-plugin-${id}";
installPhase = ''
runHook preInstall
mkdir -p $out && cp -r . $out
runHook postInstall
'';
src = files."${file}";
} // specialPluginsInfo."${id}");
selectFile = id: ide: build:
if !builtins.elem ide pluginsJson.plugins."${id}".compatible then
throw "Plugin with id ${id} does not support IDE ${ide}"
else if !pluginsJson.plugins."${id}".builds ? "${build}" then
throw "Jetbrains IDEs with build ${build} are not in nixpkgs. Try update_plugins.py with --with-build?"
else if pluginsJson.plugins."${id}".builds."${build}" == null then
throw "Plugin with id ${id} does not support build ${build}"
else
pluginsJson.plugins."${id}".builds."${build}";
byId = builtins.listToAttrs
(map
(id: {
name = id;
value = ide: build: mkPlugin id (selectFile id ide build);
})
ids);
byName = builtins.listToAttrs
(map
(id: {
name = pluginsJson.plugins."${id}".name;
value = byId."${id}";
})
ids);
in
rec {
# Only use if you know what youre doing
raw = { inherit files byId byName; };
addPlugins = ide: unprocessedPlugins:
let
processPlugin = plugin:
if lib.isDerivation plugin then plugin else
if byId ? "${plugin}" then byId."${plugin}" ide.pname ide.buildNumber else
if byName ? "${plugin}" then byName."${plugin}" ide.pname ide.buildNumber else
throw "Could not resolve plugin ${plugin}";
plugins = map processPlugin unprocessedPlugins;
in
stdenv.mkDerivation rec {
pname = meta.mainProgram + "-with-plugins";
version = ide.version;
src = ide;
dontInstall = true;
dontFixup = true;
passthru.plugins = plugins ++ (ide.plugins or [ ]);
newPlugins = plugins;
disallowedReferences = [ ide ];
nativeBuildInputs = [ autoPatchelfHook ] ++ (ide.nativeBuildInputs or [ ]);
buildInputs = lib.unique ((ide.buildInputs or [ ]) ++ [ glib ]);
inherit (ide) meta;
buildPhase =
let
pluginCmdsLines = map (plugin: "ln -s ${plugin} \"$out\"/${meta.mainProgram}/plugins/${baseNameOf plugin}") plugins;
pluginCmds = builtins.concatStringsSep "\n" pluginCmdsLines;
extraBuildPhase = rec {
clion = ''
sed "s|${ide}|$out|" -i $out/bin/.clion-wrapped
'';
goland = ''
sed "s|${ide}|$out|" -i $out/bin/.goland-wrapped
'';
};
in
''
cp -r ${ide} $out
chmod +w -R $out
IFS=' ' read -ra pluginArray <<< "$newPlugins"
for plugin in "''${pluginArray[@]}"
do
ln -s "$plugin" -t $out/${meta.mainProgram}/plugins/
done
sed "s|${ide.outPath}|$out|" -i $out/bin/${meta.mainProgram}
autoPatchelf $out/${meta.mainProgram}/bin
'' + (extraBuildPhase."${ide.meta.mainProgram}" or "");
};
}

View File

@ -0,0 +1,387 @@
{
"plugins": {
"164": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/164/275091/IdeaVim-2.1.0.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip"
},
"name": "ideavim"
},
"631": {
"compatible": [
"idea-ultimate"
],
"builds": {
"231.9161.38": "https://plugins.jetbrains.com/files/631/350772/python-231.9161.38.zip"
},
"name": "python"
},
"6954": {
"compatible": [
"idea-community",
"idea-ultimate"
],
"builds": {
"231.9161.38": null
},
"name": "kotlin"
},
"6981": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": null,
"231.9011.31": "https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/6981/351503/ini-231.9161.47.zip"
},
"name": "ini"
},
"7322": {
"compatible": [
"datagrip",
"goland",
"idea-community",
"rider"
],
"builds": {
"231.9011.34": "https://plugins.jetbrains.com/files/7322/326457/python-ce-231.8770.65.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/7322/326457/python-ce-231.8770.65.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/7322/326457/python-ce-231.8770.65.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/7322/326457/python-ce-231.8770.65.zip"
},
"name": "python-community-edition"
},
"8182": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/8182/329558/intellij-rust-0.4.194.5382-223.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip"
},
"name": "rust"
},
"8182-beta": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/8182/330017/intellij-rust-0.4.194.5384-223-beta.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip"
},
"name": "rust-beta"
},
"8554": {
"compatible": [
"goland",
"idea-community",
"idea-ultimate",
"pycharm-community",
"pycharm-professional",
"ruby-mine",
"webstorm"
],
"builds": {
"231.9011.34": "https://plugins.jetbrains.com/files/8554/326468/featuresTrainer-231.8770.66.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/8554/326468/featuresTrainer-231.8770.66.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/8554/326468/featuresTrainer-231.8770.66.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/8554/326468/featuresTrainer-231.8770.66.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/8554/326468/featuresTrainer-231.8770.66.zip"
},
"name": "ide-features-trainer"
},
"8607": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip"
},
"name": "nixidea"
},
"9568": {
"compatible": [
"idea-ultimate"
],
"builds": {
"231.9161.38": "https://plugins.jetbrains.com/files/9568/343928/go-plugin-231.9161.14.zip"
},
"name": "go"
},
"10037": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/10037/332761/CSVEditor-3.2.0-223.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip"
},
"name": "csv-editor"
},
"12559": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/12559/257029/keymap-eclipse-223.7571.125.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip"
},
"name": "eclipse-keymap"
},
"13017": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/13017/257030/keymap-visualStudio-223.7571.125.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip"
},
"name": "visual-studio-keymap"
},
"14059": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9011.31": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9011.34": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9011.35": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9011.38": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9011.39": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9011.41": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar",
"231.9161.38": "https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar"
},
"name": "darcula-pitch-black"
},
"17718": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip"
},
"name": "github-copilot"
},
"18444": {
"compatible": [
"clion",
"datagrip",
"goland",
"idea-community",
"idea-ultimate",
"mps",
"phpstorm",
"pycharm-community",
"pycharm-professional",
"rider",
"ruby-mine",
"webstorm"
],
"builds": {
"223.8836.1185": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9011.31": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9011.34": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9011.35": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9011.38": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9011.39": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9011.41": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip",
"231.9161.38": "https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip"
},
"name": "netbeans-6-5-keymap"
}
},
"files": {
"https://plugins.jetbrains.com/files/10037/332760/CSVEditor-3.2.0-231.zip": "sha256-TZs6ColXUvrp2jw74h8M+6UhSqi9u/gDXlzTNhIt+oo=",
"https://plugins.jetbrains.com/files/10037/332761/CSVEditor-3.2.0-223.zip": "sha256-m52ukvz7pqOBPoyNr5l58glD19wXluguZVQKYajCYN8=",
"https://plugins.jetbrains.com/files/12559/257029/keymap-eclipse-223.7571.125.zip": "sha256-0hMn8Qt+xJjB9HnYz7OMw8xmI0FxDFy+lYfXHURhTKY=",
"https://plugins.jetbrains.com/files/12559/307825/keymap-eclipse-231.8109.91.zip": "sha256-8jUsRK4evNMzjuWQIjIMrvQ0sIXPoY1C/buu1nod5X8=",
"https://plugins.jetbrains.com/files/13017/257030/keymap-visualStudio-223.7571.125.zip": "sha256-YiJALivO1a+I4bCtZEv68PZ21Vydk5UW6gAgErj28DQ=",
"https://plugins.jetbrains.com/files/13017/307831/keymap-visualStudio-231.8109.91.zip": "sha256-b/SFrQX+pIV/R/Dd72EjqbbRgaSgppe3kv4aSxWr//Y=",
"https://plugins.jetbrains.com/files/14059/82616/darcula-pitch-black.jar": "sha256-eXInfAqY3yEZRXCAuv3KGldM1pNKEioNwPB0rIGgJFw=",
"https://plugins.jetbrains.com/files/164/275091/IdeaVim-2.1.0.zip": "sha256-2dM/r79XT+1MHDeRAUnZw6WO3dmw7MZfx9alHmBqMk0=",
"https://plugins.jetbrains.com/files/164/347833/IdeaVim-2.3.0-signed.zip": "sha256-K4HQXGdvFhs7X25Kw+pljep/lqJ9lwewnGSEvbnNetE=",
"https://plugins.jetbrains.com/files/17718/351707/github-copilot-intellij-1.2.9.2684.zip": "sha256-imh+3U+HWM9jia2HfRXInHl1pfw+T6D4ls3DGqbqbsw=",
"https://plugins.jetbrains.com/files/18444/165585/NetBeans6.5Keymap.zip": "sha256-KrzZTKZMQqoEMw+vDUv2jjs0EX0leaPBkU8H/ecq/oI=",
"https://plugins.jetbrains.com/files/631/350772/python-231.9161.38.zip": "sha256-vQfCR7WMrknRminRcd0AoGrxofAf5dcD8/aXLwWBo3k=",
"https://plugins.jetbrains.com/files/6981/336613/ini-231.9011.41.zip": "sha256-PtBDN+FNA518HaewPIr9pq5S3Z9RGSCA2NT+YnZ0l8c=",
"https://plugins.jetbrains.com/files/6981/351503/ini-231.9161.47.zip": "sha256-oAgTPyTnfqEKjaGcK50k9O16hDY+A4lfL2l9IpGKyCY=",
"https://plugins.jetbrains.com/files/7322/326457/python-ce-231.8770.65.zip": "sha256-LjHpwdBtC4C9KXrHQ+EvmGL1A+Tfbqzc17Kf80SP/lE=",
"https://plugins.jetbrains.com/files/8182/329558/intellij-rust-0.4.194.5382-223.zip": "sha256-AgaKH4ZaxLhumk1P9BVJGpvluKnpYIulCDIRQpaWlKA=",
"https://plugins.jetbrains.com/files/8182/330017/intellij-rust-0.4.194.5384-223-beta.zip": "sha256-+iYFqpc4Qn+KGWX3IXpM1sHQV+IPYJZBLFNo0kdx8oE=",
"https://plugins.jetbrains.com/files/8182/346574/intellij-rust-0.4.196.5423-231.zip": "sha256-dyJc5O06QLNLQ/D1tX9cGRLqalPX4prcRXz0WcD2RU4=",
"https://plugins.jetbrains.com/files/8182/351209/intellij-rust-0.4.197.5433-231-beta.zip": "sha256-P/8tr5n8yVFFTLB4ML2tobJqeuxHWkkEargMjVpnF2Y=",
"https://plugins.jetbrains.com/files/8554/326468/featuresTrainer-231.8770.66.zip": "sha256-N5woM9O9y+UequeWcjCLL93rjHDW0Tnvh8h3iLrwmjk=",
"https://plugins.jetbrains.com/files/8607/318851/NixIDEA-0.4.0.9.zip": "sha256-byShwSfnAG8kXhoNu7CfOwvy4Viav784NT0UmzKY6hQ=",
"https://plugins.jetbrains.com/files/9568/343928/go-plugin-231.9161.14.zip": "sha256-67SuJKJZEzEYojsL33zvtWArvADkkjd643cVb4s9EUk="
}
}

View File

@ -0,0 +1,63 @@
{ delve, autoPatchelfHook, stdenv, lib, glibc, gcc-unwrapped }:
# This is a list of plugins that need special treatment. For example, the go plugin (id is 9568) comes with delve, a
# debugger, but that needs various linking fixes. The changes here replace it with the system one.
{
"631" = {
# Python
nativeBuildInputs = [ autoPatchelfHook ];
buildInputs = [ stdenv.cc.cc.lib ];
};
"7322" = {
# Python community edition
nativeBuildInputs = [ autoPatchelfHook ];
buildInputs = [ stdenv.cc.cc.lib ];
};
"8182" = {
# Rust
nativeBuildInputs = [ autoPatchelfHook ];
buildInputs = [ stdenv.cc.cc.lib ];
buildPhase = ''
runHook preBuild
chmod +x -R bin
runHook postBuild
'';
};
"9568" = {
# Go
buildInputs = [ delve ];
buildPhase =
let
arch = (if stdenv.isLinux then "linux" else "mac") + (if stdenv.isAarch64 then "arm" else "");
in ''
runHook preBuild
ln -sf ${delve}/bin/dlv lib/dlv/${arch}/dlv
runHook postBuild
'';
};
"17718" = {
# Github Copilot
# Modified version of https://github.com/ktor/nixos/commit/35f4071faab696b2a4d86643726c9dd3e4293964
buildPhase = ''
agent="copilot-agent/bin/copilot-agent-linux"
orig_size=$(stat --printf=%s $agent)
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $agent
patchelf --set-rpath ${lib.makeLibraryPath [glibc gcc-unwrapped]} $agent
chmod +x $agent
new_size=$(stat --printf=%s $agent)
var_skip=20
var_select=22
shift_by=$(($new_size-$orig_size))
function fix_offset {
# $1 = name of variable to adjust
location=$(grep -obUam1 "$1" $agent | cut -d: -f1)
location=$(expr $location + $var_skip)
value=$(dd if=$agent iflag=count_bytes,skip_bytes skip=$location \
bs=1 count=$var_select status=none)
value=$(expr $shift_by + $value)
echo -n $value | dd of=$agent bs=1 seek=$location conv=notrunc
}
fix_offset PAYLOAD_POSITION
fix_offset PRELUDE_POSITION
'';
};
}

View File

@ -0,0 +1,385 @@
#! /usr/bin/env nix-shell
#! nix-shell -i python3 -p python3 python3.pkgs.requests nix.out
from json import load, dumps
from pathlib import Path
from requests import get
from subprocess import run
from argparse import ArgumentParser
# Token priorities for version checking
# From https://github.com/JetBrains/intellij-community/blob/94f40c5d77f60af16550f6f78d481aaff8deaca4/platform/util-rt/src/com/intellij/util/text/VersionComparatorUtil.java#L50
TOKENS = {
"snap": 10, "snapshot": 10,
"m": 20,
"eap": 25, "pre": 25, "preview": 25,
"alpha": 30, "a": 30,
"beta": 40, "betta": 40, "b": 40,
"rc": 50,
"sp": 70,
"rel": 80, "release": 80, "r": 80, "final": 80
}
SNAPSHOT_VALUE = 99999
PLUGINS_FILE = Path(__file__).parent.joinpath("plugins.json").resolve()
IDES_FILE = Path(__file__).parent.joinpath("../versions.json").resolve()
# The plugin compatibility system uses a different naming scheme to the ide update system.
# These dicts convert between them
FRIENDLY_TO_PLUGIN = {
"clion": "CLION",
"datagrip": "DBE",
"goland": "GOLAND",
"idea-community": "IDEA_COMMUNITY",
"idea-ultimate": "IDEA",
"mps": "MPS",
"phpstorm": "PHPSTORM",
"pycharm-community": "PYCHARM_COMMUNITY",
"pycharm-professional": "PYCHARM",
"rider": "RIDER",
"ruby-mine": "RUBYMINE",
"webstorm": "WEBSTORM"
}
PLUGIN_TO_FRIENDLY = {j: i for i, j in FRIENDLY_TO_PLUGIN.items()}
def tokenize_stream(stream):
for item in stream:
if item in TOKENS:
yield TOKENS[item], 0
elif item.isalpha():
for char in item:
yield 90, ord(char) - 96
elif item.isdigit():
yield 100, int(item)
def split(version_string: str):
prev_type = None
block = ""
for char in version_string:
if char.isdigit():
cur_type = "number"
elif char.isalpha():
cur_type = "letter"
else:
cur_type = "other"
if cur_type != prev_type and block:
yield block.lower()
block = ""
if cur_type in ("letter", "number"):
block += char
prev_type = cur_type
if block:
yield block
def tokenize_string(version_string: str):
return list(tokenize_stream(split(version_string)))
def pick_newest(ver1: str, ver2: str) -> str:
if ver1 is None or ver1 == ver2:
return ver2
if ver2 is None:
return ver1
presort = [tokenize_string(ver1), tokenize_string(ver2)]
postsort = sorted(presort)
if presort == postsort:
return ver2
else:
return ver1
def is_build_older(ver1: str, ver2: str) -> int:
ver1 = [int(i) for i in ver1.replace("*", str(SNAPSHOT_VALUE)).split(".")]
ver2 = [int(i) for i in ver2.replace("*", str(SNAPSHOT_VALUE)).split(".")]
for i in range(min(len(ver1), len(ver2))):
if ver1[i] == ver2[i] and ver1[i] == SNAPSHOT_VALUE:
return 0
if ver1[i] == SNAPSHOT_VALUE:
return 1
if ver2[i] == SNAPSHOT_VALUE:
return -1
result = ver1[i] - ver2[i]
if result != 0:
return result
return len(ver1) - len(ver2)
def is_compatible(build, since, until) -> bool:
return (not since or is_build_older(since, build) < 0) and (not until or 0 < is_build_older(until, build))
def get_newest_compatible(pid: str, build: str, plugin_infos: dict, quiet: bool) -> [None, str]:
newest_ver = None
newest_index = None
for index, info in enumerate(plugin_infos):
if pick_newest(newest_ver, info["version"]) != newest_ver and \
is_compatible(build, info["since"], info["until"]):
newest_ver = info["version"]
newest_index = index
if newest_ver is not None:
return "https://plugins.jetbrains.com/files/" + plugin_infos[newest_index]["file"]
else:
if not quiet:
print(f"WARNING: Could not find version of plugin {pid} compatible with build {build}")
return None
def flatten(main_list: list[list]) -> list:
return [item for sublist in main_list for item in sublist]
def get_compatible_ides(pid: str) -> list[str]:
int_id = pid.split("-", 1)[0]
url = f"https://plugins.jetbrains.com/api/plugins/{int_id}/compatible-products"
result = get(url).json()
return sorted([PLUGIN_TO_FRIENDLY[i] for i in result if i in PLUGIN_TO_FRIENDLY])
def id_to_name(pid: str, channel="") -> str:
channel_ext = "-" + channel if channel else ""
resp = get("https://plugins.jetbrains.com/api/plugins/" + pid).json()
return resp["link"].split("-", 1)[1] + channel_ext
def sort_dict(to_sort: dict) -> dict:
return {i: to_sort[i] for i in sorted(to_sort.keys())}
def make_name_mapping(infos: dict) -> dict[str, str]:
return sort_dict({i: id_to_name(*i.split("-", 1)) for i in infos.keys()})
def make_plugin_files(plugin_infos: dict, ide_versions: dict, quiet: bool, extra_builds: list[str]) -> dict:
result = {}
names = make_name_mapping(plugin_infos)
for pid in plugin_infos:
plugin_versions = {
"compatible": get_compatible_ides(pid),
"builds": {},
"name": names[pid]
}
relevant_builds = [builds for ide, builds in ide_versions.items() if ide in plugin_versions["compatible"]] + [extra_builds]
relevant_builds = sorted(list(set(flatten(relevant_builds)))) # Flatten, remove duplicates and sort
for build in relevant_builds:
plugin_versions["builds"][build] = get_newest_compatible(pid, build, plugin_infos[pid], quiet)
result[pid] = plugin_versions
return result
def get_old_file_hashes() -> dict[str, str]:
return load(open(PLUGINS_FILE))["files"]
def get_hash(url):
print(f"Downloading {url}")
args = ["nix-prefetch-url", url, "--print-path"]
if url.endswith(".zip"):
args.append("--unpack")
else:
args.append("--executable")
path_process = run(args, capture_output=True)
path = path_process.stdout.decode().split("\n")[1]
result = run(["nix", "--extra-experimental-features", "nix-command", "hash", "path", path], capture_output=True)
result_contents = result.stdout.decode()[:-1]
if not result_contents:
raise RuntimeError(result.stderr.decode())
return result_contents
def print_file_diff(old, new):
added = new.copy()
removed = old.copy()
to_delete = []
for file in added:
if file in removed:
to_delete.append(file)
for file in to_delete:
added.remove(file)
removed.remove(file)
if removed:
print("\nRemoved:")
for file in removed:
print(" - " + file)
print()
if added:
print("\nAdded:")
for file in added:
print(" + " + file)
print()
def get_file_hashes(file_list: list[str], refetch_all: bool) -> dict[str, str]:
old = {} if refetch_all else get_old_file_hashes()
print_file_diff(list(old.keys()), file_list)
file_hashes = {}
for file in sorted(file_list):
if file in old:
file_hashes[file] = old[file]
else:
file_hashes[file] = get_hash(file)
return file_hashes
def get_args() -> tuple[list[str], list[str], bool, bool, bool, list[str]]:
parser = ArgumentParser(
description="Add/remove/update entries in plugins.json",
epilog="To update all plugins, run with no args.\n"
"To add a version of a plugin from a different channel, append -[channel] to the id.\n"
"The id of a plugin is the number before the name in the address of its page on https://plugins.jetbrains.com/"
)
parser.add_argument("-r", "--refetch-all", action="store_true",
help="don't use previously collected hashes, redownload all")
parser.add_argument("-l", "--list", action="store_true",
help="list plugin ids")
parser.add_argument("-q", "--quiet", action="store_true",
help="suppress warnings about not being able to find compatible plugin versions")
parser.add_argument("-w", "--with-build", action="append", default=[],
help="append [builds] to the list of builds to fetch plugin versions for")
sub = parser.add_subparsers(dest="action")
sub.add_parser("add").add_argument("ids", type=str, nargs="+", help="plugin(s) to add")
sub.add_parser("remove").add_argument("ids", type=str, nargs="+", help="plugin(s) to remove")
args = parser.parse_args()
add = []
remove = []
if args.action == "add":
add = args.ids
elif args.action == "remove":
remove = args.ids
return add, remove, args.refetch_all, args.list, args.quiet, args.with_build
def sort_ids(ids: list[str]) -> list[str]:
sortable_ids = []
for pid in ids:
if "-" in pid:
split_pid = pid.split("-", 1)
sortable_ids.append((int(split_pid[0]), split_pid[1]))
else:
sortable_ids.append((int(pid), ""))
sorted_ids = sorted(sortable_ids)
return [(f"{i}-{j}" if j else str(i)) for i, j in sorted_ids]
def get_plugin_ids(add: list[str], remove: list[str]) -> list[str]:
ids = list(load(open(PLUGINS_FILE))["plugins"].keys())
for pid in add:
if pid in ids:
raise ValueError(f"ID {pid} already in JSON file")
ids.append(pid)
for pid in remove:
try:
ids.remove(pid)
except ValueError:
raise ValueError(f"ID {pid} not in JSON file")
return sort_ids(ids)
def get_plugin_info(pid: str, channel: str) -> dict:
url = f"https://plugins.jetbrains.com/api/plugins/{pid}/updates?channel={channel}"
resp = get(url)
decoded = resp.json()
if resp.status_code != 200:
print(f"Server gave non-200 code {resp.status_code} with message " + decoded["message"])
exit(1)
return decoded
def ids_to_infos(ids: list[str]) -> dict:
result = {}
for pid in ids:
if "-" in pid:
int_id, channel = pid.split("-", 1)
else:
channel = ""
int_id = pid
result[pid] = get_plugin_info(int_id, channel)
return result
def get_ide_versions() -> dict:
ide_data = load(open(IDES_FILE))
result = {}
for platform in ide_data:
for product in ide_data[platform]:
version = ide_data[platform][product]["build_number"]
if product not in result:
result[product] = [version]
elif version not in result[product]:
result[product].append(version)
# Gateway isn't a normal IDE, so it doesn't use the same plugins system
del result["gateway"]
return result
def get_file_names(plugins: dict[str, dict]) -> list[str]:
result = []
for plugin_info in plugins.values():
for url in plugin_info["builds"].values():
if url is not None:
result.append(url)
return list(set(result))
def dump(obj, file):
file.write(dumps(obj, indent=2))
file.write("\n")
def write_result(to_write):
dump(to_write, open(PLUGINS_FILE, "w"))
def main():
add, remove, refetch_all, list_ids, quiet, extra_builds = get_args()
result = {}
print("Fetching plugin info")
ids = get_plugin_ids(add, remove)
if list_ids:
print(*ids)
plugin_infos = ids_to_infos(ids)
print("Working out which plugins need which files")
ide_versions = get_ide_versions()
result["plugins"] = make_plugin_files(plugin_infos, ide_versions, quiet, extra_builds)
print("Getting file hashes")
file_list = get_file_names(result["plugins"])
result["files"] = get_file_hashes(file_list, refetch_all)
write_result(result)
if __name__ == '__main__':
main()