Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2026-03-13 21:16:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Fri Mar 13 21:16:57 2026 rev:399 rq:1338590 version:5.0.0+20260313.6235a908

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2026-03-11 
20:57:30.947358707 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.8177/crmsh.changes    2026-03-13 
21:21:11.264191042 +0100
@@ -1,0 +2,19 @@
+Fri Mar 13 04:23:58 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260313.6235a908:
+  * Dev: sh: Use sh_helper.py for su commands (bsc#1254757)
+
+-------------------------------------------------------------------
+Thu Mar 12 09:00:03 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260312.ad57a9db:
+  * Dev: testcases: remove traceback
+  * Fix: log: Disable color when not on a TTY (bsc#1259178)
+
+-------------------------------------------------------------------
+Wed Mar 11 07:47:27 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260311.dfa9856b:
+  * Dev: bootstrap: Skip inactive cluster node when calling restart_cluster 
function
+
+-------------------------------------------------------------------

Old:
----
  crmsh-5.0.0+20260309.5a3c6578.tar.bz2

New:
----
  crmsh-5.0.0+20260313.6235a908.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.Jl7rz3/_old  2026-03-13 21:21:11.984220744 +0100
+++ /var/tmp/diff_new_pack.Jl7rz3/_new  2026-03-13 21:21:11.988220909 +0100
@@ -41,7 +41,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        5.0.0+20260309.5a3c6578
+Version:        5.0.0+20260313.6235a908
 Release:        0
 URL:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.Jl7rz3/_old  2026-03-13 21:21:12.044223219 +0100
+++ /var/tmp/diff_new_pack.Jl7rz3/_new  2026-03-13 21:21:12.052223549 +0100
@@ -9,7 +9,7 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">5a3c65789ebe0308f5b38e20ec0c50ff80fafc04</param>
+  <param 
name="changesrevision">6235a908e9f921dcff4e366ac780fd4c1d86e9d3</param>
 </service>
 </servicedata>
 (No newline at EOF)

++++++ crmsh-5.0.0+20260309.5a3c6578.tar.bz2 -> 
crmsh-5.0.0+20260313.6235a908.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260309.5a3c6578/crmsh/bootstrap.py 
new/crmsh-5.0.0+20260313.6235a908/crmsh/bootstrap.py
--- old/crmsh-5.0.0+20260309.5a3c6578/crmsh/bootstrap.py        2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/crmsh/bootstrap.py        2026-03-13 
04:18:36.000000000 +0100
@@ -2876,8 +2876,17 @@
 
 
 def restart_cluster():
+    service_manager = ServiceManager()
+    node_list = utils.list_cluster_nodes()
+    for node in node_list[:]:
+        if not service_manager.service_is_active(constants.PCMK_SERVICE, 
remote_addr=node):
+            logger.warning("Cluster is inactive on %s, skip restarting cluster 
service on it", node)
+            node_list.remove(node)
+    if not node_list:
+        return
+
     logger.info("Restarting cluster service")
-    utils.cluster_run_cmd("crm cluster restart")
+    utils.cluster_run_cmd("crm cluster restart", node_list)
     wait_for_cluster()
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260309.5a3c6578/crmsh/log.py 
new/crmsh-5.0.0+20260313.6235a908/crmsh/log.py
--- old/crmsh-5.0.0+20260309.5a3c6578/crmsh/log.py      2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/crmsh/log.py      2026-03-13 
04:18:36.000000000 +0100
@@ -284,7 +284,7 @@
 NO_COLOR_FORMATTERS = {
     "console_report": {
         "()": LeveledFormatter,
-        "base_formatter_factory": logging.Formatter,
+        "base_formatter_factory": NoBacktraceFormatter,
         "default_fmt": "{}: %(levelname)s: 
%(message)s".format(socket.gethostname()),
         "level_fmt": {
             DEBUG2: "{}: %(levelname)s: %(funcName)s: 
%(message)s".format(socket.gethostname()),
@@ -292,7 +292,7 @@
     },
     "console": {
         "()": LeveledFormatter,
-        "base_formatter_factory": logging.Formatter,
+        "base_formatter_factory": NoBacktraceFormatter,
         "default_fmt": "%(levelname)s: %(message)s",
         "level_fmt": {
             DEBUG2: "%(levelname)s: %(funcName)s %(message)s",
@@ -568,10 +568,11 @@
     if os.environ.get('CRMSH_REGRESSION_TEST'):
         logging.setLoggerClass(NumberedLogger)
         LOGGING_CFG['formatters'] = NO_COLOR_FORMATTERS
-        logging.config.dictConfig(LOGGING_CFG)
     else:
         logging.setLoggerClass(NumberedLoggerInterface)
-        logging.config.dictConfig(LOGGING_CFG)
+    if not all(os.isatty(fd) for fd in range(3)):
+        LOGGING_CFG['formatters'] = NO_COLOR_FORMATTERS
+    logging.config.dictConfig(LOGGING_CFG)
 
 
 def setup_logger(name):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260309.5a3c6578/crmsh/prun/prun.py 
new/crmsh-5.0.0+20260313.6235a908/crmsh/prun/prun.py
--- old/crmsh-5.0.0+20260309.5a3c6578/crmsh/prun/prun.py        2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/crmsh/prun/prun.py        2026-03-13 
04:18:36.000000000 +0100
@@ -1,7 +1,9 @@
 # prun.py - run command or copy files on multiple hosts concurrently
 import os
 import random
+import shlex
 import socket
+import sys
 import tempfile
 import typing
 
@@ -13,6 +15,8 @@
 
 _DEFAULT_CONCURRENCY = 32
 
+_SH_HELPER = os.path.join(os.path.dirname(os.path.dirname(__file__)), 
'sh_helper.py')
+
 _SUDO_SFTP_SERVER_OPTION = '-s \'sudo --preserve-env=SSH_AUTH_SOCK 
PATH=/usr/lib/ssh:/usr/lib/openssh:/usr/libexec/ssh:/usr/libexec/openssh 
/bin/sh -c "exec sftp-server"\''
 
 
@@ -125,7 +129,9 @@
         if local_sudoer == crmsh.userdir.getuser():
             args = ['/bin/sh', '-c', shell]
         elif os.geteuid() == 0:
-            args = ['su', local_sudoer, '--login', '-c', shell, '-w', 
'SSH_AUTH_SOCK']
+            helper_args = [sys.executable, _SH_HELPER, '--preserve-env', 
'SSH_AUTH_SOCK', shell]
+            shell_cmd = ' '.join(shlex.quote(a) for a in helper_args)
+            args = ['su', '-s', '/bin/sh', '-w', 'SSH_AUTH_SOCK', '-c', 
shell_cmd, local_sudoer]
         else:
             raise AssertionError('trying to run su as a non-root user')
         return Task(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260309.5a3c6578/crmsh/sh.py 
new/crmsh-5.0.0+20260313.6235a908/crmsh/sh.py
--- old/crmsh-5.0.0+20260309.5a3c6578/crmsh/sh.py       2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/crmsh/sh.py       2026-03-13 
04:18:36.000000000 +0100
@@ -22,8 +22,10 @@
 import os
 import pwd
 import re
+import shlex
 import socket
 import subprocess
+import sys
 import typing
 from io import StringIO
 from functools import cache
@@ -36,6 +38,8 @@
 
 logger = logging.getLogger(__name__)
 
+_SH_HELPER = os.path.join(os.path.dirname(__file__), 'sh_helper.py')
+
 
 class Error(ValueError):
     def __init__(self, msg, cmd):
@@ -140,12 +144,19 @@
         if user is None or self.get_effective_user_name() == user:
             args = ['/bin/sh', '-c', cmd]
         elif 0 == self.geteuid():
-            args = ['su', user, '--login', '-s', '/bin/sh', '-c', cmd]
+            helper_args = [sys.executable, _SH_HELPER]
+            if self.preserve_env:
+                helper_args.extend(['--preserve-env', 
','.join(self.preserve_env)])
+            helper_args.append(cmd)
+            shell_cmd = ' '.join(shlex.quote(a) for a in helper_args)
+
+            args = ['su', '-s', '/bin/sh']
             if tty:
                 args.append('--pty')
             if self.preserve_env:
                 args.append('-w')
                 args.append(','.join(self.preserve_env))
+            args.extend(['-c', shell_cmd, user])
         else:
             raise AuthorizationError(
                 cmd, None, user,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260309.5a3c6578/crmsh/sh_helper.py 
new/crmsh-5.0.0+20260313.6235a908/crmsh/sh_helper.py
--- old/crmsh-5.0.0+20260309.5a3c6578/crmsh/sh_helper.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/crmsh/sh_helper.py        2026-03-13 
04:18:36.000000000 +0100
@@ -0,0 +1,63 @@
+import argparse
+import os
+import pwd
+import sys
+
+
+def main():
+    argument_parser = argparse.ArgumentParser(description="Helper script to 
run command in a clean environment")
+    argument_parser.add_argument('--preserve-env', help="Environment variables 
to preserve, separated by comma")
+    argument_parser.add_argument('command', help="Command to execute")
+    args = argument_parser.parse_args()
+
+    # Get user info for USER, LOGNAME, and HOME
+    pw = pwd.getpwuid(os.getuid())
+    user_name = pw.pw_name
+    user_home = pw.pw_dir
+
+    # Prepare a clean environment
+    new_env = {}
+
+    # 1. Keep TERM if it exists
+    if 'TERM' in os.environ:
+        new_env['TERM'] = os.environ['TERM']
+
+    # 2. Keep SHELL if it was initialized by su (or previous environment)
+    if 'SHELL' in os.environ:
+        new_env['SHELL'] = os.environ['SHELL']
+
+    # 3. Setup USER, LOGNAME and HOME
+    new_env['USER'] = user_name
+    new_env['LOGNAME'] = user_name
+    new_env['HOME'] = user_home
+
+    # Handle preserve_env
+    if args.preserve_env:
+        for var in args.preserve_env.split(','):
+            var = var.strip()
+            val = os.environ.get(var)
+            if val is not None:
+                new_env[var] = val
+
+    # Default PATH if not preserved
+    if 'PATH' not in new_env:
+        # A sensible default path
+        new_env['PATH'] = 
'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+    
+    # Change to user's home directory
+    try:
+        os.chdir(new_env['HOME'])
+    except Exception as e:
+        print(f"Warning: Could not change directory to {new_env['HOME']}: 
{e}", file=sys.stderr)
+        sys.exit(1)
+
+    # Replace the current process with /bin/bash -c <command>
+    try:
+        os.execve('/bin/bash', ['/bin/bash', '-c', args.command], new_env)
+    except Exception as e:
+        print(f"Failed to execute command: {e}", file=sys.stderr)
+        sys.exit(1)
+
+
+if __name__ == '__main__':
+    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260309.5a3c6578/test/features/bootstrap_bugs.feature 
new/crmsh-5.0.0+20260313.6235a908/test/features/bootstrap_bugs.feature
--- old/crmsh-5.0.0+20260309.5a3c6578/test/features/bootstrap_bugs.feature      
2026-03-09 15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/test/features/bootstrap_bugs.feature      
2026-03-13 04:18:36.000000000 +0100
@@ -295,3 +295,15 @@
     Then    Cluster service is "stopped" on "hanode1"
     And     Cluster service is "stopped" on "hanode2"
     And     Cluster service is "stopped" on "hanode3"
+
+  # skip non-root as non-root user cannot write to `/etc/profile`
+  @skip_non_root
+  @clean
+  Scenario: When '/etc/profile' prints garbages (bsc#1254757)
+    Given   Cluster service is "stopped" on "hanode1"
+    And     Cluster service is "stopped" on "hanode2"
+    When    Run "echo 'echo Boom!' > /etc/profile" on "hanode1,hanode2"
+    When    Run "crm cluster init -y" on "hanode1"
+    When    Run "crm cluster join -c hanode1 -y" on "hanode2"
+    Then    Cluster service is "started" on "hanode1"
+    Then    Cluster service is "started" on "hanode2"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260309.5a3c6578/test/testcases/edit.exp 
new/crmsh-5.0.0+20260313.6235a908/test/testcases/edit.exp
--- old/crmsh-5.0.0+20260309.5a3c6578/test/testcases/edit.exp   2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/test/testcases/edit.exp   2026-03-13 
04:18:36.000000000 +0100
@@ -71,24 +71,8 @@
 ERROR: 1: syntax in group: child p1 listed more than once in group g1 parsing 
'group g1 p1 p2 d3 p1'
 .INP: modgroup g1 remove c1
 ERROR: 39: configure.modgroup: c1 is not member of g1
-Traceback (most recent call last):
-    rv = self.execute_command() is not False
-         ~~~~~~~~~~~~~~~~~~~~^^
-    rv = self.command_info.function(*arglist)
-    context.fatal_error("%s is not member of %s" % (prim_id, group_id))
-    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    raise ValueError(msg)
-ValueError: c1 is not member of g1
 .INP: modgroup g1 remove nosuch
 ERROR: 40: configure.modgroup: nosuch is not member of g1
-Traceback (most recent call last):
-    rv = self.execute_command() is not False
-         ~~~~~~~~~~~~~~~~~~~~^^
-    rv = self.command_info.function(*arglist)
-    context.fatal_error("%s is not member of %s" % (prim_id, group_id))
-    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    raise ValueError(msg)
-ValueError: nosuch is not member of g1
 .INP: modgroup g1 add c1
 ERROR: 41: a group may contain only primitives; c1 is clone
 .INP: modgroup g1 add nosuch
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260309.5a3c6578/test/testcases/shadow.exp 
new/crmsh-5.0.0+20260313.6235a908/test/testcases/shadow.exp
--- old/crmsh-5.0.0+20260309.5a3c6578/test/testcases/shadow.exp 2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/test/testcases/shadow.exp 2026-03-13 
04:18:36.000000000 +0100
@@ -12,14 +12,6 @@
 INFO: 5: cib.commit: committed 'regtest' shadow CIB to the cluster
 .INP: delete regtest
 ERROR: 6: cib.delete: regtest shadow CIB is in use
-Traceback (most recent call last):
-    rv = self.execute_command() is not False
-        ~~~~~~~~~~~~~~~~~~~~^^
-    rv = self.command_info.function(*arglist)
-    context.fatal_error("%s shadow CIB is in use" % name)
-    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    raise ValueError(msg)
-ValueError: regtest shadow CIB is in use
 .INP: use
 .INP: delete regtest
 .EXT >/dev/null </dev/null crm_shadow -b -D 'regtest' --force
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260309.5a3c6578/test/unittests/test_prun.py 
new/crmsh-5.0.0+20260313.6235a908/test/unittests/test_prun.py
--- old/crmsh-5.0.0+20260309.5a3c6578/test/unittests/test_prun.py       
2026-03-09 15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/test/unittests/test_prun.py       
2026-03-13 04:18:36.000000000 +0100
@@ -1,4 +1,6 @@
 import typing
+import sys
+import shlex
 
 import crmsh.constants
 import crmsh.prun.prun
@@ -38,16 +40,26 @@
             mock.call("host1"),
             mock.call("host2"),
         ])
+        shell1 = 'ssh -A {} bob@host1 sudo -H 
/bin/bash'.format(crmsh.constants.SSH_OPTION)
+        helper_args1 = [sys.executable, crmsh.prun.prun._SH_HELPER, 
'--preserve-env', 'SSH_AUTH_SOCK', shell1]
+        shell_cmd1 = ' '.join(shlex.quote(a) for a in helper_args1)
+        args1 = ['su', '-s', '/bin/sh', '-w', 'SSH_AUTH_SOCK', '-c', 
shell_cmd1, 'alice']
+
+        shell2 = 'ssh -A {} bob@host2 sudo -H 
/bin/bash'.format(crmsh.constants.SSH_OPTION)
+        helper_args2 = [sys.executable, crmsh.prun.prun._SH_HELPER, 
'--preserve-env', 'SSH_AUTH_SOCK', shell2]
+        shell_cmd2 = ' '.join(shlex.quote(a) for a in helper_args2)
+        args2 = ['su', '-s', '/bin/sh', '-w', 'SSH_AUTH_SOCK', '-c', 
shell_cmd2, 'alice']
+
         mock_runner_add_task.assert_has_calls([
             mock.call(TaskArgumentsEq(
-                ['su', 'alice', '--login', '-c', 'ssh -A {} bob@host1 sudo -H 
/bin/bash'.format(crmsh.constants.SSH_OPTION), '-w', 'SSH_AUTH_SOCK'],
+                args1,
                 b'foo',
                 stdout=crmsh.prun.runner.Task.Capture,
                 stderr=crmsh.prun.runner.Task.Capture,
                 context={"host": 'host1', "ssh_user": 'bob'},
             )),
             mock.call(TaskArgumentsEq(
-                ['su', 'alice', '--login', '-c', 'ssh -A {} bob@host2 sudo -H 
/bin/bash'.format(crmsh.constants.SSH_OPTION), '-w', 'SSH_AUTH_SOCK'],
+                args2,
                 b'bar',
                 stdout=crmsh.prun.runner.Task.Capture,
                 stderr=crmsh.prun.runner.Task.Capture,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260309.5a3c6578/test/unittests/test_sh.py 
new/crmsh-5.0.0+20260313.6235a908/test/unittests/test_sh.py
--- old/crmsh-5.0.0+20260309.5a3c6578/test/unittests/test_sh.py 2026-03-09 
15:27:39.000000000 +0100
+++ new/crmsh-5.0.0+20260313.6235a908/test/unittests/test_sh.py 2026-03-13 
04:18:36.000000000 +0100
@@ -1,3 +1,6 @@
+import os
+import sys
+import shlex
 import subprocess
 import unittest
 import pickle
@@ -23,8 +26,11 @@
             'alice', 'foo',
             input=b'bar',
         )
+        sh_helper = os.path.join(os.path.dirname(crmsh.sh.__file__), 
'sh_helper.py')
+        helper_args = [sys.executable, sh_helper, 'foo']
+        shell_cmd = ' '.join(shlex.quote(a) for a in helper_args)
         mock_run.assert_called_once_with(
-            ['su', 'alice', '--login', '-s', '/bin/sh', '-c', 'foo'],
+            ['su', '-s', '/bin/sh', '-c', shell_cmd, 'alice'],
             input=b'bar',
             env=mock_environ,
         )

Reply via email to