diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index c71741ac900b..332d1582ad2b 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -1950,6 +1950,12 @@ githubId = 543423; name = "Alex Wied"; }; + cfhammill = { + email = "cfhammill@gmail.com"; + github = "cfhammill"; + githubId = 7467038; + name = "Chris Hammill"; + }; cfouche = { email = "chaddai.fouche@gmail.com"; github = "Chaddai"; diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml index c453d9762d11..2d8086aba774 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml @@ -165,6 +165,14 @@ services.timetagger. + + + rstudio-server, + a browser-based version of the RStudio IDE for the R + programming language. Available as + services.rstudio-server. + +
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md index 88ceb902bb60..f83573a3a860 100644 --- a/nixos/doc/manual/release-notes/rl-2205.section.md +++ b/nixos/doc/manual/release-notes/rl-2205.section.md @@ -50,6 +50,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [timetagger](https://timetagger.app), an open source time-tracker with an intuitive user experience and powerful reporting. [services.timetagger](options.html#opt-services.timetagger.enable). +- [rstudio-server](https://www.rstudio.com/products/rstudio/#rstudio-server), a browser-based version of the RStudio IDE for the R programming language. Available as [services.rstudio-server](options.html#opt-services.rstudio-server.enable). + ## Backward Incompatibilities {#sec-release-22.05-incompatibilities} - `pkgs.ghc` now refers to `pkgs.targetPackages.haskellPackages.ghc`. diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index cad3ad018574..9d620084308b 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -353,6 +353,7 @@ in distcc = 321; webdav = 322; pipewire = 323; + rstudio-server = 324; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -660,6 +661,7 @@ in distcc = 321; webdav = 322; pipewire = 323; + rstudio-server = 324; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 697ed4fad723..c931f6b32f58 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -394,6 +394,7 @@ ./services/development/hoogle.nix ./services/development/jupyter/default.nix ./services/development/jupyterhub/default.nix + ./services/development/rstudio-server/default.nix ./services/development/lorri.nix ./services/display-managers/greetd.nix ./services/editors/emacs.nix diff --git a/nixos/modules/services/development/rstudio-server/default.nix b/nixos/modules/services/development/rstudio-server/default.nix new file mode 100644 index 000000000000..cd903c7e55bf --- /dev/null +++ b/nixos/modules/services/development/rstudio-server/default.nix @@ -0,0 +1,107 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.rstudio-server; + + rserver-conf = builtins.toFile "rserver.conf" '' + server-working-dir=${cfg.serverWorkingDir} + www-address=${cfg.listenAddr} + ${cfg.rserverExtraConfig} + ''; + + rsession-conf = builtins.toFile "rsession.conf" '' + ${cfg.rsessionExtraConfig} + ''; + +in +{ + meta.maintainers = with maintainers; [ jbedo cfhammill ]; + + options.services.rstudio-server = { + enable = mkEnableOption "RStudio server"; + + serverWorkingDir = mkOption { + type = types.str; + default = "/var/lib/rstudio-server"; + description = '' + Default working directory for server (server-working-dir in rserver.conf). + ''; + }; + + listenAddr = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + Address to listen on (www-address in rserver.conf). + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.rstudio-server; + defaultText = literalExpression "pkgs.rstudio-server"; + example = literalExpression "pkgs.rstudioServerWrapper.override { packages = [ pkgs.rPackages.ggplot2 ]; }"; + description = '' + Rstudio server package to use. Can be set to rstudioServerWrapper to provide packages. + ''; + }; + + rserverExtraConfig = mkOption { + type = types.str; + default = ""; + description = '' + Extra contents for rserver.conf. + ''; + }; + + rsessionExtraConfig = mkOption { + type = types.str; + default = ""; + description = '' + Extra contents for resssion.conf. + ''; + }; + + }; + + config = mkIf cfg.enable + { + systemd.services.rstudio-server = { + description = "Rstudio server"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ rserver-conf rsession-conf ]; + + serviceConfig = { + Restart = "on-failure"; + Type = "forking"; + ExecStart = "${cfg.package}/bin/rserver"; + StateDirectory = "rstudio-server"; + RuntimeDirectory = "rstudio-server"; + }; + }; + + environment.etc = { + "rstudio/rserver.conf".source = rserver-conf; + "rstudio/rsession.conf".source = rsession-conf; + "pam.d/rstudio".source = "/etc/pam.d/login"; + }; + environment.systemPackages = [ cfg.package ]; + + users = { + users.rstudio-server = { + uid = config.ids.uids.rstudio-server; + description = "rstudio-server"; + group = "rstudio-server"; + }; + groups.rstudio-server = { + gid = config.ids.gids.rstudio-server; + }; + }; + + }; +} diff --git a/nixos/tests/rstudio-server.nix b/nixos/tests/rstudio-server.nix new file mode 100644 index 000000000000..c7ac7670fbd4 --- /dev/null +++ b/nixos/tests/rstudio-server.nix @@ -0,0 +1,30 @@ +import ./make-test-python.nix ({ pkgs, ... }: + { + name = "rstudio-server-test"; + meta.maintainers = with pkgs.lib.maintainers; [ jbedo cfhammill ]; + + nodes.machine = { config, lib, pkgs, ... }: { + services.rstudio-server.enable = true; + }; + + nodes.customPackageMachine = { config, lib, pkgs, ... }: { + services.rstudio-server = { + enable = true; + package = pkgs.rstudioServerWrapper.override { packages = [ pkgs.rPackages.ggplot2 ]; }; + }; + }; + + users.testuser = { + uid = 1000; + group = "testgroup"; + }; + groups.testgroup.gid = 1000; + + testScript = '' + machine.wait_for_unit("rstudio-server.service") + machine.succeed("curl -f -vvv -s http://127.0.0.1:8787") + + customPackageMachine.wait_for_unit("rstudio-server.service") + customPackageMachine.succeed("curl -f -vvv -s http://127.0.0.1:8787") + ''; + }) diff --git a/pkgs/applications/editors/rstudio/default.nix b/pkgs/applications/editors/rstudio/default.nix index 62c3a93f76b4..d703198fa775 100644 --- a/pkgs/applications/editors/rstudio/default.nix +++ b/pkgs/applications/editors/rstudio/default.nix @@ -1,4 +1,5 @@ { lib +, stdenv , mkDerivation , fetchurl , fetchpatch @@ -30,6 +31,9 @@ , nodejs , mkYarnModules , qmake +, server ? false # build server version +, sqlite +, pam }: let @@ -61,149 +65,165 @@ let panmirrorModules = mkYarnModules { inherit pname version; packageJSON = ./package.json; - yarnLock = ./yarn.lock; + yarnLock = ./yarn.lock; yarnNix = ./yarndeps.nix; }; + description = "Set of integrated tools for the R language"; in -mkDerivation rec { - inherit pname version src RSTUDIO_VERSION_MAJOR RSTUDIO_VERSION_MINOR RSTUDIO_VERSION_PATCH; +(if server then stdenv.mkDerivation else mkDerivation) + (rec { + inherit pname version src RSTUDIO_VERSION_MAJOR RSTUDIO_VERSION_MINOR RSTUDIO_VERSION_PATCH; - nativeBuildInputs = [ - cmake - unzip - ant - jdk - makeWrapper - pandoc - nodejs - copyDesktopItems - ]; + nativeBuildInputs = [ + cmake + unzip + ant + jdk + makeWrapper + pandoc + nodejs + ] ++ lib.optional (!server) [ + copyDesktopItems + ]; - buildInputs = [ - boost - zlib - openssl - R - qtbase - qtxmlpatterns - qtsensors - qtwebengine - qtwebchannel - libuuid - libyamlcpp - soci - postgresql - ]; + buildInputs = [ + boost + zlib + openssl + R + libuuid + libyamlcpp + soci + postgresql + ] ++ (if server then [ + sqlite.dev + pam + ] else [ + qtbase + qtxmlpatterns + qtsensors + qtwebengine + qtwebchannel + ]); - cmakeFlags = [ - "-DRSTUDIO_TARGET=Desktop" - "-DCMAKE_BUILD_TYPE=Release" - "-DQT_QMAKE_EXECUTABLE=${qmake}/bin/qmake" - "-DRSTUDIO_USE_SYSTEM_SOCI=ON" - "-DRSTUDIO_USE_SYSTEM_BOOST=ON" - "-DRSTUDIO_USE_SYSTEM_YAML_CPP=ON" - "-DPANDOC_VERSION=${pandoc.version}" - "-DCMAKE_INSTALL_PREFIX=${placeholder "out"}/lib/rstudio" - ]; + cmakeFlags = [ + "-DRSTUDIO_TARGET=${if server then "Server" else "Desktop"}" + "-DCMAKE_BUILD_TYPE=Release" + "-DRSTUDIO_USE_SYSTEM_SOCI=ON" + "-DRSTUDIO_USE_SYSTEM_BOOST=ON" + "-DRSTUDIO_USE_SYSTEM_YAML_CPP=ON" + "-DPANDOC_VERSION=${pandoc.version}" + "-DCMAKE_INSTALL_PREFIX=${placeholder "out"}/lib/rstudio" + ] ++ lib.optional (!server) [ + "-DQT_QMAKE_EXECUTABLE=${qmake}/bin/qmake" + ]; - # Hack RStudio to only use the input R and provided libclang. - patches = [ - ./r-location.patch - ./clang-location.patch - # postFetch doesn't work with this | error: unexpected end-of-file - # replacing /usr/bin/node is done in postPatch - # https://src.fedoraproject.org/rpms/rstudio/tree/rawhide - (fetchpatch { - name = "system-node.patch"; - url = "https://src.fedoraproject.org/rpms/rstudio/raw/5bda2e290c9e72305582f2011040938d3e356906/f/0004-use-system-node.patch"; - sha256 = "sha256-P1Y07RB/ceFNa749nyBUWSE41eiiZgt43zVcmahvfZM="; - }) - ]; + # Hack RStudio to only use the input R and provided libclang. + patches = [ + ./r-location.patch + ./clang-location.patch + # postFetch doesn't work with this | error: unexpected end-of-file + # replacing /usr/bin/node is done in postPatch + # https://src.fedoraproject.org/rpms/rstudio/tree/rawhide + (fetchpatch { + name = "system-node.patch"; + url = "https://src.fedoraproject.org/rpms/rstudio/raw/5bda2e290c9e72305582f2011040938d3e356906/f/0004-use-system-node.patch"; + sha256 = "sha256-P1Y07RB/ceFNa749nyBUWSE41eiiZgt43zVcmahvfZM="; + }) + ]; - postPatch = '' - substituteInPlace src/cpp/core/r_util/REnvironmentPosix.cpp --replace '@R@' ${R} + postPatch = '' + substituteInPlace src/cpp/core/r_util/REnvironmentPosix.cpp --replace '@R@' ${R} - substituteInPlace src/cpp/CMakeLists.txt \ - --replace 'SOCI_LIBRARY_DIR "/usr/lib"' 'SOCI_LIBRARY_DIR "${soci}/lib"' + substituteInPlace src/cpp/CMakeLists.txt \ + --replace 'SOCI_LIBRARY_DIR "/usr/lib"' 'SOCI_LIBRARY_DIR "${soci}/lib"' - substituteInPlace src/gwt/build.xml \ - --replace '/usr/bin/node' '${nodejs}/bin/node' + substituteInPlace src/gwt/build.xml \ + --replace '/usr/bin/node' '${nodejs}/bin/node' - substituteInPlace src/cpp/core/libclang/LibClang.cpp \ - --replace '@libclang@' ${llvmPackages.libclang.lib} \ - --replace '@libclang.so@' ${llvmPackages.libclang.lib}/lib/libclang.so + substituteInPlace src/cpp/core/libclang/LibClang.cpp \ + --replace '@libclang@' ${llvmPackages.libclang.lib} \ + --replace '@libclang.so@' ${llvmPackages.libclang.lib}/lib/libclang.so - substituteInPlace src/cpp/session/include/session/SessionConstants.hpp \ - --replace "bin/pandoc" "${pandoc}/bin/pandoc" - ''; + substituteInPlace src/cpp/session/include/session/SessionConstants.hpp \ + --replace "bin/pandoc" "${pandoc}/bin/pandoc" + ''; - hunspellDictionaries = with lib; filter isDerivation (unique (attrValues hunspellDicts)); - # These dicts contain identically-named dict files, so we only keep the - # -large versions in case of clashes - largeDicts = with lib; filter (d: hasInfix "-large-wordlist" d) hunspellDictionaries; - otherDicts = with lib; filter - (d: !(hasAttr "dictFileName" d && - elem d.dictFileName (map (d: d.dictFileName) largeDicts))) - hunspellDictionaries; - dictionaries = largeDicts ++ otherDicts; + hunspellDictionaries = with lib; filter isDerivation (unique (attrValues hunspellDicts)); + # These dicts contain identically-named dict files, so we only keep the + # -large versions in case of clashes + largeDicts = with lib; filter (d: hasInfix "-large-wordlist" d) hunspellDictionaries; + otherDicts = with lib; filter + (d: !(hasAttr "dictFileName" d && + elem d.dictFileName (map (d: d.dictFileName) largeDicts))) + hunspellDictionaries; + dictionaries = largeDicts ++ otherDicts; - preConfigure = '' - mkdir dependencies/dictionaries - for dict in ${builtins.concatStringsSep " " dictionaries}; do - for i in "$dict/share/hunspell/"*; do - ln -s $i dependencies/dictionaries/ + preConfigure = '' + mkdir dependencies/dictionaries + for dict in ${builtins.concatStringsSep " " dictionaries}; do + for i in "$dict/share/hunspell/"*; do + ln -s $i dependencies/dictionaries/ + done done - done - unzip -q ${mathJaxSrc} -d dependencies/mathjax-27 + unzip -q ${mathJaxSrc} -d dependencies/mathjax-27 - mkdir -p dependencies/pandoc/${pandoc.version} - cp ${pandoc}/bin/pandoc dependencies/pandoc/${pandoc.version}/pandoc + mkdir -p dependencies/pandoc/${pandoc.version} + cp ${pandoc}/bin/pandoc dependencies/pandoc/${pandoc.version}/pandoc - cp -r ${rsconnectSrc} dependencies/rsconnect - ( cd dependencies && ${R}/bin/R CMD build -d --no-build-vignettes rsconnect ) + cp -r ${rsconnectSrc} dependencies/rsconnect + ( cd dependencies && ${R}/bin/R CMD build -d --no-build-vignettes rsconnect ) - cp -r "${panmirrorModules}" src/gwt/panmirror/src/editor/node_modules - ''; + cp -r "${panmirrorModules}" src/gwt/panmirror/src/editor/node_modules + ''; - postInstall = '' - mkdir -p $out/share/icons/hicolor/48x48/apps $out/bin - ln $out/lib/rstudio/rstudio.png $out/share/icons/hicolor/48x48/apps + postInstall = '' + mkdir -p $out/bin $out/share - for f in {diagnostics,rpostback,rstudio}; do - ln -s $out/lib/rstudio/bin/$f $out/bin - done + ${lib.optionalString (!server) '' + mkdir -p $out/share/icons/hicolor/48x48/apps + ln $out/lib/rstudio/rstudio.png $out/share/icons/hicolor/48x48/apps + ''} - for f in .gitignore .Rbuildignore LICENSE README; do - find . -name $f -delete - done - rm -r $out/lib/rstudio/{INSTALL,COPYING,NOTICE,README.md,SOURCE,VERSION} - rm -r $out/lib/rstudio/bin/{pandoc/pandoc,pandoc} - ''; + for f in {${if server + then "crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server" + else "diagnostics,rpostback,rstudio"}}; do + ln -s $out/lib/rstudio/bin/$f $out/bin + done - qtWrapperArgs = [ - "--suffix PATH : ${lib.makeBinPath [ gnumake ]}" - ]; + for f in .gitignore .Rbuildignore LICENSE README; do + find . -name $f -delete + done + rm -r $out/lib/rstudio/{INSTALL,COPYING,NOTICE,README.md,SOURCE,VERSION} + rm -r $out/lib/rstudio/bin/{pandoc/pandoc,pandoc} + ''; - desktopItems = [ - (makeDesktopItem { - name = "${pname}"; - exec = "rstudio %F"; - icon = "rstudio"; - desktopName = "RStudio"; - genericName = "IDE"; - comment = meta.description; - categories = "Development;"; - mimeType = "text/x-r-source;text/x-r;text/x-R;text/x-r-doc;text/x-r-sweave;text/x-r-markdown;text/x-r-html;text/x-r-presentation;application/x-r-data;application/x-r-project;text/x-r-history;text/x-r-profile;text/x-tex;text/x-markdown;text/html;text/css;text/javascript;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;"; - }) - ]; + meta = with lib; { + inherit description; + homepage = "https://www.rstudio.com/"; + license = licenses.agpl3Only; + maintainers = with maintainers; [ ciil cfhammill ]; + platforms = platforms.linux; + }; - meta = with lib; { - description = "Set of integrated tools for the R language"; - homepage = "https://www.rstudio.com/"; - license = licenses.agpl3Only; - maintainers = with maintainers; [ ciil ]; - platforms = platforms.linux; - }; -} + passthru = { inherit server; }; + } // lib.optionalAttrs (!server) { + qtWrapperArgs = [ + "--suffix PATH : ${lib.makeBinPath [ gnumake ]}" + ]; + + desktopItems = [ + (makeDesktopItem { + name = pname; + exec = "rstudio %F"; + icon = "rstudio"; + desktopName = "RStudio"; + genericName = "IDE"; + comment = description; + categories = "Development;"; + mimeType = "text/x-r-source;text/x-r;text/x-R;text/x-r-doc;text/x-r-sweave;text/x-r-markdown;text/x-r-html;text/x-r-presentation;application/x-r-data;application/x-r-project;text/x-r-history;text/x-r-profile;text/x-tex;text/x-markdown;text/html;text/css;text/javascript;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;"; + }) + ]; + }) diff --git a/pkgs/development/r-modules/wrapper-rstudio.nix b/pkgs/development/r-modules/wrapper-rstudio.nix index 5eeac8fed30e..b20b61d6c58d 100644 --- a/pkgs/development/r-modules/wrapper-rstudio.nix +++ b/pkgs/development/r-modules/wrapper-rstudio.nix @@ -1,16 +1,23 @@ -{ lib, runCommand, R, rstudio, wrapQtAppsHook, recommendedPackages, packages, qtbase }: +{ lib +, runCommand +, R +, rstudio +, makeWrapper +, wrapQtAppsHook +, recommendedPackages +, packages +, fontconfig +}: -let - qtVersion = with lib.versions; "${major qtbase.version}.${minor qtbase.version}"; -in -runCommand (rstudio.name + "-wrapper") { +runCommand (rstudio.name + "-wrapper") +{ preferLocalBuild = true; allowSubstitutes = false; - nativeBuildInputs = [wrapQtAppsHook]; + nativeBuildInputs = [ (if rstudio.server then makeWrapper else wrapQtAppsHook) ]; dontWrapQtApps = true; - buildInputs = [R rstudio] ++ recommendedPackages ++ packages; + buildInputs = [ R rstudio ] ++ recommendedPackages ++ packages; # rWrapper points R to a specific set of packages by using a wrapper # (as in https://nixos.org/nixpkgs/manual/#r-packages) which sets @@ -22,14 +29,27 @@ runCommand (rstudio.name + "-wrapper") { # uses R_PROFILE_USER to load this code at startup in RStudio. fixLibsR = "fix_libs.R"; } -'' -mkdir $out -ln -s ${rstudio}/share $out -echo "# Autogenerated by wrapper-rstudio.nix from R_LIBS_SITE" > $out/$fixLibsR -echo -n ".libPaths(c(.libPaths(), \"" >> $out/$fixLibsR -echo -n $R_LIBS_SITE | sed -e 's/:/", "/g' >> $out/$fixLibsR -echo -n "\"))" >> $out/$fixLibsR -echo >> $out/$fixLibsR -makeQtWrapper ${rstudio}/bin/rstudio $out/bin/rstudio \ - --set R_PROFILE_USER $out/$fixLibsR -'' + ( + '' + mkdir -p $out/bin + ln -s ${rstudio}/share $out + echo "# Autogenerated by wrapper-rstudio.nix from R_LIBS_SITE" > $out/$fixLibsR + echo -n ".libPaths(c(.libPaths(), \"" >> $out/$fixLibsR + echo -n $R_LIBS_SITE | sed -e 's/:/", "/g' >> $out/$fixLibsR + echo -n "\"))" >> $out/$fixLibsR + echo >> $out/$fixLibsR + '' + + (if + rstudio.server then '' + makeWrapper ${rstudio}/bin/rsession $out/bin/rsession \ + --set R_PROFILE_USER $out/$fixLibsR --set FONTCONFIG_FILE ${fontconfig.out}/etc/fonts/fonts.conf + + makeWrapper ${rstudio}/bin/rserver $out/bin/rserver \ + --add-flags --rsession-path=$out/bin/rsession + '' + else + '' + makeQtWrapper ${rstudio}/bin/rstudio $out/bin/rstudio \ + --set R_PROFILE_USER $out/$fixLibsR + '') + ) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 0a69daeb26eb..f1ab10bd97b7 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -20754,6 +20754,8 @@ with pkgs; packages = []; }; + rstudioServerWrapper = rstudioWrapper.override { rstudio = rstudio-server; }; + rPackages = dontRecurseIntoAttrs (callPackage ../development/r-modules { overrides = (config.rPackageOverrides or (_: {})) pkgs; }); @@ -28384,6 +28386,8 @@ with pkgs; jdk = jdk8; }; + rstudio-server = rstudio.override { server = true; }; + rsync = callPackage ../applications/networking/sync/rsync (config.rsync or {}); rrsync = callPackage ../applications/networking/sync/rsync/rrsync.nix {};