This commit is contained in:
Shelvacu
2025-04-19 13:17:36 -07:00
committed by Shelvacu on fw
parent 086a258c92
commit 9bad53f188
28 changed files with 859 additions and 628 deletions

View File

@@ -257,7 +257,11 @@ else
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJk3a190w/1TZkzVKORvz/kwyKmFY144lVeDFm80p17"; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJk3a190w/1TZkzVKORvz/kwyKmFY144lVeDFm80p17";
}; };
"rsync.net" = { "rsync.net" = {
extraHostNames = [ "rsn" "rsyncnet" "fm2382.rsync.net" ]; extraHostNames = [
"rsn"
"rsyncnet"
"fm2382.rsync.net"
];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdUkGe6kKn5ssz4WRZKjcws0InbQqZayenzk9obmP1z"; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdUkGe6kKn5ssz4WRZKjcws0InbQqZayenzk9obmP1z";
}; };

View File

@@ -87,7 +87,10 @@ lib.mkMerge [
}) })
{ {
vacu.packages.ffmpeg-vacu-full.enable = config.vacu.systemKind == "desktop"; vacu.packages.ffmpeg-vacu-full.enable = config.vacu.systemKind == "desktop";
vacu.packages.ffmpeg-vacu-headless.enable = config.vacu.systemKind != "minimal" && config.vacu.systemKind != "container" && config.vacu.systemKind != "desktop"; vacu.packages.ffmpeg-vacu-headless.enable =
config.vacu.systemKind != "minimal"
&& config.vacu.systemKind != "container"
&& config.vacu.systemKind != "desktop";
} }
{ {
vacu.packages = { vacu.packages = {
@@ -122,63 +125,62 @@ lib.mkMerge [
}; };
} }
{ {
vacu.packages = vacu.packages = with pkgs; [
with pkgs; [ bash
bash bzip2
bzip2 curl
curl ddrescue
ddrescue diffutils
diffutils dig
dig dnsutils
dnsutils ethtool
ethtool file
file findutils
findutils gnugrep
gnugrep gnused
gnused gnutar
gnutar gnutls
gnutls gzip
gzip hostname
hostname htop
htop inetutils
inetutils iperf3
iperf3 iputils
iputils jq
jq killall
killall lsof
lsof mosh
mosh nano
nano ncdu
ncdu netcat-openbsd
netcat-openbsd nixos-rebuild
nixos-rebuild openssh
openssh "p7zip-unfree"
"p7zip-unfree" pciutils
pciutils progress
progress psutils
psutils pv
pv ripgrep
ripgrep rsync
rsync screen
screen # sed => gnused
# sed => gnused sops
sops sshfs
sshfs ssh-to-age
ssh-to-age # tar => gnutar
# tar => gnutar tmux
tmux tree
tree tzdata
tzdata # units => vacu-units
# units => vacu-units unzip
unzip usbutils
usbutils util-linux
util-linux "vacu-units"
"vacu-units" vim
vim wget
wget which
which xz
xz zip
zip ];
];
} }
] ]

View File

@@ -33,7 +33,8 @@ let
readOnly = true; readOnly = true;
}; };
}; };
config.finalPackage = if config.overrides == null then config.package else config.package.override config.overrides; config.finalPackage =
if config.overrides == null then config.package else config.package.override config.overrides;
} }
) )
); );

View File

@@ -47,7 +47,9 @@ in
}; };
config = config =
{ {
vacu.sourceTree = inputsOf inputs.self // { inherit inputs; }; vacu.sourceTree = inputsOf inputs.self // {
inherit inputs;
};
# vacu.sourceTree = pkgs.runCommand "inputs-tree" { } '' # vacu.sourceTree = pkgs.runCommand "inputs-tree" { } ''
# mkdir -p $out # mkdir -p $out
# ln -s ${inputs.self} $out/self # ln -s ${inputs.self} $out/self

View File

@@ -1,8 +1,4 @@
{ { pkgs, inputs, ... }:
pkgs,
inputs,
...
}:
{ {
imports = [ imports = [
inputs.jovian.nixosModules.jovian inputs.jovian.nixosModules.jovian

View File

@@ -175,10 +175,11 @@
map (name: lib.nameValuePair name inputs.${name + suffix}) inp' map (name: lib.nameValuePair name inputs.${name + suffix}) inp'
); );
in in
thisInputsA // { thisInputsA
inherit nixpkgs; // {
inherit (inputs) self; inherit nixpkgs;
}; inherit (inputs) self;
};
mkNixosConfig = mkNixosConfig =
{ {
unstable ? false, unstable ? false,
@@ -210,7 +211,12 @@
); );
lib = { lib = {
inherit mkPlain mkPkgs mkInputs mkNixosConfig; inherit
mkPlain
mkPkgs
mkInputs
mkNixosConfig
;
}; };
nixosConfigurations = { nixosConfigurations = {
@@ -268,9 +274,7 @@
inherit (inputs) dns; inherit (inputs) dns;
vacuModuleType = "nix-on-droid"; vacuModuleType = "nix-on-droid";
}; };
pkgs = mkPkgs { pkgs = mkPkgs { system = arm; };
system = arm;
};
}; };
checks = nixpkgs.lib.genAttrs [ x86 ] ( checks = nixpkgs.lib.genAttrs [ x86 ] (
@@ -286,15 +290,15 @@
node.specialArgs.selfPackages = self.packages.${system}; node.specialArgs.selfPackages = self.packages.${system};
node.specialArgs.vacuModuleType = "nixos"; node.specialArgs.vacuModuleType = "nixos";
}; };
mkTest = name: mkTest =
name:
nixpkgs.lib.nixos.runTest { nixpkgs.lib.nixos.runTest {
imports = [ imports = [
commonTestModule commonTestModule
./tests/${name} ./tests/${name}
{ node.specialArgs.inputs = self.nixosConfigurations.${name}._module.specialArgs.inputs; } { node.specialArgs.inputs = self.nixosConfigurations.${name}._module.specialArgs.inputs; }
]; ];
} };
;
checksFromConfig = plain.config.vacu.checks; checksFromConfig = plain.config.vacu.checks;
in in
assert !(checksFromConfig ? liam) && !(checksFromConfig ? trip); assert !(checksFromConfig ? liam) && !(checksFromConfig ? trip);
@@ -421,7 +425,8 @@
// (inputs.flake-utils.lib.eachDefaultSystem ( // (inputs.flake-utils.lib.eachDefaultSystem (
system: system:
let let
mkNixvim = unstable: mkNixvim =
unstable:
let let
nixvim-input = if unstable then inputs.nixvim-unstable else inputs.nixvim; nixvim-input = if unstable then inputs.nixvim-unstable else inputs.nixvim;
in in
@@ -437,12 +442,8 @@
config.allowUnfree = true; config.allowUnfree = true;
overlays = [ inputs.sm64baserom.overlays.default ]; overlays = [ inputs.sm64baserom.overlays.default ];
}; };
pkgs-unstable = mkPkgs (nixpkgs-args // { pkgs-unstable = mkPkgs (nixpkgs-args // { useUnstable = true; });
useUnstable = true; pkgs-stable = mkPkgs (nixpkgs-args // { useUnstable = false; });
});
pkgs-stable = mkPkgs (nixpkgs-args // {
useUnstable = false;
});
_plain = mkPlain pkgs-unstable; _plain = mkPlain pkgs-unstable;
plain = _plain.config.vacu.withAsserts _plain; plain = _plain.config.vacu.withAsserts _plain;
treefmtEval = inputs.treefmt-nix.lib.evalModule pkgs-unstable ./treefmt.nix; treefmtEval = inputs.treefmt-nix.lib.evalModule pkgs-unstable ./treefmt.nix;
@@ -473,14 +474,18 @@
}; };
generated = pkgs.linkFarm "generated" { generated = pkgs.linkFarm "generated" {
nixpkgs = "${inputs.nixpkgs}"; nixpkgs = "${inputs.nixpkgs}";
"liam-test/hints.py" = pkgs.writeText "hints.py" (import ./typesForTest.nix { "liam-test/hints.py" = pkgs.writeText "hints.py" (
name = "liam"; import ./typesForTest.nix {
inherit (pkgs-stable) lib; name = "liam";
inherit self; inherit (pkgs-stable) lib;
inherit (inputs) nixpkgs; inherit self;
}); inherit (inputs) nixpkgs;
}
);
"dns/python-env" = builtins.dirOf (builtins.dirOf dns.interpreter); "dns/python-env" = builtins.dirOf (builtins.dirOf dns.interpreter);
"mailtest/python-env" = builtins.dirOf (builtins.dirOf self.checks.x86_64-linux.liam.nodes.checker.vacu.mailtest.smtp.interpreter); "mailtest/python-env" = builtins.dirOf (
builtins.dirOf self.checks.x86_64-linux.liam.nodes.checker.vacu.mailtest.smtp.interpreter
);
}; };
haproxy-auth-request = pkgs.callPackage ./packages/haproxy-auth-request.nix { haproxy-auth-request = pkgs.callPackage ./packages/haproxy-auth-request.nix {
inherit haproxy-lua-http; inherit haproxy-lua-http;

View File

@@ -1,8 +1,11 @@
{ config, ... }: { config, ... }:
{ {
vacu.packages = [ "tpm-fido" ]; vacu.packages = [ "tpm-fido" ];
users.groups.uhid = {}; users.groups.uhid = { };
users.users.shelvacu.extraGroups = [ config.security.tpm2.tssGroup config.users.groups.uhid.name ]; users.users.shelvacu.extraGroups = [
config.security.tpm2.tssGroup
config.users.groups.uhid.name
];
security.tpm2.enable = true; security.tpm2.enable = true;
security.tpm2.applyUdevRules = true; security.tpm2.applyUdevRules = true;
services.udev.extraRules = '' services.udev.extraRules = ''

View File

@@ -13,8 +13,9 @@
background: #eee; background: #eee;
padding: 0 10px; padding: 0 10px;
} }
h1,h2,h3 { line-height: 1.2; } h1, h2, h3 {
line-height: 1.2;
}
form { form {
padding: 5px; padding: 5px;
@@ -22,21 +23,34 @@
border-radius: 3px; border-radius: 3px;
margin: 5px; margin: 5px;
} }
form label { display: block; } form label {
display: block;
}
</style> </style>
</head> </head>
<body> <body>
<h1>Jobs info</h1> <h1>Jobs info</h1>
<p>Hi! I am currently looking to find a Software Engineering job. I am a professional developer specializing in Ruby on Rails and Rust, with over 10 years of experience.</p> <p>
Hi! I am currently looking to find a Software Engineering job. I am a
professional developer specializing in Ruby on Rails and Rust, with over
10 years of experience.
</p>
<p> <p>
What I want in a job: What I want in a job:
<ul> <ul>
<li><b>Fully Remote</b> - that means 1 visit to an office per month or less. Strongly preferred; non-remote jobs will have to be very tempting in every other category.</li> <li>
<b>Fully Remote</b> - that means 1 visit to an office per month or
less. Strongly preferred; non-remote jobs will have to be very
tempting in every other category.
</li>
<li>Prefer direct hire, but not required.</li> <li>Prefer direct hire, but not required.</li>
<li>W2 employment preferred, but also open to C2C/1099.</li> <li>W2 employment preferred, but also open to C2C/1099.</li>
<li>While I could be productive in nearly any language, I would most like to work with Rust.</li> <li>
While I could be productive in nearly any language, I would most like
to work with Rust.
</li>
</ul> </ul>
</p> </p>
@@ -44,41 +58,78 @@
Things you probably want to know: Things you probably want to know:
<ul> <ul>
<li>I am a US citizen (USC), for work authorization purposes I can work anywhere in the US.</li> <li>
I am a US citizen (USC), for work authorization purposes I can work
anywhere in the US.
</li>
<li>Open to both contract and permanent positions.</li> <li>Open to both contract and permanent positions.</li>
<li>I can start within a week.</li> <li>I can start within a week.</li>
<li>I do not have a formal degree, but more than enough experience to make up for it.</li> <li>
I do not have a formal degree, but more than enough experience to make
up for it.
</li>
</ul> </ul>
</p> </p>
<h2>Resume</h2> <h2>Resume</h2>
<p> <p>
<a href="/shelvacu-resume.pdf">You can see my latest, up-to-date resume here</a>. <a href="/shelvacu-resume.pdf"
>You can see my latest, up-to-date resume here</a>.
</p> </p>
<h2>To send me job offers</h2> <h2>To send me job offers</h2>
<p>Unfortunately I get a lot of bad job offers if I just give out my email to anyone. As such, I will only accept job offers sent using this form.</b> <p>
Unfortunately I get a lot of bad job offers if I just give out my email to
anyone. As such, I will only accept job offers sent using this form.
</p>
<p>Each time you want to send me an email about a new job offer, you must return to this page. The process may change from time to time. If you have multiple jobs you think I am a good fit for, please combine them in one email.</p> <p>
Each time you want to send me an email about a new job offer, you must
return to this page. The process may change from time to time. If you have
multiple jobs you think I am a good fit for, please combine them in one
email.
</p>
<form method="get" action="/email"> <form method="get" action="/email">
<label> <label>
<input type="checkbox" name="confirm_fully_remote_or_exceptional" value="yes" required> <input
<span>This is a fully remote job, or you will give an exceptional reason why the offer should be considered despite not being fully remote.</span> type="checkbox"
name="confirm_fully_remote_or_exceptional"
value="yes"
required
>
<span>This is a fully remote job, or you will give an exceptional reason
why the offer should be considered despite not being fully
remote.</span>
</label> </label>
<label> <label>
<input type="checkbox" name="confirm_i_read_the_above" value="yes" required> <input
<span>You have read the above and will not ask about work authorization status, earliest date to start, or formal education because you already have that information.</span> type="checkbox"
name="confirm_i_read_the_above"
value="yes"
required
>
<span>You have read the above and will not ask about work authorization
status, earliest date to start, or formal education because you
already have that information.</span>
</label> </label>
<label> <label>
<input type="checkbox" name="confirm_include_pay" value="yes" required> <input type="checkbox" name="confirm_include_pay" value="yes" required>
<span>You will include an estimated pay range. No "depends on experience", my resume clearly shows how much experience I have.</span> <span>You will include an estimated pay range. No "depends on
experience", my resume clearly shows how much experience I
have.</span>
</label> </label>
<label> <label>
<input type="checkbox" name="confirm_no_ask_resume" value="yes" required> <input
<span>You will not ask for an updated resume. The latest resume is always available on this page.</span> type="checkbox"
name="confirm_no_ask_resume"
value="yes"
required
>
<span>You will not ask for an updated resume. The latest resume is
always available on this page.</span>
</label> </label>
<label> <label>
<div>Your email:</div> <div>Your email:</div>
@@ -87,6 +138,10 @@
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
<p>I apologize for the hassle. I promise that if you follow everything here before sending a job offer to me, I <i>will</i> respond, and I will make every effort to respond within 1 week.</p> <p>
I apologize for the hassle. I promise that if you follow everything here
before sending a job offer to me, I <i>will</i> respond, and I will make
every effort to respond within 1 week.
</p>
</body> </body>
</html> </html>

View File

@@ -57,16 +57,20 @@ in
repo = mkOption { default = "${cfg.rsyncUser}@${cfg.rsyncHost}:borg-repos/liam-backup"; }; repo = mkOption { default = "${cfg.rsyncUser}@${cfg.rsyncHost}:borg-repos/liam-backup"; };
package = mkOption { default = pkgs.borgbackup; }; package = mkOption { default = pkgs.borgbackup; };
cmd = mkOption { default = lib.getExe cfg.package; }; cmd = mkOption { default = lib.getExe cfg.package; };
paths = mkOption { default = [ paths = mkOption {
"/var/lib/mail" default = [
"/var/lib/dovecot" "/var/lib/mail"
"/var/log" "/var/lib/dovecot"
]; }; "/var/log"
];
};
keyPath = mkOption { default = config.sops.secrets.liam-borg-key.path; }; keyPath = mkOption { default = config.sops.secrets.liam-borg-key.path; };
}; };
config = { config = {
vacu.assertions = lib.singleton { vacu.assertions = lib.singleton {
assertion = (lib.versionAtLeast cfg.package.version "1.4.0") && !(lib.versionAtLeast cfg.package.version "1.5.0"); assertion =
(lib.versionAtLeast cfg.package.version "1.4.0")
&& !(lib.versionAtLeast cfg.package.version "1.5.0");
message = "Only for version 1.4.x"; message = "Only for version 1.4.x";
fatal = true; fatal = true;
}; };
@@ -75,7 +79,7 @@ in
owner = cfg.user; owner = cfg.user;
}; };
# systemd.tmpfiles.settings."10-auto-borg" = lib.genAttrs cfg.paths (_: # systemd.tmpfiles.settings."10-auto-borg" = lib.genAttrs cfg.paths (_:
# { # {
# # A+ = append to ACLs recursively # # A+ = append to ACLs recursively
# "A+" = { # "A+" = {
@@ -88,7 +92,7 @@ in
group = cfg.user; group = cfg.user;
home = "/var/lib/auto-borg"; home = "/var/lib/auto-borg";
}; };
users.groups.${cfg.user} = {}; users.groups.${cfg.user} = { };
systemd.services.auto-borg-gen-key = { systemd.services.auto-borg-gen-key = {
script = '' script = ''
set -euo pipefail set -euo pipefail

View File

@@ -5,7 +5,17 @@
... ...
}: }:
let let
inherit (builtins) isString isList length head all isInt isAttrs isFloat isBool; inherit (builtins)
isString
isList
length
head
all
isInt
isAttrs
isFloat
isBool
;
inherit (lib) inherit (lib)
concatStrings concatStrings
concatStringsSep concatStringsSep
@@ -16,21 +26,13 @@ let
elemAt elemAt
mapAttrsToList mapAttrsToList
; ;
mapConcat = f: xs: mapConcat = f: xs: concatStrings (map f xs);
concatStrings (map f xs) mapConcatSep =
; sep: f: xs:
mapConcatSep = sep: f: xs: concatStringsSep sep (map f xs);
concatStringsSep sep (map f xs) mapConcatLines = f: xs: mapConcatSep "\n" f xs;
; isListWhere = xs: f: (isList xs) && (all f xs);
mapConcatLines = f: xs: stringOrList = val: (isString val) || ((isListWhere val isString) && (length val) > 0);
mapConcatSep "\n" f xs
;
isListWhere = xs: f:
(isList xs) && (all f xs)
;
stringOrList = val: (isString val) || (
(isListWhere val isString) && (length val) > 0
);
listify = val: if isList val then val else [ val ]; listify = val: if isList val then val else [ val ];
email_folders = [ email_folders = [
"24nm-domain@shelvacu.com" "24nm-domain@shelvacu.com"
@@ -89,36 +91,47 @@ let
is_match = regex: s: (match regex s) != null; is_match = regex: s: (match regex s) != null;
is_not_match = regex: s: !(is_match regex s); is_not_match = regex: s: !(is_match regex s);
only_printable_ascii = s: is_match "[ -~\r\n]*" s; only_printable_ascii = s: is_match "[ -~\r\n]*" s;
has_vars = s: lib.hasInfix ("$"+"{") s; has_vars = s: lib.hasInfix ("$" + "{") s;
# is_quoteable = s: (only_printable_ascii s) && (!lib.hasInfix ("$" + "{") s); # is_quoteable = s: (only_printable_ascii s) && (!lib.hasInfix ("$" + "{") s);
sieve_raw_escape_string = sieve_raw_escape_string =
s: s:
if !only_printable_ascii s then if !only_printable_ascii s then
builtins.trace s builtins.trace s throw "s failed only_printable_ascii check"
throw "s failed only_printable_ascii check"
else else
replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s;
;
sieve_encode_string = sieve_encode_string =
{ allow_vars, for_debug_comment, with_quotes }: s: {
allow_vars,
for_debug_comment,
with_quotes,
}:
s:
assert isString s; assert isString s;
assert allow_vars || for_debug_comment || (!has_vars s); assert allow_vars || for_debug_comment || (!has_vars s);
let let
a = sieve_raw_escape_string s; a = sieve_raw_escape_string s;
b = if for_debug_comment then replaceStrings [ ''*/'' ] [ ''*\/'' ] a else a; b = if for_debug_comment then replaceStrings [ ''*/'' ] [ ''*\/'' ] a else a;
res = if with_quotes then ''"${b}"'' else b; res = if with_quotes then ''"${b}"'' else b;
in res in
; res;
sieve_quote_string = sieve_encode_string { allow_vars = false; for_debug_comment = false; with_quotes = true; }; sieve_quote_string = sieve_encode_string {
sieve_quote_string_with_interp = sieve_encode_string { allow_vars = true; for_debug_comment = false; with_quotes = true; }; allow_vars = false;
for_debug_comment = false;
with_quotes = true;
};
sieve_quote_string_with_interp = sieve_encode_string {
allow_vars = true;
for_debug_comment = false;
with_quotes = true;
};
is_valid_long_ident = is_match "[a-z_][a-z0-9_]*"; is_valid_long_ident = is_match "[a-z_][a-z0-9_]*";
is_number_ident = is_match "[0-9]*"; is_number_ident = is_match "[0-9]*";
is_valid_ident = s: (is_valid_long_ident s) || (is_number_ident s); is_valid_ident = s: (is_valid_long_ident s) || (is_number_ident s);
interp = ident: interp =
ident:
assert isString ident; assert isString ident;
assert is_valid_ident ident; assert is_valid_ident ident;
"$" + "{${ident}}" "$" + "{${ident}}";
;
dest = "envelope_to"; dest = "envelope_to";
dest_domain = "envelope_to_domain"; dest_domain = "envelope_to_domain";
set_envelope = '' set_envelope = ''
@@ -133,139 +146,155 @@ let
''; '';
envelope_is = key: ''string :is "${interp dest}" ${sieve_quote_string key}''; envelope_is = key: ''string :is "${interp dest}" ${sieve_quote_string key}'';
envelope_domain_is = key: ''string :is "${interp dest_domain}" ${sieve_quote_string key}''; envelope_domain_is = key: ''string :is "${interp dest_domain}" ${sieve_quote_string key}'';
sieve_encode_list = xs: sieve_encode_list =
xs:
assert isListWhere xs isString; assert isListWhere xs isString;
"[ ${mapConcatSep ", " sieve_encode xs} ]" "[ ${mapConcatSep ", " sieve_encode xs} ]";
; sieve_encode =
sieve_encode = val: val:
if isString val then sieve_quote_string val if isString val then
else if isList val then sieve_encode_list val sieve_quote_string val
else assert "dunno what to do with this"; null else if isList val then
; sieve_encode_list val
else
assert "dunno what to do with this";
null;
sieve_debug_list = xs: "[ ${mapConcat (s: (sieve_debug s) + " ") xs}]"; sieve_debug_list = xs: "[ ${mapConcat (s: (sieve_debug s) + " ") xs}]";
sieve_debug_attrs = attrs: sieve_debug_attrs =
let attrs:
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; "; let
pairStrs = mapAttrsToList toPairStr attrs; toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
pairsStr = concatStrings pairStrs; pairStrs = mapAttrsToList toPairStr attrs;
in pairsStr = concatStrings pairStrs;
"{ ${pairsStr}}" in
; "{ ${pairsStr}}";
sieve_debug = val: sieve_debug =
if isString val then sieve_encode_string { allow_vars = true; for_debug_comment = true; with_quotes = true; } val val:
else if (isInt val) || (isFloat val) then toString val if isString val then
sieve_encode_string {
allow_vars = true;
for_debug_comment = true;
with_quotes = true;
} val
else if (isInt val) || (isFloat val) then
toString val
else if (isBool val) then else if (isBool val) then
(if val then "true" else "false") (if val then "true" else "false")
else if isNull val then "null" else if isNull val then
else if isList val then sieve_debug_list val "null"
else if isAttrs val then sieve_debug_attrs val else if isList val then
else assert "dunno what to do with this"; null sieve_debug_list val
; else if isAttrs val then
pure_flags_impl = flags: conditions: sieve_debug_attrs val
assert isListWhere flags isString; else
assert isListWhere conditions isString; assert "dunno what to do with this";
assert (length flags) > 0; null;
assert (length conditions) > 0; pure_flags_impl =
let flags: conditions:
argAttrs = { inherit flags conditions; }; assert isListWhere flags isString;
firstFlag = head flags; assert isListWhere conditions isString;
combined_condition = if (length conditions) == 1 then head conditions else (allof conditions); assert (length flags) > 0;
in assert (length conditions) > 0;
'' let
# pure_flags ${sieve_debug argAttrs}; argAttrs = { inherit flags conditions; };
removeflag ${sieve_quote_string firstFlag}; firstFlag = head flags;
if ${combined_condition} { combined_condition = if (length conditions) == 1 then head conditions else (allof conditions);
${record_action "pure_flags ${concatStringsSep " " flags}"} in
${concatStringsSep "\n" (map (flag: ''addflag ${sieve_quote_string flag};'') flags)} ''
} # pure_flags ${sieve_debug argAttrs};
# pure_flags end removeflag ${sieve_quote_string firstFlag};
''; if ${combined_condition} {
pure_flags = flags: conditions: ${record_action "pure_flags ${concatStringsSep " " flags}"}
${concatStringsSep "\n" (map (flag: ''addflag ${sieve_quote_string flag};'') flags)}
}
# pure_flags end
'';
pure_flags =
flags: conditions:
assert stringOrList flags; assert stringOrList flags;
assert stringOrList conditions; assert stringOrList conditions;
pure_flags_impl (listify flags) (listify conditions) pure_flags_impl (listify flags) (listify conditions);
; exists_impl =
exists_impl = headers: headers:
assert isListWhere headers isString; assert isListWhere headers isString;
if headers == [] then if headers == [ ] then
"/* exists START: called with empty array */ false /* exists END */" "/* exists START: called with empty array */ false /* exists END */"
else else
"/* exists START */ exists ${sieve_encode_list headers} /* exists END */" "/* exists START */ exists ${sieve_encode_list headers} /* exists END */";
; exists =
exists = headers: headers:
assert stringOrList headers; assert stringOrList headers;
exists_impl (listify headers) exists_impl (listify headers);
; header_generic =
header_generic = match_kind: header_s: match_es: match_kind: header_s: match_es:
assert stringOrList header_s; assert stringOrList header_s;
assert stringOrList match_es; assert stringOrList match_es;
''/* header_generic START */ header ${match_kind} ${sieve_encode header_s} ${sieve_encode match_es} /* header_generic END */'' ''/* header_generic START */ header ${match_kind} ${sieve_encode header_s} ${sieve_encode match_es} /* header_generic END */'';
;
header_matches = header_generic ":matches"; header_matches = header_generic ":matches";
header_is = header_generic ":is"; header_is = header_generic ":is";
subject_generic = match_kind: match_es: header_generic match_kind "Subject" match_es; subject_generic = match_kind: match_es: header_generic match_kind "Subject" match_es;
subject_matches = subject_generic ":matches"; subject_matches = subject_generic ":matches";
subject_is = subject_generic ":is"; subject_is = subject_generic ":is";
environment_generic = match_kind: environment_name_s: match_es: environment_generic =
match_kind: environment_name_s: match_es:
assert stringOrList environment_name_s; assert stringOrList environment_name_s;
assert stringOrList match_es; assert stringOrList match_es;
"environment ${match_kind} ${sieve_encode environment_name_s} ${sieve_encode match_es}" "environment ${match_kind} ${sieve_encode environment_name_s} ${sieve_encode match_es}";
;
environment_matches = environment_generic ":matches"; environment_matches = environment_generic ":matches";
environment_is = environment_generic ":is"; environment_is = environment_generic ":is";
from_is = addr_list: from_is =
addr_list:
assert stringOrList addr_list; assert stringOrList addr_list;
''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */'' ''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */'';
; var_is =
var_is = var_name: rhs: var_name: rhs:
assert isString var_name; assert isString var_name;
assert stringOrList rhs; assert stringOrList rhs;
''string :is "''${${var_name}}" ${sieve_encode rhs}'' ''string :is "''${${var_name}}" ${sieve_encode rhs}'';
;
var_is_true = var_name: var_is var_name "1"; var_is_true = var_name: var_is var_name "1";
var_is_false = var_name: not (var_is_true var_name); var_is_false = var_name: not (var_is_true var_name);
set_with_interp = var_name: new_val: set_with_interp =
var_name: new_val:
assert isString var_name; assert isString var_name;
assert is_valid_ident var_name; assert is_valid_ident var_name;
assert isString new_val; assert isString new_val;
"set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};" "set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};";
; set =
set = var_name: new_val: var_name: new_val:
assert isString var_name; assert isString var_name;
assert is_valid_ident var_name; assert is_valid_ident var_name;
assert isString new_val; assert isString new_val;
"set ${sieve_encode var_name} ${sieve_encode new_val};" "set ${sieve_encode var_name} ${sieve_encode new_val};";
; set_bool_var =
set_bool_var = var_name: bool_val: var_name: bool_val:
assert isBool bool_val; assert isBool bool_val;
set var_name (if bool_val then "1" else "0") set var_name (if bool_val then "1" else "0");
; over_test_list =
over_test_list = name: test_list: name: test_list:
assert isListWhere test_list isString; assert isListWhere test_list isString;
'' ''
${name}( ${name}(
${concatStringsSep ",\n" test_list} ${concatStringsSep ",\n" test_list}
) )
'' '';
;
anyof = over_test_list "anyof"; anyof = over_test_list "anyof";
allof = over_test_list "allof"; allof = over_test_list "allof";
not = test: "not ${test}"; not = test: "not ${test}";
record_action = action_desc: record_action =
action_desc:
assert isString action_desc; assert isString action_desc;
''addheader "X-Vacu-Action" ${sieve_encode action_desc};'' ''addheader "X-Vacu-Action" ${sieve_encode action_desc};'';
; fileinto =
fileinto = folder: folder:
assert isString folder; assert isString folder;
'' ''
${record_action "fileinto ${folder}"} ${record_action "fileinto ${folder}"}
fileinto :create ${sieve_encode folder}; fileinto :create ${sieve_encode folder};
'' '';
; ihave =
ihave = extension_name_s: extension_name_s:
assert stringOrList extension_name_s; assert stringOrList extension_name_s;
"ihave ${sieve_encode extension_name_s}" "ihave ${sieve_encode extension_name_s}";
;
email_filters = map (e: '' email_filters = map (e: ''
elsif ${envelope_is e} { # item of email_filters elsif ${envelope_is e} { # item of email_filters
${record_action "email_filters fileinto ${mk_email_folder_name e}"} ${record_action "email_filters fileinto ${mk_email_folder_name e}"}
@@ -291,7 +320,9 @@ let
${set_with_interp var (interp "1")} ${set_with_interp var (interp "1")}
} }
else { else {
${lib.optionalString warn_if_unset (maybe_debug "info: Could not set ${var} from condition ${condition}, setting to default(${default})")} ${lib.optionalString warn_if_unset (
maybe_debug "info: Could not set ${var} from condition ${condition}, setting to default(${default})"
)}
${set var default} ${set var default}
} }
# set_from END # set_from END
@@ -305,13 +336,11 @@ let
condition = ''environment :matches ${sieve_quote_string item} "*"''; condition = ''environment :matches ${sieve_quote_string item} "*"'';
inherit var; inherit var;
}; };
maybe_debug = msg: maybe_debug = msg: ''
'' if ${ihave "vnd.dovecot.debug"} {
if ${ihave "vnd.dovecot.debug"} { debug_log ${sieve_quote_string_with_interp msg};
debug_log ${sieve_quote_string_with_interp msg}; }
} '';
''
;
sieve_text = '' sieve_text = ''
require [ require [
"fileinto", "fileinto",
@@ -325,22 +354,33 @@ let
"ihave" "ihave"
]; ];
if ${allof [ if ${
(ihave "imapsieve") allof [
(environment_matches "imap.user" "*") (ihave "imapsieve")
(environment_matches "location" "MS") (environment_matches "imap.user" "*")
(environment_matches "phase" "post") (environment_matches "location" "MS")
]} { (environment_matches "phase" "post")
]
} {
${set_bool_var "in_imap" true} ${set_bool_var "in_imap" true}
} else { } else {
${set_bool_var "in_imap" false} ${set_bool_var "in_imap" false}
} }
if ${var_is_true "in_imap"} { if ${var_is_true "in_imap"} {
if ${not (allof [ if ${
(environment_is "imap.cause" ["APPEND" "COPY" ""]) not (allof [
(environment_is "imap.mailbox" ["MagicRefilter" ""]) (environment_is "imap.cause" [
])} { "APPEND"
"COPY"
""
])
(environment_is "imap.mailbox" [
"MagicRefilter"
""
])
])
} {
${maybe_debug "NOT doing anything cuz imap.cause and/or imap.mailbox isn't right"} ${maybe_debug "NOT doing anything cuz imap.cause and/or imap.mailbox isn't right"}
stop; stop;
} }
@@ -358,9 +398,7 @@ let
condition = ''currentdate :matches "iso8601" "*"''; condition = ''currentdate :matches "iso8601" "*"'';
var = "datetime"; var = "datetime";
}} }}
${set_with_interp "sieved_message" ${set_with_interp "sieved_message" ''at ''${datetime} by ${config.vacu.versionId} loc ''${env_location} phase ''${env_phase} user ''${env_imap_user} email ''${env_imap_email} cause ''${env_imap_cause} mailbox ''${env_imap_mailbox} changedflags ''${env_imap_changedflags} envelope ''${dest}''}
''at ''${datetime} by ${config.vacu.versionId} loc ''${env_location} phase ''${env_phase} user ''${env_imap_user} email ''${env_imap_email} cause ''${env_imap_cause} mailbox ''${env_imap_mailbox} changedflags ''${env_imap_changedflags} envelope ''${dest}''
}
${maybe_debug ''X-Vacu-Sieved: ''${sieved_message}''} ${maybe_debug ''X-Vacu-Sieved: ''${sieved_message}''}
if ${ihave "envelope"} { if ${ihave "envelope"} {
@@ -382,43 +420,76 @@ let
removeflag "ignore"; removeflag "ignore";
removeflag "not-spamish"; removeflag "not-spamish";
${pure_flags ["amazon-ignore" "ignore"] [ ${pure_flags
(envelope_is "amznbsns@shelvacu.com") [ "amazon-ignore" "ignore" ]
(subject_matches [ "Your Amazon.com order has shipped*" "Your Amazon.com order of * has shipped!" ]) [
]} (envelope_is "amznbsns@shelvacu.com")
${pure_flags ["bandcamp-ignore" "ignore"] [ (subject_matches [
(envelope_is "bandcamp@shelvacu.com") "Your Amazon.com order has shipped*"
(subject_matches [ "* just announced a listening party on Bandcamp" "New items from *" "Starting in *" "New from *" ]) "Your Amazon.com order of * has shipped!"
]} ])
${pure_flags ["ika-ignore" "ignore"] (envelope_is "ika@dis8.net")} ]
${pure_flags ["ally-statement" "ignore"] [ }
(envelope_is "ally@shelvacu.com") ${pure_flags
(subject_is "Your latest statement is ready to view.") [ "bandcamp-ignore" "ignore" ]
]} [
(envelope_is "bandcamp@shelvacu.com")
(subject_matches [
"* just announced a listening party on Bandcamp"
"New items from *"
"Starting in *"
"New from *"
])
]
}
${pure_flags [ "ika-ignore" "ignore" ] (envelope_is "ika@dis8.net")}
${pure_flags
[ "ally-statement" "ignore" ]
[
(envelope_is "ally@shelvacu.com")
(subject_is "Your latest statement is ready to view.")
]
}
${pure_flags "bloomberg" (envelope_is "bloomberg@shelvacu.com")} ${pure_flags "bloomberg" (envelope_is "bloomberg@shelvacu.com")}
${pure_flags ["money-stuff" "not-spamish"] [ ${pure_flags
(envelope_is "bloomberg@shelvacu.com") [ "money-stuff" "not-spamish" ]
''header :matches "From" "\"Matt Levine\" *"'' [
]} (envelope_is "bloomberg@shelvacu.com")
''header :matches "From" "\"Matt Levine\" *"''
]
}
${pure_flags ["git" "not-spamish"] (exists ["X-GitHub-Reason" "X-GitLab-Project"])} ${pure_flags [ "git" "not-spamish" ] (exists [
${pure_flags ["git-uninsane" "git" "not-spamish"] (envelope_is "git-uninsane@shelvacu.com")} "X-GitHub-Reason"
"X-GitLab-Project"
])}
${pure_flags [ "git-uninsane" "git" "not-spamish" ] (envelope_is "git-uninsane@shelvacu.com")}
${pure_flags ["discourse" "not-spamish"] (exists "X-Discourse-Post-Id")} ${pure_flags [ "discourse" "not-spamish" ] (exists "X-Discourse-Post-Id")}
${pure_flags ["agora" "not-spamish"] (envelope_is "agora@shelvacu.com")} ${pure_flags [ "agora" "not-spamish" ] (envelope_is "agora@shelvacu.com")}
${pure_flags ["postgres-list" "not-spamish"] (header_matches "List-Id" "<*.lists.postgresql.org>")} ${pure_flags [ "postgres-list" "not-spamish" ] (
${pure_flags ["secureaccesswa" "not-spamish"] (from_is "help@secureaccess.wa.gov")} header_matches "List-Id" "<*.lists.postgresql.org>"
${pure_flags ["letsencrypt-mailing-list" "not-spamish"] (envelope_is "lets-encrypt-mailing-list@shelvacu.com")} )}
${pure_flags ["jmp-news" "not-spamish"] (header_matches "List-Id" "*<jmp-news.soprani.ca>")} ${pure_flags [ "secureaccesswa" "not-spamish" ] (from_is "help@secureaccess.wa.gov")}
${pure_flags ["tf2wiki" "not-spamish"] [ ${pure_flags [ "letsencrypt-mailing-list" "not-spamish" ] (
(envelope_is "tf2wiki@shelvacu.com") envelope_is "lets-encrypt-mailing-list@shelvacu.com"
(from_is "noreply@wiki.teamfortress.com") )}
]} ${pure_flags [ "jmp-news" "not-spamish" ] (header_matches "List-Id" "*<jmp-news.soprani.ca>")}
${pure_flags
[ "tf2wiki" "not-spamish" ]
[
(envelope_is "tf2wiki@shelvacu.com")
(from_is "noreply@wiki.teamfortress.com")
]
}
${pure_flags "gmail-fwd" (envelope_is "gmailfwd-fc2e10bec8b2@shelvacu.com")} ${pure_flags "gmail-fwd" (envelope_is "gmailfwd-fc2e10bec8b2@shelvacu.com")}
${pure_flags "aliexpress" (from_is ["transaction@notice.aliexpress.com" "aliexpress@notice.aliexpress.com"])} ${pure_flags "aliexpress" (from_is [
"transaction@notice.aliexpress.com"
"aliexpress@notice.aliexpress.com"
])}
removeflag "auto-marked-read"; removeflag "auto-marked-read";
if hasflag "ignore" { if hasflag "ignore" {

View File

@@ -31,6 +31,6 @@
vacu.packages = [ vacu.packages = [
"yt-dlp" "yt-dlp"
"radicle-node" "radicle-node"
"ncurses" #want this everywhere for clear/reset commands, but its included by default in nixos so only need ta specify here "ncurses" # want this everywhere for clear/reset commands, but its included by default in nixos so only need ta specify here
]; ];
} }

View File

@@ -1,8 +1,4 @@
{ { config, lib, ... }:
config,
lib,
...
}:
{ {
imports = [ ./lean.nix ]; imports = [ ./lean.nix ];
opts = { opts = {
@@ -23,7 +19,10 @@
lua_ls.enable = true; lua_ls.enable = true;
nixd = { nixd = {
enable = true; enable = true;
cmd = [ (lib.getExe config.plugins.lsp.servers.nixd.package) "--log=error" ]; cmd = [
(lib.getExe config.plugins.lsp.servers.nixd.package)
"--log=error"
];
}; };
pyright.enable = true; pyright.enable = true;
rust_analyzer = { rust_analyzer = {

View File

@@ -9,7 +9,8 @@ let
lean = inputs.self.packages.${system}.leanLatest; lean = inputs.self.packages.${system}.leanLatest;
in in
{ {
imports = [] imports =
[ ]
++ (lib.optional (!unstable) { ++ (lib.optional (!unstable) {
plugins.lean.leanPackage = lean; plugins.lean.leanPackage = lean;
plugins.lean.mappings = true; plugins.lean.mappings = true;
@@ -17,7 +18,6 @@ in
++ (lib.optional unstable { ++ (lib.optional unstable {
dependencies.lean.enable = false; dependencies.lean.enable = false;
plugins.lean.settings.mappings = true; plugins.lean.settings.mappings = true;
}) });
;
plugins.lean.enable = true; plugins.lean.enable = true;
} }

View File

@@ -8,7 +8,7 @@
git, git,
libdbusmenu, libdbusmenu,
# options passed to the thunderbird builder, but with betterbird defaults # options passed to the thunderbird builder, but with betterbird defaults
privacySupport ? true, privacySupport ? true,
requireSigning ? false, requireSigning ? false,
allowAddonSideload ? true, allowAddonSideload ? true,
@@ -33,25 +33,28 @@ let
tag = version; tag = version;
hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w="; hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w=";
}; };
patchesFromThunderbird = runCommandNoCC "betterbird-patches-from-network" { patchesFromThunderbird =
outputHashAlgo = "sha256"; runCommandNoCC "betterbird-patches-from-network"
outputHashMode = "recursive"; {
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY="; outputHashAlgo = "sha256";
} '' outputHashMode = "recursive";
set -xev outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
mkdir -p $out }
fetchFromSeries() { ''
local seriesFile="$1" set -xev
shift mkdir -p $out
filteredSeries="$(mktemp)" fetchFromSeries() {
cat "$seriesFile" | grep " # " | grep -v "^#" | sed 's/ # / /' | sed 's:/rev/:/raw-rev/:' > "$filteredSeries" local seriesFile="$1"
while IFS=" " read -r filename url; do shift
${lib.getExe curl} -v -k "$url" --output "$out/$filename" filteredSeries="$(mktemp)"
done < "$filteredSeries" cat "$seriesFile" | grep " # " | grep -v "^#" | sed 's/ # / /' | sed 's:/rev/:/raw-rev/:' > "$filteredSeries"
} while IFS=" " read -r filename url; do
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series${stuff.MOZU} ${lib.getExe curl} -v -k "$url" --output "$out/$filename"
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series done < "$filteredSeries"
''; }
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series${stuff.MOZU}
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series
'';
mozilla_src = fetchhg { mozilla_src = fetchhg {
name = "mozilla--mozilla"; name = "mozilla--mozilla";
url = stuff.MOZILLA_REPO; url = stuff.MOZILLA_REPO;
@@ -95,54 +98,64 @@ let
applySeries ${betterbirdPatches}/${majorVersion}/series $out/comm applySeries ${betterbirdPatches}/${majorVersion}/series $out/comm
''; '';
in in
(thunderbird-128-unwrapped.override { (thunderbird-128-unwrapped.override { inherit privacySupport requireSigning allowAddonSideload; })
inherit privacySupport requireSigning allowAddonSideload; .overrideAttrs
}).overrideAttrs (old: rec { (old: rec {
pname = "betterbird"; pname = "betterbird";
inherit version; inherit version;
name = "${pname}-${version}"; name = "${pname}-${version}";
buildInputs = (old.buildInputs or []) ++ [ libdbusmenu ]; buildInputs = (old.buildInputs or [ ]) ++ [ libdbusmenu ];
src = replacement_src; src = replacement_src;
configureFlags = (old.configureFlags or []) ++ [ configureFlags = (old.configureFlags or [ ]) ++ [
# "--enable-application=comm/mail" # "--enable-application=comm/mail"
"--with-branding=comm/mail/branding/betterbird" "--with-branding=comm/mail/branding/betterbird"
# "--disable-updater" # "--disable-updater"
# "--disable-crashreporter" # "--disable-crashreporter"
# "--enable-tests" # "--enable-tests"
# "--without-wasm-sandboxed-libraries" # "--without-wasm-sandboxed-libraries"
# "--with-unsigned-addon-scopes=app,system" # "--with-unsigned-addon-scopes=app,system"
# "--allow-addon-sideload" # "--allow-addon-sideload"
# "--enable-default-toolkit=cairo-gtk3-wayland" # "--enable-default-toolkit=cairo-gtk3-wayland"
# "--enable-official-branding" # "--enable-official-branding"
]; ];
preConfigure = (old.preConfigure or "") + '' preConfigure =
# Disable enforcing that add-ons are signed. (old.preConfigure or "")
export MOZ_REQUIRE_SIGNING= + ''
export MOZ_REQUIRE_ADDON_SIGNING=0 # Disable enforcing that add-ons are signed.
export MOZ_REQUIRE_SIGNING=
# For NSS symbols export MOZ_REQUIRE_ADDON_SIGNING=0
export MOZ_DEBUG_SYMBOLS=1
# Set the WM_CLASS referenced in the .desktop file.
export MOZ_APP_REMOTINGNAME=eu.betterbird.Betterbird
# Needed to enable breakpad in application.ini
# The preceding comment appears all over the Mozilla repos, however it is misleading.
# "Official" (server) builds, as opposed to local builds, should have nothing to do
# with "breakpad" (https://chromium.googlesource.com/breakpad/) crash reporting.
# In any case, we don't want a local build.
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING= # No telemetry.
# Used for Linux to create small launcher executable for file browsers.
# See https://hg.mozilla.org/mozilla-central/rev/3cbbfc5127e4 for details.
export MOZ_NO_PIE_COMPAT=1
'';
passthru = (old.passthru or {}) // { inherit betterbirdPatches mozilla_src comm_src replacement_src patchesFromThunderbird; }; # For NSS symbols
}) export MOZ_DEBUG_SYMBOLS=1
# Set the WM_CLASS referenced in the .desktop file.
export MOZ_APP_REMOTINGNAME=eu.betterbird.Betterbird
# Needed to enable breakpad in application.ini
# The preceding comment appears all over the Mozilla repos, however it is misleading.
# "Official" (server) builds, as opposed to local builds, should have nothing to do
# with "breakpad" (https://chromium.googlesource.com/breakpad/) crash reporting.
# In any case, we don't want a local build.
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING= # No telemetry.
# Used for Linux to create small launcher executable for file browsers.
# See https://hg.mozilla.org/mozilla-central/rev/3cbbfc5127e4 for details.
export MOZ_NO_PIE_COMPAT=1
'';
passthru = (old.passthru or { }) // {
inherit
betterbirdPatches
mozilla_src
comm_src
replacement_src
patchesFromThunderbird
;
};
})

View File

@@ -1,14 +1,15 @@
# shellcheck shell=bash
translateWetransferLinks() { translateWetransferLinks() {
local old_urlsArray=("${urls[*]}") local old_urlsArray=("${urls[*]}")
local new_urlsArray=() local new_urlsArray=()
for this_url in "${old_urlsArray[@]}"; do for this_url in "${old_urlsArray[@]}"; do
if [[ "$this_url" = https://we.tl/* ]] || [[ "$this_url" = https://wetransfer.com/downloads/* ]]; then if [[ $this_url == https://we.tl/* ]] || [[ $this_url == https://wetransfer.com/downloads/* ]]; then
local new_url local new_url
new_url="$("$transferweeBin" -v -g download "$this_url")" new_url="$("${transferweeBin:?}" -v -g download "$this_url")"
new_urlsArray+=("$new_url") new_urlsArray+=("$new_url")
else else
new_urlsArray+=("$this_url") new_urlsArray+=("$this_url")
fi fi
done done
urls="${new_urlsArray[*]}" urls="${new_urlsArray[*]}"
} }

View File

@@ -1,7 +1,9 @@
self: _super: { self: _super: {
fetchurlWithWetransfer = args: (self.fetchurl args).overrideAttrs (old: { fetchurlWithWetransfer =
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ ./convert-wetransfer-links.sh ]; args:
env.transferweeBin = self.lib.getExe self.transferwee; (self.fetchurl args).overrideAttrs (old: {
env.NIX_DEBUG = "6"; nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ ./convert-wetransfer-links.sh ];
}); env.transferweeBin = self.lib.getExe self.transferwee;
env.NIX_DEBUG = "6";
});
} }

View File

@@ -1,15 +1,13 @@
{ { lean4, fetchFromGitHub }:
lean4, lean4.overrideAttrs (
fetchFromGitHub, final: prev: {
}: version = "4.19.0-rc3";
lean4.overrideAttrs (final: prev: { src = fetchFromGitHub {
version = "4.19.0-rc3"; owner = "leanprover";
src = fetchFromGitHub { repo = "lean4";
owner = "leanprover"; tag = "v4.19.0-rc3";
repo = "lean4"; hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU=";
tag = "v4.19.0-rc3"; };
hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU="; cmakeFlags = (prev.cmakeFlags or [ ]) ++ [ "-DUSE_MIMALLOC=OFF" ];
}; }
cmakeFlags = (prev.cmakeFlags or []) ++ [ "-DUSE_MIMALLOC=OFF" ]; )
})

View File

@@ -17,8 +17,9 @@ let
]; ];
python = pkgs.python312.withPackages (_: libraries); python = pkgs.python312.withPackages (_: libraries);
in in
(pkgs.writers.writePython3Bin "dns-update" { (pkgs.writers.writePython3Bin "dns-update" { inherit libraries; } pythonScript).overrideAttrs
inherit libraries; (old: {
} pythonScript).overrideAttrs (old: { passthru = (old.passthru or { }) // {
passthru = (old.passthru or {}) // { inherit libraries python; }; inherit libraries python;
}) };
})

View File

@@ -4,98 +4,98 @@ dkim_key: ENC[AES256_GCM,data:CZC/1U1cJUIyNhXAWp+YFJd0pZZKvZClJxOh3uZ3YyfEQBiK9n
dkim_pub: ENC[AES256_GCM,data:XLYRTAviK+r6DnRU4+lc58elI3FJ+FPsB1A5sQOk+pb+fNu7zFCiZdz/MwTVkE9izDP1Onv+VhV8sRgmxacTv4nW5GcukCrm3FmCp2jm6QF1/40/WRv6Lkbek0tV1bMOQPy9Zj8wdO9M05XCXUVXk4x17rj+lw8ApwJS2pJMoultMFx34tx2pNEnmO3MFtuBOxzeU2yP+NhF2sJNA62to78AiH5EblkoF0a6sUYk553U+sv3Ob0lo1nSv6c8zwl7y1WSNQnLK+/3WxSVGfePHVsVM8Zze1KFTVLQQggIzWTdcr7AgcTGbk3kaYCeucfQ60pVlqOyPnkJoUJ8HR1RSajFk6Ylzw0xBpY85qAXNT2YIRiq0HTUc1s5lD0luXLQEP+g+XUwZfzFRZgt1nWBlPmpbj2Ylj1FfrA7EXsIK9nyo+rf0qRn/4HusJATr9ddYmZdxwazl1FXkOKLHPyu1NlzwoTNSQQgMHlzxzUvrrv7+mI2nQvXRx82TSRytqrMvoBTF1NFX+pRjhNg9fcq0oPJ2ORqOVQsxzhLhB+tw7Cg+UHGWlnKnkqaKH1JDmOFyJDB96aPUnSQT2J8qkyb+hMBXz9mme8rZopkHrA4WyDXv3zpEi0P5Sj0DDwdRxKMdDdZ4hw79YQIrd63cIorN8XG6Icevb25LfekLEq/C2FS8+kADagyOM0uzCw2p/qacNz37ZNGqPK6gYkjnyoAfSm14zbgoLX/5dnf7eCuMatevTm4AcE53RawQfzz0YNJuEv5uqD/WUy+UIKHIwxPYY9FWBBPmH+8eaPC1mMPh54I444b39FwpnPU8GwxEPsjRg8TSnohawNmmhEWEpmlawEKw+C+BE6A2DmVJzyeBvVRwe/W6CPgyYxgSGWUuvfZFm1GrzwZDjCOEMRn7qMwMBxh1nr2BOAiNxA38UtsymaZO5ZOknClWlKIkIFl8NJdVITNNsI48KMuSY20o1puzkxMaAUH3OrGEhtoHrEOeIq+KCFzH2gZo6L5hbv9CHM7QgCYsbtVIMwL+cRZZaSNubS3K48OmWJnHNuqkcrSI4lqfjLhz1DbnQ==,iv:/cNMmlpq9LSOk0MwVq8NaWvp47q68lKWTx4s5nkwF5c=,tag:ZNX+yZsSxdhFsavDpX380g==,type:str] dkim_pub: ENC[AES256_GCM,data:XLYRTAviK+r6DnRU4+lc58elI3FJ+FPsB1A5sQOk+pb+fNu7zFCiZdz/MwTVkE9izDP1Onv+VhV8sRgmxacTv4nW5GcukCrm3FmCp2jm6QF1/40/WRv6Lkbek0tV1bMOQPy9Zj8wdO9M05XCXUVXk4x17rj+lw8ApwJS2pJMoultMFx34tx2pNEnmO3MFtuBOxzeU2yP+NhF2sJNA62to78AiH5EblkoF0a6sUYk553U+sv3Ob0lo1nSv6c8zwl7y1WSNQnLK+/3WxSVGfePHVsVM8Zze1KFTVLQQggIzWTdcr7AgcTGbk3kaYCeucfQ60pVlqOyPnkJoUJ8HR1RSajFk6Ylzw0xBpY85qAXNT2YIRiq0HTUc1s5lD0luXLQEP+g+XUwZfzFRZgt1nWBlPmpbj2Ylj1FfrA7EXsIK9nyo+rf0qRn/4HusJATr9ddYmZdxwazl1FXkOKLHPyu1NlzwoTNSQQgMHlzxzUvrrv7+mI2nQvXRx82TSRytqrMvoBTF1NFX+pRjhNg9fcq0oPJ2ORqOVQsxzhLhB+tw7Cg+UHGWlnKnkqaKH1JDmOFyJDB96aPUnSQT2J8qkyb+hMBXz9mme8rZopkHrA4WyDXv3zpEi0P5Sj0DDwdRxKMdDdZ4hw79YQIrd63cIorN8XG6Icevb25LfekLEq/C2FS8+kADagyOM0uzCw2p/qacNz37ZNGqPK6gYkjnyoAfSm14zbgoLX/5dnf7eCuMatevTm4AcE53RawQfzz0YNJuEv5uqD/WUy+UIKHIwxPYY9FWBBPmH+8eaPC1mMPh54I444b39FwpnPU8GwxEPsjRg8TSnohawNmmhEWEpmlawEKw+C+BE6A2DmVJzyeBvVRwe/W6CPgyYxgSGWUuvfZFm1GrzwZDjCOEMRn7qMwMBxh1nr2BOAiNxA38UtsymaZO5ZOknClWlKIkIFl8NJdVITNNsI48KMuSY20o1puzkxMaAUH3OrGEhtoHrEOeIq+KCFzH2gZo6L5hbv9CHM7QgCYsbtVIMwL+cRZZaSNubS3K48OmWJnHNuqkcrSI4lqfjLhz1DbnQ==,iv:/cNMmlpq9LSOk0MwVq8NaWvp47q68lKWTx4s5nkwF5c=,tag:ZNX+yZsSxdhFsavDpX380g==,type:str]
relay_creds: ENC[AES256_GCM,data:o0FIKyqYHo1mndY+TC6TopipDlZMoyePPPRF62+WVegWjnz+dG83WTzIduJ6qdzlkBH0tgYfau7aIzYaDWZAd935efxvwTMl8lot0xTa8SqAYxQKDkTcpUhaHtu9wlpaqv31vzPdGUJbI17e9ZPdMEPRNaEYQkYqP2YoagO17WRbzIOax+XTP08pyVJChDG++aYlkuScOXQyM830hDy2xCYA9OHN4BeyU5mh6W0BiXLYIp9oOh0y1We59CUKeo0S,iv:JHgLeQO6XE5VYsoPU4YrI+LIaWSETvfnnwjrlTc1n0g=,tag:cWafuECJy2Gv5BMGKG1NOw==,type:str] relay_creds: ENC[AES256_GCM,data:o0FIKyqYHo1mndY+TC6TopipDlZMoyePPPRF62+WVegWjnz+dG83WTzIduJ6qdzlkBH0tgYfau7aIzYaDWZAd935efxvwTMl8lot0xTa8SqAYxQKDkTcpUhaHtu9wlpaqv31vzPdGUJbI17e9ZPdMEPRNaEYQkYqP2YoagO17WRbzIOax+XTP08pyVJChDG++aYlkuScOXQyM830hDy2xCYA9OHN4BeyU5mh6W0BiXLYIp9oOh0y1We59CUKeo0S,iv:JHgLeQO6XE5VYsoPU4YrI+LIaWSETvfnnwjrlTc1n0g=,tag:cWafuECJy2Gv5BMGKG1NOw==,type:str]
sops: sops:
age: age:
- recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5 - recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNDFQQ3lMbGtBVjdBR0t2 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNDFQQ3lMbGtBVjdBR0t2
MjJvcmpjQmtYR1VkSVYzTzJFZDZibmFKc1dNCkZpWUsvcEM2MnA2OWdOdXVsZzJi MjJvcmpjQmtYR1VkSVYzTzJFZDZibmFKc1dNCkZpWUsvcEM2MnA2OWdOdXVsZzJi
VjFDOVNjdkVIZDgwWE5pQmpKWkxSb3MKLS0tIDlSbXZFY1R0dkl3NHdvSTlWYTZ6 VjFDOVNjdkVIZDgwWE5pQmpKWkxSb3MKLS0tIDlSbXZFY1R0dkl3NHdvSTlWYTZ6
bDV6UGVHd2RVKzVycHJUWllTMk1HU2MKkDag+K62PydC3jcvLaIxy0vOuANbA65P bDV6UGVHd2RVKzVycHJUWllTMk1HU2MKkDag+K62PydC3jcvLaIxy0vOuANbA65P
hzaTNzv8iotafjFDYLWim7PLnxv+IeywKoL+Pnn4o3+e0617omx1mA== hzaTNzv8iotafjFDYLWim7PLnxv+IeywKoL+Pnn4o3+e0617omx1mA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj - recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnbWUrNTN0Y2lzSjR1ckc1 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnbWUrNTN0Y2lzSjR1ckc1
cW03WXZFVzBSUzdpUzVLMWJzRjhqaWRFODFBClJGSno0QUpQaGpVSzJ0Y3h5eXFj cW03WXZFVzBSUzdpUzVLMWJzRjhqaWRFODFBClJGSno0QUpQaGpVSzJ0Y3h5eXFj
aGpoNGIycG80NkxhWEFGeU9IMk1tWFEKLS0tIDI3Q3lHNGI1VWJBcFZDRDBqNGpD aGpoNGIycG80NkxhWEFGeU9IMk1tWFEKLS0tIDI3Q3lHNGI1VWJBcFZDRDBqNGpD
RDFNajdSSWQ1ZWNNcXl0T3lLcm1YUWMKm7w5OXFeuk7Sby68ODrk9EC8SbvCTxoO RDFNajdSSWQ1ZWNNcXl0T3lLcm1YUWMKm7w5OXFeuk7Sby68ODrk9EC8SbvCTxoO
oQueOepqeeh4wip3SQpHACvtUp4s85M6ZXE96uYioRlzy3zg39tIpQ== oQueOepqeeh4wip3SQpHACvtUp4s85M6ZXE96uYioRlzy3zg39tIpQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1rz75dqzfd6gulwh270ukmt5amcau6j8dpxgzx8fm6u8sjkyx9usq69y4s2 - recipient: age1rz75dqzfd6gulwh270ukmt5amcau6j8dpxgzx8fm6u8sjkyx9usq69y4s2
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMUVLVWpHaksvZkJIb0U4 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMUVLVWpHaksvZkJIb0U4
RnVTZ0k0L0VlMjFNNFg4RVZjTmk1OHEwbGpnCkIxTXN5aWMwTlZEWERYRXV5dHEx RnVTZ0k0L0VlMjFNNFg4RVZjTmk1OHEwbGpnCkIxTXN5aWMwTlZEWERYRXV5dHEx
UVFVVEczRFhWRDJPN3g0QVh2NXlZUjAKLS0tIGNRbkk3R1RYVCs2Y2x4UmZhTXdx UVFVVEczRFhWRDJPN3g0QVh2NXlZUjAKLS0tIGNRbkk3R1RYVCs2Y2x4UmZhTXdx
UVUrQStXTU9yUWJ0SnlIbDBIRUdSb00K9oPKVn1RzK0DVtaeXnfURea9k1lNzpor UVUrQStXTU9yUWJ0SnlIbDBIRUdSb00K9oPKVn1RzK0DVtaeXnfURea9k1lNzpor
3ex6hSyrfzNazFlInCuptIFIpf5o1eeiiV2PL85w9wvpMh4MEG7peg== 3ex6hSyrfzNazFlInCuptIFIpf5o1eeiiV2PL85w9wvpMh4MEG7peg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age13x0f3glnz4jvqty2v92cxrrnjcna6ed4qegrhulw9jjy08zuy3aqzvrfc6 - recipient: age13x0f3glnz4jvqty2v92cxrrnjcna6ed4qegrhulw9jjy08zuy3aqzvrfc6
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1MHUxcU9tR3JKSjk5TGRm YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1MHUxcU9tR3JKSjk5TGRm
c2I3S0lrV1RJZHFkN1JyNHlqc2hXbGtPVlVzCk9pMmVRdC92bld6SW0rNFVyRmJs c2I3S0lrV1RJZHFkN1JyNHlqc2hXbGtPVlVzCk9pMmVRdC92bld6SW0rNFVyRmJs
QmVOMXRrb3FvVUNUYnVuczg5MklEL1kKLS0tIEE2YkRmeWFONVpDTk02S3kwSWNI QmVOMXRrb3FvVUNUYnVuczg5MklEL1kKLS0tIEE2YkRmeWFONVpDTk02S3kwSWNI
Ty9PdGYxUnRNSUIxN21RWWJUQnVqWjAKp1KybOk5/5xHHggBwE7zyuOw17GwxPCw Ty9PdGYxUnRNSUIxN21RWWJUQnVqWjAKp1KybOk5/5xHHggBwE7zyuOw17GwxPCw
UR2R5wuc0d1Uyb/z/QvRI4lbpjAhjb749JgLE2IYTYLfPsJv59K8BA== UR2R5wuc0d1Uyb/z/QvRI4lbpjAhjb749JgLE2IYTYLfPsJv59K8BA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age13j6l33g0ghk4vezn0qwfal2qmcgqwkv89ejwezpe3n47mw8yxyuslj6y7d - recipient: age13j6l33g0ghk4vezn0qwfal2qmcgqwkv89ejwezpe3n47mw8yxyuslj6y7d
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2c2cyOWh3bEtBMUF6a3hx YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2c2cyOWh3bEtBMUF6a3hx
UjVIYkN5cE1ZcWNZM1V3Y3lhR05JYUt4Q3djCk9XNWF1dnhveVlLNWxJSVcxcVRK UjVIYkN5cE1ZcWNZM1V3Y3lhR05JYUt4Q3djCk9XNWF1dnhveVlLNWxJSVcxcVRK
V2d2aWx5ZXdrYUw0TFN3VGVZTE5RTTAKLS0tIDNnWm5nbDZUbmh3QTBCWXp6aUE0 V2d2aWx5ZXdrYUw0TFN3VGVZTE5RTTAKLS0tIDNnWm5nbDZUbmh3QTBCWXp6aUE0
ZFhoeXRTOEhDT2NpOXM2L3NCdVNEQmMKBp4e23mcqrJdlcqbf6mUjitYq7MxkeoX ZFhoeXRTOEhDT2NpOXM2L3NCdVNEQmMKBp4e23mcqrJdlcqbf6mUjitYq7MxkeoX
jX8LQTucw9dhLu/SCxymRxg9/Q2+PfhUvDR2L51tdlbr77dRhic3/A== jX8LQTucw9dhLu/SCxymRxg9/Q2+PfhUvDR2L51tdlbr77dRhic3/A==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1vla9w33lsp03s46p9p6gc2mvr844vthdqhc2hzau2ph6h60gmyqqh9sf57 - recipient: age1vla9w33lsp03s46p9p6gc2mvr844vthdqhc2hzau2ph6h60gmyqqh9sf57
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoeWltSkV6aGJ1WkJOVTBp YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoeWltSkV6aGJ1WkJOVTBp
Q24wMEFuWlVQYXMrKzRrSHN2THB3TWtYQ0VvCldHUmlpUGdNTlp4QkluZjRzK0J3 Q24wMEFuWlVQYXMrKzRrSHN2THB3TWtYQ0VvCldHUmlpUGdNTlp4QkluZjRzK0J3
U0ZGYWM2eFZyZHhuT2dWSnBJdzA0dmMKLS0tIHg0citENmY1QkpXNURzY2x4QkZM U0ZGYWM2eFZyZHhuT2dWSnBJdzA0dmMKLS0tIHg0citENmY1QkpXNURzY2x4QkZM
bG9DUTFkd2t3YXFXVElKK3JsK216Rm8KGvXixYViOUwrVarBMZeUI5HlCBtoL5bp bG9DUTFkd2t3YXFXVElKK3JsK216Rm8KGvXixYViOUwrVarBMZeUI5HlCBtoL5bp
7uZ9JFKQMh9EtiUk+Pr2xr4r9Mah0Gk3AmmVKWvaQaC/bkEIhe30Eg== 7uZ9JFKQMh9EtiUk+Pr2xr4r9Mah0Gk3AmmVKWvaQaC/bkEIhe30Eg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1jy8mxcndkw6zd6q99tjgz3gsynn78x2lwtrff85u6ud9g9y9z5mspvhufl - recipient: age1jy8mxcndkw6zd6q99tjgz3gsynn78x2lwtrff85u6ud9g9y9z5mspvhufl
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYmhVb0FsdUc5RjdPWnA5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYmhVb0FsdUc5RjdPWnA5
ZmpaMi9Rek5WM1AvSVl3Nk1maG1YanJaS0RzCjM3VEJKM3dVclZxK2FSMENKTUUz ZmpaMi9Rek5WM1AvSVl3Nk1maG1YanJaS0RzCjM3VEJKM3dVclZxK2FSMENKTUUz
d0dleUU2Rk5namdUdFl4ZjNSM05xdnMKLS0tIHRzYldRM0I4MytMcGFMUnZ3QXA0 d0dleUU2Rk5namdUdFl4ZjNSM05xdnMKLS0tIHRzYldRM0I4MytMcGFMUnZ3QXA0
MGtKcDMyejNFNktCL2I4RUI3Qkk1TWMKsxjqBw5J91f3T9TDHNAKFI2cTT4i7zJw MGtKcDMyejNFNktCL2I4RUI3Qkk1TWMKsxjqBw5J91f3T9TDHNAKFI2cTT4i7zJw
N33KbrskOaOXjCsoENnqdRl9Y7v/JbOh5YQ2/oPwZEfuwgHG9lcXqw== N33KbrskOaOXjCsoENnqdRl9Y7v/JbOh5YQ2/oPwZEfuwgHG9lcXqw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age148huz6rc3q9xx5t873ncx75sja2sazlescwspxl7lsmxsqkz0apsy8cldp - recipient: age148huz6rc3q9xx5t873ncx75sja2sazlescwspxl7lsmxsqkz0apsy8cldp
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdWRCdnNybjdzSFpacjNj YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdWRCdnNybjdzSFpacjNj
eFNJNjBzYmpsRkw4czN2aWJzSnBDeFYweDM4CmZCcnZCTEJQTGtoSlo3VW45T0ZJ eFNJNjBzYmpsRkw4czN2aWJzSnBDeFYweDM4CmZCcnZCTEJQTGtoSlo3VW45T0ZJ
bmpUMHhFMy9mSUxaTWVCcFBnQlAramsKLS0tIGV3eHcxRlJZc3BxQUU3TUhsRVAr bmpUMHhFMy9mSUxaTWVCcFBnQlAramsKLS0tIGV3eHcxRlJZc3BxQUU3TUhsRVAr
VXdheGpVRFF2UFBKQTF0OFMrVzdYcjQKaEs1irVwO0OoXbBhYd1AgCCPPF3sFH3a VXdheGpVRFF2UFBKQTF0OFMrVzdYcjQKaEs1irVwO0OoXbBhYd1AgCCPPF3sFH3a
go3jAHOCnwkYQMVRd24FGZx28XuEgeXQALk7JqEEy5eCS6nKDEVqcg== go3jAHOCnwkYQMVRd24FGZx28XuEgeXQALk7JqEEy5eCS6nKDEVqcg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age197a33mlf5294amjx59hycctu6wm4l3cu3w7n9rv3fs9340ql64rqjzpr7s - recipient: age197a33mlf5294amjx59hycctu6wm4l3cu3w7n9rv3fs9340ql64rqjzpr7s
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcWZxa3NHR0Z4TmlNNHVU YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcWZxa3NHR0Z4TmlNNHVU
aFNvN2tycVd2THhFMGtMckhGOXBuZXNMSkFFCm1VR1ZwUHdabFdBWmUxUXVxTVR5 aFNvN2tycVd2THhFMGtMckhGOXBuZXNMSkFFCm1VR1ZwUHdabFdBWmUxUXVxTVR5
eFVvakFDZUV2WHByU2pRU3hrWXVaMGcKLS0tIHRjbElYOU8xaW1lVFlrL0YwMDlQ eFVvakFDZUV2WHByU2pRU3hrWXVaMGcKLS0tIHRjbElYOU8xaW1lVFlrL0YwMDlQ
MEwvd1RQd1hlNVNZL3VveUkydVNjVE0KFsyjr38WdXu4R0038Dum0VeVw+LNcI6q MEwvd1RQd1hlNVNZL3VveUkydVNjVE0KFsyjr38WdXu4R0038Dum0VeVw+LNcI6q
4R0ft0KsfLLmPgoNIdK5Dq5hUxyGVe8Ej/9KaN0UrqIRsLHCHimYyQ== 4R0ft0KsfLLmPgoNIdK5Dq5hUxyGVe8Ej/9KaN0UrqIRsLHCHimYyQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1sqj8z3feqm2dk3gj8mxpfn5dpqnsmus862e8ayd0d4cdresqffdswcf9ru - recipient: age1sqj8z3feqm2dk3gj8mxpfn5dpqnsmus862e8ayd0d4cdresqffdswcf9ru
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4QW9yVk9zN2RrZkpTWXZU YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4QW9yVk9zN2RrZkpTWXZU
U080M1pDdzV1bDFSR2UrY0o2dnoyYlpNZXc0CmJCSE84L1ZRdUVZc21GbWc3cG9t U080M1pDdzV1bDFSR2UrY0o2dnoyYlpNZXc0CmJCSE84L1ZRdUVZc21GbWc3cG9t
NHRGQUFVS3U1TjFVYWl1Q1FyODY3UjgKLS0tIGhrY1dMa251R1hCc0F5eDhtWnc2 NHRGQUFVS3U1TjFVYWl1Q1FyODY3UjgKLS0tIGhrY1dMa251R1hCc0F5eDhtWnc2
bXpqNkVobzgwMHJIdHBFZ0xDZ2RzcmcK0m4awMUrdwYvXO14L1hvhcaGgLOW3FCq bXpqNkVobzgwMHJIdHBFZ0xDZ2RzcmcK0m4awMUrdwYvXO14L1hvhcaGgLOW3FCq
UU1Vc/vX32Lsu1BN4aXlTZ1jHD6R6CnV5TbUTcM/jxFRKoRzDwdJig== UU1Vc/vX32Lsu1BN4aXlTZ1jHD6R6CnV5TbUTcM/jxFRKoRzDwdJig==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-16T00:39:52Z" lastmodified: "2025-04-16T00:39:52Z"
mac: ENC[AES256_GCM,data:5rVkhMPb+gpB1fCCvRbillDO4MXl6TbKQar+hjRqy6bYjFejZgaAtAObbGL0n05/1XKpc3ULUXq7kQEOyjic6qhLx+o6biwkJFEjoF+0CePtTS9zRHXYDK1hnpgqMzVlVUH6KHipDJL9DAnDoJks3wc7gQaj4WjoGSlydDIcFPA=,iv:4JD9s2zkZjMoWRut9aawQjRhPo3q4FZrzoSTB6qyWgY=,tag:ZLzlb9TSP1agyPbWtP/mkA==,type:str] mac: ENC[AES256_GCM,data:5rVkhMPb+gpB1fCCvRbillDO4MXl6TbKQar+hjRqy6bYjFejZgaAtAObbGL0n05/1XKpc3ULUXq7kQEOyjic6qhLx+o6biwkJFEjoF+0CePtTS9zRHXYDK1hnpgqMzVlVUH6KHipDJL9DAnDoJks3wc7gQaj4WjoGSlydDIcFPA=,iv:4JD9s2zkZjMoWRut9aawQjRhPo3q4FZrzoSTB6qyWgY=,tag:ZLzlb9TSP1agyPbWtP/mkA==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.10.1 version: 3.10.1

View File

@@ -71,31 +71,34 @@ let
mkdir -p $out/liam mkdir -p $out/liam
SOPS_AGE_KEY="${testAgeSecret}" ${pkgs.sops}/bin/sops --verbose -e --age "$(echo "${testAgeSecret}" | ${pkgs.age}/bin/age-keygen -y)" ${sopsTestSecretsYaml} --output-type yaml > $out/liam/main.yaml SOPS_AGE_KEY="${testAgeSecret}" ${pkgs.sops}/bin/sops --verbose -e --age "$(echo "${testAgeSecret}" | ${pkgs.age}/bin/age-keygen -y)" ${sopsTestSecretsYaml} --output-type yaml > $out/liam/main.yaml
''; '';
mailtestModule = { mailtestModule =
config, {
pkgs, config,
lib, pkgs,
... lib,
}: ...
let }:
libraries = with pkgs.python3Packages; [ let
imap-tools libraries = with pkgs.python3Packages; [
requests imap-tools
]; requests
mkPkg = name: pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } '' ];
# flake8: noqa mkPkg =
${builtins.readFile ./mailtest/${name}.py} name:
''; pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
in # flake8: noqa
{ ${builtins.readFile ./mailtest/${name}.py}
options.vacu.mailtest = lib.mkOption { readOnly = true; }; '';
config.vacu.mailtest = { in
smtp = mkPkg "smtp"; {
imap = mkPkg "imap"; options.vacu.mailtest = lib.mkOption { readOnly = true; };
mailpit = mkPkg "mailpit"; config.vacu.mailtest = {
smtp = mkPkg "smtp";
imap = mkPkg "imap";
mailpit = mkPkg "mailpit";
};
config.environment.systemPackages = builtins.attrValues config.vacu.mailtest;
}; };
config.environment.systemPackages = builtins.attrValues config.vacu.mailtest;
};
in in
{ {
name = "liam-receives-mail"; name = "liam-receives-mail";
@@ -168,7 +171,12 @@ in
}; };
nodes.liam = nodes.liam =
{ lib, inputs, config, ... }: {
lib,
inputs,
config,
...
}:
{ {
imports = [ imports = [
"${inputs.self}/common" "${inputs.self}/common"
@@ -209,8 +217,10 @@ in
security.pki.certificateFiles = [ rootCA.certificatePath ]; security.pki.certificateFiles = [ rootCA.certificatePath ];
vacu.liam.backup.keyPath = pkgs.writeText "test-borg-key" testBorgKey; vacu.liam.backup.keyPath = pkgs.writeText "test-borg-key" testBorgKey;
vacu.ssh.knownHosts."rsync.net".enable = lib.mkForce false; #remove known key so i can do a trust-on-first-use in the test vacu.ssh.knownHosts."rsync.net".enable = lib.mkForce false; # remove known key so i can do a trust-on-first-use in the test
networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [ config.vacu.liam.backup.rsyncHost ]; networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [
config.vacu.liam.backup.rsyncHost
];
}; };
nodes.rsyncnet = nodes.rsyncnet =
@@ -218,7 +228,7 @@ in
let let
borgCfg = nodes.liam.vacu.liam.backup; borgCfg = nodes.liam.vacu.liam.backup;
user = borgCfg.rsyncUser; user = borgCfg.rsyncUser;
loginCommand = pkgs.writers.writeBashBin "special-rsync-login" {} '' loginCommand = pkgs.writers.writeBashBin "special-rsync-login" { } ''
set -euo pipefail set -euo pipefail
if [ -n "$SSH_ORIGINAL_COMMAND" ]; then if [ -n "$SSH_ORIGINAL_COMMAND" ]; then
echo "hi there" echo "hi there"
@@ -255,7 +265,7 @@ in
}; };
environment.systemPackages = [ environment.systemPackages = [
pkgs.borgbackup pkgs.borgbackup
(pkgs.writers.writeBashBin "borg-init" {} '' (pkgs.writers.writeBashBin "borg-init" { } ''
set -euo pipefail set -euo pipefail
export BORG_PASSPHRASE=${lib.escapeShellArg testBorgKey} export BORG_PASSPHRASE=${lib.escapeShellArg testBorgKey}
export BORG_NEW_PASSPHRASE=${lib.escapeShellArg testBorgKey} export BORG_NEW_PASSPHRASE=${lib.escapeShellArg testBorgKey}
@@ -268,27 +278,26 @@ in
{ pkgs, lib, ... }: { pkgs, lib, ... }:
{ {
imports = [ mailtestModule ]; imports = [ mailtestModule ];
environment.systemPackages = [ environment.systemPackages = [ pkgs.wget ];
pkgs.wget
];
networking.nameservers = lib.mkForce (lib.singleton nodes.ns.networking.primaryIPAddress); networking.nameservers = lib.mkForce (lib.singleton nodes.ns.networking.primaryIPAddress);
}; };
testScript = testScript =
let let
rawFile = builtins.readFile ./testScript/main.py; rawFile = builtins.readFile ./testScript/main.py;
data = { data = {
checkSieve = lib.getExe nodes.liam.vacu.checkSieve; checkSieve = lib.getExe nodes.liam.vacu.checkSieve;
acmeTest = pkgs.writeText "acme-test" "test"; acmeTest = pkgs.writeText "acme-test" "test";
acmeTestDest = nodes.liam.security.acme.defaults.webroot + "/.well-known/acme-challenge/test"; acmeTestDest = nodes.liam.security.acme.defaults.webroot + "/.well-known/acme-challenge/test";
relayIP = nodes.relay.networking.primaryIPAddress; relayIP = nodes.relay.networking.primaryIPAddress;
liamIP = nodes.liam.networking.primaryIPAddress; liamIP = nodes.liam.networking.primaryIPAddress;
inherit testBorgKey; inherit testBorgKey;
borgExe = lib.getExe pkgs.borgbackup; borgExe = lib.getExe pkgs.borgbackup;
}; };
dataJson = builtins.toJSON data; dataJson = builtins.toJSON data;
res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile; res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile;
in res; in
res;
skipTypeCheck = true; skipTypeCheck = true;
skipLint = true; skipLint = true;

View File

@@ -6,18 +6,22 @@ import ssl
import json import json
from typing import NamedTuple from typing import NamedTuple
def info(msg: str): def info(msg: str):
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
def mk_ctx(): def mk_ctx():
ctx = ssl.create_default_context() ctx = ssl.create_default_context()
ctx.check_hostname = False ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE ctx.verify_mode = ssl.CERT_NONE
return ctx return ctx
def print_json(**kwargs): def print_json(**kwargs):
print(json.dumps(kwargs)) print(json.dumps(kwargs))
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--host", type=str) parser.add_argument("--host", type=str)
parser.add_argument("--insecure", default=False, action="store_true") parser.add_argument("--insecure", default=False, action="store_true")
@@ -45,6 +49,7 @@ info(f"looking for {msg_magic}")
result: bool result: bool
matching_messages = [] matching_messages = []
try: try:
def connection() -> imap_tools.BaseMailBox: def connection() -> imap_tools.BaseMailBox:
return imap_tools.MailBox(args.host, ssl_context=mk_ctx()).login( return imap_tools.MailBox(args.host, ssl_context=mk_ctx()).login(
username, password username, password
@@ -87,7 +92,7 @@ try:
assert uid is not None assert uid is not None
info(f"about to move {uid} to {args.move_to}") info(f"about to move {uid} to {args.move_to}")
res = mailbox.move(uid, args.move_to) res = mailbox.move(uid, args.move_to)
assert res[1][1][1] is not None, "failed to move" # type: ignore assert res[1][1][1] is not None, "failed to move" # type: ignore
info(f"done moving, res {res!r}") info(f"done moving, res {res!r}")
with connection() as mailbox: with connection() as mailbox:
@@ -99,6 +104,7 @@ except imaplib.IMAP4.error as e:
else: else:
result = True result = True
def mail_to_jsonish(m: MessageInFolder) -> dict: def mail_to_jsonish(m: MessageInFolder) -> dict:
return { return {
"folder": m.folder, "folder": m.folder,
@@ -106,7 +112,8 @@ def mail_to_jsonish(m: MessageInFolder) -> dict:
"body": m.message.text.strip(), "body": m.message.text.strip(),
} }
print_json( print_json(
result = result, result=result,
messages = [mail_to_jsonish(m) for m in matching_messages], messages=[mail_to_jsonish(m) for m in matching_messages],
) )

View File

@@ -3,9 +3,11 @@ import requests
import sys import sys
import json import json
def info(msg: str): def info(msg: str):
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
def print_json(**kwargs): def print_json(**kwargs):
print(json.dumps(kwargs)) print(json.dumps(kwargs))
@@ -37,4 +39,4 @@ for message_data in mails["messages"]:
found_message = True found_message = True
break break
print_json(found_message = found_message) print_json(found_message=found_message)

View File

@@ -4,18 +4,22 @@ import sys
import ssl import ssl
import json import json
def info(msg: str): def info(msg: str):
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
def mk_ctx(): def mk_ctx():
ctx = ssl.create_default_context() ctx = ssl.create_default_context()
ctx.check_hostname = False ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE ctx.verify_mode = ssl.CERT_NONE
return ctx return ctx
def print_json(**kwargs): def print_json(**kwargs):
print(json.dumps(kwargs)) print(json.dumps(kwargs))
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--host", type=str) parser.add_argument("--host", type=str)
parser.add_argument("--mailfrom", default="foo@example.com") parser.add_argument("--mailfrom", default="foo@example.com")
@@ -64,4 +68,4 @@ except smtplib.SMTPSenderRefused:
else: else:
result = True result = True
print_json(result = result) print_json(result=result)

View File

@@ -2,14 +2,17 @@ import sys
import ssl import ssl
import json import json
def info(msg: str): def info(msg: str):
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
def mk_ctx(): def mk_ctx():
ctx = ssl.create_default_context() ctx = ssl.create_default_context()
ctx.check_hostname = False ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE ctx.verify_mode = ssl.CERT_NONE
return ctx return ctx
def print_json(**kwargs): def print_json(**kwargs):
print(json.dumps(kwargs)) print(json.dumps(kwargs))

View File

@@ -6,7 +6,7 @@ DATA_JSON = """
""" """
if TYPE_CHECKING: if TYPE_CHECKING:
from hints import * # type: ignore from hints import * # type: ignore
import json import json
import shlex import shlex
@@ -32,7 +32,7 @@ liam.wait_for_unit("dovecot2.service")
relay.wait_for_unit("mailpit.service") relay.wait_for_unit("mailpit.service")
# generate and exchange keys so they can talk to eachother # generate and exchange keys so they can talk to eachother
rsyncnet.wait_for_open_port(22); rsyncnet.wait_for_open_port(22)
# liam.succeed("install -d --owner=autoborger --group=autoborger --mode=0770 /var/lib/auto-borg") # liam.succeed("install -d --owner=autoborger --group=autoborger --mode=0770 /var/lib/auto-borg")
# # not succeed, it will fail because not authorized but that's okay # # not succeed, it will fail because not authorized but that's okay
# liam.execute("sudo -u autoborger ssh -oBatchMode=yes -oStrictHostKeyChecking=accept-new fm2382.rsync.net") # liam.execute("sudo -u autoborger ssh -oBatchMode=yes -oStrictHostKeyChecking=accept-new fm2382.rsync.net")
@@ -40,7 +40,9 @@ liam.succeed("systemctl start auto-borg-gen-key.service")
# liam.wait_for_unit("auto-borg-gen-key.service") # liam.wait_for_unit("auto-borg-gen-key.service")
liam_autoborger_key = liam.succeed("cat /var/lib/auto-borg/id_ed25519.pub").strip() liam_autoborger_key = liam.succeed("cat /var/lib/auto-borg/id_ed25519.pub").strip()
rsyncnet.succeed("install -d --owner=fm2382 --mode=0700 /home/fm2382/.ssh") rsyncnet.succeed("install -d --owner=fm2382 --mode=0700 /home/fm2382/.ssh")
rsyncnet.succeed(f"install --owner=fm2382 --mode=0600 -T <(echo 'command=\"borg serve --restrict-to-repository /home/fm2382/borg-repos/liam-backup --append-only\",restrict {liam_autoborger_key}') /home/fm2382/.ssh/authorized_keys") rsyncnet.succeed(
f"install --owner=fm2382 --mode=0600 -T <(echo 'command=\"borg serve --restrict-to-repository /home/fm2382/borg-repos/liam-backup --append-only\",restrict {liam_autoborger_key}') /home/fm2382/.ssh/authorized_keys"
)
rsyncnet.succeed("sudo -u fm2382 borg-init") rsyncnet.succeed("sudo -u fm2382 borg-init")
liam.succeed("systemctl start auto-borg.service") liam.succeed("systemctl start auto-borg.service")
# liam.wait_for_unit("auto-borg.service") # liam.wait_for_unit("auto-borg.service")
@@ -51,21 +53,25 @@ class ImapMessage(TypedDict):
flags: list[str] flags: list[str]
body: str body: str
class ImapResult(TypedDict): class ImapResult(TypedDict):
result: bool result: bool
messages: list[ImapMessage] messages: list[ImapMessage]
def make_command(args: list) -> str: def make_command(args: list) -> str:
return " ".join(map(shlex.quote, (map(str, args)))) return " ".join(map(shlex.quote, (map(str, args))))
Arg = str|bool|int
Arg = str | bool | int
Args = dict[str, Arg] Args = dict[str, Arg]
def dict_args_to_list(dict_args: Args) -> list[str]: def dict_args_to_list(dict_args: Args) -> list[str]:
args:list[str] = [] args: list[str] = []
for k, v in dict_args.items(): for k, v in dict_args.items():
dashed = k.replace("_","-") dashed = k.replace("_", "-")
if isinstance(v, int) and not isinstance(v, bool): if isinstance(v, int) and not isinstance(v, bool):
v = str(v) v = str(v)
if isinstance(v, str) or (isinstance(v, bool) and v): if isinstance(v, str) or (isinstance(v, bool) and v):
@@ -74,15 +80,17 @@ def dict_args_to_list(dict_args: Args) -> list[str]:
args.append(v) args.append(v)
return args return args
class LogEntry(TypedDict): class LogEntry(TypedDict):
__CURSOR: str __CURSOR: str
_SYSTEMD_UNIT: str _SYSTEMD_UNIT: str
MESSAGE: str MESSAGE: str
pass pass
DEFAULT_JOURNALCTL_OPTS: Args = { DEFAULT_JOURNALCTL_OPTS: Args = {
"all": True, "all": True,
"output": "json", "output": "json",
} }
# --json # --json
@@ -94,22 +102,27 @@ DEFAULT_JOURNALCTL_OPTS: Args = {
# #
# 3. Fields containing non-printable or non-UTF8 bytes are encoded as arrays containing the raw bytes individually formatted as unsigned numbers. # 3. Fields containing non-printable or non-UTF8 bytes are encoded as arrays containing the raw bytes individually formatted as unsigned numbers.
def journalctl_log_entries(machine: Machine = liam, **kwargs: Arg) -> Generator[LogEntry, None, None]:
def journalctl_log_entries(
machine: Machine = liam, **kwargs: Arg
) -> Generator[LogEntry, None, None]:
with_defaults = {**kwargs, **DEFAULT_JOURNALCTL_OPTS} with_defaults = {**kwargs, **DEFAULT_JOURNALCTL_OPTS}
args = ["journalctl", *dict_args_to_list(with_defaults)] args = ["journalctl", *dict_args_to_list(with_defaults)]
res = machine.succeed(make_command(args)) res = machine.succeed(make_command(args))
for line in res.splitlines(): for line in res.splitlines():
data:LogEntry = json.loads(line) data: LogEntry = json.loads(line)
assert isinstance(data, dict) assert isinstance(data, dict)
for _, v in data.items(): for _, v in data.items():
assert isinstance(v, str) assert isinstance(v, str)
yield data yield data
class ProcessingWaiter(contextlib.AbstractContextManager): class ProcessingWaiter(contextlib.AbstractContextManager):
cursor: str = "" cursor: str = ""
timeout: int = 60 timeout: int = 60
_postfix_smtpd_connections: set[str] = set() _postfix_smtpd_connections: set[str] = set()
_postfix_queue: set[str] = set() _postfix_queue: set[str] = set()
def __init__(self, timeout=60): def __init__(self, timeout=60):
most_recent_entry = list(journalctl_log_entries(lines=1))[0] most_recent_entry = list(journalctl_log_entries(lines=1))[0]
self.cursor = most_recent_entry["__CURSOR"] self.cursor = most_recent_entry["__CURSOR"]
@@ -128,7 +141,9 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
assert sl_pid is not None assert sl_pid is not None
if message.startswith("connect from"): if message.startswith("connect from"):
self._postfix_smtpd_connections.add(sl_pid) self._postfix_smtpd_connections.add(sl_pid)
if message.startswith("disconnect from") or message.startswith("lost connection"): if message.startswith("disconnect from") or message.startswith(
"lost connection"
):
self._postfix_smtpd_connections.discard(sl_pid) self._postfix_smtpd_connections.discard(sl_pid)
if unit == "postfix.service" and sl_ident == "postfix/qmgr": if unit == "postfix.service" and sl_ident == "postfix/qmgr":
queue_id = message.split(":")[0] queue_id = message.split(":")[0]
@@ -136,7 +151,10 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
self._postfix_queue.add(queue_id) self._postfix_queue.add(queue_id)
if message.endswith("removed"): if message.endswith("removed"):
self._postfix_queue.discard(queue_id) self._postfix_queue.discard(queue_id)
if len(self._postfix_smtpd_connections) == 0 and len(self._postfix_queue) == 0: if (
len(self._postfix_smtpd_connections) == 0
and len(self._postfix_queue) == 0
):
return return
else: else:
liam.sleep(1) liam.sleep(1)
@@ -144,32 +162,28 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
def __exit__(self, _a, _b, _c): def __exit__(self, _a, _b, _c):
self.wait_until_finished() self.wait_until_finished()
class TesterThing():
class TesterThing:
uuid: str = "" uuid: str = ""
default_smtp: Args = {} default_smtp: Args = {}
default_imap: Args = {} default_imap: Args = {}
default_mailpit: Args = {} default_mailpit: Args = {}
def __init__(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: Arg): def __init__(
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: Arg
):
self.uuid = str(uuid.uuid4()) self.uuid = str(uuid.uuid4())
self.default_smtp = { self.default_smtp = {
"rcptto": "someone@example.com", "rcptto": "someone@example.com",
"host": f"{liam_ip}", "host": f"{liam_ip}",
**common, **common,
**smtp **smtp,
}
self.default_imap = {
"host": f"{liam_ip}",
**common,
**imap
}
self.default_mailpit = {
"mailpit-url": f"http://{relay_ip}:8025",
**mailpit
} }
self.default_imap = {"host": f"{liam_ip}", **common, **imap}
self.default_mailpit = {"mailpit-url": f"http://{relay_ip}:8025", **mailpit}
def run_expecting_json(self, name: str, **kwargs: Arg) -> dict[str, Any]: def run_expecting_json(self, name: str, **kwargs: Arg) -> dict[str, Any]:
args:list[str] = [name, *dict_args_to_list(kwargs)] args: list[str] = [name, *dict_args_to_list(kwargs)]
print(f"running {args!r}") print(f"running {args!r}")
with ProcessingWaiter(): with ProcessingWaiter():
res = checker.succeed(make_command(args)) res = checker.succeed(make_command(args))
@@ -195,35 +209,37 @@ class TesterThing():
def run_imap(self, **kwargs: Arg) -> ImapResult: def run_imap(self, **kwargs: Arg) -> ImapResult:
args = {"message_magic": self.uuid, **self.default_imap, **kwargs} args = {"message_magic": self.uuid, **self.default_imap, **kwargs}
res_unty = self.run_expecting_json("mailtest-imap", **args) res_unty = self.run_expecting_json("mailtest-imap", **args)
res_ty: ImapResult = res_unty # type: ignore res_ty: ImapResult = res_unty # type: ignore
return res_ty return res_ty
def imap_expect(self, mailbox: str|None = None, flags: list[str] = [], **kwargs: Arg) -> Self: def imap_expect(
self, mailbox: str | None = None, flags: list[str] = [], **kwargs: Arg
) -> Self:
res = self.run_imap(**kwargs) res = self.run_imap(**kwargs)
assert res['result'] assert res["result"]
assert len(res['messages']) == 1 assert len(res["messages"]) == 1
message = res['messages'][0] message = res["messages"][0]
if mailbox is not None: if mailbox is not None:
assert message['folder'] == mailbox assert message["folder"] == mailbox
for flag in flags: for flag in flags:
assert flag in message['flags'] assert flag in message["flags"]
return self return self
def imap_found(self, **kwargs: Arg) -> Self: def imap_found(self, **kwargs: Arg) -> Self:
return self.imap_expect(mailbox = None, flags = [], **kwargs) return self.imap_expect(mailbox=None, flags=[], **kwargs)
def imap_found_in(self, mailbox: str, **kwargs: Arg) -> Self: def imap_found_in(self, mailbox: str, **kwargs: Arg) -> Self:
return self.imap_expect(mailbox = mailbox, flags = [], **kwargs) return self.imap_expect(mailbox=mailbox, flags=[], **kwargs)
def imap_move_to(self, dest: str, **kwargs: Arg) -> Self: def imap_move_to(self, dest: str, **kwargs: Arg) -> Self:
res = self.run_imap(move_to=dest, **kwargs) res = self.run_imap(move_to=dest, **kwargs)
assert res['result'] assert res["result"]
return self return self
def run_mailpit(self, **kwargs: Arg) -> bool: def run_mailpit(self, **kwargs: Arg) -> bool:
args = {"message_magic": self.uuid, **self.default_mailpit, **kwargs} args = {"message_magic": self.uuid, **self.default_mailpit, **kwargs}
res = self.run_expecting_json("mailtest-mailpit", **args) res = self.run_expecting_json("mailtest-mailpit", **args)
return res['found_message'] return res["found_message"]
def mailpit_not_received(self, **kwargs: Arg) -> Self: def mailpit_not_received(self, **kwargs: Arg) -> Self:
received = self.run_mailpit(**kwargs) received = self.run_mailpit(**kwargs)
@@ -235,57 +251,78 @@ class TesterThing():
assert received assert received
return self return self
class Defaults():
class Defaults:
default_smtp: Args = {} default_smtp: Args = {}
default_imap: Args = {} default_imap: Args = {}
default_mailpit: Args = {} default_mailpit: Args = {}
def __init__(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool): def __init__(
self.default_smtp = { self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
**common, ):
**smtp self.default_smtp = {**common, **smtp}
} self.default_imap = {**common, **imap}
self.default_imap = { self.default_mailpit = {**mailpit}
**common,
**imap def make_tester(
} self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
self.default_mailpit = { ) -> TesterThing:
**mailpit
}
def make_tester(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool) -> TesterThing:
return TesterThing( return TesterThing(
smtp = {**self.default_smtp, **common, **smtp}, smtp={**self.default_smtp, **common, **smtp},
imap = {**self.default_imap, **common, **imap}, imap={**self.default_imap, **common, **imap},
mailpit = {**self.default_mailpit, **common, **mailpit}, mailpit={**self.default_mailpit, **common, **mailpit},
) )
# The order of these shouldn't matter, other than what fails first. Whatever is at the top is probably whatever I was working on most recently.
d = Defaults(smtp = {"submission": True, "rcptto": "someone@example.com"}, username = "vacustore")
d.make_tester(smtp = {"mailfrom": "robot@vacu.store"}).smtp_accepted().mailpit_received()
d.make_tester(smtp = {"mailfrom": "foobar@vacu.store"}).smtp_rejected()
d.make_tester(smtp = {"mailfrom": "abc@shelvacu.com"}).smtp_rejected()
d = Defaults(smtp = {"mailfrom": "whoeve2@example.com", "rcptto": "sieve2est@shelvacu.com"}, username = "shelvacu") # The order of these shouldn't matter, other than what fails first. Whatever is at the top is probably whatever I was working on most recently.
d = Defaults(
smtp={"submission": True, "rcptto": "someone@example.com"}, username="vacustore"
)
d.make_tester(smtp={"mailfrom": "robot@vacu.store"}).smtp_accepted().mailpit_received()
d.make_tester(smtp={"mailfrom": "foobar@vacu.store"}).smtp_rejected()
d.make_tester(smtp={"mailfrom": "abc@shelvacu.com"}).smtp_rejected()
d = Defaults(
smtp={"mailfrom": "whoeve2@example.com", "rcptto": "sieve2est@shelvacu.com"},
username="shelvacu",
)
# test refilter # test refilter
d.make_tester().smtp_accepted().imap_move_to("MagicRefilter").imap_found_in("com.shelvacu") d.make_tester().smtp_accepted().imap_move_to("MagicRefilter").imap_found_in(
"com.shelvacu"
)
# refilter doesnt activate on other folders # refilter doesnt activate on other folders
d.make_tester().smtp_accepted().imap_move_to("testFolder").imap_found_in("testFolder") d.make_tester().smtp_accepted().imap_move_to("testFolder").imap_found_in("testFolder")
d.make_tester().smtp_accepted().imap_move_to("INBOX").imap_found_in("INBOX") d.make_tester().smtp_accepted().imap_move_to("INBOX").imap_found_in("INBOX")
# test the sieve script is working # test the sieve script is working
d.make_tester().smtp_accepted().imap_found_in("com.shelvacu") d.make_tester().smtp_accepted().imap_found_in("com.shelvacu")
# refilter doesnt activate on julie's # refilter doesnt activate on julie's
d.make_tester(username="julie").smtp_accepted(rcptto="julie@shelvacu.com").imap_move_to("MagicRefilter").imap_found_in("MagicRefilter") d.make_tester(username="julie").smtp_accepted(rcptto="julie@shelvacu.com").imap_move_to(
"MagicRefilter"
).imap_found_in("MagicRefilter")
d = Defaults(username = "shelvacu") d = Defaults(username="shelvacu")
d.make_tester().smtp_accepted(mailfrom="asshole-spammer@example.com", rcptto="whatever@shelvacu.com", header="List-unsubscribe: whatever").imap_expect(mailbox="com.shelvacu.#spamish", flags=["spamish"]) d.make_tester().smtp_accepted(
d.make_tester().smtp_accepted(mailfrom="shipment-tracking@amazon.com", rcptto="amznbsns@shelvacu.com", subject="Your Amazon.com order has shipped (#123-1234)").imap_expect(mailbox="com.shelvacu", flags=["\\Seen","auto-marked-read","amazon-ignore"]) mailfrom="asshole-spammer@example.com",
rcptto="whatever@shelvacu.com",
header="List-unsubscribe: whatever",
).imap_expect(mailbox="com.shelvacu.#spamish", flags=["spamish"])
d.make_tester().smtp_accepted(
mailfrom="shipment-tracking@amazon.com",
rcptto="amznbsns@shelvacu.com",
subject="Your Amazon.com order has shipped (#123-1234)",
).imap_expect(
mailbox="com.shelvacu", flags=["\\Seen", "auto-marked-read", "amazon-ignore"]
)
TesterThing().smtp_accepted(rcptto="shelvacu@shelvacu.com", username="shelvacu", smtp_starttls=True) TesterThing().smtp_accepted(
rcptto="shelvacu@shelvacu.com", username="shelvacu", smtp_starttls=True
)
d = Defaults(smtp = {"submission": True, "rcptto": "foo@example.com"}, username="shelvacu") d = Defaults(
smtp={"submission": True, "rcptto": "foo@example.com"}, username="shelvacu"
)
d.make_tester().smtp_accepted(mailfrom="me@shelvacu.com").mailpit_received() d.make_tester().smtp_accepted(mailfrom="me@shelvacu.com").mailpit_received()
d.make_tester().smtp_accepted(mailfrom="me@dis8.net" ).mailpit_not_received() d.make_tester().smtp_accepted(mailfrom="me@dis8.net").mailpit_not_received()
# julie's emails should NOT get sieve'd like mine # julie's emails should NOT get sieve'd like mine
d = Defaults(username="julie") d = Defaults(username="julie")
@@ -302,7 +339,7 @@ d.make_tester().smtp_accepted(rcptto="julie@shelvacu.com").imap_found()
d.make_tester().smtp_accepted(rcptto="sales@theviolincase.com").imap_found() d.make_tester().smtp_accepted(rcptto="sales@theviolincase.com").imap_found()
d.make_tester().smtp_accepted(rcptto="superwow@theviolincase.com").imap_found() d.make_tester().smtp_accepted(rcptto="superwow@theviolincase.com").imap_found()
#incoming mail cant be from known domains # incoming mail cant be from known domains
TesterThing().smtp_rejected(mailfrom="bob@vacu.store") TesterThing().smtp_rejected(mailfrom="bob@vacu.store")
TesterThing().smtp_rejected(mailfrom="shelvacu@shelvacu.com") TesterThing().smtp_rejected(mailfrom="shelvacu@shelvacu.com")
TesterThing().smtp_rejected(mailfrom="julie@shelvacu.com") TesterThing().smtp_rejected(mailfrom="julie@shelvacu.com")
@@ -310,19 +347,27 @@ TesterThing().smtp_rejected(mailfrom="@vacu.store")
TesterThing().smtp_rejected(mailfrom="reject-spam-test@example.com") TesterThing().smtp_rejected(mailfrom="reject-spam-test@example.com")
#people cant send as the wrong person # people cant send as the wrong person
d = Defaults(smtp = {"submission": True}) d = Defaults(smtp={"submission": True})
d.make_tester().smtp_rejected(mailfrom="julie@shelvacu.com", username="shelvacu") d.make_tester().smtp_rejected(mailfrom="julie@shelvacu.com", username="shelvacu")
d.make_tester().smtp_rejected(mailfrom="fubar@theviolincase.com", username="shelvacu") d.make_tester().smtp_rejected(mailfrom="fubar@theviolincase.com", username="shelvacu")
d.make_tester().smtp_rejected(mailfrom="fubar@vacu.store", username="julie") d.make_tester().smtp_rejected(mailfrom="fubar@vacu.store", username="julie")
d = Defaults(smtp = {"submission": True, "rcptto": "foo@example.com"}) d = Defaults(smtp={"submission": True, "rcptto": "foo@example.com"})
d.make_tester().smtp_accepted(mailfrom="shelvacu@shelvacu.com", username="shelvacu") d.make_tester().smtp_accepted(mailfrom="shelvacu@shelvacu.com", username="shelvacu")
d.make_tester().smtp_accepted(mailfrom="shelvacu@shelvacu.com", username="shelvacu@shelvacu.com", password="shelvacu") d.make_tester().smtp_accepted(
mailfrom="shelvacu@shelvacu.com",
username="shelvacu@shelvacu.com",
password="shelvacu",
)
d.make_tester().smtp_accepted(mailfrom="foo@vacu.store", username="shelvacu") d.make_tester().smtp_accepted(mailfrom="foo@vacu.store", username="shelvacu")
d.make_tester().smtp_accepted(mailfrom="foo@vacu.store", username="shelvacu@shelvacu.com", password="shelvacu") d.make_tester().smtp_accepted(
mailfrom="foo@vacu.store", username="shelvacu@shelvacu.com", password="shelvacu"
)
d.make_tester().smtp_accepted(mailfrom="foo@violingifts.com", username="julie") d.make_tester().smtp_accepted(mailfrom="foo@violingifts.com", username="julie")
d.make_tester().smtp_accepted(mailfrom="foo@violingifts.com", username="julie@shelvacu.com", password="julie") d.make_tester().smtp_accepted(
mailfrom="foo@violingifts.com", username="julie@shelvacu.com", password="julie"
)
# now that there's a bunch of mail and logs and stuff, we can still run a borg backup, right? # now that there's a bunch of mail and logs and stuff, we can still run a borg backup, right?
liam.succeed("systemctl start auto-borg.service") liam.succeed("systemctl start auto-borg.service")

View File

@@ -1,9 +1,7 @@
{ { ... }:
...
}:
{ {
# systemd.services.dovecot-backup-sftp-server = { # systemd.services.dovecot-backup-sftp-server = {
# enable = true; # enable = true;
# #
# }; # };
} }

View File

@@ -22,7 +22,12 @@ in
restartIfChanged = true; restartIfChanged = true;
config = config =
{ lib, config, pkgs, ... }: {
lib,
config,
pkgs,
...
}:
let let
secrets_folder = "/var/lib/vaultwarden/secrets"; secrets_folder = "/var/lib/vaultwarden/secrets";
services = [ "vaultwarden.service" ]; services = [ "vaultwarden.service" ];

View File

@@ -9,9 +9,9 @@
let let
config = self.checks.x86_64-linux.${name}.config; config = self.checks.x86_64-linux.${name}.config;
vlans = map (m: ( vlans = map (
m.virtualisation.vlans ++ m: (m.virtualisation.vlans ++ (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes); ) (lib.attrValues config.nodes);
nodeHostNames = nodeHostNames =
let let
@@ -19,13 +19,14 @@ let
in in
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine"; nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
pythonizeName = name: pythonizeName =
name:
let let
head = lib.substring 0 1 name; head = lib.substring 0 1 name;
tail = lib.substring 1 (-1) name; tail = lib.substring 1 (-1) name;
in in
(if builtins.match "[A-z_]" head == null then "_" else head) + (if builtins.match "[A-z_]" head == null then "_" else head)
lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail; + lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
uniqueVlans = lib.unique (builtins.concatLists vlans); uniqueVlans = lib.unique (builtins.concatLists vlans);
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans; vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;