diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index ccdc39eecd8d..b931dc8eb80d 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -823,6 +823,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 000000000000..b184c0754d45
--- /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 = ''
+ hostname:port 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
+ etc/gerrit.config 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
+ gerrit.war file.
+ '';
+ };
+
+ serverId = mkOption {
+ type = types.str;
+ description = ''
+ Set a UUID that uniquely identifies the server.
+
+ This can be generated with
+ nix-shell -p utillinux --run uuidgen.
+ '';
+ };
+ };
+ };
+
+ 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 51b463747b0e..3501c551625d 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 000000000000..e8b5cb4c4feb
--- /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 be84b7abc820..1aa7afcd98bf 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;