diff options
Diffstat (limited to 'nixos/modules/services/misc/gitlab.nix')
-rw-r--r-- | nixos/modules/services/misc/gitlab.nix | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix new file mode 100644 index 00000000000..4505c5ceb84 --- /dev/null +++ b/nixos/modules/services/misc/gitlab.nix @@ -0,0 +1,300 @@ +{ config, lib, pkgs, ... }: + +# TODO: support non-postgresql + +with lib; + +let + cfg = config.services.gitlab; + + ruby = pkgs.gitlab.ruby; + bundler = pkgs.bundler; + + gemHome = "${pkgs.gitlab.env}/${ruby.gemPath}"; + + databaseYml = '' + production: + adapter: postgresql + database: ${cfg.databaseName} + host: ${cfg.databaseHost} + password: ${cfg.databasePassword} + username: ${cfg.databaseUsername} + encoding: utf8 + ''; + gitlabShellYml = '' + user: gitlab + gitlab_url: "http://${cfg.host}:${toString cfg.port}/" + http_settings: + self_signed_cert: false + repos_path: "${cfg.stateDir}/repositories" + log_file: "${cfg.stateDir}/log/gitlab-shell.log" + redis: + bin: ${pkgs.redis}/bin/redis-cli + host: 127.0.0.1 + port: 6379 + database: 0 + namespace: resque:gitlab + ''; + + unicornConfig = builtins.readFile ./defaultUnicornConfig.rb; + + gitlab-runner = pkgs.stdenv.mkDerivation rec { + name = "gitlab-runner"; + buildInputs = [ pkgs.gitlab pkgs.bundler pkgs.makeWrapper ]; + phases = "installPhase fixupPhase"; + buildPhase = ""; + installPhase = '' + mkdir -p $out/bin + makeWrapper ${bundler}/bin/bundle $out/bin/gitlab-runner\ + --set RAKEOPT '"-f ${pkgs.gitlab}/share/gitlab/Rakefile"'\ + --set GEM_HOME '${gemHome}'\ + --set UNICORN_PATH "${cfg.stateDir}/"\ + --set GITLAB_PATH "${pkgs.gitlab}/share/gitlab/"\ + --set GITLAB_APPLICATION_LOG_PATH "${cfg.stateDir}/log/application.log"\ + --set GITLAB_SATELLITES_PATH "${cfg.stateDir}/satellites"\ + --set GITLAB_SHELL_PATH "${pkgs.gitlab-shell}"\ + --set GITLAB_REPOSITORIES_PATH "${cfg.stateDir}/repositories"\ + --set GITLAB_SHELL_HOOKS_PATH "${cfg.stateDir}/shell/hooks"\ + --set BUNDLE_GEMFILE "${pkgs.gitlab}/share/gitlab/Gemfile"\ + --set GITLAB_EMAIL_FROM "${cfg.emailFrom}"\ + --set GITLAB_SHELL_CONFIG_PATH "${cfg.stateDir}/shell/config.yml"\ + --set GITLAB_SHELL_SECRET_PATH "${cfg.stateDir}/config/gitlab_shell_secret"\ + --set GITLAB_HOST "${cfg.host}"\ + --set GITLAB_PORT "${toString cfg.port}"\ + --set GITLAB_BACKUP_PATH "${cfg.backupPath}"\ + --set RAILS_ENV "production" + ''; + }; + +in { + + options = { + services.gitlab = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable the gitlab service. + ''; + }; + + satelliteDir = mkOption { + type = types.str; + default = "/var/gitlab/git-satellites"; + description = "Gitlab directory to store checked out git trees requires for operation."; + }; + + stateDir = mkOption { + type = types.str; + default = "/var/gitlab/state"; + description = "Gitlab state directory, logs are stored here."; + }; + + backupPath = mkOption { + type = types.str; + default = cfg.stateDir + "/backup"; + description = "Gitlab path for backups."; + }; + + databaseHost = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Gitlab database hostname."; + }; + + databasePassword = mkOption { + type = types.str; + default = ""; + description = "Gitlab database user password."; + }; + + databaseName = mkOption { + type = types.str; + default = "gitlab"; + description = "Gitlab database name."; + }; + + databaseUsername = mkOption { + type = types.str; + default = "gitlab"; + description = "Gitlab database user."; + }; + + emailFrom = mkOption { + type = types.str; + default = "example@example.org"; + description = "The source address for emails sent by gitlab."; + }; + + host = mkOption { + type = types.str; + default = config.networking.hostName; + description = "Gitlab host name. Used e.g. for copy-paste URLs."; + }; + + port = mkOption { + type = types.int; + default = 8080; + description = "Gitlab server listening port."; + }; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ gitlab-runner pkgs.gitlab-shell ]; + + assertions = [ + { assertion = cfg.databasePassword != ""; + message = "databasePassword must be set"; + } + ]; + + # Redis is required for the sidekiq queue runner. + services.redis.enable = mkDefault true; + # We use postgres as the main data store. + services.postgresql.enable = mkDefault true; + services.postgresql.package = mkDefault pkgs.postgresql; + # Use postfix to send out mails. + services.postfix.enable = mkDefault true; + + users.extraUsers = [ + { name = "gitlab"; + group = "gitlab"; + home = "${cfg.stateDir}/home"; + shell = "${pkgs.bash}/bin/bash"; + uid = config.ids.uids.gitlab; + } ]; + + users.extraGroups = [ + { name = "gitlab"; + gid = config.ids.gids.gitlab; + } ]; + + systemd.services.gitlab-sidekiq = { + after = [ "network.target" "redis.service" ]; + wantedBy = [ "multi-user.target" ]; + environment.HOME = "${cfg.stateDir}/home"; + environment.GEM_HOME = gemHome; + environment.UNICORN_PATH = "${cfg.stateDir}/"; + environment.GITLAB_PATH = "${pkgs.gitlab}/share/gitlab/"; + environment.GITLAB_APPLICATION_LOG_PATH = "${cfg.stateDir}/log/application.log"; + environment.GITLAB_SATELLITES_PATH = "${cfg.stateDir}/satellites"; + environment.GITLAB_SHELL_PATH = "${pkgs.gitlab-shell}"; + environment.GITLAB_REPOSITORIES_PATH = "${cfg.stateDir}/repositories"; + environment.GITLAB_SHELL_HOOKS_PATH = "${cfg.stateDir}/shell/hooks"; + environment.BUNDLE_GEMFILE = "${pkgs.gitlab}/share/gitlab/Gemfile"; + environment.GITLAB_EMAIL_FROM = "${cfg.emailFrom}"; + environment.GITLAB_SHELL_CONFIG_PATH = "${cfg.stateDir}/shell/config.yml"; + environment.GITLAB_SHELL_SECRET_PATH = "${cfg.stateDir}/config/gitlab_shell_secret"; + environment.GITLAB_HOST = "${cfg.host}"; + environment.GITLAB_PORT = "${toString cfg.port}"; + environment.GITLAB_DATABASE_HOST = "${cfg.databaseHost}"; + environment.GITLAB_DATABASE_PASSWORD = "${cfg.databasePassword}"; + environment.RAILS_ENV = "production"; + path = with pkgs; [ + config.services.postgresql.package + gitAndTools.git + ruby + openssh + nodejs + ]; + serviceConfig = { + Type = "simple"; + User = "gitlab"; + Group = "gitlab"; + TimeoutSec = "300"; + WorkingDirectory = "${pkgs.gitlab}/share/gitlab"; + ExecStart="${bundler}/bin/bundle exec \"sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.stateDir}/tmp/sidekiq.pid\""; + }; + }; + + systemd.services.gitlab = { + after = [ "network.target" "postgresql.service" "redis.service" ]; + wantedBy = [ "multi-user.target" ]; + environment.HOME = "${cfg.stateDir}/home"; + environment.GEM_HOME = gemHome; + environment.UNICORN_PATH = "${cfg.stateDir}/"; + environment.GITLAB_PATH = "${pkgs.gitlab}/share/gitlab/"; + environment.GITLAB_APPLICATION_LOG_PATH = "${cfg.stateDir}/log/application.log"; + environment.GITLAB_SATELLITES_PATH = "${cfg.stateDir}/satellites"; + environment.GITLAB_SHELL_PATH = "${pkgs.gitlab-shell}"; + environment.GITLAB_REPOSITORIES_PATH = "${cfg.stateDir}/repositories"; + environment.GITLAB_SHELL_HOOKS_PATH = "${cfg.stateDir}/shell/hooks"; + environment.BUNDLE_GEMFILE = "${pkgs.gitlab}/share/gitlab/Gemfile"; + environment.GITLAB_EMAIL_FROM = "${cfg.emailFrom}"; + environment.GITLAB_HOST = "${cfg.host}"; + environment.GITLAB_PORT = "${toString cfg.port}"; + environment.GITLAB_DATABASE_HOST = "${cfg.databaseHost}"; + environment.GITLAB_DATABASE_PASSWORD = "${cfg.databasePassword}"; + environment.RAILS_ENV = "production"; + path = with pkgs; [ + config.services.postgresql.package + gitAndTools.git + ruby + openssh + nodejs + ]; + preStart = '' + # TODO: use env vars + mkdir -p ${cfg.stateDir} + mkdir -p ${cfg.stateDir}/log + mkdir -p ${cfg.stateDir}/satellites + mkdir -p ${cfg.stateDir}/repositories + mkdir -p ${cfg.stateDir}/shell/hooks + mkdir -p ${cfg.stateDir}/tmp/pids + mkdir -p ${cfg.stateDir}/tmp/sockets + rm -rf ${cfg.stateDir}/config + mkdir -p ${cfg.stateDir}/config + # TODO: What exactly is gitlab-shell doing with the secret? + head -c 20 /dev/urandom > ${cfg.stateDir}/config/gitlab_shell_secret + mkdir -p ${cfg.stateDir}/home/.ssh + touch ${cfg.stateDir}/home/.ssh/authorized_keys + + cp -rf ${pkgs.gitlab}/share/gitlab/config ${cfg.stateDir}/ + cp ${pkgs.gitlab}/share/gitlab/VERSION ${cfg.stateDir}/VERSION + + ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.stateDir}/config/database.yml + ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.stateDir}/config/unicorn.rb + + chown -R gitlab:gitlab ${cfg.stateDir}/ + chmod -R 755 ${cfg.stateDir}/ + + if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then + if ! test -e "${cfg.stateDir}/db-created"; then + psql postgres -c "CREATE ROLE gitlab WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.databasePassword}'" + ${config.services.postgresql.package}/bin/createdb --owner gitlab gitlab || true + touch "${cfg.stateDir}/db-created" + + # force=yes disables the manual-interaction yes/no prompt + # which breaks without an stdin. + force=yes ${bundler}/bin/bundle exec rake -f ${pkgs.gitlab}/share/gitlab/Rakefile gitlab:setup RAILS_ENV=production + fi + fi + + # Install the shell required to push repositories + ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} ${cfg.stateDir}/shell/config.yml + export GITLAB_SHELL_CONFIG_PATH=""${cfg.stateDir}/shell/config.yml + ${pkgs.gitlab-shell}/bin/install + + # Change permissions in the last step because some of the + # intermediary scripts like to create directories as root. + chown -R gitlab:gitlab ${cfg.stateDir}/ + chmod -R 755 ${cfg.stateDir}/ + ''; + + serviceConfig = { + PermissionsStartOnly = true; # preStart must be run as root + Type = "simple"; + User = "gitlab"; + Group = "gitlab"; + TimeoutSec = "300"; + WorkingDirectory = "${pkgs.gitlab}/share/gitlab"; + ExecStart="${bundler}/bin/bundle exec \"unicorn -c ${cfg.stateDir}/config/unicorn.rb -E production\""; + }; + + }; + + }; + +} |