1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.youtrack;
10in
11{
12 imports = [
13 (lib.mkRenamedOptionModule
14 [ "services" "youtrack" "baseUrl" ]
15 [ "services" "youtrack" "environmentalParameters" "base-url" ]
16 )
17 (lib.mkRenamedOptionModule
18 [ "services" "youtrack" "port" ]
19 [ "services" "youtrack" "environmentalParameters" "listen-port" ]
20 )
21 (lib.mkRemovedOptionModule [
22 "services"
23 "youtrack"
24 "maxMemory"
25 ] "Please instead use `services.youtrack.generalParameters`.")
26 (lib.mkRemovedOptionModule [
27 "services"
28 "youtrack"
29 "maxMetaspaceSize"
30 ] "Please instead use `services.youtrack.generalParameters`.")
31 (lib.mkRemovedOptionModule [
32 "services"
33 "youtrack"
34 "extraParams"
35 ] "Please migrate to `services.youtrack.generalParameters`.")
36 (lib.mkRemovedOptionModule [
37 "services"
38 "youtrack"
39 "jvmOpts"
40 ] "Please migrate to `services.youtrack.generalParameters`.")
41 ];
42
43 options.services.youtrack = {
44 enable = lib.mkEnableOption "YouTrack service";
45
46 address = lib.mkOption {
47 description = ''
48 The interface youtrack will listen on.
49 '';
50 default = "127.0.0.1";
51 type = lib.types.str;
52 };
53
54 package = lib.mkOption {
55 description = ''
56 Package to use.
57 '';
58 type = lib.types.package;
59 default = pkgs.youtrack;
60 defaultText = lib.literalExpression "pkgs.youtrack";
61 };
62
63 statePath = lib.mkOption {
64 description = ''
65 Path were the YouTrack state is stored.
66 To this path the base version (e.g. 2023_1) of the used package will be appended.
67 '';
68 type = lib.types.path;
69 default = "/var/lib/youtrack";
70 };
71
72 virtualHost = lib.mkOption {
73 description = ''
74 Name of the nginx virtual host to use and setup.
75 If null, do not setup anything.
76 '';
77 default = null;
78 type = lib.types.nullOr lib.types.str;
79 };
80
81 autoUpgrade = lib.mkOption {
82 type = lib.types.bool;
83 default = true;
84 description = "Whether YouTrack should auto upgrade it without showing the upgrade dialog.";
85 };
86
87 generalParameters = lib.mkOption {
88 type = with lib.types; listOf str;
89 description = ''
90 General configuration parameters and other JVM options.
91 See <https://www.jetbrains.com/help/youtrack/server/2023.3/youtrack-java-start-parameters.html#general-parameters>
92 for more information.
93 '';
94 example = lib.literalExpression ''
95 [
96 "-Djetbrains.youtrack.admin.restore=true"
97 "-Xmx1024m"
98 ];
99 '';
100 default = [ ];
101 };
102
103 environmentalParameters = lib.mkOption {
104 type = lib.types.submodule {
105 freeformType =
106 with lib.types;
107 attrsOf (oneOf [
108 int
109 str
110 port
111 ]);
112 options = {
113 listen-address = lib.mkOption {
114 type = lib.types.str;
115 default = "0.0.0.0";
116 description = "The interface YouTrack will listen on.";
117 };
118 listen-port = lib.mkOption {
119 type = lib.types.port;
120 default = 8080;
121 description = "The port YouTrack will listen on.";
122 };
123 };
124 };
125 description = ''
126 Environmental configuration parameters, set imperatively. The values doesn't get removed, when removed in Nix.
127 See <https://www.jetbrains.com/help/youtrack/server/2023.3/youtrack-java-start-parameters.html#environmental-parameters>
128 for more information.
129 '';
130 example = lib.literalExpression ''
131 {
132 secure-mode = "tls";
133 }
134 '';
135 default = { };
136 };
137 };
138
139 config = lib.mkIf cfg.enable {
140 services.youtrack.generalParameters = [
141 "-Ddisable.configuration.wizard.on.upgrade=${lib.boolToString cfg.autoUpgrade}"
142 ];
143
144 systemd.services.youtrack =
145 let
146 jvmoptions = pkgs.writeTextFile {
147 name = "youtrack.jvmoptions";
148 text = (lib.concatStringsSep "\n" cfg.generalParameters);
149 };
150
151 package = cfg.package.override {
152 statePath = cfg.statePath;
153 };
154 in
155 {
156 after = [ "network.target" ];
157 wantedBy = [ "multi-user.target" ];
158 path = with pkgs; [ unixtools.hostname ];
159 preStart = ''
160 # This detects old (i.e. <= 2022.3) installations that were not migrated yet
161 # and migrates them to the new state directory style
162 if [[ -d ${cfg.statePath}/teamsysdata ]] && [[ ! -d ${cfg.statePath}/2022_3 ]]
163 then
164 mkdir -p ${cfg.statePath}/2022_3
165 mv ${cfg.statePath}/teamsysdata ${cfg.statePath}/2022_3
166 mv ${cfg.statePath}/.youtrack ${cfg.statePath}/2022_3
167 fi
168 mkdir -p ${cfg.statePath}/{backups,conf,data,logs,temp}
169 ${pkgs.coreutils}/bin/ln -fs ${jvmoptions} ${cfg.statePath}/conf/youtrack.jvmoptions
170 ${package}/bin/youtrack configure ${
171 lib.concatStringsSep " " (
172 lib.mapAttrsToList (name: value: "--${name}=${toString value}") cfg.environmentalParameters
173 )
174 }
175 '';
176 serviceConfig = lib.mkMerge [
177 {
178 Type = "simple";
179 User = "youtrack";
180 Group = "youtrack";
181 Restart = "on-failure";
182 ExecStart = "${package}/bin/youtrack run";
183 }
184 (lib.mkIf (cfg.statePath == "/var/lib/youtrack") {
185 StateDirectory = "youtrack";
186 })
187 ];
188 };
189
190 users.users.youtrack = {
191 description = "Youtrack service user";
192 isSystemUser = true;
193 home = cfg.statePath;
194 createHome = true;
195 group = "youtrack";
196 };
197
198 users.groups.youtrack = { };
199
200 services.nginx = lib.mkIf (cfg.virtualHost != null) {
201 upstreams.youtrack.servers."${cfg.address}:${toString cfg.environmentalParameters.listen-port}" =
202 { };
203 virtualHosts.${cfg.virtualHost}.locations = {
204 "/" = {
205 proxyPass = "http://youtrack";
206 extraConfig = ''
207 client_max_body_size 10m;
208 proxy_http_version 1.1;
209 proxy_set_header X-Forwarded-Host $http_host;
210 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
211 proxy_set_header X-Forwarded-Proto $scheme;
212 '';
213 };
214
215 "/api/eventSourceBus" = {
216 proxyPass = "http://youtrack";
217 extraConfig = ''
218 proxy_cache off;
219 proxy_buffering off;
220 proxy_read_timeout 86400s;
221 proxy_send_timeout 86400s;
222 proxy_set_header Connection "";
223 chunked_transfer_encoding off;
224 client_max_body_size 10m;
225 proxy_http_version 1.1;
226 proxy_set_header X-Forwarded-Host $http_host;
227 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
228 proxy_set_header X-Forwarded-Proto $scheme;
229 '';
230 };
231 };
232 };
233 };
234
235 meta.doc = ./youtrack.md;
236 meta.maintainers = [ lib.maintainers.leona ];
237}