uboot: (firmwareOdroidC2/C4) don't invoke patch tool, use patches = [] instead

https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh#L948
this can do it nicely.

Signed-off-by: Anton Arapov <anton@deadbeef.mx>
This commit is contained in:
Anton Arapov 2021-04-03 12:58:10 +02:00 committed by Alan Daniels
commit 56de2bcd43
30691 changed files with 3076956 additions and 0 deletions

View file

@ -0,0 +1,60 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.blackfire-agent;
agentConfigFile = lib.generators.toINI {} {
blackfire = cfg.settings;
};
agentSock = "blackfire/agent.sock";
in {
meta = {
maintainers = pkgs.blackfire.meta.maintainers;
doc = ./blackfire.xml;
};
options = {
services.blackfire-agent = {
enable = lib.mkEnableOption "Blackfire profiler agent";
settings = lib.mkOption {
description = ''
See https://blackfire.io/docs/up-and-running/configuration/agent
'';
type = lib.types.submodule {
freeformType = with lib.types; attrsOf str;
options = {
server-id = lib.mkOption {
type = lib.types.str;
description = ''
Sets the server id used to authenticate with Blackfire
You can find your personal server-id at https://blackfire.io/my/settings/credentials
'';
};
server-token = lib.mkOption {
type = lib.types.str;
description = ''
Sets the server token used to authenticate with Blackfire
You can find your personal server-token at https://blackfire.io/my/settings/credentials
'';
};
};
};
};
};
};
config = lib.mkIf cfg.enable {
environment.etc."blackfire/agent".text = agentConfigFile;
services.blackfire-agent.settings.socket = "unix:///run/${agentSock}";
systemd.packages = [
pkgs.blackfire
];
};
}

View file

@ -0,0 +1,46 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="module-services-blackfire">
<title>Blackfire profiler</title>
<para>
<emphasis>Source:</emphasis>
<filename>modules/services/development/blackfire.nix</filename>
</para>
<para>
<emphasis>Upstream documentation:</emphasis>
<link xlink:href="https://blackfire.io/docs/introduction"/>
</para>
<para>
<link xlink:href="https://blackfire.io">Blackfire</link> is a proprietary tool for profiling applications. There are several languages supported by the product but currently only PHP support is packaged in Nixpkgs. The back-end consists of a module that is loaded into the language runtime (called <firstterm>probe</firstterm>) and a service (<firstterm>agent</firstterm>) that the probe connects to and that sends the profiles to the server.
</para>
<para>
To use it, you will need to enable the agent and the probe on your server. The exact method will depend on the way you use PHP but here is an example of NixOS configuration for PHP-FPM:
<programlisting>let
php = pkgs.php.withExtensions ({ enabled, all }: enabled ++ (with all; [
blackfire
]));
in {
# Enable the probe extension for PHP-FPM.
services.phpfpm = {
phpPackage = php;
};
# Enable and configure the agent.
services.blackfire-agent = {
enable = true;
settings = {
# You will need to get credentials at https://blackfire.io/my/settings/credentials
# You can also use other options described in https://blackfire.io/docs/up-and-running/configuration/agent
server-id = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
server-token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
};
};
# Make the agent run on start-up.
# (WantedBy= from the upstream unit not respected: https://github.com/NixOS/nixpkgs/issues/81138)
# Alternately, you can start it manually with `systemctl start blackfire-agent`.
systemd.services.blackfire-agent.wantedBy = [ "phpfpm-foo.service" ];
}</programlisting>
</para>
<para>
On your developer machine, you will also want to install <link xlink:href="https://blackfire.io/docs/up-and-running/installation#install-a-profiling-client">the client</link> (see <package>blackfire</package> package) or the browser extension to actually trigger the profiling.
</para>
</chapter>

View file

@ -0,0 +1,54 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.bloop;
in {
options.services.bloop = {
extraOptions = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"-J-Xmx2G"
"-J-XX:MaxInlineLevel=20"
"-J-XX:+UseParallelGC"
];
description = ''
Specifies additional command line argument to pass to bloop
java process.
'';
};
install = mkOption {
type = types.bool;
default = false;
description = ''
Whether to install a user service for the Bloop server.
The service must be manually started for each user with
"systemctl --user start bloop".
'';
};
};
config = mkIf (cfg.install) {
systemd.user.services.bloop = {
description = "Bloop Scala build server";
environment = {
PATH = mkForce "${makeBinPath [ config.programs.java.package ]}";
};
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.bloop}/bin/bloop server";
Restart = "always";
};
};
environment.systemPackages = [ pkgs.bloop ];
};
}

View file

@ -0,0 +1,155 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.distccd;
in
{
options = {
services.distccd = {
enable = mkEnableOption "distccd";
allowedClients = mkOption {
type = types.listOf types.str;
default = [ "127.0.0.1" ];
example = [ "127.0.0.1" "192.168.0.0/24" "10.0.0.0/24" ];
description = ''
Client IPs which are allowed to connect to distccd in CIDR notation.
Anyone who can connect to the distccd server can run arbitrary
commands on that system as the distcc user, therefore you should use
this judiciously.
'';
};
jobTimeout = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
Maximum duration, in seconds, of a single compilation request.
'';
};
logLevel = mkOption {
type = types.nullOr (types.enum [ "critical" "error" "warning" "notice" "info" "debug" ]);
default = "warning";
description = ''
Set the minimum severity of error that will be included in the log
file. Useful if you only want to see error messages rather than an
entry for each connection.
'';
};
maxJobs = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
Maximum number of tasks distccd should execute at any time.
'';
};
nice = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
Niceness of the compilation tasks.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Opens the specified TCP port for distcc.
'';
};
package = mkOption {
type = types.package;
default = pkgs.distcc;
defaultText = literalExpression "pkgs.distcc";
description = ''
The distcc package to use.
'';
};
port = mkOption {
type = types.port;
default = 3632;
description = ''
The TCP port which distccd will listen on.
'';
};
stats = {
enable = mkEnableOption "statistics reporting via HTTP server";
port = mkOption {
type = types.port;
default = 3633;
description = ''
The TCP port which the distccd statistics HTTP server will listen
on.
'';
};
};
zeroconf = mkOption {
type = types.bool;
default = false;
description = ''
Whether to register via mDNS/DNS-SD
'';
};
};
};
config = mkIf cfg.enable {
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ]
++ optionals cfg.stats.enable [ cfg.stats.port ];
};
systemd.services.distccd = {
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
description = "Distributed C, C++ and Objective-C compiler";
documentation = [ "man:distccd(1)" ];
serviceConfig = {
User = "distcc";
Group = "distcc";
# FIXME: I'd love to get rid of `--enable-tcp-insecure` here, but I'm
# not sure how I'm supposed to get distccd to "accept" running a binary
# (the compiler) that's outside of /usr/lib.
ExecStart = pkgs.writeShellScript "start-distccd" ''
export PATH="${pkgs.distccMasquerade}/bin"
${cfg.package}/bin/distccd \
--no-detach \
--daemon \
--enable-tcp-insecure \
--port ${toString cfg.port} \
${optionalString (cfg.jobTimeout != null) "--job-lifetime ${toString cfg.jobTimeout}"} \
${optionalString (cfg.logLevel != null) "--log-level ${cfg.logLevel}"} \
${optionalString (cfg.maxJobs != null) "--jobs ${toString cfg.maxJobs}"} \
${optionalString (cfg.nice != null) "--nice ${toString cfg.nice}"} \
${optionalString cfg.stats.enable "--stats"} \
${optionalString cfg.stats.enable "--stats-port ${toString cfg.stats.port}"} \
${optionalString cfg.zeroconf "--zeroconf"} \
${concatMapStrings (c: "--allow ${c} ") cfg.allowedClients}
'';
};
};
users = {
groups.distcc.gid = config.ids.gids.distcc;
users.distcc = {
description = "distccd user";
group = "distcc";
uid = config.ids.uids.distcc;
};
};
};
}

View file

@ -0,0 +1,81 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.hoogle;
hoogleEnv = pkgs.buildEnv {
name = "hoogle";
paths = [ (cfg.haskellPackages.ghcWithHoogle cfg.packages) ];
};
in {
options.services.hoogle = {
enable = mkEnableOption "Haskell documentation server";
port = mkOption {
type = types.port;
default = 8080;
description = ''
Port number Hoogle will be listening to.
'';
};
packages = mkOption {
type = types.functionTo (types.listOf types.package);
default = hp: [];
defaultText = literalExpression "hp: []";
example = literalExpression "hp: with hp; [ text lens ]";
description = ''
The Haskell packages to generate documentation for.
The option value is a function that takes the package set specified in
the <varname>haskellPackages</varname> option as its sole parameter and
returns a list of packages.
'';
};
haskellPackages = mkOption {
description = "Which haskell package set to use.";
type = types.attrs;
default = pkgs.haskellPackages;
defaultText = literalExpression "pkgs.haskellPackages";
};
home = mkOption {
type = types.str;
description = "Url for hoogle logo";
default = "https://hoogle.haskell.org";
};
host = mkOption {
type = types.str;
description = "Set the host to bind on.";
default = "127.0.0.1";
};
};
config = mkIf cfg.enable {
systemd.services.hoogle = {
description = "Haskell documentation server";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Restart = "always";
ExecStart = ''${hoogleEnv}/bin/hoogle server --local --port ${toString cfg.port} --home ${cfg.home} --host ${cfg.host}'';
DynamicUser = true;
ProtectHome = true;
RuntimeDirectory = "hoogle";
WorkingDirectory = "%t/hoogle";
};
};
};
}

View file

@ -0,0 +1,202 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.jupyter;
package = cfg.package;
kernels = (pkgs.jupyter-kernel.create {
definitions = if cfg.kernels != null
then cfg.kernels
else pkgs.jupyter-kernel.default;
});
notebookConfig = pkgs.writeText "jupyter_config.py" ''
${cfg.notebookConfig}
c.NotebookApp.password = ${cfg.password}
'';
in {
meta.maintainers = with maintainers; [ aborsu ];
options.services.jupyter = {
enable = mkEnableOption "Jupyter development server";
ip = mkOption {
type = types.str;
default = "localhost";
description = ''
IP address Jupyter will be listening on.
'';
};
package = mkOption {
type = types.package;
# NOTE: We don't use top-level jupyter because we don't
# want to pass in JUPYTER_PATH but use .environment instead,
# saving a rebuild.
default = pkgs.python3.pkgs.notebook;
defaultText = literalExpression "pkgs.python3.pkgs.notebook";
description = ''
Jupyter package to use.
'';
};
command = mkOption {
type = types.str;
default = "jupyter-notebook";
example = "jupyter-lab";
description = ''
Which command the service runs. Note that not all jupyter packages
have all commands, e.g. jupyter-lab isn't present in the default package.
'';
};
port = mkOption {
type = types.int;
default = 8888;
description = ''
Port number Jupyter will be listening on.
'';
};
notebookDir = mkOption {
type = types.str;
default = "~/";
description = ''
Root directory for notebooks.
'';
};
user = mkOption {
type = types.str;
default = "jupyter";
description = ''
Name of the user used to run the jupyter service.
For security reason, jupyter should really not be run as root.
If not set (jupyter), the service will create a jupyter user with appropriate settings.
'';
example = "aborsu";
};
group = mkOption {
type = types.str;
default = "jupyter";
description = ''
Name of the group used to run the jupyter service.
Use this if you want to create a group of users that are able to view the notebook directory's content.
'';
example = "users";
};
password = mkOption {
type = types.str;
description = ''
Password to use with notebook.
Can be generated using:
In [1]: from notebook.auth import passwd
In [2]: passwd('test')
Out[2]: 'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'
NOTE: you need to keep the single quote inside the nix string.
Or you can use a python oneliner:
"open('/path/secret_file', 'r', encoding='utf8').read().strip()"
It will be interpreted at the end of the notebookConfig.
'';
example = "'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'";
};
notebookConfig = mkOption {
type = types.lines;
default = "";
description = ''
Raw jupyter config.
'';
};
kernels = mkOption {
type = types.nullOr (types.attrsOf(types.submodule (import ./kernel-options.nix {
inherit lib;
})));
default = null;
example = literalExpression ''
{
python3 = let
env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
ipykernel
pandas
scikit-learn
]));
in {
displayName = "Python 3 for machine learning";
argv = [
"''${env.interpreter}"
"-m"
"ipykernel_launcher"
"-f"
"{connection_file}"
];
language = "python";
logo32 = "''${env.sitePackages}/ipykernel/resources/logo-32x32.png";
logo64 = "''${env.sitePackages}/ipykernel/resources/logo-64x64.png";
};
}
'';
description = "Declarative kernel config
Kernels can be declared in any language that supports and has the required
dependencies to communicate with a jupyter server.
In python's case, it means that ipykernel package must always be included in
the list of packages of the targeted environment.
";
};
};
config = mkMerge [
(mkIf cfg.enable {
systemd.services.jupyter = {
description = "Jupyter development server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
# TODO: Patch notebook so we can explicitly pass in a shell
path = [ pkgs.bash ]; # needed for sh in cell magic to work
environment = {
JUPYTER_PATH = toString kernels;
};
serviceConfig = {
Restart = "always";
ExecStart = ''${package}/bin/${cfg.command} \
--no-browser \
--ip=${cfg.ip} \
--port=${toString cfg.port} --port-retries 0 \
--notebook-dir=${cfg.notebookDir} \
--NotebookApp.config_file=${notebookConfig}
'';
User = cfg.user;
Group = cfg.group;
WorkingDirectory = "~";
};
};
})
(mkIf (cfg.enable && (cfg.group == "jupyter")) {
users.groups.jupyter = {};
})
(mkIf (cfg.enable && (cfg.user == "jupyter")) {
users.extraUsers.jupyter = {
extraGroups = [ cfg.group ];
home = "/var/lib/jupyter";
createHome = true;
isSystemUser = true;
useDefaultShell = true; # needed so that the user can start a terminal.
};
})
];
}

View file

@ -0,0 +1,60 @@
# Options that can be used for creating a jupyter kernel.
{lib }:
with lib;
{
options = {
displayName = mkOption {
type = types.str;
default = "";
example = literalExpression ''
"Python 3"
"Python 3 for Data Science"
'';
description = ''
Name that will be shown to the user.
'';
};
argv = mkOption {
type = types.listOf types.str;
example = [
"{customEnv.interpreter}"
"-m"
"ipykernel_launcher"
"-f"
"{connection_file}"
];
description = ''
Command and arguments to start the kernel.
'';
};
language = mkOption {
type = types.str;
example = "python";
description = ''
Language of the environment. Typically the name of the binary.
'';
};
logo32 = mkOption {
type = types.nullOr types.path;
default = null;
example = literalExpression ''"''${env.sitePackages}/ipykernel/resources/logo-32x32.png"'';
description = ''
Path to 32x32 logo png.
'';
};
logo64 = mkOption {
type = types.nullOr types.path;
default = null;
example = literalExpression ''"''${env.sitePackages}/ipykernel/resources/logo-64x64.png"'';
description = ''
Path to 64x64 logo png.
'';
};
};
}

View file

@ -0,0 +1,202 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.jupyterhub;
kernels = (pkgs.jupyter-kernel.create {
definitions = if cfg.kernels != null
then cfg.kernels
else pkgs.jupyter-kernel.default;
});
jupyterhubConfig = pkgs.writeText "jupyterhub_config.py" ''
c.JupyterHub.bind_url = "http://${cfg.host}:${toString cfg.port}"
c.JupyterHub.authenticator_class = "${cfg.authentication}"
c.JupyterHub.spawner_class = "${cfg.spawner}"
c.SystemdSpawner.default_url = '/lab'
c.SystemdSpawner.cmd = "${cfg.jupyterlabEnv}/bin/jupyterhub-singleuser"
c.SystemdSpawner.environment = {
'JUPYTER_PATH': '${kernels}'
}
${cfg.extraConfig}
'';
in {
meta.maintainers = with maintainers; [ costrouc ];
options.services.jupyterhub = {
enable = mkEnableOption "Jupyterhub development server";
authentication = mkOption {
type = types.str;
default = "jupyterhub.auth.PAMAuthenticator";
description = ''
Jupyterhub authentication to use
There are many authenticators available including: oauth, pam,
ldap, kerberos, etc.
'';
};
spawner = mkOption {
type = types.str;
default = "systemdspawner.SystemdSpawner";
description = ''
Jupyterhub spawner to use
There are many spawners available including: local process,
systemd, docker, kubernetes, yarn, batch, etc.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Extra contents appended to the jupyterhub configuration
Jupyterhub configuration is a normal python file using
Traitlets. https://jupyterhub.readthedocs.io/en/stable/getting-started/config-basics.html. The
base configuration of this module was designed to have sane
defaults for configuration but you can override anything since
this is a python file.
'';
example = ''
c.SystemdSpawner.mem_limit = '8G'
c.SystemdSpawner.cpu_limit = 2.0
'';
};
jupyterhubEnv = mkOption {
type = types.package;
default = pkgs.python3.withPackages (p: with p; [
jupyterhub
jupyterhub-systemdspawner
]);
defaultText = literalExpression ''
pkgs.python3.withPackages (p: with p; [
jupyterhub
jupyterhub-systemdspawner
])
'';
description = ''
Python environment to run jupyterhub
Customizing will affect the packages available in the hub and
proxy. This will allow packages to be available for the
extraConfig that you may need. This will not normally need to
be changed.
'';
};
jupyterlabEnv = mkOption {
type = types.package;
default = pkgs.python3.withPackages (p: with p; [
jupyterhub
jupyterlab
]);
defaultText = literalExpression ''
pkgs.python3.withPackages (p: with p; [
jupyterhub
jupyterlab
])
'';
description = ''
Python environment to run jupyterlab
Customizing will affect the packages available in the
jupyterlab server and the default kernel provided. This is the
way to customize the jupyterlab extensions and jupyter
notebook extensions. This will not normally need to
be changed.
'';
};
kernels = mkOption {
type = types.nullOr (types.attrsOf(types.submodule (import ../jupyter/kernel-options.nix {
inherit lib;
})));
default = null;
example = literalExpression ''
{
python3 = let
env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
ipykernel
pandas
scikit-learn
]));
in {
displayName = "Python 3 for machine learning";
argv = [
"''${env.interpreter}"
"-m"
"ipykernel_launcher"
"-f"
"{connection_file}"
];
language = "python";
logo32 = "''${env}/''${env.sitePackages}/ipykernel/resources/logo-32x32.png";
logo64 = "''${env}/''${env.sitePackages}/ipykernel/resources/logo-64x64.png";
};
}
'';
description = ''
Declarative kernel config
Kernels can be declared in any language that supports and has
the required dependencies to communicate with a jupyter server.
In python's case, it means that ipykernel package must always be
included in the list of packages of the targeted environment.
'';
};
port = mkOption {
type = types.port;
default = 8000;
description = ''
Port number Jupyterhub will be listening on
'';
};
host = mkOption {
type = types.str;
default = "0.0.0.0";
description = ''
Bind IP JupyterHub will be listening on
'';
};
stateDirectory = mkOption {
type = types.str;
default = "jupyterhub";
description = ''
Directory for jupyterhub state (token + database)
'';
};
};
config = mkMerge [
(mkIf cfg.enable {
systemd.services.jupyterhub = {
description = "Jupyterhub development server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Restart = "always";
ExecStart = "${cfg.jupyterhubEnv}/bin/jupyterhub --config ${jupyterhubConfig}";
User = "root";
StateDirectory = cfg.stateDirectory;
WorkingDirectory = "/var/lib/${cfg.stateDirectory}";
};
};
})
];
}

View file

@ -0,0 +1,55 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.lorri;
socketPath = "lorri/daemon.socket";
in {
options = {
services.lorri = {
enable = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Enables the daemon for `lorri`, a nix-shell replacement for project
development. The socket-activated daemon starts on the first request
issued by the `lorri` command.
'';
};
package = lib.mkOption {
default = pkgs.lorri;
type = lib.types.package;
description = ''
The lorri package to use.
'';
defaultText = lib.literalExpression "pkgs.lorri";
};
};
};
config = lib.mkIf cfg.enable {
systemd.user.sockets.lorri = {
description = "Socket for Lorri Daemon";
wantedBy = [ "sockets.target" ];
socketConfig = {
ListenStream = "%t/${socketPath}";
RuntimeDirectory = "lorri";
};
};
systemd.user.services.lorri = {
description = "Lorri Daemon";
requires = [ "lorri.socket" ];
after = [ "lorri.socket" ];
path = with pkgs; [ config.nix.package git gnutar gzip ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/lorri daemon";
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = "read-only";
Restart = "on-failure";
};
};
environment.systemPackages = [ cfg.package ];
};
}

View file

@ -0,0 +1,107 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.rstudio-server;
rserver-conf = builtins.toFile "rserver.conf" ''
server-working-dir=${cfg.serverWorkingDir}
www-address=${cfg.listenAddr}
${cfg.rserverExtraConfig}
'';
rsession-conf = builtins.toFile "rsession.conf" ''
${cfg.rsessionExtraConfig}
'';
in
{
meta.maintainers = with maintainers; [ jbedo cfhammill ];
options.services.rstudio-server = {
enable = mkEnableOption "RStudio server";
serverWorkingDir = mkOption {
type = types.str;
default = "/var/lib/rstudio-server";
description = ''
Default working directory for server (server-working-dir in rserver.conf).
'';
};
listenAddr = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Address to listen on (www-address in rserver.conf).
'';
};
package = mkOption {
type = types.package;
default = pkgs.rstudio-server;
defaultText = literalExpression "pkgs.rstudio-server";
example = literalExpression "pkgs.rstudioServerWrapper.override { packages = [ pkgs.rPackages.ggplot2 ]; }";
description = ''
Rstudio server package to use. Can be set to rstudioServerWrapper to provide packages.
'';
};
rserverExtraConfig = mkOption {
type = types.str;
default = "";
description = ''
Extra contents for rserver.conf.
'';
};
rsessionExtraConfig = mkOption {
type = types.str;
default = "";
description = ''
Extra contents for resssion.conf.
'';
};
};
config = mkIf cfg.enable
{
systemd.services.rstudio-server = {
description = "Rstudio server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ rserver-conf rsession-conf ];
serviceConfig = {
Restart = "on-failure";
Type = "forking";
ExecStart = "${cfg.package}/bin/rserver";
StateDirectory = "rstudio-server";
RuntimeDirectory = "rstudio-server";
};
};
environment.etc = {
"rstudio/rserver.conf".source = rserver-conf;
"rstudio/rsession.conf".source = rsession-conf;
"pam.d/rstudio".source = "/etc/pam.d/login";
};
environment.systemPackages = [ cfg.package ];
users = {
users.rstudio-server = {
uid = config.ids.uids.rstudio-server;
description = "rstudio-server";
group = "rstudio-server";
};
groups.rstudio-server = {
gid = config.ids.gids.rstudio-server;
};
};
};
}

View file

@ -0,0 +1,323 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.zammad;
settingsFormat = pkgs.formats.yaml { };
filterNull = filterAttrs (_: v: v != null);
serviceConfig = {
Type = "simple";
Restart = "always";
User = "zammad";
Group = "zammad";
PrivateTmp = true;
StateDirectory = "zammad";
WorkingDirectory = cfg.dataDir;
};
environment = {
RAILS_ENV = "production";
NODE_ENV = "production";
RAILS_SERVE_STATIC_FILES = "true";
RAILS_LOG_TO_STDOUT = "true";
};
databaseConfig = settingsFormat.generate "database.yml" cfg.database.settings;
in
{
options = {
services.zammad = {
enable = mkEnableOption "Zammad, a web-based, open source user support/ticketing solution.";
package = mkOption {
type = types.package;
default = pkgs.zammad;
defaultText = literalExpression "pkgs.zammad";
description = "Zammad package to use.";
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/zammad";
description = ''
Path to a folder that will contain Zammad working directory.
'';
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "192.168.23.42";
description = "Host address.";
};
openPorts = mkOption {
type = types.bool;
default = false;
description = "Whether to open firewall ports for Zammad";
};
port = mkOption {
type = types.port;
default = 3000;
description = "Web service port.";
};
websocketPort = mkOption {
type = types.port;
default = 6042;
description = "Websocket service port.";
};
database = {
type = mkOption {
type = types.enum [ "PostgreSQL" "MySQL" ];
default = "PostgreSQL";
example = "MySQL";
description = "Database engine to use.";
};
host = mkOption {
type = types.nullOr types.str;
default = {
PostgreSQL = "/run/postgresql";
MySQL = "localhost";
}.${cfg.database.type};
defaultText = literalExpression ''
{
PostgreSQL = "/run/postgresql";
MySQL = "localhost";
}.''${config.services.zammad.database.type};
'';
description = ''
Database host address.
'';
};
port = mkOption {
type = types.nullOr types.port;
default = null;
description = "Database port. Use <literal>null</literal> for default port.";
};
name = mkOption {
type = types.str;
default = "zammad";
description = ''
Database name.
'';
};
user = mkOption {
type = types.nullOr types.str;
default = "zammad";
description = "Database user.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/zammad-dbpassword";
description = ''
A file containing the password for <option>services.zammad.database.user</option>.
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = "Whether to create a local database automatically.";
};
settings = mkOption {
type = settingsFormat.type;
default = { };
example = literalExpression ''
{
}
'';
description = ''
The <filename>database.yml</filename> configuration file as key value set.
See <link xlink:href='TODO' />
for list of configuration parameters.
'';
};
};
secretKeyBaseFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/secret_key_base";
description = ''
The path to a file containing the
<literal>secret_key_base</literal> secret.
Zammad uses <literal>secret_key_base</literal> to encrypt
the cookie store, which contains session data, and to digest
user auth tokens.
Needs to be a 64 byte long string of hexadecimal
characters. You can generate one by running
<screen>
<prompt>$ </prompt>openssl rand -hex 64 >/path/to/secret_key_base_file
</screen>
This should be a string, not a nix path, since nix paths are
copied into the world-readable nix store.
'';
};
};
};
config = mkIf cfg.enable {
services.zammad.database.settings = {
production = mapAttrs (_: v: mkDefault v) (filterNull {
adapter = {
PostgreSQL = "postgresql";
MySQL = "mysql2";
}.${cfg.database.type};
database = cfg.database.name;
pool = 50;
timeout = 5000;
encoding = "utf8";
username = cfg.database.user;
host = cfg.database.host;
port = cfg.database.port;
});
};
networking.firewall.allowedTCPPorts = mkIf cfg.openPorts [
config.services.zammad.port
config.services.zammad.websocketPort
];
users.users.zammad = {
isSystemUser = true;
home = cfg.dataDir;
group = "zammad";
};
users.groups.zammad = { };
assertions = [
{
assertion = cfg.database.createLocally -> cfg.database.user == "zammad";
message = "services.zammad.database.user must be set to \"zammad\" if services.zammad.database.createLocally is set to true";
}
{
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = "a password cannot be specified if services.zammad.database.createLocally is set to true";
}
];
services.mysql = optionalAttrs (cfg.database.createLocally && cfg.database.type == "MySQL") {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{
name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
services.postgresql = optionalAttrs (cfg.database.createLocally && cfg.database.type == "PostgreSQL") {
enable = true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{
name = cfg.database.user;
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
systemd.services.zammad-web = {
inherit environment;
serviceConfig = serviceConfig // {
# loading all the gems takes time
TimeoutStartSec = 1200;
};
after = [
"network.target"
"postgresql.service"
];
requires = [
"postgresql.service"
];
description = "Zammad web";
wantedBy = [ "multi-user.target" ];
preStart = ''
# Blindly copy the whole project here.
chmod -R +w .
rm -rf ./public/assets/*
rm -rf ./tmp/*
rm -rf ./log/*
cp -r --no-preserve=owner ${cfg.package}/* .
chmod -R +w .
# config file
cp ${databaseConfig} ./config/database.yml
chmod -R +w .
${optionalString (cfg.database.passwordFile != null) ''
{
echo -n " password: "
cat ${cfg.database.passwordFile}
} >> ./config/database.yml
''}
${optionalString (cfg.secretKeyBaseFile != null) ''
{
echo "production: "
echo -n " secret_key_base: "
cat ${cfg.secretKeyBaseFile}
} > ./config/secrets.yml
''}
if [ `${config.services.postgresql.package}/bin/psql \
--host ${cfg.database.host} \
${optionalString
(cfg.database.port != null)
"--port ${toString cfg.database.port}"} \
--username ${cfg.database.user} \
--dbname ${cfg.database.name} \
--command "SELECT COUNT(*) FROM pg_class c \
JOIN pg_namespace s ON s.oid = c.relnamespace \
WHERE s.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') \
AND s.nspname NOT LIKE 'pg_temp%';" | sed -n 3p` -eq 0 ]; then
echo "Initialize database"
./bin/rake --no-system db:migrate
./bin/rake --no-system db:seed
else
echo "Migrate database"
./bin/rake --no-system db:migrate
fi
echo "Done"
'';
script = "./script/rails server -b ${cfg.host} -p ${toString cfg.port}";
};
systemd.services.zammad-websocket = {
inherit serviceConfig environment;
after = [ "zammad-web.service" ];
requires = [ "zammad-web.service" ];
description = "Zammad websocket";
wantedBy = [ "multi-user.target" ];
script = "./script/websocket-server.rb -b ${cfg.host} -p ${toString cfg.websocketPort} start";
};
systemd.services.zammad-scheduler = {
inherit environment;
serviceConfig = serviceConfig // { Type = "forking"; };
after = [ "zammad-web.service" ];
requires = [ "zammad-web.service" ];
description = "Zammad scheduler";
wantedBy = [ "multi-user.target" ];
script = "./script/scheduler.rb start";
};
};
meta.maintainers = with lib.maintainers; [ garbas taeer ];
}