1{ config, lib, pkgs, ... }:
2
3# TODO: support non-postgresql
4
5with lib;
6
7let
8 cfg = config.services.gitlab;
9
10 ruby = cfg.packages.gitlab.ruby;
11 bundler = pkgs.bundler;
12
13 gemHome = "${cfg.packages.gitlab.rubyEnv}/${ruby.gemPath}";
14
15 gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket";
16 gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket";
17 pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
18 pgSuperUser = config.services.postgresql.superUser;
19
20 databaseYml = ''
21 production:
22 adapter: postgresql
23 database: ${cfg.databaseName}
24 host: ${cfg.databaseHost}
25 password: ${cfg.databasePassword}
26 username: ${cfg.databaseUsername}
27 encoding: utf8
28 '';
29
30 gitalyToml = pkgs.writeText "gitaly.toml" ''
31 socket_path = "${lib.escape ["\""] gitalySocket}"
32 bin_dir = "${cfg.packages.gitaly}/bin"
33 prometheus_listen_addr = "localhost:9236"
34
35 [git]
36 bin_path = "${pkgs.git}/bin/git"
37
38 [gitaly-ruby]
39 dir = "${cfg.packages.gitaly.ruby}"
40
41 [gitlab-shell]
42 dir = "${cfg.packages.gitlab-shell}"
43
44 ${concatStringsSep "\n" (attrValues (mapAttrs (k: v: ''
45 [[storage]]
46 name = "${lib.escape ["\""] k}"
47 path = "${lib.escape ["\""] v.path}"
48 '') gitlabConfig.production.repositories.storages))}
49 '';
50
51 gitlabShellYml = ''
52 user: ${cfg.user}
53 gitlab_url: "http+unix://${pathUrlQuote gitlabSocket}"
54 http_settings:
55 self_signed_cert: false
56 repos_path: "${cfg.statePath}/repositories"
57 secret_file: "${cfg.statePath}/config/gitlab_shell_secret"
58 log_file: "${cfg.statePath}/log/gitlab-shell.log"
59 redis:
60 bin: ${pkgs.redis}/bin/redis-cli
61 host: 127.0.0.1
62 port: 6379
63 database: 0
64 namespace: resque:gitlab
65 '';
66
67 redisYml = ''
68 production:
69 url: redis://localhost:6379/
70 '';
71
72 secretsYml = ''
73 production:
74 secret_key_base: ${cfg.secrets.secret}
75 otp_key_base: ${cfg.secrets.otp}
76 db_key_base: ${cfg.secrets.db}
77 openid_connect_signing_key: ${builtins.toJSON cfg.secrets.jws}
78 '';
79
80 gitlabConfig = {
81 # These are the default settings from config/gitlab.example.yml
82 production = flip recursiveUpdate cfg.extraConfig {
83 gitlab = {
84 host = cfg.host;
85 port = cfg.port;
86 https = cfg.https;
87 user = cfg.user;
88 email_enabled = true;
89 email_display_name = "GitLab";
90 email_reply_to = "noreply@localhost";
91 default_theme = 2;
92 default_projects_features = {
93 issues = true;
94 merge_requests = true;
95 wiki = true;
96 snippets = true;
97 builds = true;
98 container_registry = true;
99 };
100 };
101 repositories.storages.default.path = "${cfg.statePath}/repositories";
102 repositories.storages.default.gitaly_address = "unix:${gitalySocket}";
103 artifacts.enabled = true;
104 lfs.enabled = true;
105 gravatar.enabled = true;
106 cron_jobs = { };
107 gitlab_ci.builds_path = "${cfg.statePath}/builds";
108 ldap.enabled = false;
109 omniauth.enabled = false;
110 shared.path = "${cfg.statePath}/shared";
111 gitaly.client_path = "${cfg.packages.gitaly}/bin";
112 backup.path = "${cfg.backupPath}";
113 gitlab_shell = {
114 path = "${cfg.packages.gitlab-shell}";
115 hooks_path = "${cfg.statePath}/shell/hooks";
116 secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
117 upload_pack = true;
118 receive_pack = true;
119 };
120 workhorse = {
121 secret_file = "${cfg.statePath}/.gitlab_workhorse_secret";
122 };
123 git = {
124 bin_path = "git";
125 };
126 monitoring = {
127 ip_whitelist = [ "127.0.0.0/8" "::1/128" ];
128 sidekiq_exporter = {
129 enable = true;
130 address = "localhost";
131 port = 3807;
132 };
133 };
134 extra = {};
135 };
136 };
137
138 gitlabEnv = {
139 HOME = "${cfg.statePath}/home";
140 GEM_HOME = gemHome;
141 BUNDLE_GEMFILE = "${cfg.packages.gitlab}/share/gitlab/Gemfile";
142 UNICORN_PATH = "${cfg.statePath}/";
143 GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
144 GITLAB_STATE_PATH = "${cfg.statePath}";
145 GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
146 GITLAB_LOG_PATH = "${cfg.statePath}/log";
147 GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
148 GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
149 GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret";
150 GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks";
151 GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "gitlab-redis.yml" redisYml;
152 prometheus_multiproc_dir = "/run/gitlab";
153 RAILS_ENV = "production";
154 };
155
156 unicornConfig = builtins.readFile ./defaultUnicornConfig.rb;
157
158 gitlab-rake = pkgs.stdenv.mkDerivation rec {
159 name = "gitlab-rake";
160 buildInputs = [ cfg.packages.gitlab cfg.packages.gitlab.rubyEnv pkgs.makeWrapper ];
161 phases = "installPhase fixupPhase";
162 buildPhase = "";
163 installPhase = ''
164 mkdir -p $out/bin
165 makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/bundle $out/bin/gitlab-bundle \
166 ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
167 --set GITLAB_CONFIG_PATH '${cfg.statePath}/config' \
168 --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package ]}:$PATH' \
169 --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \
170 --run 'cd ${cfg.packages.gitlab}/share/gitlab'
171 makeWrapper $out/bin/gitlab-bundle $out/bin/gitlab-rake \
172 --add-flags "exec rake"
173 '';
174 };
175
176 smtpSettings = pkgs.writeText "gitlab-smtp-settings.rb" ''
177 if Rails.env.production?
178 Rails.application.config.action_mailer.delivery_method = :smtp
179
180 ActionMailer::Base.delivery_method = :smtp
181 ActionMailer::Base.smtp_settings = {
182 address: "${cfg.smtp.address}",
183 port: ${toString cfg.smtp.port},
184 ${optionalString (cfg.smtp.username != null) ''user_name: "${cfg.smtp.username}",''}
185 ${optionalString (cfg.smtp.password != null) ''password: "${cfg.smtp.password}",''}
186 domain: "${cfg.smtp.domain}",
187 ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"}
188 enable_starttls_auto: ${toString cfg.smtp.enableStartTLSAuto},
189 openssl_verify_mode: '${cfg.smtp.opensslVerifyMode}'
190 }
191 end
192 '';
193
194in {
195
196 options = {
197 services.gitlab = {
198 enable = mkOption {
199 type = types.bool;
200 default = false;
201 description = ''
202 Enable the gitlab service.
203 '';
204 };
205
206 packages.gitlab = mkOption {
207 type = types.package;
208 default = pkgs.gitlab;
209 defaultText = "pkgs.gitlab";
210 description = "Reference to the gitlab package";
211 };
212
213 packages.gitlab-shell = mkOption {
214 type = types.package;
215 default = pkgs.gitlab-shell;
216 defaultText = "pkgs.gitlab-shell";
217 description = "Reference to the gitlab-shell package";
218 };
219
220 packages.gitlab-workhorse = mkOption {
221 type = types.package;
222 default = pkgs.gitlab-workhorse;
223 defaultText = "pkgs.gitlab-workhorse";
224 description = "Reference to the gitlab-workhorse package";
225 };
226
227 packages.gitaly = mkOption {
228 type = types.package;
229 default = pkgs.gitaly;
230 defaultText = "pkgs.gitaly";
231 description = "Reference to the gitaly package";
232 };
233
234 statePath = mkOption {
235 type = types.str;
236 default = "/var/gitlab/state";
237 description = "Gitlab state directory, logs are stored here.";
238 };
239
240 backupPath = mkOption {
241 type = types.str;
242 default = cfg.statePath + "/backup";
243 description = "Gitlab path for backups.";
244 };
245
246 databaseHost = mkOption {
247 type = types.str;
248 default = "127.0.0.1";
249 description = "Gitlab database hostname.";
250 };
251
252 databasePassword = mkOption {
253 type = types.str;
254 description = "Gitlab database user password.";
255 };
256
257 databaseName = mkOption {
258 type = types.str;
259 default = "gitlab";
260 description = "Gitlab database name.";
261 };
262
263 databaseUsername = mkOption {
264 type = types.str;
265 default = "gitlab";
266 description = "Gitlab database user.";
267 };
268
269 host = mkOption {
270 type = types.str;
271 default = config.networking.hostName;
272 description = "Gitlab host name. Used e.g. for copy-paste URLs.";
273 };
274
275 port = mkOption {
276 type = types.int;
277 default = 8080;
278 description = ''
279 Gitlab server port for copy-paste URLs, e.g. 80 or 443 if you're
280 service over https.
281 '';
282 };
283
284 https = mkOption {
285 type = types.bool;
286 default = false;
287 description = "Whether gitlab prints URLs with https as scheme.";
288 };
289
290 user = mkOption {
291 type = types.str;
292 default = "gitlab";
293 description = "User to run gitlab and all related services.";
294 };
295
296 group = mkOption {
297 type = types.str;
298 default = "gitlab";
299 description = "Group to run gitlab and all related services.";
300 };
301
302 initialRootEmail = mkOption {
303 type = types.str;
304 default = "admin@local.host";
305 description = ''
306 Initial email address of the root account if this is a new install.
307 '';
308 };
309
310 initialRootPassword = mkOption {
311 type = types.str;
312 default = "UseNixOS!";
313 description = ''
314 Initial password of the root account if this is a new install.
315 '';
316 };
317
318 smtp = {
319 enable = mkOption {
320 type = types.bool;
321 default = false;
322 description = "Enable gitlab mail delivery over SMTP.";
323 };
324
325 address = mkOption {
326 type = types.str;
327 default = "localhost";
328 description = "Address of the SMTP server for Gitlab.";
329 };
330
331 port = mkOption {
332 type = types.int;
333 default = 465;
334 description = "Port of the SMTP server for Gitlab.";
335 };
336
337 username = mkOption {
338 type = types.nullOr types.str;
339 default = null;
340 description = "Username of the SMTP server for Gitlab.";
341 };
342
343 password = mkOption {
344 type = types.nullOr types.str;
345 default = null;
346 description = "Password of the SMTP server for Gitlab.";
347 };
348
349 domain = mkOption {
350 type = types.str;
351 default = "localhost";
352 description = "HELO domain to use for outgoing mail.";
353 };
354
355 authentication = mkOption {
356 type = types.nullOr types.str;
357 default = null;
358 description = "Authentitcation type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
359 };
360
361 enableStartTLSAuto = mkOption {
362 type = types.bool;
363 default = true;
364 description = "Whether to try to use StartTLS.";
365 };
366
367 opensslVerifyMode = mkOption {
368 type = types.str;
369 default = "peer";
370 description = "How OpenSSL checks the certificate, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
371 };
372 };
373
374 secrets.secret = mkOption {
375 type = types.str;
376 description = ''
377 The secret is used to encrypt variables in the DB. If
378 you change or lose this key you will be unable to access variables
379 stored in database.
380
381 Make sure the secret is at least 30 characters and all random,
382 no regular words or you'll be exposed to dictionary attacks.
383 '';
384 };
385
386 secrets.db = mkOption {
387 type = types.str;
388 description = ''
389 The secret is used to encrypt variables in the DB. If
390 you change or lose this key you will be unable to access variables
391 stored in database.
392
393 Make sure the secret is at least 30 characters and all random,
394 no regular words or you'll be exposed to dictionary attacks.
395 '';
396 };
397
398 secrets.otp = mkOption {
399 type = types.str;
400 description = ''
401 The secret is used to encrypt secrets for OTP tokens. If
402 you change or lose this key, users which have 2FA enabled for login
403 won't be able to login anymore.
404
405 Make sure the secret is at least 30 characters and all random,
406 no regular words or you'll be exposed to dictionary attacks.
407 '';
408 };
409
410 secrets.jws = mkOption {
411 type = types.str;
412 description = ''
413 The secret is used to encrypt session keys. If you change or lose
414 this key, users will be disconnected.
415
416 Make sure the secret is an RSA private key in PEM format. You can
417 generate one with
418
419 openssl genrsa 2048
420 '';
421 };
422
423 extraConfig = mkOption {
424 type = types.attrs;
425 default = {};
426 example = {
427 gitlab = {
428 default_projects_features = {
429 builds = false;
430 };
431 };
432 };
433 description = ''
434 Extra options to be merged into config/gitlab.yml as nix
435 attribute set.
436 '';
437 };
438 };
439 };
440
441 config = mkIf cfg.enable {
442
443 environment.systemPackages = [ pkgs.git gitlab-rake cfg.packages.gitlab-shell ];
444
445 # Redis is required for the sidekiq queue runner.
446 services.redis.enable = mkDefault true;
447 # We use postgres as the main data store.
448 services.postgresql.enable = mkDefault true;
449 # Use postfix to send out mails.
450 services.postfix.enable = mkDefault true;
451
452 users.extraUsers = [
453 { name = cfg.user;
454 group = cfg.group;
455 home = "${cfg.statePath}/home";
456 shell = "${pkgs.bash}/bin/bash";
457 uid = config.ids.uids.gitlab;
458 }
459 ];
460
461 users.extraGroups = [
462 { name = cfg.group;
463 gid = config.ids.gids.gitlab;
464 }
465 ];
466
467 systemd.services.gitlab-sidekiq = {
468 after = [ "network.target" "redis.service" ];
469 wantedBy = [ "multi-user.target" ];
470 partOf = [ "gitlab.service" ];
471 environment = gitlabEnv;
472 path = with pkgs; [
473 config.services.postgresql.package
474 gitAndTools.git
475 ruby
476 openssh
477 nodejs
478 gnupg
479 ];
480 serviceConfig = {
481 Type = "simple";
482 User = cfg.user;
483 Group = cfg.group;
484 TimeoutSec = "300";
485 Restart = "on-failure";
486 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
487 ExecStart="${cfg.packages.gitlab.rubyEnv}/bin/bundle exec \"sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production -P ${cfg.statePath}/tmp/sidekiq.pid\"";
488 };
489 };
490
491 systemd.services.gitaly = {
492 after = [ "network.target" "gitlab.service" ];
493 wantedBy = [ "multi-user.target" ];
494 environment.HOME = gitlabEnv.HOME;
495 environment.GEM_HOME = "${cfg.packages.gitaly.rubyEnv}/${ruby.gemPath}";
496 environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
497 path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv ruby ];
498 serviceConfig = {
499 #PermissionsStartOnly = true; # preStart must be run as root
500 Type = "simple";
501 User = cfg.user;
502 Group = cfg.group;
503 TimeoutSec = "300";
504 Restart = "on-failure";
505 WorkingDirectory = gitlabEnv.HOME;
506 ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}";
507 };
508 };
509
510 systemd.services.gitlab-workhorse = {
511 after = [ "network.target" "gitlab.service" ];
512 wantedBy = [ "multi-user.target" ];
513 environment.HOME = gitlabEnv.HOME;
514 environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
515 path = with pkgs; [
516 gitAndTools.git
517 gnutar
518 gzip
519 openssh
520 gitlab-workhorse
521 ];
522 preStart = ''
523 mkdir -p /run/gitlab
524 chown ${cfg.user}:${cfg.group} /run/gitlab
525 '';
526 serviceConfig = {
527 PermissionsStartOnly = true; # preStart must be run as root
528 Type = "simple";
529 User = cfg.user;
530 Group = cfg.group;
531 TimeoutSec = "300";
532 Restart = "on-failure";
533 WorkingDirectory = gitlabEnv.HOME;
534 ExecStart =
535 "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse "
536 + "-listenUmask 0 "
537 + "-listenNetwork unix "
538 + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
539 + "-authSocket ${gitlabSocket} "
540 + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public "
541 + "-secretPath ${cfg.statePath}/.gitlab_workhorse_secret";
542 };
543 };
544
545 systemd.services.gitlab = {
546 after = [ "network.target" "postgresql.service" "redis.service" ];
547 requires = [ "gitlab-sidekiq.service" ];
548 wantedBy = [ "multi-user.target" ];
549 environment = gitlabEnv;
550 path = with pkgs; [
551 config.services.postgresql.package
552 gitAndTools.git
553 openssh
554 nodejs
555 procps
556 gnupg
557 ];
558 preStart = ''
559 mkdir -p ${cfg.backupPath}
560 mkdir -p ${cfg.statePath}/builds
561 mkdir -p ${cfg.statePath}/repositories
562 mkdir -p ${gitlabConfig.production.shared.path}/artifacts
563 mkdir -p ${gitlabConfig.production.shared.path}/lfs-objects
564 mkdir -p ${gitlabConfig.production.shared.path}/pages
565 mkdir -p ${cfg.statePath}/log
566 mkdir -p ${cfg.statePath}/tmp/pids
567 mkdir -p ${cfg.statePath}/tmp/sockets
568 mkdir -p ${cfg.statePath}/shell
569
570 rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks
571 mkdir -p ${cfg.statePath}/config
572
573 ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/config/gitlab_shell_secret
574
575 # The uploads directory is hardcoded somewhere deep in rails. It is
576 # symlinked in the gitlab package to /run/gitlab/uploads to make it
577 # configurable
578 mkdir -p /run/gitlab
579 mkdir -p ${cfg.statePath}/{log,uploads}
580 ln -sf ${cfg.statePath}/log /run/gitlab/log
581 ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
582 ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
583 chown -R ${cfg.user}:${cfg.group} /run/gitlab
584
585 # Prepare home directory
586 mkdir -p ${gitlabEnv.HOME}/.ssh
587 touch ${gitlabEnv.HOME}/.ssh/authorized_keys
588 chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/
589
590 cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
591 ${optionalString cfg.smtp.enable ''
592 ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
593 ''}
594 ln -sf ${cfg.statePath}/config /run/gitlab/config
595 cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
596
597 # JSON is a subset of YAML
598 ln -fs ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml
599 ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.statePath}/config/database.yml
600 ln -fs ${pkgs.writeText "secrets.yml" secretsYml} ${cfg.statePath}/config/secrets.yml
601 ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.statePath}/config/unicorn.rb
602
603 chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/
604 chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/
605
606 # Install the shell required to push repositories
607 ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} "$GITLAB_SHELL_CONFIG_PATH"
608 ln -fs ${cfg.packages.gitlab-shell}/hooks "$GITLAB_SHELL_HOOKS_PATH"
609 ${cfg.packages.gitlab-shell}/bin/install
610
611 if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
612 if ! test -e "${cfg.statePath}/db-created"; then
613 ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.databasePassword}'"
614 ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} ${config.services.postgresql.package}/bin/createdb --owner ${cfg.databaseUsername} ${cfg.databaseName}
615 touch "${cfg.statePath}/db-created"
616 fi
617 fi
618
619 # enable required pg_trgm extension for gitlab
620 ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql ${cfg.databaseName} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"
621 # Always do the db migrations just to be sure the database is up-to-date
622 ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production
623
624 # The gitlab:setup task is horribly broken somehow, the db:migrate
625 # task above and the db:seed_fu below will do the same for setting
626 # up the initial database
627 if ! test -e "${cfg.statePath}/db-seeded"; then
628 ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \
629 GITLAB_ROOT_PASSWORD='${cfg.initialRootPassword}' GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}'
630 touch "${cfg.statePath}/db-seeded"
631 fi
632
633 # The gitlab:shell:create_hooks task seems broken for fixing links
634 # so we instead delete all the hooks and create them anew
635 rm -f ${cfg.statePath}/repositories/**/*.git/hooks
636 ${gitlab-rake}/bin/gitlab-rake gitlab:shell:create_hooks RAILS_ENV=production
637
638 # Change permissions in the last step because some of the
639 # intermediary scripts like to create directories as root.
640 chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}
641 chmod -R ug+rwX,o-rwx+X ${cfg.statePath}
642 chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}
643 chmod -R ug+rwX,o-rwx ${cfg.statePath}/repositories
644 chmod -R ug-s ${cfg.statePath}/repositories
645 find ${cfg.statePath}/repositories -type d -print0 | xargs -0 chmod g+s
646 chmod 770 ${cfg.statePath}/uploads
647 chown -R ${cfg.user} ${cfg.statePath}/uploads
648 find ${cfg.statePath}/uploads -type f -exec chmod 0644 {} \;
649 find ${cfg.statePath}/uploads -type d -not -path ${cfg.statePath}/uploads -exec chmod 0770 {} \;
650 '';
651
652 serviceConfig = {
653 PermissionsStartOnly = true; # preStart must be run as root
654 Type = "simple";
655 User = cfg.user;
656 Group = cfg.group;
657 TimeoutSec = "300";
658 Restart = "on-failure";
659 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
660 ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/bundle exec \"unicorn -c ${cfg.statePath}/config/unicorn.rb -E production\"";
661 };
662
663 };
664
665 };
666
667 meta.doc = ./gitlab.xml;
668
669}