diff options
-rw-r--r-- | lib/generators.nix | 53 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/gerrit.nix | 218 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/gerrit.nix | 56 | ||||
-rw-r--r-- | pkgs/applications/version-management/gerrit/default.nix | 18 |
6 files changed, 347 insertions, 0 deletions
diff --git a/lib/generators.nix b/lib/generators.nix index 240a19789b54b..efe6ea6031d33 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -126,6 +126,59 @@ rec { # map input to ini sections mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; + /* Generate a git-config file from an attrset. + * + * It has two major differences from the regular INI format: + * + * 1. values are indented with tabs + * 2. sections can have sub-sections + * + * generators.toGitINI { + * url."ssh://git@github.com/".insteadOf = "https://github.com"; + * user.name = "edolstra"; + * } + * + *> [url "ssh://git@github.com/"] + *> insteadOf = https://github.com/ + *> + *> [user] + *> name = edolstra + */ + toGitINI = attrs: + with builtins; + let + mkSectionName = name: + let + containsQuote = libStr.hasInfix ''"'' name; + sections = libStr.splitString "." name; + section = head sections; + subsections = tail sections; + subsection = concatStringsSep "." subsections; + in if containsQuote || subsections == [ ] then + name + else + ''${section} "${subsection}"''; + + # generation for multiple ini values + mkKeyValue = k: v: + let mkKeyValue = mkKeyValueDefault { } " = " k; + in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (lib.toList v)); + + # converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI + gitFlattenAttrs = let + recurse = path: value: + if isAttrs value then + lib.mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value + else if length path > 1 then { + ${concatStringsSep "." (lib.reverseList (tail path))}.${head path} = value; + } else { + ${head path} = value; + }; + in attrs: lib.foldl lib.recursiveUpdate { } (lib.flatten (recurse [ ] attrs)); + + toINI_ = toINI { inherit mkKeyValue mkSectionName; }; + in + toINI_ (gitFlattenAttrs attrs); /* Generates JSON from an arbitrary (non-function) value. * For more information see the documentation of the builtin. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 8c7f5e39a3bdc..fb8021e848eca 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -821,6 +821,7 @@ ./services/web-apps/documize.nix ./services/web-apps/dokuwiki.nix ./services/web-apps/frab.nix + ./services/web-apps/gerrit.nix ./services/web-apps/gotify-server.nix ./services/web-apps/grocy.nix ./services/web-apps/icingaweb2/icingaweb2.nix diff --git a/nixos/modules/services/web-apps/gerrit.nix b/nixos/modules/services/web-apps/gerrit.nix new file mode 100644 index 0000000000000..b184c0754d45b --- /dev/null +++ b/nixos/modules/services/web-apps/gerrit.nix @@ -0,0 +1,218 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.gerrit; + + # NixOS option type for git-like configs + gitIniType = with types; + let + primitiveType = either str (either bool int); + multipleType = either primitiveType (listOf primitiveType); + sectionType = lazyAttrsOf multipleType; + supersectionType = lazyAttrsOf (either multipleType sectionType); + in lazyAttrsOf supersectionType; + + gerritConfig = pkgs.writeText "gerrit.conf" ( + lib.generators.toGitINI cfg.settings + ); + + # Wrap the gerrit java with all the java options so it can be called + # like a normal CLI app + gerrit-cli = pkgs.writeShellScriptBin "gerrit" '' + set -euo pipefail + jvmOpts=( + ${lib.escapeShellArgs cfg.jvmOpts} + -Xmx${cfg.jvmHeapLimit} + ) + exec ${cfg.jvmPackage}/bin/java \ + "''${jvmOpts[@]}" \ + -jar ${cfg.package}/webapps/${cfg.package.name}.war \ + "$@" + ''; + + gerrit-plugins = pkgs.runCommand + "gerrit-plugins" + { + buildInputs = [ gerrit-cli ]; + } + '' + shopt -s nullglob + mkdir $out + + for name in ${toString cfg.builtinPlugins}; do + echo "Installing builtin plugin $name.jar" + gerrit cat plugins/$name.jar > $out/$name.jar + done + + for file in ${toString cfg.plugins}; do + name=$(echo "$file" | cut -d - -f 2-) + echo "Installing plugin $name" + ln -sf "$file" $out/$name + done + ''; +in +{ + options = { + services.gerrit = { + enable = mkEnableOption "Gerrit service"; + + package = mkOption { + type = types.package; + default = pkgs.gerrit; + description = "Gerrit package to use"; + }; + + jvmPackage = mkOption { + type = types.package; + default = pkgs.jre_headless; + defaultText = "pkgs.jre_headless"; + description = "Java Runtime Environment package to use"; + }; + + jvmOpts = mkOption { + type = types.listOf types.str; + default = [ + "-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance" + "-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance" + ]; + description = "A list of JVM options to start gerrit with."; + }; + + jvmHeapLimit = mkOption { + type = types.str; + default = "1024m"; + description = '' + How much memory to allocate to the JVM heap + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "[::]:8080"; + description = '' + <literal>hostname:port</literal> to listen for HTTP traffic. + + This is bound using the systemd socket activation. + ''; + }; + + settings = mkOption { + type = gitIniType; + default = {}; + description = '' + Gerrit configuration. This will be generated to the + <literal>etc/gerrit.config</literal> file. + ''; + }; + + plugins = mkOption { + type = types.listOf types.package; + default = []; + description = '' + List of plugins to add to Gerrit. Each derivation is a jar file + itself where the name of the derivation is the name of plugin. + ''; + }; + + builtinPlugins = mkOption { + type = types.listOf (types.enum cfg.package.passthru.plugins); + default = []; + description = '' + List of builtins plugins to install. Those are shipped in the + <literal>gerrit.war</literal> file. + ''; + }; + + serverId = mkOption { + type = types.str; + description = '' + Set a UUID that uniquely identifies the server. + + This can be generated with + <literal>nix-shell -p utillinux --run uuidgen</literal>. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + services.gerrit.settings = { + cache.directory = "/var/cache/gerrit"; + container.heapLimit = cfg.jvmHeapLimit; + gerrit.basePath = lib.mkDefault "git"; + gerrit.serverId = cfg.serverId; + httpd.inheritChannel = "true"; + httpd.listenUrl = lib.mkDefault "http://${cfg.listenAddress}"; + index.type = lib.mkDefault "lucene"; + }; + + # Add the gerrit CLI to the system to run `gerrit init` and friends. + environment.systemPackages = [ gerrit-cli ]; + + systemd.sockets.gerrit = { + unitConfig.Description = "Gerrit HTTP socket"; + wantedBy = [ "sockets.target" ]; + listenStreams = [ cfg.listenAddress ]; + }; + + systemd.services.gerrit = { + description = "Gerrit"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "gerrit.socket" ]; + after = [ "gerrit.socket" "network.target" ]; + + path = [ + gerrit-cli + pkgs.bash + pkgs.coreutils + pkgs.git + pkgs.openssh + ]; + + environment = { + GERRIT_HOME = "%S/gerrit"; + GERRIT_TMP = "%T"; + HOME = "%S/gerrit"; + XDG_CONFIG_HOME = "%S/gerrit/.config"; + }; + + preStart = '' + set -euo pipefail + + # bootstrap if nothing exists + if [[ ! -d git ]]; then + gerrit init --batch --no-auto-start + fi + + # install gerrit.war for the plugin manager + rm -rf bin + mkdir bin + ln -sfv ${cfg.package}/webapps/${cfg.package.name}.war bin/gerrit.war + + # copy the config, keep it mutable because Gerrit + ln -sfv ${gerritConfig} etc/gerrit.config + + # install the plugins + rm -rf plugins + ln -sv ${gerrit-plugins} plugins + '' + ; + + serviceConfig = { + CacheDirectory = "gerrit"; + DynamicUser = true; + ExecStart = "${gerrit-cli}/bin/gerrit daemon --console-log"; + LimitNOFILE = 4096; + StandardInput = "socket"; + StandardOutput = "journal"; + StateDirectory = "gerrit"; + WorkingDirectory = "%S/gerrit"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ edef zimbatm ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 51b463747b0e7..3501c551625d4 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -97,6 +97,7 @@ in fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix {}; freeswitch = handleTest ./freeswitch.nix {}; fsck = handleTest ./fsck.nix {}; + gerrit = handleTest ./gerrit.nix {}; gotify-server = handleTest ./gotify-server.nix {}; grocy = handleTest ./grocy.nix {}; gitdaemon = handleTest ./gitdaemon.nix {}; diff --git a/nixos/tests/gerrit.nix b/nixos/tests/gerrit.nix new file mode 100644 index 0000000000000..e8b5cb4c4feb7 --- /dev/null +++ b/nixos/tests/gerrit.nix @@ -0,0 +1,56 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + lfs = pkgs.fetchurl { + url = "https://gerrit-ci.gerritforge.com/job/plugin-lfs-bazel-master/90/artifact/bazel-bin/plugins/lfs/lfs.jar"; + sha256 = "023b0kd8djm3cn1lf1xl67yv3j12yl8bxccn42lkfmwxjwjfqw6h"; + }; + +in { + name = "gerrit"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ flokli zimbatm ]; + }; + + nodes = { + server = + { config, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 2222 ]; + + virtualisation.memorySize = 1024; + + services.gerrit = { + enable = true; + serverId = "aa76c84b-50b0-4711-a0a0-1ee30e45bbd0"; + listenAddress = "[::]:80"; + jvmPackage = pkgs.jdk12_headless; + jvmHeapLimit = "1g"; + + plugins = [ lfs ]; + builtinPlugins = [ "hooks" "webhooks" ]; + settings = { + gerrit.canonicalWebUrl = "http://server"; + lfs.plugin = "lfs"; + plugins.allowRemoteAdmin = true; + sshd.listenAddress = "[::]:2222"; + sshd.advertisedAddress = "[::]:2222"; + }; + }; + }; + + client = + { ... }: { + }; + }; + + testScript = '' + start_all() + server.wait_for_unit("gerrit.service") + server.wait_for_open_port(80) + client.succeed("curl http://server") + + server.wait_for_open_port(2222) + client.succeed("nc -z server 2222") + ''; +}) diff --git a/pkgs/applications/version-management/gerrit/default.nix b/pkgs/applications/version-management/gerrit/default.nix index be84b7abc8201..1aa7afcd98bfc 100644 --- a/pkgs/applications/version-management/gerrit/default.nix +++ b/pkgs/applications/version-management/gerrit/default.nix @@ -14,6 +14,24 @@ stdenv.mkDerivation rec { ln -s ${src} "$out"/webapps/gerrit-${version}.war ''; + passthru = { + # A list of plugins that are part of the gerrit.war file. + # Use `java -jar gerrit.war ls | grep plugins/` to generate that list. + plugins = [ + "codemirror-editor" + "commit-message-length-validator" + "delete-project" + "download-commands" + "gitiles" + "hooks" + "plugin-manager" + "replication" + "reviewnotes" + "singleusergroup" + "webhooks" + ]; + }; + meta = with stdenv.lib; { homepage = "https://www.gerritcodereview.com/index.md"; license = licenses.asl20; |