nix fmt
This commit is contained in:
@@ -257,7 +257,11 @@ else
|
||||
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJk3a190w/1TZkzVKORvz/kwyKmFY144lVeDFm80p17";
|
||||
};
|
||||
"rsync.net" = {
|
||||
extraHostNames = [ "rsn" "rsyncnet" "fm2382.rsync.net" ];
|
||||
extraHostNames = [
|
||||
"rsn"
|
||||
"rsyncnet"
|
||||
"fm2382.rsync.net"
|
||||
];
|
||||
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdUkGe6kKn5ssz4WRZKjcws0InbQqZayenzk9obmP1z";
|
||||
};
|
||||
|
||||
|
@@ -87,7 +87,10 @@ lib.mkMerge [
|
||||
})
|
||||
{
|
||||
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 = {
|
||||
@@ -122,8 +125,7 @@ lib.mkMerge [
|
||||
};
|
||||
}
|
||||
{
|
||||
vacu.packages =
|
||||
with pkgs; [
|
||||
vacu.packages = with pkgs; [
|
||||
bash
|
||||
bzip2
|
||||
curl
|
||||
|
@@ -33,7 +33,8 @@ let
|
||||
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;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@@ -47,7 +47,9 @@ in
|
||||
};
|
||||
config =
|
||||
{
|
||||
vacu.sourceTree = inputsOf inputs.self // { inherit inputs; };
|
||||
vacu.sourceTree = inputsOf inputs.self // {
|
||||
inherit inputs;
|
||||
};
|
||||
# vacu.sourceTree = pkgs.runCommand "inputs-tree" { } ''
|
||||
# mkdir -p $out
|
||||
# ln -s ${inputs.self} $out/self
|
||||
|
@@ -1,8 +1,4 @@
|
||||
{
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{ pkgs, inputs, ... }:
|
||||
{
|
||||
imports = [
|
||||
inputs.jovian.nixosModules.jovian
|
||||
|
41
flake.nix
41
flake.nix
@@ -175,7 +175,8 @@
|
||||
map (name: lib.nameValuePair name inputs.${name + suffix}) inp'
|
||||
);
|
||||
in
|
||||
thisInputsA // {
|
||||
thisInputsA
|
||||
// {
|
||||
inherit nixpkgs;
|
||||
inherit (inputs) self;
|
||||
};
|
||||
@@ -210,7 +211,12 @@
|
||||
);
|
||||
|
||||
lib = {
|
||||
inherit mkPlain mkPkgs mkInputs mkNixosConfig;
|
||||
inherit
|
||||
mkPlain
|
||||
mkPkgs
|
||||
mkInputs
|
||||
mkNixosConfig
|
||||
;
|
||||
};
|
||||
|
||||
nixosConfigurations = {
|
||||
@@ -268,9 +274,7 @@
|
||||
inherit (inputs) dns;
|
||||
vacuModuleType = "nix-on-droid";
|
||||
};
|
||||
pkgs = mkPkgs {
|
||||
system = arm;
|
||||
};
|
||||
pkgs = mkPkgs { system = arm; };
|
||||
};
|
||||
|
||||
checks = nixpkgs.lib.genAttrs [ x86 ] (
|
||||
@@ -286,15 +290,15 @@
|
||||
node.specialArgs.selfPackages = self.packages.${system};
|
||||
node.specialArgs.vacuModuleType = "nixos";
|
||||
};
|
||||
mkTest = name:
|
||||
mkTest =
|
||||
name:
|
||||
nixpkgs.lib.nixos.runTest {
|
||||
imports = [
|
||||
commonTestModule
|
||||
./tests/${name}
|
||||
{ node.specialArgs.inputs = self.nixosConfigurations.${name}._module.specialArgs.inputs; }
|
||||
];
|
||||
}
|
||||
;
|
||||
};
|
||||
checksFromConfig = plain.config.vacu.checks;
|
||||
in
|
||||
assert !(checksFromConfig ? liam) && !(checksFromConfig ? trip);
|
||||
@@ -421,7 +425,8 @@
|
||||
// (inputs.flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
mkNixvim = unstable:
|
||||
mkNixvim =
|
||||
unstable:
|
||||
let
|
||||
nixvim-input = if unstable then inputs.nixvim-unstable else inputs.nixvim;
|
||||
in
|
||||
@@ -437,12 +442,8 @@
|
||||
config.allowUnfree = true;
|
||||
overlays = [ inputs.sm64baserom.overlays.default ];
|
||||
};
|
||||
pkgs-unstable = mkPkgs (nixpkgs-args // {
|
||||
useUnstable = true;
|
||||
});
|
||||
pkgs-stable = mkPkgs (nixpkgs-args // {
|
||||
useUnstable = false;
|
||||
});
|
||||
pkgs-unstable = mkPkgs (nixpkgs-args // { useUnstable = true; });
|
||||
pkgs-stable = mkPkgs (nixpkgs-args // { useUnstable = false; });
|
||||
_plain = mkPlain pkgs-unstable;
|
||||
plain = _plain.config.vacu.withAsserts _plain;
|
||||
treefmtEval = inputs.treefmt-nix.lib.evalModule pkgs-unstable ./treefmt.nix;
|
||||
@@ -473,14 +474,18 @@
|
||||
};
|
||||
generated = pkgs.linkFarm "generated" {
|
||||
nixpkgs = "${inputs.nixpkgs}";
|
||||
"liam-test/hints.py" = pkgs.writeText "hints.py" (import ./typesForTest.nix {
|
||||
"liam-test/hints.py" = pkgs.writeText "hints.py" (
|
||||
import ./typesForTest.nix {
|
||||
name = "liam";
|
||||
inherit (pkgs-stable) lib;
|
||||
inherit self;
|
||||
inherit (inputs) nixpkgs;
|
||||
});
|
||||
}
|
||||
);
|
||||
"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 {
|
||||
inherit haproxy-lua-http;
|
||||
|
@@ -2,7 +2,10 @@
|
||||
{
|
||||
vacu.packages = [ "tpm-fido" ];
|
||||
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.applyUdevRules = true;
|
||||
services.udev.extraRules = ''
|
||||
|
@@ -13,8 +13,9 @@
|
||||
background: #eee;
|
||||
padding: 0 10px;
|
||||
}
|
||||
h1,h2,h3 { line-height: 1.2; }
|
||||
|
||||
h1, h2, h3 {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
form {
|
||||
padding: 5px;
|
||||
@@ -22,21 +23,34 @@
|
||||
border-radius: 3px;
|
||||
margin: 5px;
|
||||
}
|
||||
form label { display: block; }
|
||||
form label {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
What I want in a job:
|
||||
|
||||
<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>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>
|
||||
</p>
|
||||
|
||||
@@ -44,41 +58,78 @@
|
||||
Things you probably want to know:
|
||||
|
||||
<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>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>
|
||||
</p>
|
||||
|
||||
<h2>Resume</h2>
|
||||
|
||||
<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>
|
||||
|
||||
<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">
|
||||
<label>
|
||||
<input 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>
|
||||
<input
|
||||
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>
|
||||
<input 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>
|
||||
<input
|
||||
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>
|
||||
<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>
|
||||
<input 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>
|
||||
<input
|
||||
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>
|
||||
<div>Your email:</div>
|
||||
@@ -87,6 +138,10 @@
|
||||
<button type="submit">Submit</button>
|
||||
</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>
|
||||
</html>
|
||||
|
@@ -57,16 +57,20 @@ in
|
||||
repo = mkOption { default = "${cfg.rsyncUser}@${cfg.rsyncHost}:borg-repos/liam-backup"; };
|
||||
package = mkOption { default = pkgs.borgbackup; };
|
||||
cmd = mkOption { default = lib.getExe cfg.package; };
|
||||
paths = mkOption { default = [
|
||||
paths = mkOption {
|
||||
default = [
|
||||
"/var/lib/mail"
|
||||
"/var/lib/dovecot"
|
||||
"/var/log"
|
||||
]; };
|
||||
];
|
||||
};
|
||||
keyPath = mkOption { default = config.sops.secrets.liam-borg-key.path; };
|
||||
};
|
||||
config = {
|
||||
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";
|
||||
fatal = true;
|
||||
};
|
||||
|
311
liam/sieve.nix
311
liam/sieve.nix
@@ -5,7 +5,17 @@
|
||||
...
|
||||
}:
|
||||
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)
|
||||
concatStrings
|
||||
concatStringsSep
|
||||
@@ -16,21 +26,13 @@ let
|
||||
elemAt
|
||||
mapAttrsToList
|
||||
;
|
||||
mapConcat = f: xs:
|
||||
concatStrings (map f xs)
|
||||
;
|
||||
mapConcatSep = sep: f: xs:
|
||||
concatStringsSep sep (map f xs)
|
||||
;
|
||||
mapConcatLines = f: xs:
|
||||
mapConcatSep "\n" f xs
|
||||
;
|
||||
isListWhere = xs: f:
|
||||
(isList xs) && (all f xs)
|
||||
;
|
||||
stringOrList = val: (isString val) || (
|
||||
(isListWhere val isString) && (length val) > 0
|
||||
);
|
||||
mapConcat = f: xs: concatStrings (map f xs);
|
||||
mapConcatSep =
|
||||
sep: f: xs:
|
||||
concatStringsSep sep (map f xs);
|
||||
mapConcatLines = f: xs: 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 ];
|
||||
email_folders = [
|
||||
"24nm-domain@shelvacu.com"
|
||||
@@ -94,31 +96,42 @@ let
|
||||
sieve_raw_escape_string =
|
||||
s:
|
||||
if !only_printable_ascii s then
|
||||
builtins.trace s
|
||||
throw "s failed only_printable_ascii check"
|
||||
builtins.trace s throw "s failed only_printable_ascii check"
|
||||
else
|
||||
replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s
|
||||
;
|
||||
replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s;
|
||||
sieve_encode_string =
|
||||
{ allow_vars, for_debug_comment, with_quotes }: s:
|
||||
{
|
||||
allow_vars,
|
||||
for_debug_comment,
|
||||
with_quotes,
|
||||
}:
|
||||
s:
|
||||
assert isString s;
|
||||
assert allow_vars || for_debug_comment || (!has_vars s);
|
||||
let
|
||||
a = sieve_raw_escape_string s;
|
||||
b = if for_debug_comment then replaceStrings [ ''*/'' ] [ ''*\/'' ] a else a;
|
||||
res = if with_quotes then ''"${b}"'' else b;
|
||||
in res
|
||||
;
|
||||
sieve_quote_string = sieve_encode_string { 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; };
|
||||
in
|
||||
res;
|
||||
sieve_quote_string = sieve_encode_string {
|
||||
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_number_ident = is_match "[0-9]*";
|
||||
is_valid_ident = s: (is_valid_long_ident s) || (is_number_ident s);
|
||||
interp = ident:
|
||||
interp =
|
||||
ident:
|
||||
assert isString ident;
|
||||
assert is_valid_ident ident;
|
||||
"$" + "{${ident}}"
|
||||
;
|
||||
"$" + "{${ident}}";
|
||||
dest = "envelope_to";
|
||||
dest_domain = "envelope_to_domain";
|
||||
set_envelope = ''
|
||||
@@ -133,35 +146,51 @@ let
|
||||
'';
|
||||
envelope_is = key: ''string :is "${interp dest}" ${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;
|
||||
"[ ${mapConcatSep ", " sieve_encode xs} ]"
|
||||
;
|
||||
sieve_encode = val:
|
||||
if isString val then sieve_quote_string val
|
||||
else if isList val then sieve_encode_list val
|
||||
else assert "dunno what to do with this"; null
|
||||
;
|
||||
"[ ${mapConcatSep ", " sieve_encode xs} ]";
|
||||
sieve_encode =
|
||||
val:
|
||||
if isString val then
|
||||
sieve_quote_string val
|
||||
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_attrs = attrs:
|
||||
sieve_debug_attrs =
|
||||
attrs:
|
||||
let
|
||||
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
|
||||
pairStrs = mapAttrsToList toPairStr attrs;
|
||||
pairsStr = concatStrings pairStrs;
|
||||
in
|
||||
"{ ${pairsStr}}"
|
||||
;
|
||||
sieve_debug = 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
|
||||
"{ ${pairsStr}}";
|
||||
sieve_debug =
|
||||
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
|
||||
(if val then "true" else "false")
|
||||
else if isNull val then "null"
|
||||
else if isList val then sieve_debug_list val
|
||||
else if isAttrs val then sieve_debug_attrs val
|
||||
else assert "dunno what to do with this"; null
|
||||
;
|
||||
pure_flags_impl = flags: conditions:
|
||||
else if isNull val then
|
||||
"null"
|
||||
else if isList val then
|
||||
sieve_debug_list val
|
||||
else if isAttrs val then
|
||||
sieve_debug_attrs val
|
||||
else
|
||||
assert "dunno what to do with this";
|
||||
null;
|
||||
pure_flags_impl =
|
||||
flags: conditions:
|
||||
assert isListWhere flags isString;
|
||||
assert isListWhere conditions isString;
|
||||
assert (length flags) > 0;
|
||||
@@ -180,92 +209,92 @@ let
|
||||
}
|
||||
# pure_flags end
|
||||
'';
|
||||
pure_flags = flags: conditions:
|
||||
pure_flags =
|
||||
flags: conditions:
|
||||
assert stringOrList flags;
|
||||
assert stringOrList conditions;
|
||||
pure_flags_impl (listify flags) (listify conditions)
|
||||
;
|
||||
exists_impl = headers:
|
||||
pure_flags_impl (listify flags) (listify conditions);
|
||||
exists_impl =
|
||||
headers:
|
||||
assert isListWhere headers isString;
|
||||
if headers == [ ] then
|
||||
"/* exists START: called with empty array */ false /* exists END */"
|
||||
else
|
||||
"/* exists START */ exists ${sieve_encode_list headers} /* exists END */"
|
||||
;
|
||||
exists = headers:
|
||||
"/* exists START */ exists ${sieve_encode_list headers} /* exists END */";
|
||||
exists =
|
||||
headers:
|
||||
assert stringOrList headers;
|
||||
exists_impl (listify headers)
|
||||
;
|
||||
header_generic = match_kind: header_s: match_es:
|
||||
exists_impl (listify headers);
|
||||
header_generic =
|
||||
match_kind: header_s: match_es:
|
||||
assert stringOrList header_s;
|
||||
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_is = header_generic ":is";
|
||||
subject_generic = match_kind: match_es: header_generic match_kind "Subject" match_es;
|
||||
subject_matches = subject_generic ":matches";
|
||||
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 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_is = environment_generic ":is";
|
||||
from_is = addr_list:
|
||||
from_is =
|
||||
addr_list:
|
||||
assert stringOrList addr_list;
|
||||
''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */''
|
||||
;
|
||||
var_is = var_name: rhs:
|
||||
''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */'';
|
||||
var_is =
|
||||
var_name: rhs:
|
||||
assert isString var_name;
|
||||
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_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 is_valid_ident var_name;
|
||||
assert isString new_val;
|
||||
"set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};"
|
||||
;
|
||||
set = var_name: new_val:
|
||||
"set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};";
|
||||
set =
|
||||
var_name: new_val:
|
||||
assert isString var_name;
|
||||
assert is_valid_ident var_name;
|
||||
assert isString new_val;
|
||||
"set ${sieve_encode var_name} ${sieve_encode new_val};"
|
||||
;
|
||||
set_bool_var = var_name: bool_val:
|
||||
"set ${sieve_encode var_name} ${sieve_encode new_val};";
|
||||
set_bool_var =
|
||||
var_name: bool_val:
|
||||
assert isBool bool_val;
|
||||
set var_name (if bool_val then "1" else "0")
|
||||
;
|
||||
over_test_list = name: test_list:
|
||||
set var_name (if bool_val then "1" else "0");
|
||||
over_test_list =
|
||||
name: test_list:
|
||||
assert isListWhere test_list isString;
|
||||
''
|
||||
${name}(
|
||||
${concatStringsSep ",\n" test_list}
|
||||
)
|
||||
''
|
||||
;
|
||||
'';
|
||||
anyof = over_test_list "anyof";
|
||||
allof = over_test_list "allof";
|
||||
not = test: "not ${test}";
|
||||
record_action = action_desc:
|
||||
record_action =
|
||||
action_desc:
|
||||
assert isString action_desc;
|
||||
''addheader "X-Vacu-Action" ${sieve_encode action_desc};''
|
||||
;
|
||||
fileinto = folder:
|
||||
''addheader "X-Vacu-Action" ${sieve_encode action_desc};'';
|
||||
fileinto =
|
||||
folder:
|
||||
assert isString folder;
|
||||
''
|
||||
${record_action "fileinto ${folder}"}
|
||||
fileinto :create ${sieve_encode folder};
|
||||
''
|
||||
;
|
||||
ihave = extension_name_s:
|
||||
'';
|
||||
ihave =
|
||||
extension_name_s:
|
||||
assert stringOrList extension_name_s;
|
||||
"ihave ${sieve_encode extension_name_s}"
|
||||
;
|
||||
"ihave ${sieve_encode extension_name_s}";
|
||||
email_filters = map (e: ''
|
||||
elsif ${envelope_is e} { # item of email_filters
|
||||
${record_action "email_filters fileinto ${mk_email_folder_name e}"}
|
||||
@@ -291,7 +320,9 @@ let
|
||||
${set_with_interp var (interp "1")}
|
||||
}
|
||||
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_from END
|
||||
@@ -305,13 +336,11 @@ let
|
||||
condition = ''environment :matches ${sieve_quote_string item} "*"'';
|
||||
inherit var;
|
||||
};
|
||||
maybe_debug = msg:
|
||||
''
|
||||
maybe_debug = msg: ''
|
||||
if ${ihave "vnd.dovecot.debug"} {
|
||||
debug_log ${sieve_quote_string_with_interp msg};
|
||||
}
|
||||
''
|
||||
;
|
||||
'';
|
||||
sieve_text = ''
|
||||
require [
|
||||
"fileinto",
|
||||
@@ -325,22 +354,33 @@ let
|
||||
"ihave"
|
||||
];
|
||||
|
||||
if ${allof [
|
||||
if ${
|
||||
allof [
|
||||
(ihave "imapsieve")
|
||||
(environment_matches "imap.user" "*")
|
||||
(environment_matches "location" "MS")
|
||||
(environment_matches "phase" "post")
|
||||
]} {
|
||||
]
|
||||
} {
|
||||
${set_bool_var "in_imap" true}
|
||||
} else {
|
||||
${set_bool_var "in_imap" false}
|
||||
}
|
||||
|
||||
if ${var_is_true "in_imap"} {
|
||||
if ${not (allof [
|
||||
(environment_is "imap.cause" ["APPEND" "COPY" ""])
|
||||
(environment_is "imap.mailbox" ["MagicRefilter" ""])
|
||||
])} {
|
||||
if ${
|
||||
not (allof [
|
||||
(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"}
|
||||
stop;
|
||||
}
|
||||
@@ -358,9 +398,7 @@ let
|
||||
condition = ''currentdate :matches "iso8601" "*"'';
|
||||
var = "datetime";
|
||||
}}
|
||||
${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}''
|
||||
}
|
||||
${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}''}
|
||||
${maybe_debug ''X-Vacu-Sieved: ''${sieved_message}''}
|
||||
|
||||
if ${ihave "envelope"} {
|
||||
@@ -382,43 +420,76 @@ let
|
||||
removeflag "ignore";
|
||||
removeflag "not-spamish";
|
||||
|
||||
${pure_flags ["amazon-ignore" "ignore"] [
|
||||
${pure_flags
|
||||
[ "amazon-ignore" "ignore" ]
|
||||
[
|
||||
(envelope_is "amznbsns@shelvacu.com")
|
||||
(subject_matches [ "Your Amazon.com order has shipped*" "Your Amazon.com order of * has shipped!" ])
|
||||
]}
|
||||
${pure_flags ["bandcamp-ignore" "ignore"] [
|
||||
(subject_matches [
|
||||
"Your Amazon.com order has shipped*"
|
||||
"Your Amazon.com order of * has shipped!"
|
||||
])
|
||||
]
|
||||
}
|
||||
${pure_flags
|
||||
[ "bandcamp-ignore" "ignore" ]
|
||||
[
|
||||
(envelope_is "bandcamp@shelvacu.com")
|
||||
(subject_matches [ "* just announced a listening party on Bandcamp" "New items from *" "Starting in *" "New from *" ])
|
||||
]}
|
||||
(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"] [
|
||||
${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 ["money-stuff" "not-spamish"] [
|
||||
${pure_flags
|
||||
[ "money-stuff" "not-spamish" ]
|
||||
[
|
||||
(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 [
|
||||
"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 [ "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" ] (
|
||||
header_matches "List-Id" "<*.lists.postgresql.org>"
|
||||
)}
|
||||
${pure_flags [ "secureaccesswa" "not-spamish" ] (from_is "help@secureaccess.wa.gov")}
|
||||
${pure_flags ["letsencrypt-mailing-list" "not-spamish"] (envelope_is "lets-encrypt-mailing-list@shelvacu.com")}
|
||||
${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 ["tf2wiki" "not-spamish"] [
|
||||
${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 "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";
|
||||
if hasflag "ignore" {
|
||||
|
@@ -1,8 +1,4 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
imports = [ ./lean.nix ];
|
||||
opts = {
|
||||
@@ -23,7 +19,10 @@
|
||||
lua_ls.enable = true;
|
||||
nixd = {
|
||||
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;
|
||||
rust_analyzer = {
|
||||
|
@@ -9,7 +9,8 @@ let
|
||||
lean = inputs.self.packages.${system}.leanLatest;
|
||||
in
|
||||
{
|
||||
imports = []
|
||||
imports =
|
||||
[ ]
|
||||
++ (lib.optional (!unstable) {
|
||||
plugins.lean.leanPackage = lean;
|
||||
plugins.lean.mappings = true;
|
||||
@@ -17,7 +18,6 @@ in
|
||||
++ (lib.optional unstable {
|
||||
dependencies.lean.enable = false;
|
||||
plugins.lean.settings.mappings = true;
|
||||
})
|
||||
;
|
||||
});
|
||||
plugins.lean.enable = true;
|
||||
}
|
||||
|
@@ -33,11 +33,14 @@ let
|
||||
tag = version;
|
||||
hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w=";
|
||||
};
|
||||
patchesFromThunderbird = runCommandNoCC "betterbird-patches-from-network" {
|
||||
patchesFromThunderbird =
|
||||
runCommandNoCC "betterbird-patches-from-network"
|
||||
{
|
||||
outputHashAlgo = "sha256";
|
||||
outputHashMode = "recursive";
|
||||
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
|
||||
} ''
|
||||
}
|
||||
''
|
||||
set -xev
|
||||
mkdir -p $out
|
||||
fetchFromSeries() {
|
||||
@@ -95,9 +98,9 @@ let
|
||||
applySeries ${betterbirdPatches}/${majorVersion}/series $out/comm
|
||||
'';
|
||||
in
|
||||
(thunderbird-128-unwrapped.override {
|
||||
inherit privacySupport requireSigning allowAddonSideload;
|
||||
}).overrideAttrs (old: rec {
|
||||
(thunderbird-128-unwrapped.override { inherit privacySupport requireSigning allowAddonSideload; })
|
||||
.overrideAttrs
|
||||
(old: rec {
|
||||
pname = "betterbird";
|
||||
inherit version;
|
||||
name = "${pname}-${version}";
|
||||
@@ -119,7 +122,9 @@ in
|
||||
# "--enable-official-branding"
|
||||
];
|
||||
|
||||
preConfigure = (old.preConfigure or "") + ''
|
||||
preConfigure =
|
||||
(old.preConfigure or "")
|
||||
+ ''
|
||||
# Disable enforcing that add-ons are signed.
|
||||
export MOZ_REQUIRE_SIGNING=
|
||||
export MOZ_REQUIRE_ADDON_SIGNING=0
|
||||
@@ -144,5 +149,13 @@ in
|
||||
export MOZ_NO_PIE_COMPAT=1
|
||||
'';
|
||||
|
||||
passthru = (old.passthru or {}) // { inherit betterbirdPatches mozilla_src comm_src replacement_src patchesFromThunderbird; };
|
||||
passthru = (old.passthru or { }) // {
|
||||
inherit
|
||||
betterbirdPatches
|
||||
mozilla_src
|
||||
comm_src
|
||||
replacement_src
|
||||
patchesFromThunderbird
|
||||
;
|
||||
};
|
||||
})
|
||||
|
@@ -1,10 +1,11 @@
|
||||
# shellcheck shell=bash
|
||||
translateWetransferLinks() {
|
||||
local old_urlsArray=("${urls[*]}")
|
||||
local new_urlsArray=()
|
||||
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
|
||||
new_url="$("$transferweeBin" -v -g download "$this_url")"
|
||||
new_url="$("${transferweeBin:?}" -v -g download "$this_url")"
|
||||
new_urlsArray+=("$new_url")
|
||||
else
|
||||
new_urlsArray+=("$this_url")
|
||||
|
@@ -1,5 +1,7 @@
|
||||
self: _super: {
|
||||
fetchurlWithWetransfer = args: (self.fetchurl args).overrideAttrs (old: {
|
||||
fetchurlWithWetransfer =
|
||||
args:
|
||||
(self.fetchurl args).overrideAttrs (old: {
|
||||
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ ./convert-wetransfer-links.sh ];
|
||||
env.transferweeBin = self.lib.getExe self.transferwee;
|
||||
env.NIX_DEBUG = "6";
|
||||
|
@@ -1,8 +1,6 @@
|
||||
{
|
||||
lean4,
|
||||
fetchFromGitHub,
|
||||
}:
|
||||
lean4.overrideAttrs (final: prev: {
|
||||
{ lean4, fetchFromGitHub }:
|
||||
lean4.overrideAttrs (
|
||||
final: prev: {
|
||||
version = "4.19.0-rc3";
|
||||
src = fetchFromGitHub {
|
||||
owner = "leanprover";
|
||||
@@ -11,5 +9,5 @@ lean4.overrideAttrs (final: prev: {
|
||||
hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU=";
|
||||
};
|
||||
cmakeFlags = (prev.cmakeFlags or [ ]) ++ [ "-DUSE_MIMALLOC=OFF" ];
|
||||
})
|
||||
|
||||
}
|
||||
)
|
||||
|
@@ -17,8 +17,9 @@ let
|
||||
];
|
||||
python = pkgs.python312.withPackages (_: libraries);
|
||||
in
|
||||
(pkgs.writers.writePython3Bin "dns-update" {
|
||||
inherit libraries;
|
||||
} pythonScript).overrideAttrs (old: {
|
||||
passthru = (old.passthru or {}) // { inherit libraries python; };
|
||||
(pkgs.writers.writePython3Bin "dns-update" { inherit libraries; } pythonScript).overrideAttrs
|
||||
(old: {
|
||||
passthru = (old.passthru or { }) // {
|
||||
inherit libraries python;
|
||||
};
|
||||
})
|
||||
|
@@ -71,7 +71,8 @@ let
|
||||
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
|
||||
'';
|
||||
mailtestModule = {
|
||||
mailtestModule =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
@@ -82,7 +83,9 @@ let
|
||||
imap-tools
|
||||
requests
|
||||
];
|
||||
mkPkg = name: pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
|
||||
mkPkg =
|
||||
name:
|
||||
pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
|
||||
# flake8: noqa
|
||||
${builtins.readFile ./mailtest/${name}.py}
|
||||
'';
|
||||
@@ -168,7 +171,12 @@ in
|
||||
};
|
||||
|
||||
nodes.liam =
|
||||
{ lib, inputs, config, ... }:
|
||||
{
|
||||
lib,
|
||||
inputs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
"${inputs.self}/common"
|
||||
@@ -210,7 +218,9 @@ in
|
||||
|
||||
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
|
||||
networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [ config.vacu.liam.backup.rsyncHost ];
|
||||
networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [
|
||||
config.vacu.liam.backup.rsyncHost
|
||||
];
|
||||
};
|
||||
|
||||
nodes.rsyncnet =
|
||||
@@ -268,9 +278,7 @@ in
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
imports = [ mailtestModule ];
|
||||
environment.systemPackages = [
|
||||
pkgs.wget
|
||||
];
|
||||
environment.systemPackages = [ pkgs.wget ];
|
||||
networking.nameservers = lib.mkForce (lib.singleton nodes.ns.networking.primaryIPAddress);
|
||||
};
|
||||
|
||||
@@ -288,7 +296,8 @@ in
|
||||
};
|
||||
dataJson = builtins.toJSON data;
|
||||
res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile;
|
||||
in res;
|
||||
in
|
||||
res;
|
||||
|
||||
skipTypeCheck = true;
|
||||
skipLint = true;
|
||||
|
@@ -6,18 +6,22 @@ import ssl
|
||||
import json
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
def info(msg: str):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
def mk_ctx():
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
return ctx
|
||||
|
||||
|
||||
def print_json(**kwargs):
|
||||
print(json.dumps(kwargs))
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--host", type=str)
|
||||
parser.add_argument("--insecure", default=False, action="store_true")
|
||||
@@ -45,6 +49,7 @@ info(f"looking for {msg_magic}")
|
||||
result: bool
|
||||
matching_messages = []
|
||||
try:
|
||||
|
||||
def connection() -> imap_tools.BaseMailBox:
|
||||
return imap_tools.MailBox(args.host, ssl_context=mk_ctx()).login(
|
||||
username, password
|
||||
@@ -99,6 +104,7 @@ except imaplib.IMAP4.error as e:
|
||||
else:
|
||||
result = True
|
||||
|
||||
|
||||
def mail_to_jsonish(m: MessageInFolder) -> dict:
|
||||
return {
|
||||
"folder": m.folder,
|
||||
@@ -106,6 +112,7 @@ def mail_to_jsonish(m: MessageInFolder) -> dict:
|
||||
"body": m.message.text.strip(),
|
||||
}
|
||||
|
||||
|
||||
print_json(
|
||||
result=result,
|
||||
messages=[mail_to_jsonish(m) for m in matching_messages],
|
||||
|
@@ -3,9 +3,11 @@ import requests
|
||||
import sys
|
||||
import json
|
||||
|
||||
|
||||
def info(msg: str):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
def print_json(**kwargs):
|
||||
print(json.dumps(kwargs))
|
||||
|
||||
|
@@ -4,18 +4,22 @@ import sys
|
||||
import ssl
|
||||
import json
|
||||
|
||||
|
||||
def info(msg: str):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
def mk_ctx():
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
return ctx
|
||||
|
||||
|
||||
def print_json(**kwargs):
|
||||
print(json.dumps(kwargs))
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--host", type=str)
|
||||
parser.add_argument("--mailfrom", default="foo@example.com")
|
||||
|
@@ -2,14 +2,17 @@ import sys
|
||||
import ssl
|
||||
import json
|
||||
|
||||
|
||||
def info(msg: str):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
def mk_ctx():
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
return ctx
|
||||
|
||||
|
||||
def print_json(**kwargs):
|
||||
print(json.dumps(kwargs))
|
||||
|
@@ -32,7 +32,7 @@ liam.wait_for_unit("dovecot2.service")
|
||||
relay.wait_for_unit("mailpit.service")
|
||||
|
||||
# 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")
|
||||
# # 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")
|
||||
@@ -40,7 +40,9 @@ liam.succeed("systemctl start 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()
|
||||
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")
|
||||
liam.succeed("systemctl start auto-borg.service")
|
||||
# liam.wait_for_unit("auto-borg.service")
|
||||
@@ -51,17 +53,21 @@ class ImapMessage(TypedDict):
|
||||
flags: list[str]
|
||||
body: str
|
||||
|
||||
|
||||
class ImapResult(TypedDict):
|
||||
result: bool
|
||||
messages: list[ImapMessage]
|
||||
|
||||
|
||||
def make_command(args: list) -> str:
|
||||
return " ".join(map(shlex.quote, (map(str, args))))
|
||||
|
||||
|
||||
Arg = str | bool | int
|
||||
|
||||
Args = dict[str, Arg]
|
||||
|
||||
|
||||
def dict_args_to_list(dict_args: Args) -> list[str]:
|
||||
args: list[str] = []
|
||||
for k, v in dict_args.items():
|
||||
@@ -74,12 +80,14 @@ def dict_args_to_list(dict_args: Args) -> list[str]:
|
||||
args.append(v)
|
||||
return args
|
||||
|
||||
|
||||
class LogEntry(TypedDict):
|
||||
__CURSOR: str
|
||||
_SYSTEMD_UNIT: str
|
||||
MESSAGE: str
|
||||
pass
|
||||
|
||||
|
||||
DEFAULT_JOURNALCTL_OPTS: Args = {
|
||||
"all": True,
|
||||
"output": "json",
|
||||
@@ -94,7 +102,10 @@ 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.
|
||||
|
||||
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}
|
||||
args = ["journalctl", *dict_args_to_list(with_defaults)]
|
||||
res = machine.succeed(make_command(args))
|
||||
@@ -105,11 +116,13 @@ def journalctl_log_entries(machine: Machine = liam, **kwargs: Arg) -> Generator[
|
||||
assert isinstance(v, str)
|
||||
yield data
|
||||
|
||||
|
||||
class ProcessingWaiter(contextlib.AbstractContextManager):
|
||||
cursor: str = ""
|
||||
timeout: int = 60
|
||||
_postfix_smtpd_connections: set[str] = set()
|
||||
_postfix_queue: set[str] = set()
|
||||
|
||||
def __init__(self, timeout=60):
|
||||
most_recent_entry = list(journalctl_log_entries(lines=1))[0]
|
||||
self.cursor = most_recent_entry["__CURSOR"]
|
||||
@@ -128,7 +141,9 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
|
||||
assert sl_pid is not None
|
||||
if message.startswith("connect from"):
|
||||
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)
|
||||
if unit == "postfix.service" and sl_ident == "postfix/qmgr":
|
||||
queue_id = message.split(":")[0]
|
||||
@@ -136,7 +151,10 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
|
||||
self._postfix_queue.add(queue_id)
|
||||
if message.endswith("removed"):
|
||||
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
|
||||
else:
|
||||
liam.sleep(1)
|
||||
@@ -144,29 +162,25 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
|
||||
def __exit__(self, _a, _b, _c):
|
||||
self.wait_until_finished()
|
||||
|
||||
class TesterThing():
|
||||
|
||||
class TesterThing:
|
||||
uuid: str = ""
|
||||
default_smtp: Args = {}
|
||||
default_imap: 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.default_smtp = {
|
||||
"rcptto": "someone@example.com",
|
||||
"host": f"{liam_ip}",
|
||||
**common,
|
||||
**smtp
|
||||
}
|
||||
self.default_imap = {
|
||||
"host": f"{liam_ip}",
|
||||
**common,
|
||||
**imap
|
||||
}
|
||||
self.default_mailpit = {
|
||||
"mailpit-url": f"http://{relay_ip}:8025",
|
||||
**mailpit
|
||||
**smtp,
|
||||
}
|
||||
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]:
|
||||
args: list[str] = [name, *dict_args_to_list(kwargs)]
|
||||
@@ -198,15 +212,17 @@ class TesterThing():
|
||||
res_ty: ImapResult = res_unty # type: ignore
|
||||
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)
|
||||
assert res['result']
|
||||
assert len(res['messages']) == 1
|
||||
message = res['messages'][0]
|
||||
assert res["result"]
|
||||
assert len(res["messages"]) == 1
|
||||
message = res["messages"][0]
|
||||
if mailbox is not None:
|
||||
assert message['folder'] == mailbox
|
||||
assert message["folder"] == mailbox
|
||||
for flag in flags:
|
||||
assert flag in message['flags']
|
||||
assert flag in message["flags"]
|
||||
return self
|
||||
|
||||
def imap_found(self, **kwargs: Arg) -> Self:
|
||||
@@ -217,13 +233,13 @@ class TesterThing():
|
||||
|
||||
def imap_move_to(self, dest: str, **kwargs: Arg) -> Self:
|
||||
res = self.run_imap(move_to=dest, **kwargs)
|
||||
assert res['result']
|
||||
assert res["result"]
|
||||
return self
|
||||
|
||||
def run_mailpit(self, **kwargs: Arg) -> bool:
|
||||
args = {"message_magic": self.uuid, **self.default_mailpit, **kwargs}
|
||||
res = self.run_expecting_json("mailtest-mailpit", **args)
|
||||
return res['found_message']
|
||||
return res["found_message"]
|
||||
|
||||
def mailpit_not_received(self, **kwargs: Arg) -> Self:
|
||||
received = self.run_mailpit(**kwargs)
|
||||
@@ -235,55 +251,76 @@ class TesterThing():
|
||||
assert received
|
||||
return self
|
||||
|
||||
class Defaults():
|
||||
|
||||
class Defaults:
|
||||
default_smtp: Args = {}
|
||||
default_imap: Args = {}
|
||||
default_mailpit: Args = {}
|
||||
|
||||
def __init__(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool):
|
||||
self.default_smtp = {
|
||||
**common,
|
||||
**smtp
|
||||
}
|
||||
self.default_imap = {
|
||||
**common,
|
||||
**imap
|
||||
}
|
||||
self.default_mailpit = {
|
||||
**mailpit
|
||||
}
|
||||
def __init__(
|
||||
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
|
||||
):
|
||||
self.default_smtp = {**common, **smtp}
|
||||
self.default_imap = {**common, **imap}
|
||||
self.default_mailpit = {**mailpit}
|
||||
|
||||
def make_tester(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool) -> TesterThing:
|
||||
def make_tester(
|
||||
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
|
||||
) -> TesterThing:
|
||||
return TesterThing(
|
||||
smtp={**self.default_smtp, **common, **smtp},
|
||||
imap={**self.default_imap, **common, **imap},
|
||||
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 = 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")
|
||||
d = Defaults(
|
||||
smtp={"mailfrom": "whoeve2@example.com", "rcptto": "sieve2est@shelvacu.com"},
|
||||
username="shelvacu",
|
||||
)
|
||||
# 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
|
||||
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")
|
||||
# test the sieve script is working
|
||||
d.make_tester().smtp_accepted().imap_found_in("com.shelvacu")
|
||||
# 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.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(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"])
|
||||
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(
|
||||
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@dis8.net").mailpit_not_received()
|
||||
|
||||
@@ -318,11 +355,19 @@ d.make_tester().smtp_rejected(mailfrom="fubar@vacu.store", username="julie")
|
||||
|
||||
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@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@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@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?
|
||||
liam.succeed("systemctl start auto-borg.service")
|
||||
|
@@ -1,6 +1,4 @@
|
||||
{
|
||||
...
|
||||
}:
|
||||
{ ... }:
|
||||
{
|
||||
# systemd.services.dovecot-backup-sftp-server = {
|
||||
# enable = true;
|
||||
|
@@ -22,7 +22,12 @@ in
|
||||
restartIfChanged = true;
|
||||
|
||||
config =
|
||||
{ lib, config, pkgs, ... }:
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
secrets_folder = "/var/lib/vaultwarden/secrets";
|
||||
services = [ "vaultwarden.service" ];
|
||||
|
@@ -9,9 +9,9 @@
|
||||
let
|
||||
config = self.checks.x86_64-linux.${name}.config;
|
||||
|
||||
vlans = map (m: (
|
||||
m.virtualisation.vlans ++
|
||||
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
|
||||
vlans = map (
|
||||
m: (m.virtualisation.vlans ++ (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))
|
||||
) (lib.attrValues config.nodes);
|
||||
|
||||
nodeHostNames =
|
||||
let
|
||||
@@ -19,13 +19,14 @@ let
|
||||
in
|
||||
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
|
||||
|
||||
pythonizeName = name:
|
||||
pythonizeName =
|
||||
name:
|
||||
let
|
||||
head = lib.substring 0 1 name;
|
||||
tail = lib.substring 1 (-1) name;
|
||||
in
|
||||
(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;
|
||||
(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;
|
||||
|
||||
uniqueVlans = lib.unique (builtins.concatLists vlans);
|
||||
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
|
||||
|
Reference in New Issue
Block a user