[FFmpeg-cvslog] [ffmpeg-patchwork_job_runner] UNNAMED PROJECT branch master updated. 5b4d43c Use logger module for logging messages

ffmpeg-git at ffmpeg.org ffmpeg-git at ffmpeg.org
Mon Jun 9 09:17:14 EEST 2025


The branch, master has been updated
       via  5b4d43c24523813f633ae7709e856a78eb64a296 (commit)
       via  4554a24397b86df1bbe31458840e8a0ccfa2a8fb (commit)
       via  6dfccdace750c90631f12460367f14c00f0d96cf (commit)
       via  c6bc759eee12f838d5ccb59be8c4552d164940e1 (commit)
       via  f8cecf67f39e465770befd7415ebd98f07a559a7 (commit)
       via  a1f4a250a7b4043c9aa313193529138c1877853f (commit)
       via  f0a2c4488c4dbd3c011c512e76c31f21bc24ad6d (commit)
       via  e47da1e3f463403e5dbd9f461a296f7de21443c1 (commit)
       via  995c010fa21279be55ae04896cb9a71cf11b3334 (commit)
       via  1c64a6a32fc0372f95851c084cbda1eb5e630e13 (commit)
      from  006e68406a6a002bf31dd8b2525e968422b7bda6 (commit)


- Log -----------------------------------------------------------------
commit 5b4d43c24523813f633ae7709e856a78eb64a296
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Jun 8 22:12:34 2025 -0700
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 22:12:34 2025 -0700

    Use logger module for logging messages

diff --git a/patchwork_runner.py b/patchwork_runner.py
index 9b0eb66..a562927 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -1,5 +1,6 @@
 import email
 import json
+import logging
 import re
 import requests
 import smtplib
@@ -19,6 +20,9 @@ from mysql_helper import SQLDatabase
 from sqlite_helper import SQLiteDatabase
 from proxy_smtplib import ProxySMTP
 
+logger = logging.getLogger("Runner:")
+logger.setLevel(logging.INFO)
+
 def post_check(check_url, type_check, context, msg_short, msg_long, config_pw):
 
     if (isinstance(msg_long, bytes)):
@@ -39,8 +43,6 @@ def post_check(check_url, type_check, context, msg_short, msg_long, config_pw):
     headers = {"Authorization" : "Token %s" % config_pw["token"]}
     payload = {"state" : type_check, "context" : context, "description" : msg_short, "description_long" : msg_long}
     resp = requests.post(check_url, headers=headers, data=payload)
-    print(resp)
-    print(resp.content)
 
 def submit_job_result(mydb, job, job_result, check_url, config_pw):
 
@@ -69,8 +71,7 @@ def run_job(mydb, commit_hash, job):
     commit_hash = commit_hash.decode("utf-8")
     job_result = mydb.query(job.name, keys, "WHERE commit_hash = \"%s\"" % commit_hash)
     if job_result:
-        print("\nFound cashed result: %s\n" % commit_hash)
-        print(job_result)
+        logger.info(f"Found cached result: {commit_hash}")
         return job_result
 
     job_result = { "commit_hash" : commit_hash, "setup_success" : 0, "setup_log" : "",
@@ -110,13 +111,12 @@ def run_job(mydb, commit_hash, job):
         else:
             job_result["unit_test_success"] = 1
 
-    print (job_result)
     mydb.insert(job.name, job_result)
     return job_result
 
 def notify_by_email(mydb, patch, config_smtp):
 
-    print ("Sending email notification")
+    logger.info ("Sending email notification")
 
     keys = list()
     keys.append("email_sent")
@@ -147,10 +147,10 @@ def notify_by_email(mydb, patch, config_smtp):
     msg_email["References"] = patch["msg_id"]
 
     config_proxy = config_smtp["proxy"]
-    print ("Proxy is %d" % config_proxy["enabled"])
+    proxy_enabled = config_proxy["enabled"]
+    logger.info (f"Proxy enabled: {proxy_enabled}")
     if config_proxy["enabled"]:
-        print ("Using proxy")
-        ret = subprocess.run(config_proxy["cmd"], shell=True)
+        subprocess.run(config_proxy["cmd"], shell=True)
         smtp = ProxySMTP(config_smtp["host"], config_smtp["port"], proxy_addr = config_proxy["proxy_addr"], proxy_port = config_proxy["proxy_port"])
     else:
         smtp = smtplib.SMTP(config_smtp["host"], config_smtp["port"])
@@ -226,13 +226,13 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
         msg_id = msg_id[:256]
 
         check_url     = reply_patch["checks"]
-        print ("Author email: %s" % author_email)
-        print ("Subject email: %s" % subject_email)
-        print ("Series id: %s" % series_id)
-        print ("Check url: %s" % check_url)
-        print ("Patch url: %s" % patch_url)
-        print ("Mbox: %s" % mbox)
-        print ("User link: %s" % mbox[:-5])
+        logger.info (f"Author email: {author_email}")
+        logger.info (f"Subject email: {subject_email}")
+        logger.info (f"Series id: {series_id}")
+        logger.info (f"Check url: {check_url}")
+        logger.info (f"Patch url: {patch_url}")
+        logger.info (f"Mbox: {mbox}")
+        logger.info (f"User link: {mbox[:-5]}")
 
         keys = list()
         keys.append("msg_id")
@@ -255,7 +255,7 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
             mydb.insert("series", {"series_id" : "%d" % series_id, "email_sent" : 0})
 
 
-    print ("Number of patches in list: %d" % len(patch_list))
+    logger.info (f"Number of patches in list: {len(patch_list)}")
 
     for patch in patch_list:
 
@@ -287,7 +287,8 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
             time.sleep(1*60)
 
         if retries == max_retries:
-            print ("Failed to fetch patch %s" % patch["mbox"])
+            patch_mbox= patch["mbox"]
+            logger.error(f"Failed to fetch patch {patch_mbox}")
             continue
 
         git_cmd = git_cmd_template + "am --keep-cr -3 --committer-date-is-author-date --exclude=Changelog mbox_file"
@@ -310,7 +311,6 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
         commit_msg = ret.stdout.decode("utf-8")
         warn = check_commit_message(commit_msg)
         if warn:
-            print (warn)
             post_check(patch["check_url"], "warning", "commit_msg_" + job.name, warn, "", config_pw)
             notify_by_email(mydb, patch, config_pw["smtp"])
 
@@ -318,7 +318,6 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
         ret = subprocess.run(git_cmd, capture_output=True, shell=True)
         current_hash = ret.stdout
         current_hash = current_hash[0:32]
-        print ("Current hash %s" % current_hash)
         job_result = run_job(mydb, current_hash, job)
         submit_job_result(mydb, job, job_result, patch["check_url"], config_pw)
 
@@ -349,7 +348,7 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
 if __name__ == "__main__":
 
     if len(sys.argv) != 2:
-        print("Usage:\n $ python3 patchwork_runner.py config.yaml")
+        logger.error("Usage:\n $ python3 patchwork_runner.py config.yaml")
         sys.exit(1)
 
     with  open(sys.argv[1], 'r') as file:
@@ -361,7 +360,7 @@ if __name__ == "__main__":
     elif config["db"]["type"] == "mysql":
         mydb = SQLDatabase(config["db"])
     else:
-        print("Invalid database type %s" % config["db"]["type"])
+        logger.error("Invalid database type %s" % config["db"]["type"])
         sys.exit(1)
 
     jobs_list = list()
@@ -398,13 +397,13 @@ if __name__ == "__main__":
             patch_list = fetch_and_process_patches(mydb, jobs_list, time_interval, config["patchwork"])
 
         except Exception as e:
-            print("Error processing patches %s" % str(e))
-            print("Continuing after 60 seconds")
+            logger.error(f"Error processing patches {str(e)}")
+            logger.info("Continuing after 60 seconds")
             time.sleep(60)
             patch_list = None
 
         if not patch_list:
-            print ("No patches, sleeping for 5 minutes")
+            logger.info ("No patches, sleeping for 5 minutes")
             time.sleep(60*5)
         end_time = time.time()
     mydb.close()

commit 4554a24397b86df1bbe31458840e8a0ccfa2a8fb
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Jun 8 21:54:00 2025 -0700
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:54:00 2025 -0700

    Catch exceptions when processing the patches

diff --git a/patchwork_runner.py b/patchwork_runner.py
index d24f6c2..9b0eb66 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -394,7 +394,15 @@ if __name__ == "__main__":
             time_interval = time_interval + 24 * 60
             first_run = 1
         start_time = time.time()
-        patch_list = fetch_and_process_patches(mydb, jobs_list, time_interval, config["patchwork"])
+        try:
+            patch_list = fetch_and_process_patches(mydb, jobs_list, time_interval, config["patchwork"])
+
+        except Exception as e:
+            print("Error processing patches %s" % str(e))
+            print("Continuing after 60 seconds")
+            time.sleep(60)
+            patch_list = None
+
         if not patch_list:
             print ("No patches, sleeping for 5 minutes")
             time.sleep(60*5)

commit 6dfccdace750c90631f12460367f14c00f0d96cf
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Jun 8 21:44:16 2025 -0700
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:47:32 2025 -0700

    Fetch more patches when the runner is started

diff --git a/patchwork_runner.py b/patchwork_runner.py
index a8d4b30..d24f6c2 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -387,8 +387,12 @@ if __name__ == "__main__":
     # in minutes
     start_time = 0
     end_time = 0
+    first_run = 1
     while 1:
         time_interval = (end_time - start_time) / 60 + 10
+        if first_run == 1:
+            time_interval = time_interval + 24 * 60
+            first_run = 1
         start_time = time.time()
         patch_list = fetch_and_process_patches(mydb, jobs_list, time_interval, config["patchwork"])
         if not patch_list:

commit c6bc759eee12f838d5ccb59be8c4552d164940e1
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Jun 8 19:11:15 2025 -0700
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:47:28 2025 -0700

    Add option to use sqlite instead of mysql

diff --git a/README b/README
index e7f3c40..c31b66b 100644
--- a/README
+++ b/README
@@ -17,7 +17,7 @@ user by email). Custom jobs can therefore be created modifying the job class. Us
 recomended for isolating the process.
 
 --- Caching results ---
-The code currently uses a mysql database to track information and cache job
+The code currently uses a sqlite or mysql database to track information and cache job
 results. The database client settings are defined in the config yaml file. Multiple runners
 on different physical machines may share a common mysql database or use a dedicated database. Sharing
 a common database allows to check whether an automated email was already
@@ -49,7 +49,5 @@ Section "runners" stores the CI jobs to run.
 permissions.
 2. Modify class Job in job.py for your CI job setup.
 3. Set the configurations in the config.yaml file.
-4. Start a mysql server instance, i.e. using docker:
-$ docker run --name "ffmpeg_mysql" -e MYSQL_ROOT_PASSWORD=${PATCHWORK_DB_PASSWORD} --rm -v sqldb:/var/lib/mysql -p 3306:3306 mysql:5.7
-5. Start the job runner with:
+4. Start the job runner with:
 $ python patchwork_runner.py config.yaml
diff --git a/config.yaml b/config_mysql.yaml
similarity index 96%
copy from config.yaml
copy to config_mysql.yaml
index 1bf46bf..880eea5 100644
--- a/config.yaml
+++ b/config_mysql.yaml
@@ -20,6 +20,7 @@ runners:
         run_full_series: True
 
 db:
+    type: "mysql"
     host: "127.0.0.1"
     user: "user"
     password: "password"
@@ -35,7 +36,7 @@ patchwork:
     token: "abcdefg12345"
 
     smtp:
-        enabled: True
+        enabled: False
         host: "smtp.gmail.com"
         port: 587
         user: "user at gmail.com"
diff --git a/config.yaml b/config_sqlite.yaml
similarity index 80%
rename from config.yaml
rename to config_sqlite.yaml
index 1bf46bf..31c036a 100644
--- a/config.yaml
+++ b/config_sqlite.yaml
@@ -20,22 +20,15 @@ runners:
         run_full_series: True
 
 db:
-    host: "127.0.0.1"
-    user: "user"
-    password: "password"
-    db_name: "mysql"
-    ssl:
-        enabled: False
-        ssl_ca: "ssl_keys/ca-cert.pem"
-        ssl_cert: "ssl_keys/client-cert.pem"
-        ssl_key: "ssl_keys/client-key.pem"
+    type: "sqlite"
+    db_path: "./patchwork.sqlite"
 
 patchwork:
     host: "patchwork.ffmpeg.org"
     token: "abcdefg12345"
 
     smtp:
-        enabled: True
+        enabled: False
         host: "smtp.gmail.com"
         port: 587
         user: "user at gmail.com"
diff --git a/patchwork_runner.py b/patchwork_runner.py
index 8d6fc8b..a8d4b30 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -16,6 +16,7 @@ from dateutil.relativedelta import relativedelta
 from email.message import EmailMessage
 from job import Job
 from mysql_helper import SQLDatabase
+from sqlite_helper import SQLiteDatabase
 from proxy_smtplib import ProxySMTP
 
 def post_check(check_url, type_check, context, msg_short, msg_long, config_pw):
@@ -66,7 +67,7 @@ def run_job(mydb, commit_hash, job):
             "unit_test_success", "unit_test_log", "number_of_warnings")
 
     commit_hash = commit_hash.decode("utf-8")
-    job_result = mydb.query(job.name, keys, "WHERE commit_hash = 0x%s" % commit_hash)
+    job_result = mydb.query(job.name, keys, "WHERE commit_hash = \"%s\"" % commit_hash)
     if job_result:
         print("\nFound cashed result: %s\n" % commit_hash)
         print(job_result)
@@ -316,7 +317,7 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
         git_cmd = git_cmd_template + " rev-parse master"
         ret = subprocess.run(git_cmd, capture_output=True, shell=True)
         current_hash = ret.stdout
-        current_hash = current_hash[0:40]
+        current_hash = current_hash[0:32]
         print ("Current hash %s" % current_hash)
         job_result = run_job(mydb, current_hash, job)
         submit_job_result(mydb, job, job_result, patch["check_url"], config_pw)
@@ -355,7 +356,13 @@ if __name__ == "__main__":
         config = yaml.safe_load(file)
 
     # local database for storing cached job results
-    mydb = SQLDatabase(config["db"])
+    if config["db"]["type"] == "sqlite":
+        mydb = SQLiteDatabase(config["db"])
+    elif config["db"]["type"] == "mysql":
+        mydb = SQLDatabase(config["db"])
+    else:
+        print("Invalid database type %s" % config["db"]["type"])
+        sys.exit(1)
 
     jobs_list = list()
 
@@ -364,9 +371,9 @@ if __name__ == "__main__":
 
     # when the db is first setup there are no tables. so init them
     for job in jobs_list:
-        mydb.create_missing_table(job.name, ("(id INT AUTO_INCREMENT PRIMARY KEY, commit_hash BINARY(20), "
-                                             "setup_success BIT(1), setup_log LONGTEXT, build_success BIT(1), build_log LONGTEXT,"
-                                             "unit_test_success BIT(1), unit_test_log LONGTEXT, number_of_warnings INT)"))
+        mydb.create_missing_table(job.name, ("(id INT AUTO_INCREMENT PRIMARY KEY, commit_hash VARCHAR(32), "
+                                             "setup_success INT, setup_log LONGTEXT, build_success INT, build_log LONGTEXT,"
+                                             "unit_test_success INT, unit_test_log LONGTEXT, number_of_warnings INT)"))
 
         # this tables stores the patches we have already processed locally
         # it is used for checking we don't run the same job twice
@@ -375,7 +382,7 @@ if __name__ == "__main__":
     # this table is used to track if we have sent an email to user for a specific
     # series. We don't want to send an email for each commit that's failed, but
     # only once per series
-    mydb.create_missing_table("series", "(id INT AUTO_INCREMENT PRIMARY KEY, series_id INT, email_sent BIT(1))")
+    mydb.create_missing_table("series", "(id INT AUTO_INCREMENT PRIMARY KEY, series_id INT, email_sent INT)")
 
     # in minutes
     start_time = 0

commit f8cecf67f39e465770befd7415ebd98f07a559a7
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Jun 8 18:47:21 2025 -0700
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:41:38 2025 -0700

    Add sqlite helpers
    
    From git.ffmpeg.org/gitweb/patchwork_jobs_devops.git

diff --git a/sqlite_helper.py b/sqlite_helper.py
new file mode 100644
index 0000000..bb77d5b
--- /dev/null
+++ b/sqlite_helper.py
@@ -0,0 +1,166 @@
+import sqlite3
+import os
+import time
+import threading
+
+class SQLiteDatabase:
+    """
+    SQLite database helper for FFmpeg Patchwork CI
+    Provides simplified interface for database operations
+    """
+
+    def __init__(self, config_db):
+        """
+        Initialize the SQLite database
+
+        Args:
+            db_path: Path to the SQLite database file
+        """
+        self.db_path = config_db["db_path"]
+        self.connection = None
+        self._connect()
+
+    def _connect(self):
+        """Create a new database connection"""
+        # Ensure directory exists
+        os.makedirs(os.path.dirname(os.path.abspath(self.db_path)), exist_ok=True)
+        self.connection = sqlite3.connect(self.db_path)
+        self.connection.row_factory = sqlite3.Row
+
+    def get_cursor(self):
+        """Get a database cursor, reconnecting if necessary"""
+        try:
+            # Test connection
+            self.connection.execute("SELECT 1")
+        except (sqlite3.Error, AttributeError):
+            # Reconnect if connection is lost or was never established
+            self._connect()
+
+        return self.connection.cursor()
+
+    def create_missing_table(self, name, columns):
+        """
+        Create a table if it doesn't exist
+
+        Args:
+            name: Table name
+            columns: SQL column definitions as a string
+        """
+        cursor = self.get_cursor()
+        cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name=?", (name,))
+        if cursor.fetchone() is not None:
+            # print(f"Table {name} already exists")
+            return
+
+        query = f"CREATE TABLE {name} {columns}"
+        # print(query)
+        cursor.execute(query)
+        self.connection.commit()
+        return
+
+    def query(self, table_name, keys, filter_command=""):
+        """
+        Execute a SELECT query and return the first matching row
+
+        Args:
+            table_name: Table to query
+            keys: List of column names to select
+            filter_command: WHERE clause and other SQL filters
+
+        Returns:
+            Dict containing the query results, or empty dict if no match
+        """
+        cursor = self.get_cursor()
+
+        str_cols = ", ".join(keys)
+        sql_query = f"SELECT {str_cols} FROM {table_name} {filter_command}"
+        # print(sql_query)
+        cursor.execute(sql_query)
+        db_out = cursor.fetchone()
+        out = {}
+        if not db_out:
+            return out
+
+        for k in keys:
+            out[k] = db_out[k]
+        return out
+
+    def query_all(self, table_name, keys, filter_command=""):
+        """
+        Execute a SELECT query and return all matching rows
+
+        Args:
+            table_name: Table to query
+            keys: List of column names to select
+            filter_command: WHERE clause and other SQL filters
+
+        Returns:
+            List of dicts containing the query results
+        """
+        cursor = self.get_cursor()
+
+        str_cols = ", ".join(keys)
+        sql_query = f"SELECT {str_cols} FROM {table_name} {filter_command}"
+        # print(sql_query)
+        cursor.execute(sql_query)
+        db_out = cursor.fetchall()
+
+        results = []
+        for row in db_out:
+            out = {}
+            for k in keys:
+                out[k] = row[k]
+            results.append(out)
+        return results
+
+    def insert(self, table, key_value_dict):
+        """
+        Insert a new row into a table
+
+        Args:
+            table: Table name
+            key_value_dict: Dict mapping column names to values
+        """
+        cursor = self.get_cursor()
+
+        keys = list(key_value_dict.keys())
+        values = list(key_value_dict.values())
+
+        placeholders = ", ".join(["?" for _ in keys])
+        keys_str = ", ".join(keys)
+
+        sql_request = f'INSERT INTO {table} ({keys_str}) VALUES ({placeholders})'
+        # print(f"{sql_request} with values {values}")
+        cursor.execute(sql_request, values)
+        self.connection.commit()
+        return cursor.lastrowid
+
+    def update(self, table, ref_key, ref_value, keys, values):
+        """
+        Update existing rows in a table
+
+        Args:
+            table: Table name
+            ref_key: List of column names to use in WHERE clause
+            ref_value: List of values corresponding to ref_key
+            keys: List of column names to update
+            values: List of new values corresponding to keys
+        """
+        cursor = self.get_cursor()
+
+        set_clauses = [f"{k} = ?" for k in keys]
+        where_clauses = [f"{k} = ?" for k in ref_key]
+
+        str_set = ", ".join(set_clauses)
+        str_where = " AND ".join(where_clauses)
+
+        sql_request = f'UPDATE {table} SET {str_set} WHERE {str_where}'
+        # print(f"{sql_request} with values {values + ref_value}")
+        cursor.execute(sql_request, values + ref_value)
+        self.connection.commit()
+
+    def close(self):
+        """Close the database connection"""
+        if self.connection:
+            self.connection.close()
+            self.connection = None

commit a1f4a250a7b4043c9aa313193529138c1877853f
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Jun 8 18:42:09 2025 -0700
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:41:38 2025 -0700

    Remove direct call to internals of MySQL close

diff --git a/mysql_helper.py b/mysql_helper.py
index f0adb55..de0abd2 100644
--- a/mysql_helper.py
+++ b/mysql_helper.py
@@ -98,3 +98,6 @@ class SQLDatabase():
         print (sql_request)
         cursor.execute(sql_request)
         self.mydb.commit()
+
+    def close(self):
+        self.mydb.close()
diff --git a/patchwork_runner.py b/patchwork_runner.py
index d5f3e26..8d6fc8b 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -388,4 +388,4 @@ if __name__ == "__main__":
             print ("No patches, sleeping for 5 minutes")
             time.sleep(60*5)
         end_time = time.time()
-    mydb.mydb.close()
+    mydb.close()

commit f0a2c4488c4dbd3c011c512e76c31f21bc24ad6d
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Thu Mar 10 00:30:39 2022 -0500
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:41:38 2025 -0700

    Use mariadb connector instead mysql-connector

diff --git a/mysql_helper.py b/mysql_helper.py
index eafe8d4..f0adb55 100644
--- a/mysql_helper.py
+++ b/mysql_helper.py
@@ -1,4 +1,4 @@
-import mysql.connector
+import mariadb
 import time
 import threading
 
@@ -10,16 +10,16 @@ class SQLDatabase():
 
     def init_db(self):
         if self.config["ssl"]["enabled"]:
-            return mysql.connector.connect(host=self.config["host"], user=self.config["user"],
+            return mariadb.connect(host=self.config["host"], user=self.config["user"],
                     database=self.config["db_name"], ssl_ca=self.config["ssl"]["ssl_ca"],
                     ssl_cert=self.config["ssl"]["ssl_cert"], ssl_key=self.config["ssl"]["ssl_key"])
         else:
-            return mysql.connector.connect(host=self.config["host"], user=self.config["user"],
+            return mariadb.connect(host=self.config["host"], user=self.config["user"],
                     password=self.config["password"], database=self.config["db_name"])
 
     def get_cursor(self):
         try:
-            self.mydb.ping(reconnect=True, attempts=3, delay=5)
+            self.mydb.ping()
         except:
             self.mydb = self.init_db()
 

commit e47da1e3f463403e5dbd9f461a296f7de21443c1
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Feb 27 23:30:47 2022 -0500
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:41:38 2025 -0700

    Add option to connect to mysql server with ssl

diff --git a/config.yaml b/config.yaml
index c6129a5..1bf46bf 100644
--- a/config.yaml
+++ b/config.yaml
@@ -24,6 +24,11 @@ db:
     user: "user"
     password: "password"
     db_name: "mysql"
+    ssl:
+        enabled: False
+        ssl_ca: "ssl_keys/ca-cert.pem"
+        ssl_cert: "ssl_keys/client-cert.pem"
+        ssl_key: "ssl_keys/client-key.pem"
 
 patchwork:
     host: "patchwork.ffmpeg.org"
diff --git a/mysql_helper.py b/mysql_helper.py
index 2ab87ee..eafe8d4 100644
--- a/mysql_helper.py
+++ b/mysql_helper.py
@@ -9,8 +9,13 @@ class SQLDatabase():
         self.mydb = self.init_db()
 
     def init_db(self):
-        return mysql.connector.connect(host=self.config["host"], user=self.config["user"],
-                password=self.config["password"], database=self.config["db_name"])
+        if self.config["ssl"]["enabled"]:
+            return mysql.connector.connect(host=self.config["host"], user=self.config["user"],
+                    database=self.config["db_name"], ssl_ca=self.config["ssl"]["ssl_ca"],
+                    ssl_cert=self.config["ssl"]["ssl_cert"], ssl_key=self.config["ssl"]["ssl_key"])
+        else:
+            return mysql.connector.connect(host=self.config["host"], user=self.config["user"],
+                    password=self.config["password"], database=self.config["db_name"])
 
     def get_cursor(self):
         try:
diff --git a/patchwork_runner.py b/patchwork_runner.py
index 3ca8b1e..d5f3e26 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -193,7 +193,7 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
 
     patch_list = list()
 
-    headers = {"Authorization" : "Token %s" % config_pw["token"], "Host": config_pw["token"]}
+    headers = {"Authorization" : "Token: %s" % config_pw["token"], "Host": config_pw["host"]}
 
     utc_time = datetime.utcnow()
     utc_time = utc_time - relativedelta(minutes = time_interval)
@@ -203,7 +203,6 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
     url = "https://" + config_pw["host"] + url_request
 
     resp = requests.get(url, headers = headers)
-    print (resp)
     reply_list = json.loads(resp.content)
 
     for reply in reply_list:
@@ -360,7 +359,7 @@ if __name__ == "__main__":
 
     jobs_list = list()
 
-    for name, config_runner in config["runner"].items():
+    for name, config_runner in config["runners"].items():
         jobs_list.append(Job(name, config_runner))
 
     # when the db is first setup there are no tables. so init them

commit 995c010fa21279be55ae04896cb9a71cf11b3334
Author:     Andriy Gelman <andriy.gelman at gmail.com>
AuthorDate: Sun Feb 27 17:59:52 2022 -0500
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sun Jun 8 21:41:31 2025 -0700

    Use a yaml config file instead of environment variables
    
    Also add example config file config.yaml.

diff --git a/README b/README
index 9793c0a..e7f3c40 100644
--- a/README
+++ b/README
@@ -18,9 +18,8 @@ recomended for isolating the process.
 
 --- Caching results ---
 The code currently uses a mysql database to track information and cache job
-results. The config of the database are set by the environment variables:
-PATCHWORK_DB_{HOST,USER,PASSWORD}. Multiple runners on different physical
-machines may share a common mysql database or use a dedicated database. Sharing
+results. The database client settings are defined in the config yaml file. Multiple runners
+on different physical machines may share a common mysql database or use a dedicated database. Sharing
 a common database allows to check whether an automated email was already
 sent for a series.
 
@@ -30,31 +29,27 @@ link to the patchwork site where the warning or error is shown. To prevent
 spamming the author, only one email is triggered per patch series. An email is
 also only sent if the parent commit builds successfully. Thus if current
 origin/master doesn't build, an email will not be sent (unless a commit fixes
-the issue and breaks it another commit of the series). The environment variables
-for connecting to an SMTP server are
-PATCHWORK_SMTP_{HOST,PORT}, PATCHWORK_{USER_EMAIL,PASSWORD_EMAIL}.
-The environment variable PATCHWORK_CC_EMAIL is used to add a cc email address.
+the issue and breaks it another commit of the series). The client configs for
+connecting to the smtp server are set in the config yaml file in the section
+"smtp".
 
 --- Patchwork authentication ---
 An account (on https://patchwork.ffmpeg.org) and proper permission are needed to post
 CI results back to the patchwork site. Email your patchwork site maintainer in
 (FFmpeg/MAINTERNERS) with your username if you want the permissions added to your account.
 After the permissions are set up, an API token can be obtained after logging in
-to the patchwork site. The environment variable PATCHWORK_TOKEN stores the api
-token. The variable PATCHWORK_HOST needs to be set to patchwork.ffmpeg.org or
-another patchwork site.
+to the patchwork site. The section "patchwork" in config yaml file stores the
+host and token for connecting to the patchwork site.
 
--- Other environemnt variables --
-The following variables are used by the docker container for the CI job:
-PATCHWORK_{UID,GID} set the uid/gid of the docker container.
-PATCHWORK_PROJECT_ROOT_PATH is the path to the main FFmpeg directory.
+-- Other yaml config sections --
+Section "runners" stores the CI jobs to run.
 
 -- Steps to setup a CI job runner --
 1. Create an account on patchwork.ffmpeg.org and email your patchwork maintainer to setup
 permissions.
 2. Modify class Job in job.py for your CI job setup.
-3. Export the environment variables described above.
+3. Set the configurations in the config.yaml file.
 4. Start a mysql server instance, i.e. using docker:
 $ docker run --name "ffmpeg_mysql" -e MYSQL_ROOT_PASSWORD=${PATCHWORK_DB_PASSWORD} --rm -v sqldb:/var/lib/mysql -p 3306:3306 mysql:5.7
 5. Start the job runner with:
-$ python patchwork_runner.py
+$ python patchwork_runner.py config.yaml
diff --git a/config.yaml b/config.yaml
new file mode 100644
index 0000000..c6129a5
--- /dev/null
+++ b/config.yaml
@@ -0,0 +1,43 @@
+runners:
+    x86:
+        wd: "/home/user/ffmpeg_sources/ffmpeg"
+        uid: 1001
+        gid: 1001
+        docker_image: "ffmpeg_build:latest"
+        setup_command: "source run_configure"
+        build_flags: "-j44"
+        fate_flags: "-k -j44"
+        run_full_series: True
+
+    ppc:
+        wd: "/home/user/ffmpeg_sources/ffmpeg"
+        uid: 1001
+        gid: 1001
+        docker_image: "ffmpeg_build_ppc:latest"
+        setup_command: "source run_configure"
+        build_flags: "-j44"
+        fate_flags: "-k -j44"
+        run_full_series: True
+
+db:
+    host: "127.0.0.1"
+    user: "user"
+    password: "password"
+    db_name: "mysql"
+
+patchwork:
+    host: "patchwork.ffmpeg.org"
+    token: "abcdefg12345"
+
+    smtp:
+        enabled: True
+        host: "smtp.gmail.com"
+        port: 587
+        user: "user at gmail.com"
+        password: "password"
+        cc_email: "ccemail at gmail.com"
+        proxy:
+            enabled: False
+            cmd: "ssh -f -D 2345 -p 5678 user at 127.0.0.1 sleep 10"
+            proxy_addr: "localhost"
+            proxy_port: 2345
diff --git a/mysql_helper.py b/mysql_helper.py
index d53f42c..2ab87ee 100644
--- a/mysql_helper.py
+++ b/mysql_helper.py
@@ -4,14 +4,13 @@ import threading
 
 class SQLDatabase():
 
-    def __init__(self, host, user, password):
-        self.host = host
-        self.user = user
-        self.password = password
+    def __init__(self, config_db):
+        self.config = config_db
         self.mydb = self.init_db()
 
     def init_db(self):
-        return mysql.connector.connect(host=self.host, user=self.user, password=self.password, database="mysql")
+        return mysql.connector.connect(host=self.config["host"], user=self.config["user"],
+                password=self.config["password"], database=self.config["db_name"])
 
     def get_cursor(self):
         try:
diff --git a/patchwork_runner.py b/patchwork_runner.py
index d701b53..3ca8b1e 100644
--- a/patchwork_runner.py
+++ b/patchwork_runner.py
@@ -1,6 +1,5 @@
 import email
 import json
-import os
 import re
 import requests
 import smtplib
@@ -9,6 +8,7 @@ import subprocess
 import sys
 import time
 import urllib.parse
+import yaml
 
 from commit_message_filter import check_commit_message
 from datetime import datetime, timezone
@@ -18,32 +18,7 @@ from job import Job
 from mysql_helper import SQLDatabase
 from proxy_smtplib import ProxySMTP
 
-env = os.environ
-use_proxy = int(env["PATCHWORK_USE_PROXY"])
-socks_dynamic_port = int(env["PATCHWORK_SOCKS_DYNAMIC_PORT"])
-proxy_host = env["PATCHWORK_PROXY_HOST"]
-socks_proxy_uname = env["PATCHWORK_SOCKS_PROXY_UNAME"]
-socks_proxy_ip = env["PATCHWORK_SOCKS_PROXY_IP"]
-socks_proxy_port = int(env["PATCHWORK_SOCKS_PROXY_PORT"])
-
-db_host = env["PATCHWORK_DB_HOST"]
-db_user = env["PATCHWORK_DB_USER"]
-db_password = env["PATCHWORK_DB_PASSWORD"]
-
-smtp_host = env["PATCHWORK_SMTP_HOST"]
-smtp_port = int(env["PATCHWORK_SMTP_PORT"])
-user_email = env["PATCHWORK_USER_EMAIL"]
-cc_email = env["PATCHWORK_CC_EMAIL"]
-password_email = env["PATCHWORK_PASSWORD_EMAIL"]
-
-uid = int(env["PATCHWORK_UID"])
-gid = int(env["PATCHWORK_GID"])
-
-patchwork_token = env["PATCHWORK_TOKEN"]
-patchwork_host = env["PATCHWORK_HOST"]
-project_root_path = env["PATCHWORK_PROJECT_ROOT_PATH"]
-
-def post_check(check_url, type_check, context, msg_short, msg_long):
+def post_check(check_url, type_check, context, msg_short, msg_long, config_pw):
 
     if (isinstance(msg_long, bytes)):
         split_char = b'\n'
@@ -60,28 +35,28 @@ def post_check(check_url, type_check, context, msg_short, msg_long):
 
     msg_long = split_char.join(msg_long_split)
 
-    headers = {"Authorization" : "Token %s" % patchwork_token}
+    headers = {"Authorization" : "Token %s" % config_pw["token"]}
     payload = {"state" : type_check, "context" : context, "description" : msg_short, "description_long" : msg_long}
     resp = requests.post(check_url, headers=headers, data=payload)
     print(resp)
     print(resp.content)
 
-def submit_job_result(mydb, job, job_result, check_url):
+def submit_job_result(mydb, job, job_result, check_url, config_pw):
 
     if job_result["setup_success"] == 0:
-        post_check(check_url, "warning", "configure_" + job.name, "Failed to run configure", job_result["setup_log"])
+        post_check(check_url, "warning", "configure_" + job.name, "Failed to run configure", job_result["setup_log"], config_pw)
         return
 
     if job_result["build_success"] == 1:
-        post_check(check_url, "success", "make_" + job.name, "Make finished", b'')
+        post_check(check_url, "success", "make_" + job.name, "Make finished", b'', config_pw)
     else:
-        post_check(check_url, "fail", "make_" + job.name, "Make failed", job_result["build_log"])
+        post_check(check_url, "fail", "make_" + job.name, "Make failed", job_result["build_log"], config_pw)
         return
 
     if job_result["unit_test_success"] == 1:
-        post_check(check_url, "success", "make_fate_" + job.name, "Make fate finished", b'')
+        post_check(check_url, "success", "make_fate_" + job.name, "Make fate finished", b'', config_pw)
     else:
-        post_check(check_url, "fail", "make_fate_" + job.name, "Make fate failed", job_result["unit_test_log"])
+        post_check(check_url, "fail", "make_fate_" + job.name, "Make fate failed", job_result["unit_test_log"], config_pw)
         return
 
 
@@ -138,7 +113,7 @@ def run_job(mydb, commit_hash, job):
     mydb.insert(job.name, job_result)
     return job_result
 
-def notify_by_email(mydb, patch):
+def notify_by_email(mydb, patch, config_smtp):
 
     print ("Sending email notification")
 
@@ -146,8 +121,11 @@ def notify_by_email(mydb, patch):
     keys.append("email_sent")
 
     series_id = patch["series_id"]
-    res = mydb.query("series", keys, "WHERE series_id = %d" % series_id)
-    email_sent = res["email_sent"]
+    email_sent = False
+    if mydb is not None:
+        res = mydb.query("series", keys, "WHERE series_id = %d" % series_id)
+        email_sent = res["email_sent"]
+
     if email_sent:
         return
 
@@ -161,27 +139,28 @@ def notify_by_email(mydb, patch):
     msg_email = EmailMessage()
     msg_email.set_content(msg)
     msg_email["Subject"] = "Re: " + patch["subject_email"]
-    msg_email["From"] = "Patchwork <%s>" % user_email
+    msg_email["From"] = "Patchwork <%s>" % config_smtp["user"]
     msg_email["To"] = patch["author_email"]
-    msg_email["Cc"] = cc_email
+    msg_email["Cc"] = config_smtp["cc_email"]
     msg_email["In-Reply-To"] = patch["msg_id"]
     msg_email["References"] = patch["msg_id"]
 
-    print ("Proxy is %d" % use_proxy)
-    if use_proxy == 1:
+    config_proxy = config_smtp["proxy"]
+    print ("Proxy is %d" % config_proxy["enabled"])
+    if config_proxy["enabled"]:
         print ("Using proxy")
-        proxy_setup_cmd = "ssh -f -D %d -p %d %s@%s sleep 10" % (socks_dynamic_port, socks_proxy_port, socks_proxy_uname, socks_proxy_ip)
-        ret = subprocess.run(proxy_setup_cmd, shell=True)
-        smtp = ProxySMTP(smtp_host, smtp_port, proxy_addr = proxy_host, proxy_port = socks_dynamic_port)
+        ret = subprocess.run(config_proxy["cmd"], shell=True)
+        smtp = ProxySMTP(config_smtp["host"], config_smtp["port"], proxy_addr = config_proxy["proxy_addr"], proxy_port = config_proxy["proxy_port"])
     else:
-        smtp = smtplib.SMTP(smtp_host, smtp_port)
+        smtp = smtplib.SMTP(config_smtp["host"], config_smtp["port"])
 
     smtp.starttls()
-    smtp.login(user_email, password_email)
+    smtp.login(config_smtp["user"], config_smtp["password"])
     smtp.sendmail(msg_email["From"], msg_email["To"], msg_email.as_string())
     smtp.quit()
 
-    mydb.update("series", ["series_id"], ["%d " % series_id], ["email_sent"], ["1"])
+    if mydb is not None:
+        mydb.update("series", ["series_id"], ["%d " % series_id], ["email_sent"], ["1"])
 
 def regex_version_and_commit(subject):
     subject_clean_re = re.compile('\[[^]]*\]\s+(\[[^]]*\])')
@@ -210,18 +189,18 @@ def regex_version_and_commit(subject):
 
     return version_num, commit_entry_num, commit_entry_den
 
-def fetch_and_process_patches(mydb, jobs_list, time_interval):
+def fetch_and_process_patches(mydb, jobs_list, time_interval, config_pw):
 
     patch_list = list()
 
-    headers = {"Authorization" : "Token %s" % patchwork_token, "Host": patchwork_host}
+    headers = {"Authorization" : "Token %s" % config_pw["token"], "Host": config_pw["token"]}
 
     utc_time = datetime.utcnow()
     utc_time = utc_time - relativedelta(minutes = time_interval)
     str_time = utc_time.strftime("%Y-%m-%dT%H:%M:%S")
     str_time = urllib.parse.quote(str_time)
     url_request = "/api/events/?category=patch-completed&since=" + str_time
-    url = "https://" + patchwork_host + url_request
+    url = "https://" + config_pw["host"] + url_request
 
     resp = requests.get(url, headers = headers)
     print (resp)
@@ -275,13 +254,13 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval):
         if not res:
             mydb.insert("series", {"series_id" : "%d" % series_id, "email_sent" : 0})
 
-    git_cmd_template = "git --git-dir=%s/.git --work-tree=%s " % (project_root_path, project_root_path)
 
     print ("Number of patches in list: %d" % len(patch_list))
 
     for patch in patch_list:
 
         job = patch["job"]
+        git_cmd_template = "git --git-dir=%s/.git --work-tree=%s " % (job.config["wd"], job.config["wd"])
         _, commit_num, commit_den = regex_version_and_commit(patch["subject_email"])
         if job.config["run_full_series"] == False and commit_num != commit_den:
             continue
@@ -301,7 +280,7 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval):
         max_retries = 10
         retries = 0
         while 1:
-            ret = subprocess.run("curl %s/?series=%d > %s/mbox_file" % (patch["mbox"], patch["series_id"], project_root_path), shell=True)
+            ret = subprocess.run("curl %s/?series=%d > %s/mbox_file" % (patch["mbox"], patch["series_id"], job.config["wd"]), shell=True)
             if ret.returncode == 0 or retries == max_retries:
                 break
             retries = retries + 1
@@ -319,10 +298,10 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval):
                 git_cmd = git_cmd_template + "am --keep-cr --skip"
                 ret = subprocess.run(git_cmd, capture_output=True, shell=True)
                 if ret.returncode != 0:
-                    post_check(patch["check_url"], "warning", "configure_" + job.name, "Failed to apply patch", "")
+                    post_check(patch["check_url"], "warning", "configure_" + job.name, "Failed to apply patch", "", config_pw)
                     continue
             else:
-                post_check(patch["check_url"], "warning", "configure_" + job.name, "Failed to apply patch", ret.stderr)
+                post_check(patch["check_url"], "warning", "configure_" + job.name, "Failed to apply patch", ret.stderr, config_pw)
                 continue
 
         # check commit message
@@ -332,8 +311,8 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval):
         warn = check_commit_message(commit_msg)
         if warn:
             print (warn)
-            post_check(patch["check_url"], "warning", "commit_msg_" + job.name, warn, "")
-            notify_by_email(mydb, patch)
+            post_check(patch["check_url"], "warning", "commit_msg_" + job.name, warn, "", config_pw)
+            notify_by_email(mydb, patch, config_pw["smtp"])
 
         git_cmd = git_cmd_template + " rev-parse master"
         ret = subprocess.run(git_cmd, capture_output=True, shell=True)
@@ -341,7 +320,7 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval):
         current_hash = current_hash[0:40]
         print ("Current hash %s" % current_hash)
         job_result = run_job(mydb, current_hash, job)
-        submit_job_result(mydb, job, job_result, patch["check_url"])
+        submit_job_result(mydb, job, job_result, patch["check_url"], config_pw)
 
         #  get the hash of HEAD~
         git_cmd = git_cmd_template + " rev-parse master~"
@@ -354,48 +333,35 @@ def fetch_and_process_patches(mydb, jobs_list, time_interval):
         job_result_prev = run_job(mydb, prev_hash, job)
 
         if job_result["number_of_warnings"] > job_result_prev["number_of_warnings"]:
-            post_check(patch["check_url"], "warning", "make_" + job.name, "New warnings during build", "")
+            post_check(patch["check_url"], "warning", "make_" + job.name, "New warnings during build", "", config_pw)
 
         if job_result["setup_success"] == 0 and job_result_prev["setup_success"] == 1:
-            notify_by_email(mydb, patch)
+            notify_by_email(mydb, patch, config_pw["smtp"])
 
         if job_result['build_success'] == 0 and job_result_prev['build_success'] == 1:
-            notify_by_email(mydb, patch)
+            notify_by_email(mydb, patch, config_pw["smtp"])
 
         if job_result['unit_test_success'] == 0 and job_result_prev['unit_test_success'] == 1:
-            notify_by_email(mydb, patch)
+            notify_by_email(mydb, patch, config_pw["smtp"])
 
     return patch_list
 
 if __name__ == "__main__":
 
+    if len(sys.argv) != 2:
+        print("Usage:\n $ python3 patchwork_runner.py config.yaml")
+        sys.exit(1)
+
+    with  open(sys.argv[1], 'r') as file:
+        config = yaml.safe_load(file)
+
     # local database for storing cached job results
-    mydb = SQLDatabase(db_host, db_user, db_password)
+    mydb = SQLDatabase(config["db"])
 
     jobs_list = list()
 
-    # setup configuration
-    config_x86 = dict()
-    config_x86["wd"]            = project_root_path
-    config_x86["docker_image"]  = "ffmpeg_build:latest"
-    config_x86["setup_command"] = "source run_configure"
-    config_x86["build_flags"]   = "-j44"
-    config_x86["fate_flags"]    = "-k -j44"
-    config_x86["uid"]           = uid
-    config_x86["gid"]           = gid
-    config_x86["run_full_series"] = True
-    jobs_list.append(Job("x86", config_x86))
-
-    config_ppc = dict()
-    config_ppc["wd"]            = project_root_path
-    config_ppc["docker_image"]  = "ffmpeg_build_ppc:latest"
-    config_ppc["setup_command"] = "source run_configure_ppc"
-    config_ppc["build_flags"]   = "-j44"
-    config_ppc["fate_flags"]    = "-k -j44"
-    config_ppc["uid"]           = uid
-    config_ppc["gid"]           = gid
-    config_ppc["run_full_series"] = True
-    jobs_list.append(Job("ppc", config_ppc))
+    for name, config_runner in config["runner"].items():
+        jobs_list.append(Job(name, config_runner))
 
     # when the db is first setup there are no tables. so init them
     for job in jobs_list:
@@ -418,7 +384,7 @@ if __name__ == "__main__":
     while 1:
         time_interval = (end_time - start_time) / 60 + 10
         start_time = time.time()
-        patch_list = fetch_and_process_patches(mydb, jobs_list, time_interval)
+        patch_list = fetch_and_process_patches(mydb, jobs_list, time_interval, config["patchwork"])
         if not patch_list:
             print ("No patches, sleeping for 5 minutes")
             time.sleep(60*5)
diff --git a/test_send_email.py b/test_send_email.py
index 25e12bd..14cdf30 100644
--- a/test_send_email.py
+++ b/test_send_email.py
@@ -1,62 +1,27 @@
-import os
 import smtplib
 import socks
 import subprocess
+import sys
+import yaml
 
 from email.message import EmailMessage
+from patchwork_runner import notify_by_email
 from proxy_smtplib import ProxySMTP
 
-env = os.environ
-use_proxy = int(env["PATCHWORK_USE_PROXY"])
-socks_dynamic_port = int(env["PATCHWORK_SOCKS_DYNAMIC_PORT"])
-proxy_host = env["PATCHWORK_PROXY_HOST"]
-socks_proxy_uname = env["PATCHWORK_SOCKS_PROXY_UNAME"]
-socks_proxy_ip = env["PATCHWORK_SOCKS_PROXY_IP"]
-socks_proxy_port = int(env["PATCHWORK_SOCKS_PROXY_PORT"])
-
-db_host = env["PATCHWORK_DB_HOST"]
-db_user = env["PATCHWORK_DB_USER"]
-db_password = env["PATCHWORK_DB_PASSWORD"]
-
-smtp_host = env["PATCHWORK_SMTP_HOST"]
-smtp_port = int(env["PATCHWORK_SMTP_PORT"])
-user_email = env["PATCHWORK_USER_EMAIL"]
-cc_email = env["PATCHWORK_CC_EMAIL"]
-password_email = env["PATCHWORK_PASSWORD_EMAIL"]
-
-uid = int(env["PATCHWORK_UID"])
-gid = int(env["PATCHWORK_GID"])
-
-patchwork_token = env["PATCHWORK_TOKEN"]
-patchwork_host = env["PATCHWORK_HOST"]
-project_root_path = env["PATCHWORK_PROJECT_ROOT_PATH"]
-
-def send_email_test():
-    msg = ("Hello,\n\n"
-           "Thank you for submitting a patch to ffmpeg-devel.\n"
-           "An error occurred during an automated build/fate test. Please review the following link for more details:\n"
-           "%s\n\n"
-           "Thank you,\n"
-           "ffmpeg-devel") % "this is a test"
-
-    msg_email = EmailMessage()
-    msg_email.set_content(msg)
-    msg_email["Subject"] = "hi"
-    msg_email["From"] = "Patchwork <%s>" % user_email
-    msg_email["To"] = user_email
-
-    if use_proxy == 1:
-        print ("Using proxy")
-        proxy_setup_cmd = "ssh -f -D %d -p %d %s@%s sleep 10" % (socks_dynamic_port, socks_proxy_port, socks_proxy_uname, socks_proxy_ip)
-        ret = subprocess.run(proxy_setup_cmd, shell=True)
-        smtp = ProxySMTP(smtp_host, smtp_port, proxy_addr = proxy_host, proxy_port = socks_dynamic_port)
-    else:
-        smtp = smtplib.SMTP(smtp_host, smtp_port)
-
-    smtp.starttls()
-    smtp.login(user_email, password_email)
-    smtp.sendmail(msg_email["From"], msg_email["To"], msg_email.as_string())
-    smtp.quit()
-
 if __name__ == "__main__":
-    send_email_test()
+
+    if len(sys.argv) != 2:
+        print("Usage:\n $ python3 patchwork_runner.py config.yaml")
+        sys.exit(1)
+
+    with  open(sys.argv[1], 'r') as file:
+        config = yaml.safe_load(file)
+
+    sample_patch = {
+            "series_id" : 1234,
+            "msg_id" : "",
+            "mbox" : "https://example.com/mbox/",
+            "author_email" : "andriy.gelman at gmail.com",
+            "subject_email" : "patchwork: this is a test email",
+            }
+    notify_by_email(None, sample_patch, config["patchwork"]["smtp"])

commit 1c64a6a32fc0372f95851c084cbda1eb5e630e13
Author:     Shiyou Yin <yinshiyou-hf at loongson.cn>
AuthorDate: Thu Feb 10 19:24:54 2022 +0800
Commit:     Andriy Gelman <andriy.gelman at gmail.com>
CommitDate: Sat Feb 26 17:51:30 2022 -0500

    Fix TypeError during mydb.ping.
    
    TypeError: ping() got an unexpected keyword argument 'attemts'.

diff --git a/mysql_helper.py b/mysql_helper.py
index 3eb1f50..d53f42c 100644
--- a/mysql_helper.py
+++ b/mysql_helper.py
@@ -15,7 +15,7 @@ class SQLDatabase():
 
     def get_cursor(self):
         try:
-            self.mydb.ping(reconnect=True, attemts=3, delay=5)
+            self.mydb.ping(reconnect=True, attempts=3, delay=5)
         except:
             self.mydb = self.init_db()
 

-----------------------------------------------------------------------

Summary of changes:
 README              |  33 ++++-----
 config_mysql.yaml   |  49 +++++++++++++
 config_sqlite.yaml  |  41 +++++++++++
 mysql_helper.py     |  21 ++++--
 patchwork_runner.py | 205 ++++++++++++++++++++++++----------------------------
 sqlite_helper.py    | 166 ++++++++++++++++++++++++++++++++++++++++++
 test_send_email.py  |  73 +++++--------------
 7 files changed, 396 insertions(+), 192 deletions(-)
 create mode 100644 config_mysql.yaml
 create mode 100644 config_sqlite.yaml
 create mode 100644 sqlite_helper.py


hooks/post-receive
-- 
UNNAMED PROJECT


More information about the ffmpeg-cvslog mailing list