nixos/weblate: init module and test
Co-authored-by: Taeer Bar-Yam <Radvendii@users.noreply.github.com>
This commit is contained in:
parent
a5730c658f
commit
13c96978c3
@ -1484,6 +1484,7 @@
|
|||||||
./services/web-apps/trilium.nix
|
./services/web-apps/trilium.nix
|
||||||
./services/web-apps/tt-rss.nix
|
./services/web-apps/tt-rss.nix
|
||||||
./services/web-apps/vikunja.nix
|
./services/web-apps/vikunja.nix
|
||||||
|
./services/web-apps/weblate.nix
|
||||||
./services/web-apps/whitebophir.nix
|
./services/web-apps/whitebophir.nix
|
||||||
./services/web-apps/wiki-js.nix
|
./services/web-apps/wiki-js.nix
|
||||||
./services/web-apps/windmill.nix
|
./services/web-apps/windmill.nix
|
||||||
|
388
nixos/modules/services/web-apps/weblate.nix
Normal file
388
nixos/modules/services/web-apps/weblate.nix
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.weblate;
|
||||||
|
|
||||||
|
dataDir = "/var/lib/weblate";
|
||||||
|
settingsDir = "${dataDir}/settings";
|
||||||
|
|
||||||
|
finalPackage = cfg.package.overridePythonAttrs (old: {
|
||||||
|
# We only support the PostgreSQL backend in this module
|
||||||
|
dependencies = old.dependencies ++ cfg.package.optional-dependencies.postgres;
|
||||||
|
# Use a settings module in dataDir, to avoid having to rebuild the package
|
||||||
|
# when user changes settings.
|
||||||
|
makeWrapperArgs = (old.makeWrapperArgs or [ ]) ++ [
|
||||||
|
"--set PYTHONPATH \"${settingsDir}\""
|
||||||
|
"--set DJANGO_SETTINGS_MODULE \"settings\""
|
||||||
|
];
|
||||||
|
});
|
||||||
|
inherit (finalPackage) python;
|
||||||
|
|
||||||
|
pythonEnv = python.buildEnv.override {
|
||||||
|
extraLibs = with python.pkgs; [
|
||||||
|
(toPythonModule finalPackage)
|
||||||
|
celery
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# This extends and overrides the weblate/settings_example.py code found in upstream.
|
||||||
|
weblateConfig =
|
||||||
|
''
|
||||||
|
# This was autogenerated by the NixOS module.
|
||||||
|
|
||||||
|
SITE_TITLE = "Weblate"
|
||||||
|
SITE_DOMAIN = "${cfg.localDomain}"
|
||||||
|
# TLS terminates at the reverse proxy, but this setting controls how links to weblate are generated.
|
||||||
|
ENABLE_HTTPS = True
|
||||||
|
SESSION_COOKIE_SECURE = ENABLE_HTTPS
|
||||||
|
DATA_DIR = "${dataDir}"
|
||||||
|
CACHE_DIR = f"{DATA_DIR}/cache"
|
||||||
|
STATIC_ROOT = "${finalPackage.static}/static"
|
||||||
|
MEDIA_ROOT = "/var/lib/weblate/media"
|
||||||
|
COMPRESS_ROOT = "${finalPackage.static}/compressor-cache"
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.postgresql",
|
||||||
|
"HOST": "/run/postgresql",
|
||||||
|
"NAME": "weblate",
|
||||||
|
"USER": "weblate",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with open("${cfg.djangoSecretKeyFile}") as f:
|
||||||
|
SECRET_KEY = f.read().rstrip("\n")
|
||||||
|
|
||||||
|
CACHES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
|
"LOCATION": "unix://${config.services.redis.servers.weblate.unixSocket}",
|
||||||
|
"OPTIONS": {
|
||||||
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
|
"PASSWORD": None,
|
||||||
|
"CONNECTION_POOL_KWARGS": {},
|
||||||
|
},
|
||||||
|
"KEY_PREFIX": "weblate",
|
||||||
|
"TIMEOUT": 3600,
|
||||||
|
},
|
||||||
|
"avatar": {
|
||||||
|
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
|
||||||
|
"LOCATION": "/var/lib/weblate/avatar-cache",
|
||||||
|
"TIMEOUT": 86400,
|
||||||
|
"OPTIONS": {"MAX_ENTRIES": 1000},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CELERY_TASK_ALWAYS_EAGER = False
|
||||||
|
CELERY_BROKER_URL = "redis+socket://${config.services.redis.servers.weblate.unixSocket}"
|
||||||
|
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
|
||||||
|
|
||||||
|
VCS_BACKENDS = ("weblate.vcs.git.GitRepository",)
|
||||||
|
|
||||||
|
''
|
||||||
|
+ lib.optionalString cfg.smtp.enable ''
|
||||||
|
ADMINS = (("Weblate Admin", "${cfg.smtp.user}"),)
|
||||||
|
|
||||||
|
EMAIL_HOST = "${cfg.smtp.host}"
|
||||||
|
EMAIL_USE_TLS = True
|
||||||
|
EMAIL_HOST_USER = "${cfg.smtp.user}"
|
||||||
|
SERVER_EMAIL = "${cfg.smtp.user}"
|
||||||
|
DEFAULT_FROM_EMAIL = "${cfg.smtp.user}"
|
||||||
|
EMAIL_PORT = 587
|
||||||
|
with open("${cfg.smtp.passwordFile}") as f:
|
||||||
|
EMAIL_HOST_PASSWORD = f.read().rstrip("\n")
|
||||||
|
|
||||||
|
''
|
||||||
|
+ cfg.extraConfig;
|
||||||
|
settings_py =
|
||||||
|
pkgs.runCommand "weblate_settings.py"
|
||||||
|
{
|
||||||
|
inherit weblateConfig;
|
||||||
|
passAsFile = [ "weblateConfig" ];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkdir -p $out
|
||||||
|
cat \
|
||||||
|
${finalPackage}/${python.sitePackages}/weblate/settings_example.py \
|
||||||
|
$weblateConfigPath \
|
||||||
|
> $out/settings.py
|
||||||
|
'';
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
PYTHONPATH = "${settingsDir}:${pythonEnv}/${python.sitePackages}/";
|
||||||
|
DJANGO_SETTINGS_MODULE = "settings";
|
||||||
|
# We run Weblate through gunicorn, so we can't utilise the env var set in the wrapper.
|
||||||
|
inherit (finalPackage) GI_TYPELIB_PATH;
|
||||||
|
};
|
||||||
|
|
||||||
|
weblatePath = with pkgs; [
|
||||||
|
gitSVN
|
||||||
|
|
||||||
|
#optional
|
||||||
|
git-review
|
||||||
|
tesseract
|
||||||
|
licensee
|
||||||
|
mercurial
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
|
||||||
|
options = {
|
||||||
|
services.weblate = {
|
||||||
|
enable = lib.mkEnableOption "Weblate service";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "weblate" { };
|
||||||
|
|
||||||
|
localDomain = lib.mkOption {
|
||||||
|
description = "The domain name serving your Weblate instance.";
|
||||||
|
example = "weblate.example.org";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
djangoSecretKeyFile = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Location of the Django secret key.
|
||||||
|
|
||||||
|
This should be a path pointing to a file with secure permissions (not /nix/store).
|
||||||
|
|
||||||
|
Can be generated with `weblate-generate-secret-key` which is available as the `weblate` user.
|
||||||
|
'';
|
||||||
|
type = lib.types.path;
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = lib.mkOption {
|
||||||
|
type = lib.types.lines;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Text to append to `settings.py` Weblate configuration file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
smtp = {
|
||||||
|
enable = lib.mkEnableOption "Weblate SMTP support";
|
||||||
|
user = lib.mkOption {
|
||||||
|
description = "SMTP login name.";
|
||||||
|
example = "weblate@example.org";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
host = lib.mkOption {
|
||||||
|
description = "SMTP host used when sending emails to users.";
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "127.0.0.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Location of a file containing the SMTP password.
|
||||||
|
|
||||||
|
This should be a path pointing to a file with secure permissions (not /nix/store).
|
||||||
|
'';
|
||||||
|
type = lib.types.path;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [ "L+ ${settingsDir} - - - - ${settings_py}" ];
|
||||||
|
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."${cfg.localDomain}" = {
|
||||||
|
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
|
||||||
|
locations = {
|
||||||
|
"= /favicon.ico".alias = "${finalPackage}/${python.sitePackages}/weblate/static/favicon.ico";
|
||||||
|
"/static/".alias = "${finalPackage.static}/static/";
|
||||||
|
"/static/CACHE/".alias = "${finalPackage.static}/compressor-cache/CACHE/";
|
||||||
|
"/media/".alias = "/var/lib/weblate/media/";
|
||||||
|
"/".proxyPass = "http://unix:///run/weblate.socket";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.weblate-postgresql-setup = {
|
||||||
|
description = "Weblate PostgreSQL setup";
|
||||||
|
after = [ "postgresql.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "postgres";
|
||||||
|
Group = "postgres";
|
||||||
|
ExecStart = ''
|
||||||
|
${config.services.postgresql.package}/bin/psql weblate -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.weblate-migrate = {
|
||||||
|
description = "Weblate migration";
|
||||||
|
after = [ "weblate-postgresql-setup.service" ];
|
||||||
|
requires = [ "weblate-postgresql-setup.service" ];
|
||||||
|
# We want this to be active on boot, not just on socket activation
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
inherit environment;
|
||||||
|
path = weblatePath;
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
StateDirectory = "weblate";
|
||||||
|
User = "weblate";
|
||||||
|
Group = "weblate";
|
||||||
|
ExecStart = "${finalPackage}/bin/weblate migrate --noinput";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.weblate-celery = {
|
||||||
|
description = "Weblate Celery";
|
||||||
|
after = [
|
||||||
|
"network.target"
|
||||||
|
"redis.service"
|
||||||
|
"postgresql.service"
|
||||||
|
];
|
||||||
|
# We want this to be active on boot, not just on socket activation
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
environment = environment // {
|
||||||
|
CELERY_WORKER_RUNNING = "1";
|
||||||
|
};
|
||||||
|
path = weblatePath;
|
||||||
|
# Recommendations from:
|
||||||
|
# https://github.com/WeblateOrg/weblate/blob/main/weblate/examples/celery-weblate.service
|
||||||
|
serviceConfig =
|
||||||
|
let
|
||||||
|
# We have to push %n through systemd's replacement, therefore %%n.
|
||||||
|
pidFile = "/run/celery/weblate-%%n.pid";
|
||||||
|
nodes = "celery notify memory backup translate";
|
||||||
|
cmd = verb: ''
|
||||||
|
${pythonEnv}/bin/celery multi ${verb} \
|
||||||
|
${nodes} \
|
||||||
|
-A "weblate.utils" \
|
||||||
|
--pidfile=${pidFile} \
|
||||||
|
--logfile=/var/log/celery/weblate-%%n%%I.log \
|
||||||
|
--loglevel=DEBUG \
|
||||||
|
--beat:celery \
|
||||||
|
--queues:celery=celery \
|
||||||
|
--prefetch-multiplier:celery=4 \
|
||||||
|
--queues:notify=notify \
|
||||||
|
--prefetch-multiplier:notify=10 \
|
||||||
|
--queues:memory=memory \
|
||||||
|
--prefetch-multiplier:memory=10 \
|
||||||
|
--queues:translate=translate \
|
||||||
|
--prefetch-multiplier:translate=4 \
|
||||||
|
--concurrency:backup=1 \
|
||||||
|
--queues:backup=backup \
|
||||||
|
--prefetch-multiplier:backup=2
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
Type = "forking";
|
||||||
|
User = "weblate";
|
||||||
|
Group = "weblate";
|
||||||
|
WorkingDirectory = "${finalPackage}/${python.sitePackages}/weblate/";
|
||||||
|
RuntimeDirectory = "celery";
|
||||||
|
RuntimeDirectoryPreserve = "restart";
|
||||||
|
LogsDirectory = "celery";
|
||||||
|
ExecStart = cmd "start";
|
||||||
|
ExecReload = cmd "restart";
|
||||||
|
ExecStop = ''
|
||||||
|
${pythonEnv}/bin/celery multi stopwait \
|
||||||
|
${nodes} \
|
||||||
|
--pidfile=${pidFile}
|
||||||
|
'';
|
||||||
|
Restart = "always";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.weblate = {
|
||||||
|
description = "Weblate Gunicorn app";
|
||||||
|
after = [
|
||||||
|
"network.target"
|
||||||
|
"weblate-migrate.service"
|
||||||
|
"weblate-celery.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"weblate-migrate.service"
|
||||||
|
"weblate-celery.service"
|
||||||
|
"weblate.socket"
|
||||||
|
];
|
||||||
|
inherit environment;
|
||||||
|
path = weblatePath;
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "notify";
|
||||||
|
NotifyAccess = "all";
|
||||||
|
ExecStart =
|
||||||
|
let
|
||||||
|
gunicorn = python.pkgs.gunicorn.overridePythonAttrs (old: {
|
||||||
|
# Allows Gunicorn to set a meaningful process name
|
||||||
|
dependencies = (old.dependencies or [ ]) ++ old.optional-dependencies.setproctitle;
|
||||||
|
});
|
||||||
|
in
|
||||||
|
''
|
||||||
|
${gunicorn}/bin/gunicorn \
|
||||||
|
--name=weblate \
|
||||||
|
--bind='unix:///run/weblate.socket' \
|
||||||
|
weblate.wsgi
|
||||||
|
'';
|
||||||
|
ExecReload = "kill -s HUP $MAINPID";
|
||||||
|
KillMode = "mixed";
|
||||||
|
PrivateTmp = true;
|
||||||
|
WorkingDirectory = dataDir;
|
||||||
|
StateDirectory = "weblate";
|
||||||
|
RuntimeDirectory = "weblate";
|
||||||
|
User = "weblate";
|
||||||
|
Group = "weblate";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.sockets.weblate = {
|
||||||
|
before = [ "nginx.service" ];
|
||||||
|
wantedBy = [ "sockets.target" ];
|
||||||
|
socketConfig = {
|
||||||
|
ListenStream = "/run/weblate.socket";
|
||||||
|
SocketUser = "weblate";
|
||||||
|
SocketGroup = "weblate";
|
||||||
|
SocketMode = "770";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.redis.servers.weblate = {
|
||||||
|
enable = true;
|
||||||
|
user = "weblate";
|
||||||
|
unixSocket = "/run/redis-weblate/redis.sock";
|
||||||
|
unixSocketPerm = 770;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postgresql = {
|
||||||
|
enable = true;
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = "weblate";
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
ensureDatabases = [ "weblate" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.weblate = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "weblate";
|
||||||
|
packages = [ finalPackage ] ++ weblatePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.weblate.members = [ config.services.nginx.user ];
|
||||||
|
};
|
||||||
|
|
||||||
|
meta.maintainers = with lib.maintainers; [ erictapen ];
|
||||||
|
|
||||||
|
}
|
@ -1073,6 +1073,7 @@ in {
|
|||||||
wastebin = handleTest ./wastebin.nix {};
|
wastebin = handleTest ./wastebin.nix {};
|
||||||
watchdogd = handleTest ./watchdogd.nix {};
|
watchdogd = handleTest ./watchdogd.nix {};
|
||||||
webhook = runTest ./webhook.nix;
|
webhook = runTest ./webhook.nix;
|
||||||
|
weblate = handleTest ./web-apps/weblate.nix {};
|
||||||
wiki-js = handleTest ./wiki-js.nix {};
|
wiki-js = handleTest ./wiki-js.nix {};
|
||||||
wine = handleTest ./wine.nix {};
|
wine = handleTest ./wine.nix {};
|
||||||
wireguard = handleTest ./wireguard {};
|
wireguard = handleTest ./wireguard {};
|
||||||
|
104
nixos/tests/web-apps/weblate.nix
Normal file
104
nixos/tests/web-apps/weblate.nix
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import ../make-test-python.nix (
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
certs = import ../common/acme/server/snakeoil-certs.nix;
|
||||||
|
|
||||||
|
serverDomain = certs.domain;
|
||||||
|
|
||||||
|
admin = {
|
||||||
|
username = "admin";
|
||||||
|
password = "snakeoilpass";
|
||||||
|
};
|
||||||
|
# An API token that we manually insert into the db as a valid one.
|
||||||
|
apiToken = "OVJh65sXaAfQMZ4NTcIGbFZIyBZbEZqWTi7azdDf";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "weblate";
|
||||||
|
meta.maintainers = with pkgs.lib.maintainers; [ erictapen ];
|
||||||
|
|
||||||
|
nodes.server =
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.memorySize = 2048;
|
||||||
|
|
||||||
|
services.weblate = {
|
||||||
|
enable = true;
|
||||||
|
localDomain = "${serverDomain}";
|
||||||
|
djangoSecretKeyFile = pkgs.writeText "weblate-django-secret" "thisissnakeoilsecretwithmorethan50characterscorrecthorsebatterystaple";
|
||||||
|
extraConfig = ''
|
||||||
|
# Weblate tries to fetch Avatars from the network
|
||||||
|
ENABLE_AVATARS = False
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."${serverDomain}" = {
|
||||||
|
enableACME = lib.mkForce false;
|
||||||
|
sslCertificate = certs."${serverDomain}".cert;
|
||||||
|
sslCertificateKey = certs."${serverDomain}".key;
|
||||||
|
};
|
||||||
|
|
||||||
|
security.pki.certificateFiles = [ certs.ca.cert ];
|
||||||
|
|
||||||
|
networking.hosts."::1" = [ "${serverDomain}" ];
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
80
|
||||||
|
443
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.weblate.shell = pkgs.bashInteractive;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client =
|
||||||
|
{ pkgs, nodes, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = [ pkgs.wlc ];
|
||||||
|
|
||||||
|
environment.etc."xdg/weblate".text = ''
|
||||||
|
[weblate]
|
||||||
|
url = https://${serverDomain}/api/
|
||||||
|
key = ${apiToken}
|
||||||
|
'';
|
||||||
|
|
||||||
|
networking.hosts."${nodes.server.networking.primaryIPAddress}" = [ "${serverDomain}" ];
|
||||||
|
|
||||||
|
security.pki.certificateFiles = [ certs.ca.cert ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
import json
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_unit("weblate.socket")
|
||||||
|
server.wait_until_succeeds("curl -f https://${serverDomain}/")
|
||||||
|
server.succeed("sudo -iu weblate -- weblate createadmin --username ${admin.username} --password ${admin.password} --email weblate@example.org")
|
||||||
|
|
||||||
|
# It's easier to replace the generated API token with a predefined one than
|
||||||
|
# to extract it at runtime.
|
||||||
|
server.succeed("sudo -iu weblate -- psql -d weblate -c \"UPDATE authtoken_token SET key = '${apiToken}' WHERE user_id = (SELECT id FROM weblate_auth_user WHERE username = 'admin');\"")
|
||||||
|
|
||||||
|
client.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
# Test the official Weblate client wlc.
|
||||||
|
client.wait_until_succeeds("REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt wlc --debug list-projects")
|
||||||
|
|
||||||
|
def call_wl_api(arg):
|
||||||
|
(rv, result) = client.execute("curl -H \"Content-Type: application/json\" -H \"Authorization: Token ${apiToken}\" https://${serverDomain}/api/{}".format(arg))
|
||||||
|
assert rv == 0
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
call_wl_api("users/ --data '{}'".format(
|
||||||
|
json.dumps(
|
||||||
|
{"username": "test1",
|
||||||
|
"full_name": "test1",
|
||||||
|
"email": "test1@example.org"
|
||||||
|
})))
|
||||||
|
|
||||||
|
# TODO: Check sending and receiving email.
|
||||||
|
# server.wait_for_unit("postfix.service")
|
||||||
|
|
||||||
|
# TODO: The goal is for this to succeed, but there are still some checks failing.
|
||||||
|
# server.succeed("sudo -iu weblate -- weblate check --deploy")
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user