kkpyutil 1.47.1__py3-none-any.whl → 1.49.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kkpyutil
3
- Version: 1.47.1
3
+ Version: 1.49.0
4
4
  Summary: Building blocks for sysadmin and DevOps
5
5
  Home-page: https://github.com/kakyoism/kkpyutil/
6
6
  License: MIT
@@ -0,0 +1,7 @@
1
+ kkpyutil.py,sha256=1iRFRACJJ87sA4ZimVznl4-caKzxB_VVkMnWiqVn_3U,121453
2
+ kkpyutil_helper/windows/kkttssave.ps1,sha256=xa3-WzqNh2rGYlOx_I4ewOuCE94gkTO5cEwYH0M67_0,446
3
+ kkpyutil_helper/windows/kkttsspeak.ps1,sha256=7WUUHMmjTQroUWA2Mvdt4JtSt475nZUHQx-qP-7rS6o,305
4
+ kkpyutil-1.49.0.dist-info/LICENSE,sha256=uISevGnCxB5QOU0ftbofN75_yUtd6E2h_MWE1zqagC8,1065
5
+ kkpyutil-1.49.0.dist-info/METADATA,sha256=4zhDruYqdoicThlEjVhbwFlAaDQzkr9fPTjZvjZ16-o,1136
6
+ kkpyutil-1.49.0.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
7
+ kkpyutil-1.49.0.dist-info/RECORD,,
kkpyutil.py CHANGED
@@ -2079,28 +2079,75 @@ def move_file(src, dst, isdstdir=False):
2079
2079
  return dst if not isdstdir else osp.join(dst, osp.basename(src))
2080
2080
 
2081
2081
 
2082
- def sync_dirs(src_root, dst_root, logger=glogger):
2082
+ def sync_dirs(src_root, dst_root, logger=glogger, sudo=False):
2083
2083
  """
2084
2084
  - assume src and dst folders are the same level of folder tree
2085
2085
  - the result will be dst_root mirrors src_root
2086
2086
  """
2087
+ def _run_sudo_command(_cmd, password, _logger):
2088
+ sudo_cmd = ['sudo', '-S'] + _cmd
2089
+ try:
2090
+ process = subprocess.Popen(
2091
+ sudo_cmd,
2092
+ stdin=subprocess.PIPE,
2093
+ stdout=subprocess.PIPE,
2094
+ stderr=subprocess.PIPE,
2095
+ text=True
2096
+ )
2097
+ stdout, stderr = process.communicate(input=f'{password}\n')
2098
+ if process.returncode == 0:
2099
+ _logger.debug(f"Successfully executed: {' '.join(_cmd)}")
2100
+ return True
2101
+ else:
2102
+ _logger.error(f"Sudo command failed: {stderr}")
2103
+ return False
2104
+ except Exception as e:
2105
+ _logger.error(f"Failed to execute sudo command: {e}")
2106
+ return False
2087
2107
  # Ensure the source directory exists
2088
2108
  if not os.path.exists(src_root):
2089
2109
  logger.error(f"Error: Source directory {src_root} does not exist.")
2090
2110
  return False
2091
- # Iterate through the items inside the source directory
2092
- for item in os.listdir(src_root):
2093
- src_path = os.path.join(src_root, item)
2094
- dst_path = os.path.join(dst_root, item)
2095
- if os.path.isdir(src_path):
2096
- # copytree with dirs_exist_ok=True will overwrite existing files
2097
- # and merge directories without warning.
2098
- shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
2099
- logger.info(f"Copied directory: {item}")
2100
- else:
2101
- # For individual files at the root of my_src_dir
2102
- shutil.copy2(src_path, dst_path)
2103
- logger.info(f"Copied file: {item}")
2111
+ if not sudo:
2112
+ # Iterate through the items inside the source directory
2113
+ for item in os.listdir(src_root):
2114
+ src_path = os.path.join(src_root, item)
2115
+ dst_path = os.path.join(dst_root, item)
2116
+ if os.path.isdir(src_path):
2117
+ # copytree with dirs_exist_ok=True will overwrite existing files
2118
+ # and merge directories without warning.
2119
+ shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
2120
+ logger.info(f"Copied directory: {item}")
2121
+ else:
2122
+ # For individual files at the root of my_src_dir
2123
+ shutil.copy2(src_path, dst_path)
2124
+ logger.info(f"Copied file: {item}")
2125
+ return True
2126
+ # on macOS, merge to system folders
2127
+ # Identify the original user (the one who called sudo)
2128
+ # Default to current effective user if SUDO_USER isn't set
2129
+ user = os.environ.get('SUDO_USER') or os.environ.get('USER')
2130
+ if not user:
2131
+ logger.error("Could not determine the current user.")
2132
+ return False
2133
+ pwd = prompt_macos_admin_password()
2134
+ if pwd is None:
2135
+ logger.error('Failed to get admin password')
2136
+ return False
2137
+ # rsync flags:
2138
+ # -a: Archive mode (preserves links, etc.)
2139
+ # --chown: Forces the owner:group of all copied files
2140
+ # Note: 'staff' is the default group for users on macOS
2141
+ cmd = [
2142
+ 'rsync', '-av', '--inplace',
2143
+ f'--chown={user}:staff',
2144
+ osp.join(src_root, ''), # Trailing slash copies contents
2145
+ dst_root
2146
+ ]
2147
+ logger.info(f"Syncing to {dst_root} and setting owner to '{user}'...")
2148
+ if not _run_sudo_command(cmd, pwd, logger):
2149
+ logger.error(f'Sync failed with command: {cmd}; you may have entered wrong password')
2150
+ return False
2104
2151
  return True
2105
2152
 
2106
2153
 
@@ -3163,6 +3210,25 @@ def json_from_text(json_str):
3163
3210
  except json.JSONDecodeError as e:
3164
3211
  return None, e
3165
3212
 
3213
+
3214
+ def prompt_macos_admin_password(action="Your operation"):
3215
+ applescript = f'''
3216
+ display dialog "{action} requires administrator privileges. Please enter your password:" default answer "" with hidden answer buttons {"Cancel", "OK"} default button "OK"
3217
+ text returned of the result
3218
+ '''
3219
+ try:
3220
+ result = subprocess.run(
3221
+ ['osascript', '-e', applescript],
3222
+ capture_output=True,
3223
+ text=True,
3224
+ check=True
3225
+ )
3226
+ return result.stdout.strip()
3227
+ except subprocess.CalledProcessError:
3228
+ # User cancelled the dialog
3229
+ return None
3230
+
3231
+
3166
3232
  # endregion
3167
3233
 
3168
3234
 
@@ -1,7 +0,0 @@
1
- kkpyutil.py,sha256=49PcKby8JHlsbkZ63LMqA5R7lzKQd5hRUfBJnjsK8OM,118921
2
- kkpyutil_helper/windows/kkttssave.ps1,sha256=xa3-WzqNh2rGYlOx_I4ewOuCE94gkTO5cEwYH0M67_0,446
3
- kkpyutil_helper/windows/kkttsspeak.ps1,sha256=7WUUHMmjTQroUWA2Mvdt4JtSt475nZUHQx-qP-7rS6o,305
4
- kkpyutil-1.47.1.dist-info/LICENSE,sha256=uISevGnCxB5QOU0ftbofN75_yUtd6E2h_MWE1zqagC8,1065
5
- kkpyutil-1.47.1.dist-info/METADATA,sha256=8Xb7Imhz5_S0yvYfCY7hr4M8-XzMDpCjdQQvt-4wO1E,1136
6
- kkpyutil-1.47.1.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
7
- kkpyutil-1.47.1.dist-info/RECORD,,