Merge pull request 'zerotier: fix eval if meshname is not known yet' (#510) from Mic92-sops-nix into main
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
testScript = ''
|
testScript = ''
|
||||||
start_all()
|
start_all()
|
||||||
machine.wait_for_unit("meshnamed")
|
machine.wait_for_unit("meshnamed")
|
||||||
out = machine.succeed("${pkgs.dnsutils}/bin/dig -p 53535 AAAA foo.7vbx332lkaunatuzsndtanix54.vpn @localhost +short")
|
out = machine.succeed("${pkgs.dnsutils}/bin/dig AAAA foo.7vbx332lkaunatuzsndtanix54.vpn @meshnamed +short")
|
||||||
print(out)
|
print(out)
|
||||||
assert out.strip() == "fd43:7def:4b50:28d0:4e99:9347:3035:17ef"
|
assert out.strip() == "fd43:7def:4b50:28d0:4e99:9347:3035:17ef"
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -2,139 +2,145 @@
|
|||||||
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
|
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
|
||||||
environment.systemPackages = [ pkgs.deltachat-desktop ];
|
environment.systemPackages = [ pkgs.deltachat-desktop ];
|
||||||
|
|
||||||
services.maddy = {
|
services.maddy =
|
||||||
enable = true;
|
let
|
||||||
primaryDomain = "${config.clanCore.machineName}.local";
|
# FIXME move this to public setting
|
||||||
config = ''
|
meshname = config.clanCore.secrets.zerotier.facts.zerotier-meshname.value or null;
|
||||||
# Minimal configuration with TLS disabled, adapted from upstream example
|
domain = if meshname == null then "${config.clanCore.machineName}.local" else "${meshname}.vpn";
|
||||||
# configuration here https://github.com/foxcpp/maddy/blob/master/maddy.conf
|
in
|
||||||
# Do not use this in unencrypted networks!
|
{
|
||||||
|
enable = true;
|
||||||
|
primaryDomain = domain;
|
||||||
|
config = ''
|
||||||
|
# Minimal configuration with TLS disabled, adapted from upstream example
|
||||||
|
# configuration here https://github.com/foxcpp/maddy/blob/master/maddy.conf
|
||||||
|
# Do not use this in unencrypted networks!
|
||||||
|
|
||||||
auth.pass_table local_authdb {
|
auth.pass_table local_authdb {
|
||||||
table sql_table {
|
table sql_table {
|
||||||
driver sqlite3
|
driver sqlite3
|
||||||
dsn credentials.db
|
dsn credentials.db
|
||||||
table_name passwords
|
table_name passwords
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.imapsql local_mailboxes {
|
|
||||||
driver sqlite3
|
|
||||||
dsn imapsql.db
|
|
||||||
}
|
|
||||||
|
|
||||||
table.chain local_rewrites {
|
|
||||||
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
|
|
||||||
optional_step static {
|
|
||||||
entry postmaster postmaster@$(primary_domain)
|
|
||||||
}
|
|
||||||
optional_step file /etc/maddy/aliases
|
|
||||||
}
|
|
||||||
|
|
||||||
msgpipeline local_routing {
|
|
||||||
destination postmaster $(local_domains) {
|
|
||||||
modify {
|
|
||||||
replace_rcpt &local_rewrites
|
|
||||||
}
|
}
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
}
|
||||||
default_destination {
|
|
||||||
reject 550 5.1.1 "User doesn't exist"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
smtp tcp://[::]:25 {
|
storage.imapsql local_mailboxes {
|
||||||
limits {
|
driver sqlite3
|
||||||
all rate 20 1s
|
dsn imapsql.db
|
||||||
all concurrency 10
|
|
||||||
}
|
}
|
||||||
dmarc yes
|
|
||||||
check {
|
table.chain local_rewrites {
|
||||||
require_mx_record
|
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
|
||||||
dkim
|
optional_step static {
|
||||||
spf
|
entry postmaster postmaster@$(primary_domain)
|
||||||
|
}
|
||||||
|
optional_step file /etc/maddy/aliases
|
||||||
}
|
}
|
||||||
source $(local_domains) {
|
|
||||||
reject 501 5.1.8 "Use Submission for outgoing SMTP"
|
msgpipeline local_routing {
|
||||||
}
|
|
||||||
default_source {
|
|
||||||
destination postmaster $(local_domains) {
|
destination postmaster $(local_domains) {
|
||||||
deliver_to &local_routing
|
modify {
|
||||||
|
replace_rcpt &local_rewrites
|
||||||
|
}
|
||||||
|
deliver_to &local_mailboxes
|
||||||
}
|
}
|
||||||
default_destination {
|
default_destination {
|
||||||
reject 550 5.1.1 "User doesn't exist"
|
reject 550 5.1.1 "User doesn't exist"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
submission tcp://[::1]:587 {
|
smtp tcp://[::]:25 {
|
||||||
limits {
|
limits {
|
||||||
all rate 50 1s
|
all rate 20 1s
|
||||||
}
|
all concurrency 10
|
||||||
auth &local_authdb
|
}
|
||||||
source $(local_domains) {
|
dmarc yes
|
||||||
check {
|
check {
|
||||||
authorize_sender {
|
require_mx_record
|
||||||
prepare_email &local_rewrites
|
dkim
|
||||||
user_to_email identity
|
spf
|
||||||
}
|
|
||||||
}
|
}
|
||||||
destination postmaster $(local_domains) {
|
source $(local_domains) {
|
||||||
|
reject 501 5.1.8 "Use Submission for outgoing SMTP"
|
||||||
|
}
|
||||||
|
default_source {
|
||||||
|
destination postmaster $(local_domains) {
|
||||||
deliver_to &local_routing
|
deliver_to &local_routing
|
||||||
}
|
}
|
||||||
default_destination {
|
default_destination {
|
||||||
modify {
|
reject 550 5.1.1 "User doesn't exist"
|
||||||
dkim $(primary_domain) $(local_domains) default
|
}
|
||||||
}
|
|
||||||
deliver_to &remote_queue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default_source {
|
|
||||||
reject 501 5.1.8 "Non-local sender domain"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target.remote outbound_delivery {
|
submission tcp://[::1]:587 {
|
||||||
limits {
|
limits {
|
||||||
destination rate 20 1s
|
all rate 50 1s
|
||||||
destination concurrency 10
|
|
||||||
}
|
|
||||||
mx_auth {
|
|
||||||
dane
|
|
||||||
mtasts {
|
|
||||||
cache fs
|
|
||||||
fs_dir mtasts_cache/
|
|
||||||
}
|
}
|
||||||
local_policy {
|
auth &local_authdb
|
||||||
min_tls_level encrypted
|
source $(local_domains) {
|
||||||
min_mx_level none
|
check {
|
||||||
|
authorize_sender {
|
||||||
|
prepare_email &local_rewrites
|
||||||
|
user_to_email identity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destination postmaster $(local_domains) {
|
||||||
|
deliver_to &local_routing
|
||||||
|
}
|
||||||
|
default_destination {
|
||||||
|
modify {
|
||||||
|
dkim $(primary_domain) $(local_domains) default
|
||||||
|
}
|
||||||
|
deliver_to &remote_queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_source {
|
||||||
|
reject 501 5.1.8 "Non-local sender domain"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
target.queue remote_queue {
|
target.remote outbound_delivery {
|
||||||
target &outbound_delivery
|
limits {
|
||||||
autogenerated_msg_domain $(primary_domain)
|
destination rate 20 1s
|
||||||
bounce {
|
destination concurrency 10
|
||||||
destination postmaster $(local_domains) {
|
|
||||||
deliver_to &local_routing
|
|
||||||
}
|
}
|
||||||
default_destination {
|
mx_auth {
|
||||||
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
|
dane
|
||||||
|
mtasts {
|
||||||
|
cache fs
|
||||||
|
fs_dir mtasts_cache/
|
||||||
|
}
|
||||||
|
local_policy {
|
||||||
|
min_tls_level encrypted
|
||||||
|
min_mx_level none
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
imap tcp://[::1]:143 {
|
target.queue remote_queue {
|
||||||
auth &local_authdb
|
target &outbound_delivery
|
||||||
storage &local_mailboxes
|
autogenerated_msg_domain $(primary_domain)
|
||||||
}
|
bounce {
|
||||||
'';
|
destination postmaster $(local_domains) {
|
||||||
ensureAccounts = [
|
deliver_to &local_routing
|
||||||
"user@${config.clanCore.machineName}.local"
|
}
|
||||||
];
|
default_destination {
|
||||||
ensureCredentials = {
|
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
|
||||||
"user@${config.clanCore.machineName}.local".passwordFile = pkgs.writeText "dummy" "foobar";
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imap tcp://[::1]:143 {
|
||||||
|
auth &local_authdb
|
||||||
|
storage &local_mailboxes
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
ensureAccounts = [
|
||||||
|
"user@${config.clanCore.machineName}.local"
|
||||||
|
];
|
||||||
|
ensureCredentials = {
|
||||||
|
"user@${config.clanCore.machineName}.local".passwordFile = pkgs.writeText "dummy" "foobar";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ self, inputs, lib, ... }: {
|
{ self, inputs, lib, ... }: {
|
||||||
flake.nixosModules.clanCore = { config, pkgs, options, ... }: {
|
flake.nixosModules.clanCore = { config, pkgs, options, ... }: {
|
||||||
imports = [
|
imports = [
|
||||||
../clanImports
|
./clan-imports
|
||||||
./secrets
|
./secrets
|
||||||
./zerotier
|
./zerotier
|
||||||
./meshnamed
|
./meshnamed
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
localAddress = "fd66:29e9:f422:8dfe:beba:68ec:bd09:7876";
|
||||||
|
in
|
||||||
{
|
{
|
||||||
options.clan.networking.meshnamed = {
|
options.clan.networking.meshnamed = {
|
||||||
enable = (lib.mkEnableOption "meshnamed") // {
|
enable = (lib.mkEnableOption "meshnamed") // {
|
||||||
@@ -28,6 +31,24 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkIf config.clan.networking.meshnamed.enable {
|
config = lib.mkIf config.clan.networking.meshnamed.enable {
|
||||||
|
# we assign this random source address to bind meshnamed to.
|
||||||
|
systemd.network.networks.loopback-addresses = {
|
||||||
|
matchConfig.Name = "lo";
|
||||||
|
networkConfig.Address = [ localAddress ];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
services.resolved.extraConfig = ''
|
||||||
|
[Resolve]
|
||||||
|
DNS=${localAddress}
|
||||||
|
Domains=~${lib.concatMapStringsSep " " (network: network.name) (builtins.attrValues config.clan.networking.meshnamed.networks)}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# for convience, so we can debug with dig
|
||||||
|
networking.extraHosts = ''
|
||||||
|
${localAddress} meshnamed
|
||||||
|
'';
|
||||||
|
|
||||||
systemd.services.meshnamed =
|
systemd.services.meshnamed =
|
||||||
let
|
let
|
||||||
networks = lib.concatMapStringsSep "," (network: "${network.name}=${network.subnet}")
|
networks = lib.concatMapStringsSep "," (network: "${network.name}=${network.subnet}")
|
||||||
@@ -38,7 +59,10 @@
|
|||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "simple";
|
Type = "simple";
|
||||||
ExecStart = "${pkgs.callPackage ../../../pkgs/meshname/default.nix { }}/bin/meshnamed -networks ${networks}";
|
ExecStart = "${pkgs.callPackage ../../../pkgs/meshname/default.nix { }}/bin/meshnamed -networks ${networks} -listenaddr [${localAddress}]:53";
|
||||||
|
|
||||||
|
# to bind port 53
|
||||||
|
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||||
DynamicUser = true;
|
DynamicUser = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,4 +18,17 @@
|
|||||||
default = "root@${config.networking.hostName}";
|
default = "root@${config.networking.hostName}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
config = {
|
||||||
|
# conflicts with systemd-resolved
|
||||||
|
networking.useHostResolvConf = false;
|
||||||
|
|
||||||
|
# The notion of "online" is a broken concept
|
||||||
|
# https://github.com/systemd/systemd/blob/e1b45a756f71deac8c1aa9a008bd0dab47f64777/NEWS#L13
|
||||||
|
systemd.services.NetworkManager-wait-online.enable = false;
|
||||||
|
systemd.network.wait-online.enable = false;
|
||||||
|
|
||||||
|
# Use networkd instead of the pile of shell scripts
|
||||||
|
networking.useNetworkd = lib.mkDefault true;
|
||||||
|
networking.useDHCP = lib.mkDefault false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.clan.networking.zerotier;
|
cfg = config.clan.networking.zerotier;
|
||||||
facts = config.clanCore.secrets.zerotier.facts;
|
facts = config.clanCore.secrets.zerotier.facts or { };
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
authTokens = [
|
authTokens = [
|
||||||
null
|
null
|
||||||
@@ -56,7 +56,9 @@ in
|
|||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default =
|
default =
|
||||||
if cfg.networkId != null then
|
if cfg.networkId == null then
|
||||||
|
null
|
||||||
|
else
|
||||||
let
|
let
|
||||||
part0 = builtins.substring 0 2 cfg.networkId;
|
part0 = builtins.substring 0 2 cfg.networkId;
|
||||||
part1 = builtins.substring 2 2 cfg.networkId;
|
part1 = builtins.substring 2 2 cfg.networkId;
|
||||||
@@ -67,9 +69,7 @@ in
|
|||||||
part6 = builtins.substring 12 2 cfg.networkId;
|
part6 = builtins.substring 12 2 cfg.networkId;
|
||||||
part7 = builtins.substring 14 2 cfg.networkId;
|
part7 = builtins.substring 14 2 cfg.networkId;
|
||||||
in
|
in
|
||||||
"fd${part0}:${part1}${part2}:${part3}${part4}:${part5}${part6}:${part7}99:9300::/88"
|
"fd${part0}:${part1}${part2}:${part3}${part4}:${part5}${part6}:${part7}99:9300::/88";
|
||||||
else
|
|
||||||
null;
|
|
||||||
description = ''
|
description = ''
|
||||||
zerotier subnet
|
zerotier subnet
|
||||||
'';
|
'';
|
||||||
@@ -91,9 +91,10 @@ in
|
|||||||
# having to re-import nixpkgs.
|
# having to re-import nixpkgs.
|
||||||
services.zerotierone.package = lib.mkDefault (pkgs.zerotierone.overrideAttrs (_old: { meta = { }; }));
|
services.zerotierone.package = lib.mkDefault (pkgs.zerotierone.overrideAttrs (_old: { meta = { }; }));
|
||||||
})
|
})
|
||||||
(lib.mkIf (cfg.networkId != null) {
|
(lib.mkIf (facts ? zerotier-meshname && (facts.zerotier-meshname.value or null) != null) {
|
||||||
environment.etc."zerotier/hostname".text = "${facts.zerotier-meshname.value}.vpn";
|
environment.etc."zerotier/hostname".text = "${facts.zerotier-meshname.value}.vpn";
|
||||||
|
})
|
||||||
|
(lib.mkIf (cfg.networkId != null) {
|
||||||
clan.networking.meshnamed.networks.vpn.subnet = cfg.subnet;
|
clan.networking.meshnamed.networks.vpn.subnet = cfg.subnet;
|
||||||
|
|
||||||
systemd.network.enable = true;
|
systemd.network.enable = true;
|
||||||
@@ -152,7 +153,7 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.mkIf (cfg.controller.enable && config.clanCore.secrets ? zerotier && facts.zerotier-network-id.value != null) {
|
(lib.mkIf (cfg.controller.enable && (facts.zerotier-network-id.value or null) != null) {
|
||||||
clan.networking.zerotier.networkId = facts.zerotier-network-id.value;
|
clan.networking.zerotier.networkId = facts.zerotier-network-id.value;
|
||||||
environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value;
|
environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value;
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class Command:
|
|||||||
cmd: list[str],
|
cmd: list[str],
|
||||||
env: Optional[dict[str, str]] = None,
|
env: Optional[dict[str, str]] = None,
|
||||||
cwd: Optional[Path] = None,
|
cwd: Optional[Path] = None,
|
||||||
|
name: str = "command",
|
||||||
) -> None:
|
) -> None:
|
||||||
self.running = True
|
self.running = True
|
||||||
self.log.debug(f"Command: {shlex.join(cmd)}")
|
self.log.debug(f"Command: {shlex.join(cmd)}")
|
||||||
@@ -70,11 +71,10 @@ class Command:
|
|||||||
for fd in rlist:
|
for fd in rlist:
|
||||||
try:
|
try:
|
||||||
for line in fd:
|
for line in fd:
|
||||||
|
self.log.debug(f"[{name}] {line.rstrip()}")
|
||||||
if fd == self.p.stderr:
|
if fd == self.p.stderr:
|
||||||
self.log.debug(f"[{cmd}] stderr: {line}")
|
|
||||||
self.stderr.append(line)
|
self.stderr.append(line)
|
||||||
else:
|
else:
|
||||||
self.log.debug(f"[{cmd}] stdout: {line}")
|
|
||||||
self.stdout.append(line)
|
self.stdout.append(line)
|
||||||
self._output.put(line)
|
self._output.put(line)
|
||||||
except BlockingIOError:
|
except BlockingIOError:
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ class BuildVmTask(BaseTask):
|
|||||||
f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.system.clan.vm.create'
|
f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.system.clan.vm.create'
|
||||||
]
|
]
|
||||||
+ self.nix_options
|
+ self.nix_options
|
||||||
)
|
),
|
||||||
|
name="buildvm",
|
||||||
)
|
)
|
||||||
vm_json = "".join(cmd.stdout).strip()
|
vm_json = "".join(cmd.stdout).strip()
|
||||||
self.log.debug(f"VM JSON path: {vm_json}")
|
self.log.debug(f"VM JSON path: {vm_json}")
|
||||||
@@ -52,7 +53,10 @@ class BuildVmTask(BaseTask):
|
|||||||
def get_clan_name(self, cmds: Iterator[Command]) -> str:
|
def get_clan_name(self, cmds: Iterator[Command]) -> str:
|
||||||
clan_dir = self.vm.flake_url
|
clan_dir = self.vm.flake_url
|
||||||
cmd = next(cmds)
|
cmd = next(cmds)
|
||||||
cmd.run(nix_eval([f"{clan_dir}#clanInternals.clanName"]) + self.nix_options)
|
cmd.run(
|
||||||
|
nix_eval([f"{clan_dir}#clanInternals.clanName"]) + self.nix_options,
|
||||||
|
name="clanname",
|
||||||
|
)
|
||||||
clan_name = cmd.stdout[0].strip().strip('"')
|
clan_name = cmd.stdout[0].strip().strip('"')
|
||||||
return clan_name
|
return clan_name
|
||||||
|
|
||||||
@@ -95,6 +99,7 @@ class BuildVmTask(BaseTask):
|
|||||||
cmd.run(
|
cmd.run(
|
||||||
[vm_config["generateSecrets"], clan_name],
|
[vm_config["generateSecrets"], clan_name],
|
||||||
env=env,
|
env=env,
|
||||||
|
name="generateSecrets",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.log.warning("won't generate secrets for non local clan")
|
self.log.warning("won't generate secrets for non local clan")
|
||||||
@@ -103,6 +108,7 @@ class BuildVmTask(BaseTask):
|
|||||||
cmd.run(
|
cmd.run(
|
||||||
[vm_config["uploadSecrets"]],
|
[vm_config["uploadSecrets"]],
|
||||||
env=env,
|
env=env,
|
||||||
|
name="uploadSecrets",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd = next(cmds)
|
cmd = next(cmds)
|
||||||
@@ -117,7 +123,8 @@ class BuildVmTask(BaseTask):
|
|||||||
str(disk_img),
|
str(disk_img),
|
||||||
"1024M",
|
"1024M",
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
name="createDisk",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd = next(cmds)
|
cmd = next(cmds)
|
||||||
@@ -130,7 +137,8 @@ class BuildVmTask(BaseTask):
|
|||||||
"nixos",
|
"nixos",
|
||||||
str(disk_img),
|
str(disk_img),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
name="formatDisk",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd = next(cmds)
|
cmd = next(cmds)
|
||||||
@@ -184,7 +192,7 @@ class BuildVmTask(BaseTask):
|
|||||||
if not self.vm.graphics:
|
if not self.vm.graphics:
|
||||||
qemu_command.append("-nographic")
|
qemu_command.append("-nographic")
|
||||||
print("$ " + shlex.join(qemu_command))
|
print("$ " + shlex.join(qemu_command))
|
||||||
cmd.run(nix_shell(["qemu"], qemu_command))
|
cmd.run(nix_shell(["qemu"], qemu_command), name="qemu")
|
||||||
|
|
||||||
|
|
||||||
def create_vm(vm: VmConfig, nix_options: list[str] = []) -> BuildVmTask:
|
def create_vm(vm: VmConfig, nix_options: list[str] = []) -> BuildVmTask:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ log_level = "DEBUG"
|
|||||||
log_format = "%(levelname)s: %(message)s\n %(pathname)s:%(lineno)d::%(funcName)s"
|
log_format = "%(levelname)s: %(message)s\n %(pathname)s:%(lineno)d::%(funcName)s"
|
||||||
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --durations 5 --color=yes --new-first" # Add --pdb for debugging
|
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --durations 5 --color=yes --new-first" # Add --pdb for debugging
|
||||||
norecursedirs = "tests/helpers"
|
norecursedirs = "tests/helpers"
|
||||||
markers = [ "impure" ]
|
markers = [ "impure", "with_core" ]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
plugins = ["deal.mypy"]
|
plugins = ["deal.mypy"]
|
||||||
|
|||||||
Reference in New Issue
Block a user