-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
25d015b
commit 5d97ae3
Showing
8 changed files
with
322 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
#! /usr/bin/env python3 | ||
# -*- coding:utf-8 -*- | ||
# @Time : 2024/08/27 | ||
# @Author : 寂静的羽夏(wingsummer) | ||
# @FileName: installer.py | ||
|
||
import argparse | ||
import os | ||
import shutil | ||
import hashlib | ||
import psutil | ||
import subprocess | ||
|
||
from colorama import Fore, Style | ||
|
||
PACKAGE_NAME = "WingGifEditor2" | ||
INSTALL_PATH = f"/opt/{PACKAGE_NAME}" | ||
APP_DESKTOP_PATH = "/usr/share/applications" | ||
DESKTOP_FILE_NAME = "com.wingsummer.winggifeditor2.desktop" | ||
|
||
|
||
def is_root(): | ||
return os.geteuid() == 0 | ||
|
||
|
||
def check_is_running(processName): | ||
''' | ||
Check if there is any running process that contains the given name processName. | ||
''' | ||
# Iterate over the all the running process | ||
for proc in psutil.process_iter(): | ||
try: | ||
# Check if process name contains the given name string. | ||
if processName.lower() in proc.name().lower(): | ||
return True | ||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): | ||
pass | ||
return False | ||
|
||
|
||
def run_command_interactive(command): | ||
""" | ||
Run a command interactively, printing its stdout in real-time. | ||
:param command: List of command arguments (e.g., ["your_command", "arg1", "arg2"]) | ||
:return: The return code of the command | ||
""" | ||
process = subprocess.Popen( | ||
command, | ||
stdout=subprocess.PIPE, | ||
stderr=subprocess.STDOUT, # Capture both stdout and stderr | ||
text=True # Ensure the output is in text mode | ||
) | ||
|
||
while True: | ||
output = process.stdout.readline() | ||
if output == "" and process.poll() is not None: | ||
break | ||
if output: | ||
print(Fore.GREEN + output.strip() + Style.RESET_ALL) | ||
|
||
return_code = process.wait() | ||
return return_code | ||
|
||
|
||
def create_dir(dir): | ||
if not os.path.exists(dir): | ||
os.mkdir(dir) | ||
|
||
|
||
def update(build_path): | ||
if (os.path.exists(INSTALL_PATH) == False): | ||
print( | ||
Fore.RED + "[Error] The installing path not exists! Please use 'install' instead." + Style.RESET_ALL) | ||
exit(3) | ||
|
||
installer_path = os.path.dirname(os.path.abspath(__file__)) | ||
projectbase = os.path.abspath(os.path.join(installer_path, "..")) | ||
|
||
print(Fore.GREEN + ">> Checking file integrity..." + Style.RESET_ALL) | ||
|
||
if (os.path.exists(build_path) == False): | ||
print( | ||
Fore.RED + "[Error] Not found a CMake build directory!" + Style.RESET_ALL) | ||
exit(-1) | ||
|
||
if (os.path.exists(os.path.join(build_path, "CMakeCache.txt")) == False): | ||
print( | ||
Fore.RED + "[Error] This is not a CMake build directory!" + Style.RESET_ALL) | ||
exit(-1) | ||
|
||
version_file_src = os.path.join(build_path, "WINGGIF_VERSION") | ||
if (os.path.exists(version_file_src) == False): | ||
print( | ||
Fore.RED + "[Error] WINGGIF_VERSION file not found, maybe not a CMake build directory!" + Style.RESET_ALL) | ||
exit(-1) | ||
|
||
version_qt_src = os.path.join(build_path, "QT_VERSION") | ||
if (os.path.exists(version_qt_src) == False): | ||
print( | ||
Fore.RED + "[Error] QT_VERSION file not found, maybe not a CMake build directory!" + Style.RESET_ALL) | ||
exit(-1) | ||
|
||
# ok, start copying files | ||
exemain_src = os.path.join(build_path, PACKAGE_NAME) | ||
if (os.path.exists(exemain_src) == False): | ||
print( | ||
Fore.RED + f"[Error] {PACKAGE_NAME} is not found!" + Style.RESET_ALL) | ||
exit(-3) | ||
|
||
# calculate the md5 checksum | ||
with open(exemain_src, 'rb') as file_to_check: | ||
data = file_to_check.read() | ||
md5_returned = hashlib.md5(data).hexdigest().upper() | ||
|
||
print(Fore.GREEN + ">> Get MD5: " + md5_returned + Style.RESET_ALL) | ||
print(Fore.GREEN + ">> Installing..." + Style.RESET_ALL) | ||
|
||
shutil.copy2(exemain_src, os.path.join(INSTALL_PATH, PACKAGE_NAME)) | ||
|
||
create_dir(os.path.join(INSTALL_PATH, "plugin")) | ||
|
||
shutil.copytree(os.path.join(installer_path, "images"), | ||
os.path.join(INSTALL_PATH, "images"), dirs_exist_ok=True) | ||
|
||
shutil.copytree(os.path.join(build_path, "lang"), | ||
os.path.join(INSTALL_PATH, "lang"), dirs_exist_ok=True) | ||
|
||
print(Fore.GREEN + ">> Copying License and other materials..." + Style.RESET_ALL) | ||
|
||
material_files = ["LICENSE", "authorband.svg", | ||
"licenseband.svg", "screenshot.png", "README.md"] | ||
|
||
for f in material_files: | ||
shutil.copyfile(os.path.join(projectbase, f), | ||
os.path.join(INSTALL_PATH, f)) | ||
|
||
shutil.copyfile(os.path.join(projectbase, "images", "author.jpg"), | ||
os.path.join(INSTALL_PATH, "author.jpg")) | ||
|
||
with open(os.path.join(INSTALL_PATH, "md5sums"), 'w') as md5_file: | ||
md5_file.write(md5_returned) | ||
|
||
shutil.copyfile(os.path.join(installer_path, DESKTOP_FILE_NAME), | ||
os.path.join(APP_DESKTOP_PATH, DESKTOP_FILE_NAME)) | ||
|
||
run_command_interactive( | ||
["update-desktop-database", "/usr/share/applications"]) | ||
print(Fore.GREEN + ">> Installation finished..." + Style.RESET_ALL) | ||
|
||
|
||
def install(build_path): | ||
if (os.path.exists(INSTALL_PATH)): | ||
print( | ||
Fore.RED + "[Error] The installing path exists! Please use 'update' instead or 'uninstall' before it." + Style.RESET_ALL) | ||
exit(3) | ||
os.makedirs(INSTALL_PATH) | ||
update(build_path) | ||
|
||
|
||
def remove(path): | ||
""" param <path> could either be relative or absolute. """ | ||
if os.path.isfile(path) or os.path.islink(path): | ||
os.remove(path) # remove the file | ||
elif os.path.isdir(path): | ||
shutil.rmtree(path) # remove dir and all contains | ||
|
||
|
||
def is_empty(path): | ||
if os.path.exists(path) and not os.path.isfile(path): | ||
# Checking if the directory is empty or not | ||
if not os.listdir(path): | ||
return True | ||
else: | ||
return False | ||
else: | ||
return False | ||
|
||
|
||
def uninstall(): | ||
remove(os.path.join(APP_DESKTOP_PATH, DESKTOP_FILE_NAME)) | ||
run_command_interactive( | ||
["update-desktop-database", "/usr/share/applications"]) | ||
remove(INSTALL_PATH) | ||
print(Fore.GREEN + ">> Uninstallation finished..." + Style.RESET_ALL) | ||
|
||
|
||
CFG_PATH = ".config/WingCloudStudio" | ||
AUTO_START = ".config/autostart" | ||
CONF_FILE = "WingGifEditor2.conf" | ||
CONTENT_PATH = ".local/share/WingCloudStudio" | ||
|
||
|
||
def clear_usrdata(usr): | ||
usr_path = "" | ||
if (usr == "root"): | ||
usr_path = "/root" | ||
else: | ||
usr_path = f"/home/{usr}" | ||
|
||
desktop_file = os.path.join(usr_path, AUTO_START, DESKTOP_FILE_NAME) | ||
remove(desktop_file) | ||
|
||
cfgpath = os.path.join(usr_path, CFG_PATH) | ||
|
||
remove(os.path.join(cfgpath, CONF_FILE)) | ||
if is_empty(cfgpath): | ||
remove(cfgpath) | ||
|
||
cfgpath = os.path.join(usr_path, CONTENT_PATH, PACKAGE_NAME) | ||
remove(cfgpath) | ||
|
||
cfgpath = os.path.join(usr_path, CONTENT_PATH) | ||
if is_empty(cfgpath): | ||
remove(cfgpath) | ||
|
||
print(Fore.GREEN + f">> Purged ${usr}..." + Style.RESET_ALL) | ||
|
||
|
||
def purge(): | ||
uninstall() | ||
for p in os.listdir("/home"): | ||
if p == "lost+found": | ||
continue | ||
clear_usrdata(p) | ||
clear_usrdata("root") | ||
print(Fore.GREEN + ">> Clean-up finished..." + Style.RESET_ALL) | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
prog="installer.py", description=f"A installing manager for {PACKAGE_NAME}") | ||
parser.add_argument( | ||
"action", | ||
choices=["install", "update", "uninstall", "purge"], | ||
nargs="?", | ||
default="install", | ||
help="Action to perform: install (default), update, uninstall, or purge." | ||
) | ||
parser.add_argument( | ||
"-b", "--build", | ||
type=str, | ||
required=False, | ||
help="Path to the building files for installation (required for 'install' and 'update' action)." | ||
) | ||
args = parser.parse_args() | ||
|
||
if check_is_running(PACKAGE_NAME): | ||
print( | ||
Fore.RED + f"[Error] You should exit {PACKAGE_NAME} berfore installation." + Style.RESET_ALL) | ||
exit(1) | ||
|
||
if not is_root(): | ||
print( | ||
Fore.RED + "[Error] root is required for installation." + Style.RESET_ALL) | ||
exit(2) | ||
|
||
# checking build toolkits | ||
if args.action == "install": | ||
if not args.build: | ||
print( | ||
Fore.RED + "[Error] --build path is required for installation." + Style.RESET_ALL) | ||
exit(2) | ||
install(args.build) | ||
elif args.action == "update": | ||
if not args.build: | ||
print( | ||
Fore.RED + "[Error] --build path is required for installation." + Style.RESET_ALL) | ||
exit(2) | ||
update(args.build) | ||
elif args.action == "uninstall": | ||
uninstall() | ||
elif args.action == "purge": | ||
purge() | ||
|
||
exit(0) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
else: | ||
print( | ||
Fore.RED + "[Error] Please run this script in main mode" + Style.RESET_ALL) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.