2022-08-04 16:56:48 +00:00
|
|
|
{ config, lib, options, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.woodpecker-server;
|
|
|
|
useMysql = cfg.database.type == "mysql";
|
|
|
|
usePostgresql = cfg.database.type == "postgres";
|
|
|
|
useSqlite = cfg.database.type == "sqlite3";
|
|
|
|
in
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
services.woodpecker-server = {
|
|
|
|
enable = mkOption {
|
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
description = lib.mdDoc "Enable Woodpecker Server.";
|
|
|
|
};
|
|
|
|
|
|
|
|
stateDir = mkOption {
|
|
|
|
default = "/var/lib/woodpecker-server";
|
|
|
|
type = types.str;
|
|
|
|
description = lib.mdDoc "woodpecker server data directory.";
|
|
|
|
};
|
|
|
|
|
|
|
|
user = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "woodpecker-server";
|
|
|
|
description = lib.mdDoc "User account under which woodpecker server runs.";
|
|
|
|
};
|
|
|
|
|
|
|
|
rootUrl = mkOption {
|
|
|
|
default = "http://localhost:3030";
|
|
|
|
type = types.str;
|
|
|
|
description = lib.mkDoc "Full public URL of Woodpecker server";
|
|
|
|
};
|
|
|
|
|
|
|
|
httpPort = mkOption {
|
2022-08-14 07:39:04 +00:00
|
|
|
type = types.port;
|
2022-08-04 16:56:48 +00:00
|
|
|
default = 3030;
|
|
|
|
description = lib.mdDoc "HTTP listen port.";
|
|
|
|
};
|
|
|
|
|
|
|
|
gRPCPort = mkOption {
|
2022-08-14 07:39:04 +00:00
|
|
|
type = types.port;
|
2022-08-04 16:56:48 +00:00
|
|
|
default = 9000;
|
|
|
|
description = lib.mdDoc "The gPRC listener port.";
|
|
|
|
};
|
|
|
|
|
|
|
|
admins = mkOption {
|
|
|
|
default = "";
|
|
|
|
type = types.str;
|
|
|
|
description = lib.mdDoc "Woodpecker admin users.";
|
|
|
|
};
|
|
|
|
|
|
|
|
agentSecretFile = mkOption {
|
|
|
|
type = types.nullOr types.path;
|
|
|
|
default = null;
|
|
|
|
description = lib.mdDoc "Read the agent secret from this file path.";
|
|
|
|
};
|
|
|
|
|
|
|
|
database = {
|
|
|
|
type = mkOption {
|
|
|
|
type = types.enum [ "sqlite3" "mysql" "postgres" ];
|
|
|
|
example = "mysql";
|
|
|
|
default = "sqlite3";
|
|
|
|
description = lib.mdDoc "Database engine to use.";
|
|
|
|
};
|
|
|
|
|
|
|
|
host = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "127.0.0.1";
|
|
|
|
description = lib.mdDoc "Database host address.";
|
|
|
|
};
|
|
|
|
|
|
|
|
port = mkOption {
|
|
|
|
type = types.port;
|
2022-08-14 07:39:04 +00:00
|
|
|
default = (if !usePostgresql then 3306 else config.services.postgresql.port);
|
2022-08-04 16:56:48 +00:00
|
|
|
defaultText = literalExpression ''
|
|
|
|
if config.${opt.database.type} != "postgresql"
|
|
|
|
then 3306
|
|
|
|
else config.${options.services.postgresql.port}
|
|
|
|
'';
|
|
|
|
description = lib.mdDoc "Database host port.";
|
|
|
|
};
|
|
|
|
|
|
|
|
name = mkOption {
|
|
|
|
type = types.str;
|
2022-08-14 07:39:04 +00:00
|
|
|
default = "woodpecker_server";
|
2022-08-04 16:56:48 +00:00
|
|
|
description = lib.mdDoc "Database name.";
|
|
|
|
};
|
|
|
|
|
|
|
|
password = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "";
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
The password corresponding to {option}`database.user`.
|
|
|
|
Warning: this is stored in cleartext in the Nix store!
|
|
|
|
Use {option}`database.passwordFile` instead.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
user = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "woodpecker-server";
|
|
|
|
description = lib.mdDoc "Database user.";
|
|
|
|
};
|
|
|
|
|
|
|
|
socket = mkOption {
|
|
|
|
type = types.nullOr types.path;
|
|
|
|
default = if (cfg.database.createDatabase && usePostgresql) then "/run/postgresql" else if (cfg.database.createDatabase && useMysql) then "/run/mysqld/mysqld.sock" else null;
|
|
|
|
defaultText = literalExpression "null";
|
|
|
|
example = "/run/mysqld/mysqld.sock";
|
|
|
|
description = lib.mdDoc "Path to the unix socket file to use for authentication.";
|
|
|
|
};
|
|
|
|
|
|
|
|
createDatabase = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = true;
|
|
|
|
description = lib.mdDoc "Whether to create a local database automatically.";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-08-14 07:39:04 +00:00
|
|
|
limitMem = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 0;
|
|
|
|
description = lib.mdDoc "The maximum amount of memory a single pipeline container can use, configured in bytes. There is no limit if 0.";
|
|
|
|
};
|
|
|
|
|
|
|
|
limitSwap = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 0;
|
|
|
|
description = lib.mdDoc "The maximum amount of memory a single pipeline container is allowed to swap to disk, configured in bytes. There is no limit if 0.";
|
|
|
|
};
|
|
|
|
|
|
|
|
limitCPU = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 0;
|
|
|
|
description = lib.mdDoc "The number of microseconds per CPU period that the container is limited to before throttled. There is no limit if 0.";
|
|
|
|
};
|
|
|
|
|
2022-08-04 16:56:48 +00:00
|
|
|
useGitea = mkOption {
|
2022-08-14 07:39:04 +00:00
|
|
|
default = config.services.gitea.enable;
|
2022-08-04 16:56:48 +00:00
|
|
|
type = types.bool;
|
|
|
|
description = lib.mkDoc "Whether to integrate with gitea.";
|
|
|
|
};
|
|
|
|
|
|
|
|
giteaUrl = mkOption {
|
2022-08-14 07:39:04 +00:00
|
|
|
default = config.services.gitea.rootUrl;
|
2022-08-04 16:56:48 +00:00
|
|
|
type = types.str;
|
|
|
|
description = lib.mkDoc "Full public URL of gitea server.";
|
|
|
|
};
|
|
|
|
|
|
|
|
giteaClientIdFile = mkOption {
|
|
|
|
type = types.nullOr types.path;
|
|
|
|
default = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
giteaClientSecretFile = mkOption {
|
|
|
|
type = types.nullOr types.path;
|
|
|
|
default = null;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
2022-08-14 07:39:04 +00:00
|
|
|
assertions = [
|
|
|
|
{ assertion = cfg.database.createDatabase -> cfg.database.user == cfg.user;
|
|
|
|
message = "services.woodpecker-server.database.user must match services.woodpecker-server.user if the database is to be automatically provisioned";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
2022-08-04 16:56:48 +00:00
|
|
|
systemd.services.woodpecker-server = {
|
|
|
|
description = "woodpecker-server";
|
|
|
|
after = [ "network.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
2022-08-14 07:39:04 +00:00
|
|
|
serviceConfig = mkMerge [
|
|
|
|
{
|
|
|
|
Type = "simple";
|
|
|
|
User = cfg.user;
|
|
|
|
Group = "woodpecker-server";
|
|
|
|
WorkingDirectory = cfg.stateDir;
|
|
|
|
Restart = "always";
|
|
|
|
# TODO add security/sandbox params.
|
|
|
|
}
|
|
|
|
(if cfg.useGitea then {
|
|
|
|
# HACK For some godforsaken reason this seems to be needed.
|
|
|
|
ExecStart="/bin/sh -c '" +
|
|
|
|
"WOODPECKER_GITEA_CLIENT=$(cat \"${cfg.giteaClientIdFile}\") " +
|
|
|
|
"WOODPECKER_GITEA_SECRET=$(cat \"${cfg.giteaClientSecretFile}\") " +
|
|
|
|
"\"${pkgs.woodpecker-server}/bin/woodpecker-server\"'";
|
|
|
|
} else {
|
|
|
|
ExecStart = "${pkgs.woodpecker-server}/bin/woodpecker-server";
|
|
|
|
})
|
|
|
|
];
|
2022-08-04 16:56:48 +00:00
|
|
|
environment = mkMerge [
|
|
|
|
{
|
2022-08-14 07:39:04 +00:00
|
|
|
WOODPECKER_OPEN="false";
|
2022-08-04 16:56:48 +00:00
|
|
|
WOODPECKER_ADMIN=cfg.admins;
|
|
|
|
WOODPECKER_HOST=cfg.rootUrl;
|
|
|
|
WOODPECKER_SERVER_ADDR=":${toString cfg.httpPort}";
|
2022-08-14 07:39:04 +00:00
|
|
|
WOODPECKER_GRPC_ADDR=":${toString cfg.gRPCPort}";
|
|
|
|
WOODPECKER_LIMIT_MEM_SWAP=toString cfg.limitSwap;
|
|
|
|
WOODPECKER_LIMIT_MEM=toString cfg.limitMem;
|
|
|
|
WOODPECKER_LIMIT_CPU_QUOTA=toString cfg.limitCPU;
|
2022-08-04 16:56:48 +00:00
|
|
|
}
|
|
|
|
(mkIf cfg.useGitea {
|
2022-08-14 07:39:04 +00:00
|
|
|
WOODPECKER_GITEA="true";
|
2022-08-04 16:56:48 +00:00
|
|
|
WOODPECKER_GITEA_URL=cfg.giteaUrl;
|
2022-08-14 07:39:04 +00:00
|
|
|
# WOODPECKER_GITEA_CLIENT_FILE=cfg.giteaClientIdFile;
|
|
|
|
# WOODPECKER_GITEA_SECRET_FILE=cfg.giteaClientSecretFile;
|
2022-08-04 16:56:48 +00:00
|
|
|
})
|
|
|
|
(mkIf usePostgresql {
|
|
|
|
WOODPECKER_DATABASE_DRIVER="postgres";
|
|
|
|
WOODPECKER_DATABASE_DATASOURCE=
|
2022-08-14 07:39:04 +00:00
|
|
|
"postgres://${cfg.database.user}:${cfg.database.password}" +
|
|
|
|
"@/${cfg.database.name}" +
|
2022-08-04 16:56:48 +00:00
|
|
|
"?host=${if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port}";
|
|
|
|
})
|
|
|
|
(mkIf (cfg.agentSecretFile != null) {
|
|
|
|
WOODPECKER_AGENT_SECRET_FILE=cfg.agentSecretFile;
|
|
|
|
})
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
services.postgresql = optionalAttrs (usePostgresql && cfg.database.createDatabase) {
|
|
|
|
enable = mkDefault true;
|
|
|
|
|
|
|
|
ensureDatabases = [ cfg.database.name ];
|
|
|
|
ensureUsers = [
|
|
|
|
{ name = cfg.database.user;
|
|
|
|
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
services.mysql = optionalAttrs (useMysql && cfg.database.createDatabase) {
|
|
|
|
enable = mkDefault true;
|
|
|
|
package = mkDefault pkgs.mariadb;
|
|
|
|
|
|
|
|
ensureDatabases = [ cfg.database.name ];
|
|
|
|
ensureUsers = [
|
|
|
|
{ name = cfg.database.user;
|
|
|
|
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
users.users = mkIf (cfg.user == "woodpecker-server") {
|
|
|
|
woodpecker-server = {
|
|
|
|
createHome = true;
|
|
|
|
home = cfg.stateDir;
|
|
|
|
useDefaultShell = true;
|
|
|
|
group = "woodpecker-server";
|
|
|
|
extraGroups = [ "woodpecker" ];
|
|
|
|
isSystemUser = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
users.groups.woodpecker-server = { };
|
|
|
|
};
|
|
|
|
}
|