mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-23 15:33:13 +00:00
Merge master into staging-next
This commit is contained in:
commit
4ba71fb819
@ -894,6 +894,14 @@ environment.systemPackages = [
|
||||
Please test your setup and container images with containerd prior to upgrading.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The GitLab module now has support for automatic backups. A
|
||||
schedule can be set with the
|
||||
<link linkend="opt-services.gitlab.backup.startAt">services.gitlab.backup.startAt</link>
|
||||
option.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -8,31 +8,28 @@ let
|
||||
pythonEnv = pkgs.python3.withPackages (ps: with ps;
|
||||
[ etebase-server daphne ]);
|
||||
|
||||
dbConfig = {
|
||||
sqlite3 = ''
|
||||
engine = django.db.backends.sqlite3
|
||||
name = ${cfg.dataDir}/db.sqlite3
|
||||
'';
|
||||
};
|
||||
iniFmt = pkgs.formats.ini {};
|
||||
|
||||
defaultConfigIni = toString (pkgs.writeText "etebase-server.ini" ''
|
||||
[global]
|
||||
debug = false
|
||||
secret_file = ${if cfg.secretFile != null then cfg.secretFile else ""}
|
||||
media_root = ${cfg.dataDir}/media
|
||||
|
||||
[allowed_hosts]
|
||||
allowed_host1 = ${cfg.host}
|
||||
|
||||
[database]
|
||||
${dbConfig."${cfg.database.type}"}
|
||||
'');
|
||||
|
||||
configIni = if cfg.customIni != null then cfg.customIni else defaultConfigIni;
|
||||
configIni = iniFmt.generate "etebase-server.ini" cfg.settings;
|
||||
|
||||
defaultUser = "etebase-server";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule
|
||||
[ "services" "etebase-server" "customIni" ]
|
||||
"Set the option `services.etebase-server.settings' instead.")
|
||||
(mkRemovedOptionModule
|
||||
[ "services" "etebase-server" "database" ]
|
||||
"Set the option `services.etebase-server.settings.database' instead.")
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "etebase-server" "secretFile" ]
|
||||
[ "services" "etebase-server" "settings" "secret_file" ])
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "etebase-server" "host" ]
|
||||
[ "services" "etebase-server" "settings" "allowed_hosts" "allowed_host1" ])
|
||||
];
|
||||
|
||||
options = {
|
||||
services.etebase-server = {
|
||||
enable = mkOption {
|
||||
@ -42,21 +39,13 @@ in
|
||||
description = ''
|
||||
Whether to enable the Etebase server.
|
||||
|
||||
Once enabled you need to create an admin user using the
|
||||
shell command <literal>etebase-server createsuperuser</literal>.
|
||||
Once enabled you need to create an admin user by invoking the
|
||||
shell command <literal>etebase-server createsuperuser</literal> with
|
||||
the user specified by the <literal>user</literal> option or a superuser.
|
||||
Then you can login and create accounts on your-etebase-server.com/admin
|
||||
'';
|
||||
};
|
||||
|
||||
secretFile = mkOption {
|
||||
default = null;
|
||||
type = with types; nullOr str;
|
||||
description = ''
|
||||
The path to a file containing the secret
|
||||
used as django's SECRET_KEY.
|
||||
'';
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/etebase-server";
|
||||
@ -77,15 +66,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
example = "localhost";
|
||||
description = ''
|
||||
Host to listen on.
|
||||
'';
|
||||
};
|
||||
|
||||
unixSocket = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
@ -93,42 +73,81 @@ in
|
||||
example = "/run/etebase-server/etebase-server.sock";
|
||||
};
|
||||
|
||||
database = {
|
||||
type = mkOption {
|
||||
type = types.enum [ "sqlite3" ];
|
||||
default = "sqlite3";
|
||||
description = ''
|
||||
Database engine to use.
|
||||
Currently only sqlite3 is supported.
|
||||
Other options can be configured using <literal>extraConfig</literal>.
|
||||
'';
|
||||
settings = mkOption {
|
||||
type = lib.types.submodule {
|
||||
freeformType = iniFmt.type;
|
||||
|
||||
options = {
|
||||
global = {
|
||||
debug = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to set django's DEBUG flag.
|
||||
'';
|
||||
};
|
||||
secret_file = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
The path to a file containing the secret
|
||||
used as django's SECRET_KEY.
|
||||
'';
|
||||
};
|
||||
static_root = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.dataDir}/static";
|
||||
defaultText = "\${config.services.etebase-server.dataDir}/static";
|
||||
description = "The directory for static files.";
|
||||
};
|
||||
media_root = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.dataDir}/media";
|
||||
defaultText = "\${config.services.etebase-server.dataDir}/media";
|
||||
description = "The media directory.";
|
||||
};
|
||||
};
|
||||
allowed_hosts = {
|
||||
allowed_host1 = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
example = "localhost";
|
||||
description = ''
|
||||
The main host that is allowed access.
|
||||
'';
|
||||
};
|
||||
};
|
||||
database = {
|
||||
engine = mkOption {
|
||||
type = types.enum [ "django.db.backends.sqlite3" "django.db.backends.postgresql" ];
|
||||
default = "django.db.backends.sqlite3";
|
||||
description = "The database engine to use.";
|
||||
};
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.dataDir}/db.sqlite3";
|
||||
defaultText = "\${config.services.etebase-server.dataDir}/db.sqlite3";
|
||||
description = "The database name.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
customIni = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
default = {};
|
||||
description = ''
|
||||
Custom etebase-server.ini.
|
||||
|
||||
See <literal>etebase-src/etebase-server.ini.example</literal> for available options.
|
||||
|
||||
Setting this option overrides the default config which is generated from the options
|
||||
<literal>secretFile</literal>, <literal>host</literal> and <literal>database</literal>.
|
||||
'';
|
||||
example = literalExample ''
|
||||
[global]
|
||||
debug = false
|
||||
secret_file = /path/to/secret
|
||||
media_root = /path/to/media
|
||||
|
||||
[allowed_hosts]
|
||||
allowed_host1 = example.com
|
||||
|
||||
[database]
|
||||
engine = django.db.backends.sqlite3
|
||||
name = db.sqlite3
|
||||
Configuration for <package>etebase-server</package>. Refer to
|
||||
<link xlink:href="https://github.com/etesync/server/blob/master/etebase-server.ini.example" />
|
||||
and <link xlink:href="https://github.com/etesync/server/wiki" />
|
||||
for details on supported values.
|
||||
'';
|
||||
example = {
|
||||
global = {
|
||||
debug = true;
|
||||
media_root = "/path/to/media";
|
||||
};
|
||||
allowed_hosts = {
|
||||
allowed_host2 = "localhost";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
@ -166,14 +185,15 @@ in
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
};
|
||||
environment = {
|
||||
PYTHONPATH="${pythonEnv}/${pkgs.python3.sitePackages}";
|
||||
ETEBASE_EASY_CONFIG_PATH="${configIni}";
|
||||
PYTHONPATH = "${pythonEnv}/${pkgs.python3.sitePackages}";
|
||||
ETEBASE_EASY_CONFIG_PATH = configIni;
|
||||
};
|
||||
preStart = ''
|
||||
# Auto-migrate on first run or if the package has changed
|
||||
versionFile="${cfg.dataDir}/src-version"
|
||||
if [[ $(cat "$versionFile" 2>/dev/null) != ${pkgs.etebase-server} ]]; then
|
||||
${pythonEnv}/bin/etebase-server migrate
|
||||
${pythonEnv}/bin/etebase-server collectstatic
|
||||
echo ${pkgs.etebase-server} > "$versionFile"
|
||||
fi
|
||||
'';
|
||||
|
@ -116,7 +116,11 @@ let
|
||||
omniauth.enabled = false;
|
||||
shared.path = "${cfg.statePath}/shared";
|
||||
gitaly.client_path = "${cfg.packages.gitaly}/bin";
|
||||
backup.path = "${cfg.backupPath}";
|
||||
backup = {
|
||||
path = cfg.backup.path;
|
||||
keep_time = cfg.backup.keepTime;
|
||||
upload = cfg.backup.uploadOptions;
|
||||
};
|
||||
gitlab_shell = {
|
||||
path = "${cfg.packages.gitlab-shell}";
|
||||
hooks_path = "${cfg.statePath}/shell/hooks";
|
||||
@ -207,6 +211,7 @@ in {
|
||||
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
|
||||
(mkRenamedOptionModule [ "services" "gitlab" "backupPath" ] [ "services" "gitlab" "backup" "path" ])
|
||||
(mkRemovedOptionModule [ "services" "gitlab" "satelliteDir" ] "")
|
||||
];
|
||||
|
||||
@ -260,7 +265,7 @@ in {
|
||||
type = types.str;
|
||||
default = "/var/gitlab/state";
|
||||
description = ''
|
||||
Gitlab state directory. Configuration, repositories and
|
||||
GitLab state directory. Configuration, repositories and
|
||||
logs, among other things, are stored here.
|
||||
|
||||
The directory will be created automatically if it doesn't
|
||||
@ -270,17 +275,108 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
backupPath = mkOption {
|
||||
backup.startAt = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = [];
|
||||
example = "03:00";
|
||||
description = ''
|
||||
The time(s) to run automatic backup of GitLab
|
||||
state. Specified in systemd's time format; see
|
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle>
|
||||
<manvolnum>7</manvolnum></citerefentry>.
|
||||
'';
|
||||
};
|
||||
|
||||
backup.path = mkOption {
|
||||
type = types.str;
|
||||
default = cfg.statePath + "/backup";
|
||||
description = "Gitlab path for backups.";
|
||||
description = "GitLab path for backups.";
|
||||
};
|
||||
|
||||
backup.keepTime = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
example = 48;
|
||||
apply = x: x * 60 * 60;
|
||||
description = ''
|
||||
How long to keep the backups around, in
|
||||
hours. <literal>0</literal> means <quote>keep
|
||||
forever</quote>.
|
||||
'';
|
||||
};
|
||||
|
||||
backup.skip = mkOption {
|
||||
type = with types;
|
||||
let value = enum [
|
||||
"db"
|
||||
"uploads"
|
||||
"builds"
|
||||
"artifacts"
|
||||
"lfs"
|
||||
"registry"
|
||||
"pages"
|
||||
"repositories"
|
||||
"tar"
|
||||
];
|
||||
in
|
||||
either value (listOf value);
|
||||
default = [];
|
||||
example = [ "artifacts" "lfs" ];
|
||||
apply = x: if isString x then x else concatStringsSep "," x;
|
||||
description = ''
|
||||
Directories to exclude from the backup. The example excludes
|
||||
CI artifacts and LFS objects from the backups. The
|
||||
<literal>tar</literal> option skips the creation of a tar
|
||||
file.
|
||||
|
||||
Refer to <link xlink:href="https://docs.gitlab.com/ee/raketasks/backup_restore.html#excluding-specific-directories-from-the-backup"/>
|
||||
for more information.
|
||||
'';
|
||||
};
|
||||
|
||||
backup.uploadOptions = mkOption {
|
||||
type = types.attrs;
|
||||
default = {};
|
||||
example = literalExample ''
|
||||
{
|
||||
# Fog storage connection settings, see http://fog.io/storage/
|
||||
connection = {
|
||||
provider = "AWS";
|
||||
region = "eu-north-1";
|
||||
aws_access_key_id = "AKIAXXXXXXXXXXXXXXXX";
|
||||
aws_secret_access_key = { _secret = config.deployment.keys.aws_access_key.path; };
|
||||
};
|
||||
|
||||
# The remote 'directory' to store your backups in.
|
||||
# For S3, this would be the bucket name.
|
||||
remote_directory = "my-gitlab-backups";
|
||||
|
||||
# Use multipart uploads when file size reaches 100MB, see
|
||||
# http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html
|
||||
multipart_chunk_size = 104857600;
|
||||
|
||||
# Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional
|
||||
encryption = "AES256";
|
||||
|
||||
# Specifies Amazon S3 storage class to use for backups, this is optional
|
||||
storage_class = "STANDARD";
|
||||
};
|
||||
'';
|
||||
description = ''
|
||||
GitLab automatic upload specification. Tells GitLab to
|
||||
upload the backup to a remote location when done.
|
||||
|
||||
Attributes specified here are added under
|
||||
<literal>production -> backup -> upload</literal> in
|
||||
<filename>config/gitlab.yml</filename>.
|
||||
'';
|
||||
};
|
||||
|
||||
databaseHost = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = ''
|
||||
Gitlab database hostname. An empty string means <quote>use
|
||||
GitLab database hostname. An empty string means <quote>use
|
||||
local unix socket connection</quote>.
|
||||
'';
|
||||
};
|
||||
@ -289,7 +385,7 @@ in {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = ''
|
||||
File containing the Gitlab database user password.
|
||||
File containing the GitLab database user password.
|
||||
|
||||
This should be a string, not a nix path, since nix paths are
|
||||
copied into the world-readable nix store.
|
||||
@ -310,13 +406,13 @@ in {
|
||||
databaseName = mkOption {
|
||||
type = types.str;
|
||||
default = "gitlab";
|
||||
description = "Gitlab database name.";
|
||||
description = "GitLab database name.";
|
||||
};
|
||||
|
||||
databaseUsername = mkOption {
|
||||
type = types.str;
|
||||
default = "gitlab";
|
||||
description = "Gitlab database user.";
|
||||
description = "GitLab database user.";
|
||||
};
|
||||
|
||||
databasePool = mkOption {
|
||||
@ -360,14 +456,14 @@ in {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = config.networking.hostName;
|
||||
description = "Gitlab host name. Used e.g. for copy-paste URLs.";
|
||||
description = "GitLab host name. Used e.g. for copy-paste URLs.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
default = 8080;
|
||||
description = ''
|
||||
Gitlab server port for copy-paste URLs, e.g. 80 or 443 if you're
|
||||
GitLab server port for copy-paste URLs, e.g. 80 or 443 if you're
|
||||
service over https.
|
||||
'';
|
||||
};
|
||||
@ -420,26 +516,26 @@ in {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Address of the SMTP server for Gitlab.";
|
||||
description = "Address of the SMTP server for GitLab.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
default = 25;
|
||||
description = "Port of the SMTP server for Gitlab.";
|
||||
description = "Port of the SMTP server for GitLab.";
|
||||
};
|
||||
|
||||
username = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = "Username of the SMTP server for Gitlab.";
|
||||
description = "Username of the SMTP server for GitLab.";
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = ''
|
||||
File containing the password of the SMTP server for Gitlab.
|
||||
File containing the password of the SMTP server for GitLab.
|
||||
|
||||
This should be a string, not a nix path, since nix paths
|
||||
are copied into the world-readable nix store.
|
||||
@ -720,7 +816,7 @@ in {
|
||||
"d /run/gitlab 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -"
|
||||
"z ${gitlabEnv.HOME}/.ssh/authorized_keys 0600 ${cfg.user} ${cfg.group} -"
|
||||
"d ${cfg.backupPath} 0750 ${cfg.user} ${cfg.group} -"
|
||||
"d ${cfg.backup.path} 0750 ${cfg.user} ${cfg.group} -"
|
||||
"d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -"
|
||||
"d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -"
|
||||
"d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -"
|
||||
@ -1053,6 +1149,23 @@ in {
|
||||
|
||||
};
|
||||
|
||||
systemd.services.gitlab-backup = {
|
||||
after = [ "gitlab.service" ];
|
||||
bindsTo = [ "gitlab.service" ];
|
||||
startAt = cfg.backup.startAt;
|
||||
environment = {
|
||||
RAILS_ENV = "production";
|
||||
CRON = "1";
|
||||
} // optionalAttrs (stringLength cfg.backup.skip > 0) {
|
||||
SKIP = cfg.backup.skip;
|
||||
};
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${gitlab-rake}/bin/gitlab-rake gitlab:backup:create";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
meta.doc = ./gitlab.xml;
|
||||
|
@ -3,15 +3,15 @@
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
version="5.0"
|
||||
xml:id="module-services-gitlab">
|
||||
<title>Gitlab</title>
|
||||
<title>GitLab</title>
|
||||
<para>
|
||||
Gitlab is a feature-rich git hosting service.
|
||||
GitLab is a feature-rich git hosting service.
|
||||
</para>
|
||||
<section xml:id="module-services-gitlab-prerequisites">
|
||||
<title>Prerequisites</title>
|
||||
|
||||
<para>
|
||||
The gitlab service exposes only an Unix socket at
|
||||
The <literal>gitlab</literal> service exposes only an Unix socket at
|
||||
<literal>/run/gitlab/gitlab-workhorse.socket</literal>. You need to
|
||||
configure a webserver to proxy HTTP requests to the socket.
|
||||
</para>
|
||||
@ -39,7 +39,7 @@
|
||||
<title>Configuring</title>
|
||||
|
||||
<para>
|
||||
Gitlab depends on both PostgreSQL and Redis and will automatically enable
|
||||
GitLab depends on both PostgreSQL and Redis and will automatically enable
|
||||
both services. In the case of PostgreSQL, a database and a role will be
|
||||
created.
|
||||
</para>
|
||||
@ -85,20 +85,20 @@ services.gitlab = {
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you're setting up a new Gitlab instance, generate new
|
||||
If you're setting up a new GitLab instance, generate new
|
||||
secrets. You for instance use <literal>tr -dc A-Za-z0-9 <
|
||||
/dev/urandom | head -c 128 > /var/keys/gitlab/db</literal> to
|
||||
generate a new db secret. Make sure the files can be read by, and
|
||||
only by, the user specified by <link
|
||||
linkend="opt-services.gitlab.user">services.gitlab.user</link>. Gitlab
|
||||
linkend="opt-services.gitlab.user">services.gitlab.user</link>. GitLab
|
||||
encrypts sensitive data stored in the database. If you're restoring
|
||||
an existing Gitlab instance, you must specify the secrets secret
|
||||
from <literal>config/secrets.yml</literal> located in your Gitlab
|
||||
an existing GitLab instance, you must specify the secrets secret
|
||||
from <literal>config/secrets.yml</literal> located in your GitLab
|
||||
state folder.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When <literal>icoming_mail.enabled</literal> is set to <literal>true</literal>
|
||||
When <literal>incoming_mail.enabled</literal> is set to <literal>true</literal>
|
||||
in <link linkend="opt-services.gitlab.extraConfig">extraConfig</link> an additional
|
||||
service called <literal>gitlab-mailroom</literal> is enabled for fetching incoming mail.
|
||||
</para>
|
||||
@ -112,21 +112,40 @@ services.gitlab = {
|
||||
<section xml:id="module-services-gitlab-maintenance">
|
||||
<title>Maintenance</title>
|
||||
|
||||
<para>
|
||||
You can run Gitlab's rake tasks with <literal>gitlab-rake</literal> which
|
||||
will be available on the system when gitlab is enabled. You will have to run
|
||||
the command as the user that you configured to run gitlab with.
|
||||
</para>
|
||||
<section xml:id="module-services-gitlab-maintenance-backups">
|
||||
<title>Backups</title>
|
||||
<para>
|
||||
Backups can be configured with the options in <link
|
||||
linkend="opt-services.gitlab.backup.keepTime">services.gitlab.backup</link>. Use
|
||||
the <link
|
||||
linkend="opt-services.gitlab.backup.startAt">services.gitlab.backup.startAt</link>
|
||||
option to configure regular backups.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For example, to backup a Gitlab instance:
|
||||
<para>
|
||||
To run a manual backup, start the <literal>gitlab-backup</literal> service:
|
||||
<screen>
|
||||
<prompt>$ </prompt>sudo -u git -H gitlab-rake gitlab:backup:create
|
||||
<prompt>$ </prompt>systemctl start gitlab-backup.service
|
||||
</screen>
|
||||
A list of all availabe rake tasks can be obtained by running:
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section xml:id="module-services-gitlab-maintenance-rake">
|
||||
<title>Rake tasks</title>
|
||||
|
||||
<para>
|
||||
You can run GitLab's rake tasks with <literal>gitlab-rake</literal>
|
||||
which will be available on the system when GitLab is enabled. You
|
||||
will have to run the command as the user that you configured to run
|
||||
GitLab with.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A list of all availabe rake tasks can be obtained by running:
|
||||
<screen>
|
||||
<prompt>$ </prompt>sudo -u git -H gitlab-rake -T
|
||||
</screen>
|
||||
</para>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
@ -110,6 +110,7 @@ in
|
||||
ergo = handleTest ./ergo.nix {};
|
||||
etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
|
||||
etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
|
||||
etebase-server = handleTest ./etebase-server.nix {};
|
||||
etesync-dav = handleTest ./etesync-dav.nix {};
|
||||
fancontrol = handleTest ./fancontrol.nix {};
|
||||
fcitx = handleTest ./fcitx {};
|
||||
|
50
nixos/tests/etebase-server.nix
Normal file
50
nixos/tests/etebase-server.nix
Normal file
@ -0,0 +1,50 @@
|
||||
import ./make-test-python.nix ({ pkgs, ... }:
|
||||
|
||||
let
|
||||
dataDir = "/var/lib/foobar";
|
||||
|
||||
in {
|
||||
name = "etebase-server";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [ felschr ];
|
||||
};
|
||||
|
||||
machine = { pkgs, ... }:
|
||||
{
|
||||
services.etebase-server = {
|
||||
inherit dataDir;
|
||||
enable = true;
|
||||
settings.global.secret_file =
|
||||
toString (pkgs.writeText "secret" "123456");
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("etebase-server.service")
|
||||
machine.wait_for_open_port(8001)
|
||||
|
||||
with subtest("Database & src-version were created"):
|
||||
machine.wait_for_file("${dataDir}/src-version")
|
||||
assert (
|
||||
"${pkgs.etebase-server}"
|
||||
in machine.succeed("cat ${dataDir}/src-version")
|
||||
)
|
||||
machine.wait_for_file("${dataDir}/db.sqlite3")
|
||||
machine.wait_for_file("${dataDir}/static")
|
||||
|
||||
with subtest("Only allow access from allowed_hosts"):
|
||||
machine.succeed("curl -sSfL http://0.0.0.0:8001/")
|
||||
machine.fail("curl -sSfL http://127.0.0.1:8001/")
|
||||
machine.fail("curl -sSfL http://localhost:8001/")
|
||||
|
||||
with subtest("Run tests"):
|
||||
machine.succeed("etebase-server check")
|
||||
machine.succeed("etebase-server test")
|
||||
|
||||
with subtest("Create superuser"):
|
||||
machine.succeed(
|
||||
"etebase-server createsuperuser --no-input --username admin --email root@localhost"
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
@ -34,6 +34,8 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; {
|
||||
enableImap = true;
|
||||
};
|
||||
|
||||
systemd.services.gitlab-backup.environment.BACKUP = "dump";
|
||||
|
||||
services.gitlab = {
|
||||
enable = true;
|
||||
databasePasswordFile = pkgs.writeText "dbPassword" "xo0daiF4";
|
||||
@ -64,60 +66,89 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; {
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
let
|
||||
auth = pkgs.writeText "auth.json" (builtins.toJSON {
|
||||
grant_type = "password";
|
||||
username = "root";
|
||||
password = initialRootPassword;
|
||||
});
|
||||
testScript = { nodes, ... }:
|
||||
let
|
||||
auth = pkgs.writeText "auth.json" (builtins.toJSON {
|
||||
grant_type = "password";
|
||||
username = "root";
|
||||
password = initialRootPassword;
|
||||
});
|
||||
|
||||
createProject = pkgs.writeText "create-project.json" (builtins.toJSON {
|
||||
name = "test";
|
||||
});
|
||||
createProject = pkgs.writeText "create-project.json" (builtins.toJSON {
|
||||
name = "test";
|
||||
});
|
||||
|
||||
putFile = pkgs.writeText "put-file.json" (builtins.toJSON {
|
||||
branch = "master";
|
||||
author_email = "author@example.com";
|
||||
author_name = "Firstname Lastname";
|
||||
content = "some content";
|
||||
commit_message = "create a new file";
|
||||
});
|
||||
in
|
||||
''
|
||||
gitlab.start()
|
||||
putFile = pkgs.writeText "put-file.json" (builtins.toJSON {
|
||||
branch = "master";
|
||||
author_email = "author@example.com";
|
||||
author_name = "Firstname Lastname";
|
||||
content = "some content";
|
||||
commit_message = "create a new file";
|
||||
});
|
||||
|
||||
gitlab.wait_for_unit("gitaly.service")
|
||||
gitlab.wait_for_unit("gitlab-workhorse.service")
|
||||
gitlab.wait_for_unit("gitlab-pages.service")
|
||||
gitlab.wait_for_unit("gitlab-mailroom.service")
|
||||
gitlab.wait_for_unit("gitlab.service")
|
||||
gitlab.wait_for_unit("gitlab-sidekiq.service")
|
||||
gitlab.wait_for_file("/var/gitlab/state/tmp/sockets/gitlab.socket")
|
||||
gitlab.wait_until_succeeds("curl -sSf http://gitlab/users/sign_in")
|
||||
# Wait for all GitLab services to be fully started.
|
||||
waitForServices = ''
|
||||
gitlab.wait_for_unit("gitaly.service")
|
||||
gitlab.wait_for_unit("gitlab-workhorse.service")
|
||||
gitlab.wait_for_unit("gitlab-pages.service")
|
||||
gitlab.wait_for_unit("gitlab-mailroom.service")
|
||||
gitlab.wait_for_unit("gitlab.service")
|
||||
gitlab.wait_for_unit("gitlab-sidekiq.service")
|
||||
gitlab.wait_for_file("${nodes.gitlab.config.services.gitlab.statePath}/tmp/sockets/gitlab.socket")
|
||||
gitlab.wait_until_succeeds("curl -sSf http://gitlab/users/sign_in")
|
||||
'';
|
||||
|
||||
gitlab.succeed(
|
||||
"curl -isSf http://gitlab | grep -i location | grep -q http://gitlab/users/sign_in"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"${pkgs.sudo}/bin/sudo -u gitlab -H gitlab-rake gitlab:check 1>&2"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"echo \"Authorization: Bearer \$(curl -X POST -H 'Content-Type: application/json' -d @${auth} http://gitlab/oauth/token | ${pkgs.jq}/bin/jq -r '.access_token')\" >/tmp/headers"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"curl -X POST -H 'Content-Type: application/json' -H @/tmp/headers -d @${createProject} http://gitlab/api/v4/projects"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"curl -X POST -H 'Content-Type: application/json' -H @/tmp/headers -d @${putFile} http://gitlab/api/v4/projects/1/repository/files/some-file.txt"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"curl -H @/tmp/headers http://gitlab/api/v4/projects/1/repository/archive.tar.gz > /tmp/archive.tar.gz"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"curl -H @/tmp/headers http://gitlab/api/v4/projects/1/repository/archive.tar.bz2 > /tmp/archive.tar.bz2"
|
||||
)
|
||||
gitlab.succeed("test -s /tmp/archive.tar.gz")
|
||||
gitlab.succeed("test -s /tmp/archive.tar.bz2")
|
||||
'';
|
||||
# The actual test of GitLab. Only push data to GitLab if
|
||||
# `doSetup` is is true.
|
||||
test = doSetup: ''
|
||||
gitlab.succeed(
|
||||
"curl -isSf http://gitlab | grep -i location | grep -q http://gitlab/users/sign_in"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"${pkgs.sudo}/bin/sudo -u gitlab -H gitlab-rake gitlab:check 1>&2"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"echo \"Authorization: Bearer \$(curl -X POST -H 'Content-Type: application/json' -d @${auth} http://gitlab/oauth/token | ${pkgs.jq}/bin/jq -r '.access_token')\" >/tmp/headers"
|
||||
)
|
||||
'' + optionalString doSetup ''
|
||||
gitlab.succeed(
|
||||
"curl -X POST -H 'Content-Type: application/json' -H @/tmp/headers -d @${createProject} http://gitlab/api/v4/projects"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"curl -X POST -H 'Content-Type: application/json' -H @/tmp/headers -d @${putFile} http://gitlab/api/v4/projects/1/repository/files/some-file.txt"
|
||||
)
|
||||
'' + ''
|
||||
gitlab.succeed(
|
||||
"curl -H @/tmp/headers http://gitlab/api/v4/projects/1/repository/archive.tar.gz > /tmp/archive.tar.gz"
|
||||
)
|
||||
gitlab.succeed(
|
||||
"curl -H @/tmp/headers http://gitlab/api/v4/projects/1/repository/archive.tar.bz2 > /tmp/archive.tar.bz2"
|
||||
)
|
||||
gitlab.succeed("test -s /tmp/archive.tar.gz")
|
||||
gitlab.succeed("test -s /tmp/archive.tar.bz2")
|
||||
'';
|
||||
|
||||
in ''
|
||||
gitlab.start()
|
||||
''
|
||||
+ waitForServices
|
||||
+ test true
|
||||
+ ''
|
||||
gitlab.systemctl("start gitlab-backup.service")
|
||||
gitlab.wait_for_unit("gitlab-backup.service")
|
||||
gitlab.wait_for_file("${nodes.gitlab.config.services.gitlab.statePath}/backup/dump_gitlab_backup.tar")
|
||||
gitlab.systemctl("stop postgresql.service gitlab.target")
|
||||
gitlab.succeed(
|
||||
"find ${nodes.gitlab.config.services.gitlab.statePath} -mindepth 1 -maxdepth 1 -not -name backup -execdir rm -r {} +"
|
||||
)
|
||||
gitlab.succeed("systemd-tmpfiles --create")
|
||||
gitlab.succeed("rm -rf ${nodes.gitlab.config.services.postgresql.dataDir}")
|
||||
gitlab.systemctl("start gitlab-config.service gitlab-postgresql.service")
|
||||
gitlab.succeed(
|
||||
"sudo -u gitlab -H gitlab-rake gitlab:backup:restore RAILS_ENV=production BACKUP=dump force=yes"
|
||||
)
|
||||
gitlab.systemctl("start gitlab.target")
|
||||
''
|
||||
+ waitForServices
|
||||
+ test false;
|
||||
})
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "clightd";
|
||||
version = "5.2";
|
||||
version = "5.3";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "FedeDP";
|
||||
repo = "Clightd";
|
||||
rev = version;
|
||||
sha256 = "sha256-bb+PQsWLf4KnbzWUY6B42yHaDQKN9dtyfwSLe8FBaWU=";
|
||||
sha256 = "sha256-CuTYCNZ9oiDsm5mUDmjbxkmAl61PEXv3WMrZRzgdZeE=";
|
||||
};
|
||||
|
||||
# dbus-1.pc has datadir=/etc
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "cpu-x";
|
||||
version = "4.0.1";
|
||||
version = "4.2.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "X0rg";
|
||||
repo = "CPU-X";
|
||||
rev = "v${version}";
|
||||
sha256 = "191zkkswlbbsw492yygc3idf7wh3bxs97drrqvqqw0mqvrzykxm3";
|
||||
sha256 = "sha256-LWIcE86o+uU8G9DtumiH6iTqHhvq4y/QyQX7J3FhKEc=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ cmake pkg-config wrapGAppsHook nasm makeWrapper ];
|
||||
|
@ -5,13 +5,13 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "dasel";
|
||||
version = "1.13.5";
|
||||
version = "1.13.6";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "TomWright";
|
||||
repo = pname;
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-Fy202w1lUrymnpnCmWwnbpMsda7JrZ3B0c+a9UtKsSA=";
|
||||
sha256 = "sha256-PTi1blbMVsuftLrFIYNDI8ZFEwRxDA53Md9oZTv7nHs=";
|
||||
};
|
||||
|
||||
vendorSha256 = "sha256-BdX4DO77mIf/+aBdkNVFUzClsIml1UMcgvikDbbdgcY=";
|
||||
|
@ -2,16 +2,16 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "gh";
|
||||
version = "1.7.0";
|
||||
version = "1.8.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "cli";
|
||||
repo = "cli";
|
||||
rev = "v${version}";
|
||||
sha256 = "0ndi264rrssqin03qmv7n0fpzs3kasfqykidrlcyizw1ngyfgc1a";
|
||||
sha256 = "009jh1i1ji7d773ismifdw4qvwlb8s1zn14d0n84j9h3n0hyynbc";
|
||||
};
|
||||
|
||||
vendorSha256 = "0ywh5d41b1c5ivwngsgn46d6yb7s1wqyzl5b0j1x4mcvydi5yi98";
|
||||
vendorSha256 = "1wv30z0jg195nkpz3rwvhixyw81lg2wzwwajq9g6s3rfjj8gs9v2";
|
||||
|
||||
nativeBuildInputs = [ installShellFiles ];
|
||||
|
||||
|
@ -83,9 +83,9 @@ stdenv.mkDerivation (
|
||||
'';
|
||||
}
|
||||
|
||||
// args //
|
||||
// removeAttrs args [ "lib" ] # Propagating lib causes the evaluation to fail, because lib is a function that can't be converted to a string
|
||||
|
||||
{
|
||||
// {
|
||||
name = name + (if src ? version then "-" + src.version else "");
|
||||
|
||||
postHook = ''
|
||||
|
@ -1,5 +1,5 @@
|
||||
{ lib, fetchPypi, buildPythonPackage
|
||||
, configobj, six, traitsui
|
||||
, fetchpatch, configobj, six, traitsui
|
||||
, pytestCheckHook, tables, pandas
|
||||
, pythonOlder, importlib-resources
|
||||
}:
|
||||
@ -13,6 +13,15 @@ buildPythonPackage rec {
|
||||
sha256 = "12x5lcs1cllpybz7f0i1lcwvmqsaa5n818wb2165lj049wqxx4yh";
|
||||
};
|
||||
|
||||
patches = [
|
||||
# python39: importlib_resources -> importlib.resources. This patch will be included
|
||||
# in the next release after 5.1.0.
|
||||
(fetchpatch {
|
||||
url = "https://github.com/enthought/apptools/commit/0ae4f52f19a8c0ca9d7926e17c7de949097f24b4.patch";
|
||||
sha256 = "165aiwjisr5c3lasg7xblcha7y1y5bq23vi3g9gc80c24bzwcbsw";
|
||||
})
|
||||
];
|
||||
|
||||
propagatedBuildInputs = [
|
||||
configobj
|
||||
six
|
||||
|
@ -1,18 +1,18 @@
|
||||
{ lib, fetchPypi, fetchpatch, isPy27
|
||||
{ lib, fetchPypi, isPy27
|
||||
, buildPythonPackage
|
||||
, traits, apptools
|
||||
, python, ipykernel, ipython
|
||||
, traits, apptools, pytestCheckHook
|
||||
, ipykernel, ipython
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "envisage";
|
||||
version = "4.9.2";
|
||||
version = "5.0.0";
|
||||
|
||||
disabled = isPy27;
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "1srjmkhnz84nz5jd72vdsnc4fn7dd9jr8nyf3hzk6yx1dsn815gd";
|
||||
sha256 = "0zrxlq4v3091727vf10ngc8418sp26raxa8q83i4h0sydfkh2dic";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [ traits apptools ];
|
||||
@ -21,22 +21,10 @@ buildPythonPackage rec {
|
||||
export HOME=$PWD/HOME
|
||||
'';
|
||||
|
||||
# fix a test failure; should be merged in next release
|
||||
patches = [ (fetchpatch {
|
||||
url = "https://github.com/enthought/envisage/pull/248/commits/7b6d2dd615d5cb7455b200eb8f37e030bbf4df9e.patch";
|
||||
sha256 = "0a3dmbpxwsn1bkjcjv9v7b751rcmppj6hc9wcgiayg4l9r2nrvyh";
|
||||
}) ];
|
||||
|
||||
checkInputs = [
|
||||
ipykernel ipython
|
||||
ipykernel ipython pytestCheckHook
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
${python.interpreter} -m unittest
|
||||
runHook postCheck
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
description = "Framework for building applications whose functionalities can be extended by adding 'plug-ins'";
|
||||
homepage = "https://github.com/enthought/envisage";
|
||||
|
@ -3,12 +3,12 @@
|
||||
, fetchFromGitHub
|
||||
, isPy3k
|
||||
, xmltodict
|
||||
, requests
|
||||
, ifaddr
|
||||
, requests
|
||||
|
||||
# Test dependencies
|
||||
, pytest, pylint, flake8, graphviz
|
||||
, mock, sphinx, sphinx_rtd_theme
|
||||
# Test dependencies
|
||||
, pytestCheckHook
|
||||
, mock
|
||||
, requests-mock
|
||||
}:
|
||||
|
||||
@ -26,17 +26,17 @@ buildPythonPackage rec {
|
||||
sha256 = "0a0c7jwv39nbvpdcx32sd8kjmj4nyrd7k0yxhpmxdnx4zr4vvzqg";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [ xmltodict requests ifaddr ];
|
||||
propagatedBuildInputs = [ ifaddr requests xmltodict ];
|
||||
|
||||
checkInputs = [
|
||||
pytest pylint flake8 graphviz
|
||||
mock sphinx sphinx_rtd_theme
|
||||
pytestCheckHook
|
||||
mock
|
||||
requests-mock
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
pytest --deselect=tests/test_discovery.py::TestDiscover::test_discover
|
||||
'';
|
||||
disabledTests = [
|
||||
"test_desc_from_uri" # test requires network access
|
||||
];
|
||||
|
||||
meta = {
|
||||
homepage = "https://github.com/amelchio/pysonos";
|
||||
|
@ -15,13 +15,13 @@ let
|
||||
};
|
||||
in stdenv.mkDerivation rec {
|
||||
pname = "mgba";
|
||||
version = "0.8.4";
|
||||
version = "0.9.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "mgba-emu";
|
||||
repo = "mgba";
|
||||
rev = version;
|
||||
sha256 = "0nqj4bnn5c2z1bq4bnbw1wznc0wpmq4sy3w8pipd6n6620b9m4qq";
|
||||
sha256 = "sha256-JVauGyHJVfiXVG4Z+Ydh1lRypy5rk9SKeTbeHFNFYJs=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ wrapQtAppsHook pkg-config cmake ];
|
||||
|
@ -299,6 +299,7 @@ in with py.pkgs; buildPythonApplication rec {
|
||||
"smarttub"
|
||||
"smtp"
|
||||
"solaredge"
|
||||
"sonos"
|
||||
"sql"
|
||||
"ssdp"
|
||||
"stream"
|
||||
|
@ -18,11 +18,11 @@ buildPythonPackage rec {
|
||||
# The websites youtube-dl deals with are a very moving target. That means that
|
||||
# downloads break constantly. Because of that, updates should always be backported
|
||||
# to the latest stable release.
|
||||
version = "2021.03.25";
|
||||
version = "2021.03.31";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://yt-dl.org/downloads/${version}/${pname}-${version}.tar.gz";
|
||||
sha256 = "0ps8ydx4hbj6sl0m760zdm9pvhccjmwvx680i4akz3lk4z9wy0x3";
|
||||
sha256 = "1svcgrhq1yxpcd6k3piqs5paalrcsq9bm79h5ras1g7yjzid05gj";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ installShellFiles makeWrapper ];
|
||||
|
@ -7,13 +7,13 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "gitjacker";
|
||||
version = "0.0.3";
|
||||
version = "0.1.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "liamg";
|
||||
repo = "gitjacker";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-cMjjVjHGTVT33bknAo2DVH/qPSeazVIIw3RpXGDxF5E=";
|
||||
sha256 = "sha256-rEn9FpcRfEt2yGepIPEAO9m8JeVb+nMhYMBWhC/barc=";
|
||||
};
|
||||
|
||||
vendorSha256 = null;
|
||||
|
@ -12,16 +12,16 @@
|
||||
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "prs";
|
||||
version = "0.2.5";
|
||||
version = "0.2.6";
|
||||
|
||||
src = fetchFromGitLab {
|
||||
owner = "timvisee";
|
||||
repo = "prs";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-XJcNhIMu60H5LmoRzMqhPq33cCU9PBPfIIUtaSnmrH8=";
|
||||
sha256 = "sha256-2fpR9XCcKby+hI7Dzpr2qi1QgOzdgJp0Um57tQmi01A=";
|
||||
};
|
||||
|
||||
cargoSha256 = "sha256-4l/KQMtGfZX5Rg35AJxvwzg3aAzuPK2iKrHDRgIw+bg=";
|
||||
cargoSha256 = "sha256-0oWNGrJ24gPkPp5PR/pQ1tIYkXztQJFAdPz162V5THY=";
|
||||
|
||||
postPatch = ''
|
||||
# The GPGME backend is recommended
|
||||
|
@ -7,13 +7,13 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "gdu";
|
||||
version = "4.9.0";
|
||||
version = "4.9.1";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "dundee";
|
||||
repo = pname;
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-fQzKWgePF7SszYMwRSB3s59AmVjvzMvXPM49PXFTBGM=";
|
||||
sha256 = "sha256-blvnwsmcHf0yH2C/NUCsVQECIH4SI0BTNiMzCuNd0H0=";
|
||||
};
|
||||
|
||||
vendorSha256 = "sha256-QiO5p0x8kmIN6f0uYS0IR2MlWtRYTHeZpW6Nmupjias=";
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
buildGoPackage rec {
|
||||
pname = "asciigraph";
|
||||
version = "0.5.1";
|
||||
version = "0.5.2";
|
||||
|
||||
goPackagePath = "github.com/guptarohit/asciigraph";
|
||||
|
||||
@ -10,7 +10,7 @@ buildGoPackage rec {
|
||||
owner = "guptarohit";
|
||||
repo = pname;
|
||||
rev = "v${version}";
|
||||
sha256 = "0aqf64b5d5lf9scvxdx5f3p0vvx5s59mrvr6hcjljg1prksah9ns";
|
||||
sha256 = "sha256-iVgJtxt0B6nMA3bieZ1CmZucwLMb5av6Wn5BMDRWfcI=";
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
|
Loading…
Reference in New Issue
Block a user