Hi,

Currently, all test logs are uploaded to Postgres CI regardless of
whether the tests pass or fail. This approach has a few drawbacks:

- It can be difficult to identify failed tests quickly. You need to
remember which tests failed and then locate them within the CI
artifacts, which often requires scrolling and can be frustrating.

- Uploading all test logs adds unnecessary overhead. For example,
uploading artifacts when only a single test fails takes approximately
300 seconds on macOS and 70 seconds on other CI platforms [1].

- There may also be associated storage or transfer costs, although I
am not certain about this.

To improve this, I propose removing the output folders of successful
tests before uploading artifacts. In Meson builds, a 'test.success'
file is created in the test output directory when a test passes. I
have written a Python script that traverses these directories and
removes those directories which contain this file. At the moment, this
solution only applies to Meson builds, since the test.success file is
not generated in the Autoconf build system.

I would appreciate any thoughts or feedback on this approach.

Note: Currently NetBSD is failing with: 'env: python3: No such file or
directory', this can be fixed separately but I wanted to hear your
thoughts first.

Example CI Run after the patch is applied and CI is intentionally
broken to show how patch works:
https://cirrus-ci.com/build/6514731441192960


[1] https://cirrus-ci.com/build/6045972905590784

-- 
Regards,
Nazir Bilal Yavuz
Microsoft
From d5df7a755656ca3a0455d037857d0a74176a4a1f Mon Sep 17 00:00:00 2001
From: Nazir Bilal Yavuz <[email protected]>
Date: Tue, 7 Apr 2026 18:50:01 +0300
Subject: [PATCH v1] ci: Don't collect successful tests' logs

Add a Python script that removes subdirectories containing a test.success
file from a given testrun folder. This ensures that only failed tests'
logs are kept when uploading CI artifacts, reducing artifact size and
noise. This change affects only Meson CI builds as test.success file is
created only on Meson builds.
---
 .cirrus.tasks.yml                          |  5 ++
 src/tools/ci/ci_meson_clear_testrun_folder | 62 ++++++++++++++++++++++
 2 files changed, 67 insertions(+)
 create mode 100755 src/tools/ci/ci_meson_clear_testrun_folder

diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml
index a22cef063f3..11e3e9074ea 100644
--- a/.cirrus.tasks.yml
+++ b/.cirrus.tasks.yml
@@ -67,6 +67,11 @@ on_failure_ac: &on_failure_ac
     type: text/plain
 
 on_failure_meson: &on_failure_meson
+
+  # Remove logs of successful tests; keep only failed tests' logs for upload
+  meson_clear_testrun_folder_script:
+    src/tools/ci/ci_meson_clear_testrun_folder "build*/testrun"
+
   testrun_artifacts:
     paths:
       - "build*/testrun/**/*.log"
diff --git a/src/tools/ci/ci_meson_clear_testrun_folder b/src/tools/ci/ci_meson_clear_testrun_folder
new file mode 100755
index 00000000000..18928be05ec
--- /dev/null
+++ b/src/tools/ci/ci_meson_clear_testrun_folder
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+#
+# After a test run, each test that passed contains a test.success file in its
+# output directory. This script removes those directories so that only the logs
+# of failed tests remain, making it easier to inspect CI artifacts.
+#
+# Usage: ci_meson_clear_testrun_folder <glob-pattern>
+#   e.g. ci_meson_clear_testrun_folder build*/testrun
+#        ci_meson_clear_testrun_folder build/testrun/
+
+import argparse
+import glob
+import os
+import shutil
+import sys
+
+parser = argparse.ArgumentParser(
+    description="Remove subdirectories that contain a test.success file."
+)
+parser.add_argument(
+    "path",
+    help="Glob pattern for testrun folder(s), e.g. 'build*/testrun' (must end with 'testrun')",
+)
+args = parser.parse_args()
+pattern = args.path.rstrip("/")
+
+if not pattern.endswith('testrun'):
+    print(f"Error: path pattern must end with 'testrun', got: {pattern}")
+    sys.exit(1)
+
+# Resolve relative patterns against cwd, also support absolute patterns
+matches = glob.glob(pattern, recursive=True)
+
+if not matches:
+    print(f"Error: no directories matched pattern: {pattern}")
+    sys.exit(1)
+
+testrun_dirs = [os.path.abspath(m) for m in matches if os.path.isdir(m)]
+
+if not testrun_dirs:
+    print(f"Error: matched paths are not directories: {matches}")
+    sys.exit(1)
+
+for testrun_dir in testrun_dirs:
+    print(f"Removing successful tests from {testrun_dir}:")
+    for dirpath, dirnames, filenames in os.walk(testrun_dir):
+        if "test.success" in filenames:
+            print(f"Removing: {dirpath}")
+            shutil.rmtree(dirpath)
+            dirnames.clear()
+    print("Done\n")
+
+for testrun_dir in testrun_dirs:
+    print(f"Removing any empty directories from {testrun_dir}:")
+    for dirpath, _, _ in os.walk(testrun_dir, topdown=False):
+        # Don't remove ${testrun_dir}
+        if dirpath == testrun_dir:
+            continue
+        if not os.listdir(dirpath):
+            print(f"Removing empty dir: {dirpath}")
+            os.rmdir(dirpath)
+    print("Done\n")
-- 
2.47.3

Reply via email to