{ 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 ); replicationConfig = pkgs.writeText "replication.conf" ( lib.generators.toGitINI cfg.replicationSettings ); # 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; defaultText = literalExpression "pkgs.gerrit"; description = "Gerrit package to use"; }; jvmPackage = mkOption { type = types.package; default = pkgs.jre_headless; defaultText = literalExpression "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. ''; }; replicationSettings = mkOption { type = gitIniType; default = {}; description = '' Replication configuration. This will be generated to the etc/replication.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 util-linux --run uuidgen. ''; }; }; }; config = mkIf cfg.enable { assertions = [ { assertion = cfg.replicationSettings != {} -> elem "replication" cfg.builtinPlugins; message = "Gerrit replicationSettings require enabling the replication plugin"; } ]; 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 ln -sfv ${replicationConfig} etc/replication.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 ]; # uses attributes of the linked package meta.buildDocsInSandbox = false; }