]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
config: Replace settings-history Perl script with Python rewrite
authorFred Morcos <fred.morcos@open-xchange.com>
Mon, 7 Apr 2025 12:46:46 +0000 (14:46 +0200)
committerFred Morcos <fred.morcos@open-xchange.com>
Wed, 9 Apr 2025 06:33:18 +0000 (08:33 +0200)
src/lib-settings/Makefile.am
src/lib-settings/settings-history.pl [deleted file]
src/lib-settings/settings-history.py [new file with mode: 0755]

index 1fbd90ef6cd80048a37b46821a84d99ff693e9d7..f11abae2c2333b6f774efb69d24d902d8fcba589 100644 (file)
@@ -23,12 +23,12 @@ BUILT_SOURCES = settings-history-core.c
 
 settings-history.c: settings-history-core.c
 
-settings-history-core.c: $(top_srcdir)/src/lib-settings/settings-history.pl $(top_srcdir)/src/lib-settings/settings-history-core.txt
-       $(AM_V_GEN)$(top_srcdir)/src/lib-settings/settings-history.pl $(top_srcdir)/src/lib-settings/settings-history-core.txt $(DOVECOT_PRO_BUILD) > settings-history-core.c || rm -f settings-history-core.c
+settings-history-core.c: $(srcdir)/settings-history-core.txt $(srcdir)/settings-history.py
+       $(AM_V_GEN)$(srcdir)/settings-history.py --pro $(DOVECOT_PRO_BUILD) $< $@
 
 EXTRA_DIST = \
        settings-history-core.txt \
-       settings-history.pl
+       settings-history.py
 
 test_programs = \
        test-settings-parser \
diff --git a/src/lib-settings/settings-history.pl b/src/lib-settings/settings-history.pl
deleted file mode 100755 (executable)
index 11f7b54..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env perl
-use strict;
-use warnings;
-use version;
-use 5.010;
-
-my ($input_path, $pro_edition) = ($ARGV[0], $ARGV[1]);
-
-# output renames
-print "static const struct setting_history_rename settings_history_core_renames[] = {\n";
-my $f;
-open $f, "<:encoding(utf8)", $input_path or die "$!";
-my $prev_version = "";
-while (<$f>) {
-  chomp;
-  if (/^rename\t/) {
-    my ($type, $old_key, $new_key, $ce_version, $pro_version) = split("\t");
-    my $version = $pro_edition ? $pro_version : $ce_version;
-    if ($version ne "") {
-      die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/;
-      print "  { \"$old_key\", \"$new_key\", \"$version\" },\n";
-      if ($prev_version ne "") {
-       die "Bad version sorting order" if version->parse($version) > version->parse($prev_version);
-      }
-      $prev_version = $version;
-    }
-  } elsif (! /^default\t/) {
-    die "Unknown line: $_";
-  }
-}
-close $f;
-print "};\n";
-
-# output defaults
-print "static const struct setting_history_default settings_history_core_defaults[] = {\n";
-open $f, "<:encoding(utf8)", $input_path or die "$!";
-while (<$f>) {
-  chomp;
-  if (/^default\t/) {
-    my ($type, $key, $old_value, $ce_version, $pro_version) = split("\t");
-    my $version = $pro_edition ? $pro_version : $ce_version;
-    if ($version ne "") {
-      die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/;
-      print "  { \"$key\", \"$old_value\", \"$version\" },\n";
-      if ($prev_version ne "") {
-       die "Bad version sorting order" if version->parse($version) > version->parse($prev_version);
-      }
-      $prev_version = $version;
-    }
-  }
-}
-close $f;
-print "};\n";
diff --git a/src/lib-settings/settings-history.py b/src/lib-settings/settings-history.py
new file mode 100755 (executable)
index 0000000..2ddcbf6
--- /dev/null
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+
+"""Generate history structs for renamed settings and changed default values."""
+
+import argparse
+import sys
+
+FILE_TEMPLATE = """\
+static const struct setting_history_rename settings_history_core_renames[] = {
+%s\
+};
+static const struct setting_history_default settings_history_core_defaults[] = {
+%s\
+};
+"""
+
+STRUCT_TEMPLATE = """\
+  { "%s", "%s", "%s" },
+"""
+
+
+def die(message: str):
+    """Die with a message."""
+    print(message, file=sys.stderr)
+    sys.exit(1)
+
+
+def parse_version(line: int, version: str) -> [int]:
+    """Parse a string version into a list of integers."""
+    values = version.split(".")
+    parsed = []
+    for value in values:
+        try:
+            int_value = int(value)
+            parsed.append(int_value)
+        except ValueError as e:
+            die(f"Line {line}: Invalid version {version}: {e}")
+    return parsed
+
+
+def check_version(line: int, prev_version: [int], cur_version: [int]):
+    """Fail if version ordering is incorrect."""
+    if prev_version is not None and cur_version > prev_version:
+        die(f"Line {line}: Invalid version sort order")
+    return cur_version
+
+
+def process(contents: str, pro: bool) -> (str, str):
+    """Produce the renames and defaults structs from the input data."""
+    renames = ""
+    defaults = ""
+    renames_prev_version = None
+    defaults_prev_version = None
+    for line, data in enumerate(contents.splitlines()):
+        line = line + 1
+        values = data.split("\t")
+
+        if len(values) != 5:
+            die(f"Line {line}: Invalid contents `{data}`: Expecting 5 fields")
+
+        ce_version = values[3]
+        pro_version = values[4]
+        version_text = pro_version if pro else ce_version
+
+        if version_text == "":
+            continue
+
+        version = parse_version(line, version_text)
+
+        if values[0] == "rename":
+            old_key = values[1]
+            new_key = values[2]
+            renames += STRUCT_TEMPLATE % (old_key, new_key, version_text)
+            renames_prev_version = check_version(line, renames_prev_version, version)
+        elif values[0] == "default":
+            key = values[1]
+            old_value = values[2]
+            defaults += STRUCT_TEMPLATE % (key, old_value, version_text)
+            defaults_prev_version = check_version(line, defaults_prev_version, version)
+        else:
+            die(f"Line {line}: Unrecognized marker in `{data}`")
+    return (renames, defaults)
+
+
+def main():
+    """Entry point."""
+    parser = argparse.ArgumentParser(
+        prog="settings-history.py",
+        description="Generate header file for settings migration data",
+    )
+    parser.add_argument(
+        "input-file",
+        type=str,
+        help="Input data file e.g. settings-history-core.txt",
+    )
+    parser.add_argument(
+        "output-file",
+        type=str,
+        help="Output header file e.g. settings-history-core.h",
+    )
+    parser.add_argument(
+        "--pro",
+        type=int,
+        choices=[0, 1],
+        help="Whether to generate settings migration data for Pro",
+    )
+    args = parser.parse_args()
+
+    with open(getattr(args, "input-file"), mode="r", encoding="utf-8") as f_in:
+        contents = f_in.read()
+        (renames, defaults) = process(contents, pro=bool(args.pro))
+
+        with open(getattr(args, "output-file"), mode="w", encoding="utf-8") as f_out:
+            f_out.write(FILE_TEMPLATE % (renames, defaults))
+
+
+if __name__ == "__main__":
+    main()