commit faeb889812e7a92ac50db695dd2a18aaa71e13cb
parent e6b00b95b91ff20f13eff9e0442c2ff3281a42c2
Author: Iván Ávalos <avalos@disroot.org>
Date: Fri, 2 Oct 2026 12:05:12 +0200
CI: build libmicrohttpd2 from source and extend the jobs
Diffstat:
9 files changed, 197 insertions(+), 33 deletions(-)
diff --git a/contrib/check-db-naming.py b/contrib/check-db-naming.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+"""Enforce the sync database layer naming conventions.
+
+Adapted from the equivalent checker in the exchange repository
+(contrib/check-db-naming.py) to the conventions used by sync's syncdb:
+
+ 1. every implementation file syncdb_<X>.c defines a function named
+ SYNCDB_<X>, and a matching header <X>.h exists in the public
+ include directory declaring exactly that function
+ 2. the stem <X> starts with one of the approved operation prefixes,
+ or the file is exempt (internal modules, standalone tools)
+ 3. a .sql file (if any) belongs to the file it is named after
+ 4. every prepared statement is unique across all implementation files
+ 5. no numeric suffixes on stems
+ 6. no repeated 'syncdb'/'SYNCDB' library prefix anywhere
+
+Run from the top of the source tree; exits non-zero on any violation.
+"""
+import re
+import sys
+import pathlib
+
+IMPL_DIR = pathlib.Path("src/syncdb")
+HDR_DIR = pathlib.Path("src/include/sync/sync-database")
+
+PREFIX = "SYNCDB_"
+
+# Approved operation prefixes for the part of the file name after
+# 'syncdb_' (e.g. syncdb_lookup_account_TR.c -> 'lookup_account_TR').
+PREFIXES = (
+ "append_", "delete_", "update_", "lookup_", "store_", "increment_",
+ "create_", "drop_", "preflight_",
+)
+
+# Exact stems (without the 'syncdb_' prefix) that are not operations.
+EXACT = {
+ "gc",
+}
+
+# Files that are not part of the API surface at all.
+SKIP = {"pg", "sync-dbinit"}
+
+PREP = re.compile(r'PREPARE\s*\(\s*\w+\s*,\s*"([A-Za-z0-9_]+)"', re.S)
+DECL = re.compile(r"^" + re.escape(PREFIX) + r"(\w+)\s*\(", re.M)
+
+errors = []
+
+
+def check():
+ if not IMPL_DIR.is_dir():
+ return
+ stmt_owner = {}
+
+ for c in sorted(IMPL_DIR.glob("*.c")):
+ stem = c.stem
+ if stem in SKIP or stem.startswith("test_"):
+ continue
+ if stem.startswith("syncdb_"):
+ body = stem[len("syncdb_"):]
+ else:
+ body = stem
+
+ # 2. approved prefix, or exempt
+ if (not body.startswith(PREFIXES)
+ and body not in EXACT):
+ errors.append(f"{c}: '{body}' uses no approved prefix")
+
+ # 5. numeric suffix
+ if re.search(r"\d$", body):
+ errors.append(f"{c}: '{body}' ends in a digit")
+
+ h = HDR_DIR / (body + ".h")
+
+ # 1a. matching header
+ if not h.exists():
+ errors.append(f"{c}: no matching header {h}")
+ else:
+ names = DECL.findall(h.read_text(errors="replace"))
+ # 1b. the header declares exactly the function of this file
+ if names != [body]:
+ errors.append(
+ f"{h}: declares {names}, expected exactly ['{body}']")
+
+ text = c.read_text(errors="replace")
+
+ # 1c. the file defines its function
+ if not re.search(r"^" + re.escape(PREFIX + body) + r"\s*\(", text,
+ re.M):
+ errors.append(f"{c}: does not define {PREFIX}{body}")
+
+ # 3. a .sql file shares the name
+ sql = c.with_suffix(".sql")
+ if sql.exists() and sql.stem != stem:
+ errors.append(f"{c}: .sql file {sql} does not share the name")
+
+ # 4. prepared statement uniqueness
+ for s in dict.fromkeys(PREP.findall(text)):
+ if s in stmt_owner and stmt_owner[s] != str(c):
+ errors.append(
+ f"{c}: prepared statement '{s}' also prepared in "
+ f"{stmt_owner[s]}")
+ stmt_owner[s] = str(c)
+
+ # Also check .sql files that are not covered by a .c file: they must
+ # either belong to a .c file or be one of the schema/fragment files.
+ covered = set()
+ for c in IMPL_DIR.glob("syncdb_*.c"):
+ covered.add(c.stem)
+ for s in IMPL_DIR.glob("syncdb_*.sql"):
+ if s.stem in covered:
+ continue
+ if s.stem not in ("procedures",):
+ errors.append(
+ f"{s}: .sql file without a matching .c and not 'procedures'")
+
+ # 6. repeated library prefix anywhere in the tree
+ for f in list(IMPL_DIR.glob("*.[ch]")) + list(HDR_DIR.glob("*.h")):
+ if "unc-backup" in f.name:
+ continue
+ if re.search(r"\bSYNCDB_syncdb\b|\bsyncdb_syncdb\b", f.read_text(
+ errors="replace")):
+ errors.append(f"{f}: repeated 'sync[d]b' prefix")
+
+
+for e in errors:
+ print("ERROR:", e)
+print(f"\ndb-naming: {len(errors)} violation(s)")
+sys.exit(1 if errors else 0)
+
+
+if __name__ == "__main__":
+ check()
diff --git a/contrib/ci/Containerfile b/contrib/ci/Containerfile
@@ -9,6 +9,7 @@ RUN apt-get update -yqq && \
meson \
libjansson-dev \
libgcrypt-dev \
+ libgnutls28-dev \
libqrencode-dev \
libpq-dev \
pkg-config \
@@ -32,7 +33,22 @@ RUN apt-get install -yqq \
build-essential \
debhelper-compat \
devscripts \
- git-buildpackage
+ git-buildpackage \
+ autoconf \
+ automake
+
+# Debian does not (yet) package libmicrohttpd2, so build it from source.
+# Pinned to a specific revision of git.gnunet.org/libmicrohttpd2.
+RUN git clone https://git.gnunet.org/libmicrohttpd2.git /tmp/libmicrohttpd2 \
+ && cd /tmp/libmicrohttpd2 \
+ && git checkout 24a2913c4e1cb31a5cff52047beae146959d50b0 \
+ && ./bootstrap \
+ && ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu \
+ --disable-doc --disable-examples \
+ && make -j"$(nproc)" \
+ && make install \
+ && ldconfig \
+ && rm -rf /tmp/libmicrohttpd2
# Install docs generation utils
RUN apt-get update -yqq && \
@@ -51,7 +67,6 @@ Package: * \n\
Pin: origin "deb.taler.net" \n\
Pin-Priority: 999' > /etc/apt/preferences.d/taler
-# FIXME: we need libeufin-bank here for the CI to work!
RUN cat /etc/apt/preferences.d/taler && \
apt-get update -y && \
apt-get install -y \
diff --git a/contrib/ci/debian-package-job.sh b/contrib/ci/debian-package-job.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -exuo pipefail
+# This file is in the public domain.
+# Helper script to build the latest DEB packages in the container.
+# Shared between various jobs.
+
+unset LD_LIBRARY_PATH
+
+# Install build-time dependencies.
+# Update apt cache first
+apt-get update
+apt-get upgrade -y
+mk-build-deps --install --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
+
+# Sanity-check the version generation logic before deriving the package version.
+python3 contrib/ci/test_version.py
+
+VERSION="$(./contrib/ci/version.sh)"
+export VERSION
+: "${VERSION:?version generation returned an empty version}"
+echo "Building package version ${VERSION}"
+EMAIL=none gbp dch --dch-opt=-b --ignore-branch --debian-tag="%(version)s" --git-author --new-version="${VERSION}"
+./bootstrap
+dpkg-buildpackage -rfakeroot -b -uc -us
+
+ls -alh ../*.deb
+mkdir -p /artifacts/sync/${CI_COMMIT_REF} # Variable comes from CI environment
+mv ../*.deb /artifacts/sync/${CI_COMMIT_REF}/
diff --git a/contrib/ci/jobs/0-db-naming/config.ini b/contrib/ci/jobs/0-db-naming/config.ini
@@ -0,0 +1,6 @@
+[build]
+HALT_ON_FAILURE = True
+WARN_ON_FAILURE = True
+CONTAINER_BUILD = False
+CONTAINER_NAME = localhost/sync
+CONTAINER_ARCH = amd64
diff --git a/contrib/ci/jobs/0-db-naming/job.sh b/contrib/ci/jobs/0-db-naming/job.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+set -exuo pipefail
+
+exec ./contrib/check-db-naming.py
diff --git a/contrib/ci/jobs/1-build/build.sh b/contrib/ci/jobs/1-build/build.sh
@@ -7,4 +7,7 @@ set -exuo pipefail
--enable-logging=verbose \
--disable-doc
-make
+# New container, may contain old build artifacts
+make clean
+nump=$(grep processor /proc/cpuinfo | wc -l)
+make -j$(( $nump / 2 ))
diff --git a/contrib/ci/jobs/2-test/1-build.sh b/contrib/ci/jobs/2-test/1-build.sh
@@ -10,3 +10,8 @@ apt-get upgrade -yqq
--enable-coverage \
--enable-logging=verbose \
--disable-doc
+
+# New container, may contain old build artifacts
+make clean
+nump=$(grep processor /proc/cpuinfo | wc -l)
+make -j$(( $nump / 2 ))
diff --git a/contrib/ci/jobs/4-deb-package/job.sh b/contrib/ci/jobs/4-deb-package/job.sh
@@ -1,25 +1,2 @@
#!/bin/bash
-set -exuo pipefail
-# This file is in the public domain.
-# Helper script to build the latest DEB packages in the container.
-
-
-unset LD_LIBRARY_PATH
-
-# Install build-time dependencies.
-# Update apt cache first
-apt-get update
-apt-get upgrade -y
-mk-build-deps --install --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
-
-VERSION="$(./contrib/ci/jobs/4-deb-package/version.sh)"
-export VERSION
-: "${VERSION:?version generation returned an empty version}"
-echo "Building package version ${VERSION}"
-EMAIL=none gbp dch --dch-opt=-b --ignore-branch --debian-tag="%(version)s" --git-author --new-version="${VERSION}"
-./bootstrap
-dpkg-buildpackage -rfakeroot -b -uc -us
-
-ls -alh ../*.deb
-mkdir -p /artifacts/sync/${CI_COMMIT_REF} # Variable comes from CI environment
-mv ../*.deb /artifacts/sync/${CI_COMMIT_REF}/
+exec ./contrib/ci/debian-package-job.sh
diff --git a/contrib/ci/jobs/4-deb-package/version.sh b/contrib/ci/jobs/4-deb-package/version.sh
@@ -1,6 +0,0 @@
-#!/bin/sh
-# This file is in the public domain.
-# Compatibility entry point, also usable in generated job directories.
-set -eu
-repo_dir=$(git rev-parse --show-toplevel)
-exec "$repo_dir/contrib/ci/version.sh" "$@"