1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 cfg = config.services.gerrit;
6
7 # NixOS option type for git-like configs
8 gitIniType = with types;
9 let
10 primitiveType = either str (either bool int);
11 multipleType = either primitiveType (listOf primitiveType);
12 sectionType = lazyAttrsOf multipleType;
13 supersectionType = lazyAttrsOf (either multipleType sectionType);
14 in lazyAttrsOf supersectionType;
15
16 gerritConfig = pkgs.writeText "gerrit.conf" (
17 lib.generators.toGitINI cfg.settings
18 );
19
20 replicationConfig = pkgs.writeText "replication.conf" (
21 lib.generators.toGitINI cfg.replicationSettings
22 );
23
24 # Wrap the gerrit java with all the java options so it can be called
25 # like a normal CLI app
26 gerrit-cli = pkgs.writeShellScriptBin "gerrit" ''
27 set -euo pipefail
28 jvmOpts=(
29 ${lib.escapeShellArgs cfg.jvmOpts}
30 -Xmx${cfg.jvmHeapLimit}
31 )
32 exec ${cfg.jvmPackage}/bin/java \
33 "''${jvmOpts[@]}" \
34 -jar ${cfg.package}/webapps/${cfg.package.name}.war \
35 "$@"
36 '';
37
38 gerrit-plugins = pkgs.runCommand
39 "gerrit-plugins"
40 {
41 buildInputs = [ gerrit-cli ];
42 }
43 ''
44 shopt -s nullglob
45 mkdir $out
46
47 for name in ${toString cfg.builtinPlugins}; do
48 echo "Installing builtin plugin $name.jar"
49 gerrit cat plugins/$name.jar > $out/$name.jar
50 done
51
52 for file in ${toString cfg.plugins}; do
53 name=$(echo "$file" | cut -d - -f 2-)
54 echo "Installing plugin $name"
55 ln -sf "$file" $out/$name
56 done
57 '';
58in
59{
60 options = {
61 services.gerrit = {
62 enable = mkEnableOption (lib.mdDoc "Gerrit service");
63
64 package = mkOption {
65 type = types.package;
66 default = pkgs.gerrit;
67 defaultText = literalExpression "pkgs.gerrit";
68 description = lib.mdDoc "Gerrit package to use";
69 };
70
71 jvmPackage = mkOption {
72 type = types.package;
73 default = pkgs.jre_headless;
74 defaultText = literalExpression "pkgs.jre_headless";
75 description = lib.mdDoc "Java Runtime Environment package to use";
76 };
77
78 jvmOpts = mkOption {
79 type = types.listOf types.str;
80 default = [
81 "-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance"
82 "-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance"
83 ];
84 description = lib.mdDoc "A list of JVM options to start gerrit with.";
85 };
86
87 jvmHeapLimit = mkOption {
88 type = types.str;
89 default = "1024m";
90 description = lib.mdDoc ''
91 How much memory to allocate to the JVM heap
92 '';
93 };
94
95 listenAddress = mkOption {
96 type = types.str;
97 default = "[::]:8080";
98 description = lib.mdDoc ''
99 `hostname:port` to listen for HTTP traffic.
100
101 This is bound using the systemd socket activation.
102 '';
103 };
104
105 settings = mkOption {
106 type = gitIniType;
107 default = {};
108 description = lib.mdDoc ''
109 Gerrit configuration. This will be generated to the
110 `etc/gerrit.config` file.
111 '';
112 };
113
114 replicationSettings = mkOption {
115 type = gitIniType;
116 default = {};
117 description = lib.mdDoc ''
118 Replication configuration. This will be generated to the
119 `etc/replication.config` file.
120 '';
121 };
122
123 plugins = mkOption {
124 type = types.listOf types.package;
125 default = [];
126 description = lib.mdDoc ''
127 List of plugins to add to Gerrit. Each derivation is a jar file
128 itself where the name of the derivation is the name of plugin.
129 '';
130 };
131
132 builtinPlugins = mkOption {
133 type = types.listOf (types.enum cfg.package.passthru.plugins);
134 default = [];
135 description = lib.mdDoc ''
136 List of builtins plugins to install. Those are shipped in the
137 `gerrit.war` file.
138 '';
139 };
140
141 serverId = mkOption {
142 type = types.str;
143 description = lib.mdDoc ''
144 Set a UUID that uniquely identifies the server.
145
146 This can be generated with
147 `nix-shell -p util-linux --run uuidgen`.
148 '';
149 };
150 };
151 };
152
153 config = mkIf cfg.enable {
154
155 assertions = [
156 {
157 assertion = cfg.replicationSettings != {} -> elem "replication" cfg.builtinPlugins;
158 message = "Gerrit replicationSettings require enabling the replication plugin";
159 }
160 ];
161
162 services.gerrit.settings = {
163 cache.directory = "/var/cache/gerrit";
164 container.heapLimit = cfg.jvmHeapLimit;
165 gerrit.basePath = lib.mkDefault "git";
166 gerrit.serverId = cfg.serverId;
167 httpd.inheritChannel = "true";
168 httpd.listenUrl = lib.mkDefault "http://${cfg.listenAddress}";
169 index.type = lib.mkDefault "lucene";
170 };
171
172 # Add the gerrit CLI to the system to run `gerrit init` and friends.
173 environment.systemPackages = [ gerrit-cli ];
174
175 systemd.sockets.gerrit = {
176 unitConfig.Description = "Gerrit HTTP socket";
177 wantedBy = [ "sockets.target" ];
178 listenStreams = [ cfg.listenAddress ];
179 };
180
181 systemd.services.gerrit = {
182 description = "Gerrit";
183
184 wantedBy = [ "multi-user.target" ];
185 requires = [ "gerrit.socket" ];
186 after = [ "gerrit.socket" "network.target" ];
187
188 path = [
189 gerrit-cli
190 pkgs.bash
191 pkgs.coreutils
192 pkgs.git
193 pkgs.openssh
194 ];
195
196 environment = {
197 GERRIT_HOME = "%S/gerrit";
198 GERRIT_TMP = "%T";
199 HOME = "%S/gerrit";
200 XDG_CONFIG_HOME = "%S/gerrit/.config";
201 };
202
203 preStart = ''
204 set -euo pipefail
205
206 # bootstrap if nothing exists
207 if [[ ! -d git ]]; then
208 gerrit init --batch --no-auto-start
209 fi
210
211 # install gerrit.war for the plugin manager
212 rm -rf bin
213 mkdir bin
214 ln -sfv ${cfg.package}/webapps/${cfg.package.name}.war bin/gerrit.war
215
216 # copy the config, keep it mutable because Gerrit
217 ln -sfv ${gerritConfig} etc/gerrit.config
218 ln -sfv ${replicationConfig} etc/replication.config
219
220 # install the plugins
221 rm -rf plugins
222 ln -sv ${gerrit-plugins} plugins
223 ''
224 ;
225
226 serviceConfig = {
227 CacheDirectory = "gerrit";
228 DynamicUser = true;
229 ExecStart = "${gerrit-cli}/bin/gerrit daemon --console-log";
230 LimitNOFILE = 4096;
231 StandardInput = "socket";
232 StandardOutput = "journal";
233 StateDirectory = "gerrit";
234 WorkingDirectory = "%S/gerrit";
235 };
236 };
237 };
238
239 meta.maintainers = with lib.maintainers; [ edef zimbatm ];
240 # uses attributes of the linked package
241 meta.buildDocsInSandbox = false;
242}