nixpkgs/nixos/modules/services/misc/gitlab.nix
2015-12-04 01:14:24 +01:00

319 lines
12 KiB
Nix

{ 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"
secret_file: "${cfg.stateDir}/config/gitlab_shell_secret"
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 = [ pkgs.git 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;
# 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-git-http-server = {
after = [ "network.target" "gitlab.service" ];
wantedBy = [ "multi-user.target" ];
environment.HOME = "${cfg.stateDir}/home";
path = with pkgs; [
gitAndTools.git
openssh
];
serviceConfig = {
Type = "simple";
User = "gitlab";
Group = "gitlab";
TimeoutSec = "300";
ExecStart = "${pkgs.gitlab-git-http-server}/bin/gitlab-git-http-server -listenUmask 0 -listenNetwork unix -listenAddr ${cfg.stateDir}/tmp/sockets/gitlab-git-http-server.socket -authBackend http://localhost:8080 ${cfg.stateDir}/repositories";
};
};
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_SHELL_CONFIG_PATH = "${cfg.stateDir}/shell/config.yml";
environment.GITLAB_SHELL_SECRET_PATH = "${cfg.stateDir}/config/gitlab_shell_secret";
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?
tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 20 > ${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
${bundler}/bin/bundle exec rake -f ${pkgs.gitlab}/share/gitlab/Rakefile db:migrate RAILS_ENV=production
# 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\"";
};
};
};
}