New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nixos/dovecot: More sieve options and precompiling of scripts #35536
Conversation
''; | ||
}; | ||
beforeScript = mkOption { | ||
type = types.nullOr types.path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason to ask for a path to a sieve script instead of an actual string containing the sieve script here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the sieve script needs to be written to a file no matter what and as a user of this module I can prefer to store my sieve scripts either in files or inline in the configuration.
If I prefer to store my sieve scripts in files and the option is a path I can just provide the path to the sieve script. And if I prefer to store my sieve scripts inline in the configuration I can simply use the writeText
builder.
On the other hand if this option was a string and I prefer to store my sieve scripts in files I would have to use fileContents
to load the data from the file, only for the module to write that content to a different file which seems a waste.
I will also admit that my personal preference is to store my sieve scripts in files so that things like syntax highlighting work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the other hand if this option was a string and I prefer to store my sieve scripts in files I would have to use fileContents to load the data from the file, only for the module to write that content to a different file which seems a waste
Yes, but using a string is standard… and it's not like there's not a lot of copying going on during builds ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wmertens I know. I guess I just like writeText
more than fileContents
.
I have considered supporting both strings
and path
by making the value an attrset
like environment.etc
or services.rspamd.locals
but a listOf attrset
is not as nice to work with as the named submodules like environment.etc
and services.rspamd.locals
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about having both text and path attributes? Or, if a Sieve script can never be a single line starting with /
, autodetect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wmertens I looked at the Sieve RFC and from that it seems that while a sieve script can start with /
it has to start with /*
in that case which is obviously not a valid store path. So I am working on an update for this PR the resolves merge conflicts and supports having both text and path attributes for Sieve scripts.
Thank you for your contributions.
|
I don't use Sieve but LGTM. I'm not that bothered by the paths vs text issue. |
aa03ee2
to
957439e
Compare
I have now resolved all the merge conflicts and removed the commit that was no longer relevant since the functionality had since been implemented in another PR. I have also done as @wmertens sugested and implemented support for Sieve scripts being either a string with the script or a path and the module will automatically detect the string script and write it to the store. @uwap I hope this addresses the reservations you had. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes are really nice!
Here some minor comments and i still would like to play with this PR a bit to see what is generated and better understand it.
${concatStringsSep "\n" (imap1 (i: v: '' | ||
mkdir -p after/${toString i} | ||
if [ -d '${v}' ]; then | ||
cp -p '${v}'/*.sieve after/${toString i} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to create symlinks instead of copying files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! And while looking at this I found that when the path was outside the store the user feedback wasn't that great and would result in an unclean build in non-sandboxed builds so I have added logic to use fileContents
to write paths outside the store to the store first.
plugins = cfg.sieve.plugins; | ||
extensions = cfg.sieve.extensions; | ||
globalExtensions = cfg.sieve.globalExtensions; | ||
phases = ["buildPhase" "installPhase" "fixupPhase"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is recommended to use dontConfigure
instead of defining your own list of phases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done! But needed to also set dontUnpack
@@ -11,6 +11,15 @@ stdenv.mkDerivation rec { | |||
|
|||
buildInputs = [ dovecot openssl ]; | |||
|
|||
patches = [ | |||
# Fix mtime comparison so that scripts can be precompiled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you link your PR also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
stateDir = "/var/lib/dovecot"; | ||
|
||
sieveToPath = text: | ||
if isString text && builtins.substring 0 2 text == "/*" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lib.hasPrefix
would be more explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
957439e
to
1e6aad5
Compare
I marked this as stale due to inactivity. → More info |
What's the current status of this PR? may I ask. |
1e6aad5
to
ccd41f7
Compare
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: |
then let value = config.services.dovecot2.sieveScripts."${type}${indexName}"; | ||
in if (substring 0 1 (toString value) == "/") | ||
then [value] ++ (listOfScripts type (idx + 1) config) | ||
else [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use lib.optionals here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! And also rewrote the function to use or
and hasPrefix
so that it is shorter and nicer.
ccd41f7
to
cbcff2d
Compare
else | ||
echo "Compiling $k -> $(readlink -f "$k")" | ||
fi | ||
${dovecot_pigeonhole}/bin/sievec -c "${config}" "$out/sieve/$k" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
${dovecot_pigeonhole}/bin/sievec -c "${config}" "$out/sieve/$k" | |
sievec -c "${config}" "$out/sieve/$k" |
This should use nativeBuildInputs where we add dovecot_pigeonhole.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
cbcff2d
to
e513ff5
Compare
Can you fix the conflicts please? I think this PR is ready to merge. |
e513ff5
to
0115539
Compare
@RaitoBezarius this has been ready to merge for years so I wasn't that motivated to rebase it again and again. In the meantime the patch to pigeonhole has been rejected so I also needed to move the 2 line patch into nixpkgs. |
Apologies for the delay, I missed your message… @2xsaiko became maintainer of that module, can I let you shepherd this PR and give me the go for a merge? |
Sure, I'll take a look in a bit! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rebase.
sieveToPath = text: | ||
# Sieve scripts can start with a block comment | ||
if isString text && hasPrefix "/*" text | ||
then pkgs.writeText "script.sieve" text | ||
# Paths in the store are used as is | ||
else if types.path.check text && hasPrefix "${builtins.storeDir}/" (toString text) | ||
then text | ||
# Paths outside the store get written to the store | ||
else if types.path.check text | ||
then pkgs.writeText "script.sieve" (fileContents text) | ||
# All other is assumed to be file content | ||
else pkgs.writeText "script.sieve" text; | ||
scriptType = types.coercedTo types.lines sieveToPath types.path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just use the same mechanism as environment.etc where you can set either text
, source
or target
options to explicitly specify what it is, instead of trying to be overly smart trying to figure out what the string means.
mkdir sieve | ||
cd sieve | ||
${concatStringsSep "\n" (imap1 (i: v: '' | ||
if [ -d '${v}' ]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if [ -d '${v}' ]; then | |
if [[ -d ${lib.escapeShellArg v} ]]; then |
generally use escapeShellArg instead of simple quoting especially with user given text, and we have bash, you can use [[
@@ -1,4 +1,4 @@ | |||
{ lib, stdenv, fetchurl, dovecot, openssl }: | |||
{ lib, stdenv, fetchpatch, fetchurl, dovecot, openssl }: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is used.
for k in $(find * -name \*.sieve); do | ||
mkdir -p "$out/sieve/$(dirname "$k")" | ||
cp -a "$k" "$out/sieve/$k" | ||
if [[ ! -L "$k" ]]; then | ||
echo "Compiling $k" | ||
touch -m --date=@0 "$out/sieve/$k" | ||
else | ||
echo "Compiling $k -> $(readlink -f "$k")" | ||
fi | ||
sievec -c "${config}" "$out/sieve/$k" | ||
done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for k in $(find * -name \*.sieve); do | |
mkdir -p "$out/sieve/$(dirname "$k")" | |
cp -a "$k" "$out/sieve/$k" | |
if [[ ! -L "$k" ]]; then | |
echo "Compiling $k" | |
touch -m --date=@0 "$out/sieve/$k" | |
else | |
echo "Compiling $k -> $(readlink -f "$k")" | |
fi | |
sievec -c "${config}" "$out/sieve/$k" | |
done | |
while IFS= read -r k; do | |
mkdir -p "$out/sieve/$(dirname "$k")" | |
cp -a "$k" "$out/sieve/$k" | |
if [[ ! -L "$k" ]]; then | |
echo "Compiling $k" | |
touch -m --date=@0 "$out/sieve/$k" | |
else | |
echo "Compiling $k -> $(readlink -f "$k")" | |
fi | |
sievec -c "${config}" "$out/sieve/$k" | |
done < <(find -name '*.sieve') |
to handle files with spaces
else pkgs.writeText "script.sieve" text; | ||
scriptType = types.coercedTo types.lines sieveToPath types.path; | ||
|
||
compileSieveScripts = pkgs.buildSieveScripts { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compileSieveScripts = pkgs.buildSieveScripts { | |
compiledSieveScripts = pkgs.buildSieveScripts { |
Does compiling these in advance break include
for non-store paths? Someone may be using that for dynamically generated rules. If it does, would they be doable another way?
Can this be closed now that #275031 was merged? |
Motivation for this change
This PR does 4 different things in the 3 commit:
nixos/tests/dovecot.nix
test of sieve scripts
Before making changes to how sieve scripts were handled I thought it best that the dovecot test included some testing of the sieve script functionality.
Patching dovecot_pigeonhole and builder to precompile scripts
One of the changes I wanted to make was to precompile sieve scripts as part of a Nix derivation. But to do that dovecot_pigeonhole needs to be changed since when source and compiled script have same mtime it thinks that the compiled script is out of date. I have tried to upstream that change with dovecot/pigeonhole#4 but it is as of yet not merged.
The builder does its compilation in the install phase because the compiled
.svbin
files contain a reference to the full path of the source.sieve
file and so the source needs to be in$out
when doing the compile.Sieve related options in the dovecot2 module
When precompiling sieve scripts the compiler needs a dovecot configuration that defines
sieve_plugins
,sieve_extensions
andsieve_global_extensions
to know which extensions and plugin functionality to allow but you can’t just use the dovecot2 config file directly because that would create a dependency cycle. So to better support precompilation I have added explicit options for these 3 things to the dovecot2 module so that I can reuse their values when precompiling.The
sieveScripts
option followed very closely how dovecot configures global scripts and was a direct mapping from attribute name tosieve_${attribute name}
which has some problems. E.g. if you definedsieveScripts.after2
without definingsieveScripts.after
your script would never be called. It also wasn’t totally clear without reading the dovecot documentation what scripts you could define.So I have added explicit options
sieve.beforeScripts
,sieve.defaultScript
,sieve.afterScripts
andsieve.discardScript
for each of the scripts you can define withbeforeScripts
andafterScripts
being lists of paths that are automatically translated to the correct numbered dovecot configuration and with the added bonus that multiple definitions of the option are merged into one list. I have also included functionality so that existing configurations that use thesieveScripts
option continue to work as before but now with a warning pointing to the new options.The other place were you might use global sieve scripts and so would like to precompile the scripts is when using the
imapsieve
plugin. Before there were no options for this so I have added options for enabling and configuringimapsieve
with support for automatic precompilation of scripts.Things done
build-use-sandbox
innix.conf
on non-NixOS)nix-shell -p nox --run "nox-review wip"
./result/bin/
)