diff --git a/.gitignore b/.gitignore
index 226ccf4cd..0c4fae104 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,6 @@
/tmp
/vcpkg
/vcpkg_installed
-/scripts/**/*.pyc
/.cache
/.venv
aqtinstall.log
@@ -37,3 +36,6 @@ CMakeFiles/*
# vscode folder
/.vscode
+
+# scripts folder
+/scripts
diff --git a/scripts/lib/cmd_utils.py b/scripts/lib/cmd_utils.py
deleted file mode 100644
index 26fbaa958..000000000
--- a/scripts/lib/cmd_utils.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# Deskflow -- mouse and keyboard sharing utility
-# Copyright (C) 2024 Symless Ltd.
-#
-# This package is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# found in the file LICENSE that should have accompanied this file.
-#
-# This package is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import subprocess
-import sys
-import lib.env as env
-
-try:
- import colorama # type: ignore
- from colorama import Fore # type: ignore
-
- colorama.init()
-except ImportError:
-
- class Fore:
- RESET = ""
- YELLOW = ""
-
-
-def has_command(command):
- platform = sys.platform
- if platform == "win32":
- cmd = f"where {command}"
- else:
- cmd = f"which {command}"
- try:
- subprocess.check_output(cmd, shell=True)
- return True
- except subprocess.CalledProcessError:
- return False
-
-
-def strip_continuation_sequences(command, strip_newlines=True):
- """
- Remove the continuation sequences (\\) from a command.
-
- To spread strings over multiple lines in YAML files, like in bash, a backslash is used at
- the end of each line as continuation character.
- """
-
- if isinstance(command, list):
- raise ValueError("List commands are not supported")
-
- cmd_continuation = " \\"
- command = command.replace(cmd_continuation, "")
-
- # Some versions of pyyaml will remove the newlines already, so always stripping
- # makes the output more consistent.
- if strip_newlines:
- command = command.replace("\n", " ")
-
- return command
-
-
-def run(
- command,
- check=True, # true by default to fail fast
- shell=False, # false by default for security
- get_output=False,
- print_cmd=False, # false by default for security
-):
- """
- Convenience wrapper around `subprocess.run` to:
- - print the command before running it (if `print_cmd` is True)
-
- This differs to `subprocess.run` in that by default it:
- - checks the return code by default
- - prints list commands as a readable string on failure
-
- This is the same as `subprocess.run` in that it:
- - does not use shell by default for security (shell is less secure)
-
- Args:
- command (str or list): The command to run.
- check (bool): Raise an exception if the command fails.
- shell (bool): Run the command in a shell (false by default for security)
- get_output (bool): Return the output of the command.
- print_cmd (bool): Print the command before running it (false by default for security)
- """
-
- is_list_cmd = isinstance(command, list)
-
- # create string version of list command, only for debugging purposes
- command_str = command
- if is_list_cmd:
- command_str = " ".join(command)
-
- if print_cmd:
- print(f"Running: {command_str}")
- else:
- print("Running command...")
- command_str = "***"
-
- # TODO: You can definitely use a list command with shell=True on Windows,
- # but can you use a string command with shell=False on Windows?
- #
- # The `subprocess.run` function has a little gotcha:
- # - a string command must be used when `shell=True`
- # - a list command must be used when shell isn't or `shell=False`
- # however, it allows you to pass a string command when shell isn't used or `shell=False`
- # then fails with a vague error message. same problem with list commands and `shell=True`
- if not env.is_windows() and is_list_cmd and shell:
- raise ValueError("List commands cannot be used when shell=True on Unix systems")
- elif not is_list_cmd and not shell:
- raise ValueError("String commands cannot be used when shell=False or not set")
-
- # Flush the output to ensure the command is printed before the output of the command,
- # which seems to happen in the GitHub runner logs.
- sys.stdout.flush()
- sys.stderr.flush()
-
- try:
- if get_output:
- result = subprocess.run(
- command,
- shell=shell,
- check=check,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- )
- else:
- result = subprocess.run(command, check=check, shell=shell)
-
- except subprocess.CalledProcessError as e:
- # Take control of how failed commands are printed:
- # - if `print_cmd` is false, it will print `***` instead of the command
- # - if the command was a list, the command is printed as a readable string
- raise RuntimeError(
- f"Command exited with code {e.returncode}: {command_str}"
- ) from None
- except Exception:
- # Take control of how failed commands are printed:
- # - if `print_cmd` is false, it will print `***` instead of the command
- # - if the command was a list, the command is printed as a readable string
- raise RuntimeError(f"Command failed: {command_str}")
-
- if result.returncode != 0:
- print(
- f"{Fore.YELLOW}Command exited with code {result.returncode}:{Fore.RESET} {command_str}",
- file=sys.stderr,
- )
-
- return result
diff --git a/scripts/lib/colors.py b/scripts/lib/colors.py
deleted file mode 100644
index ff020aa44..000000000
--- a/scripts/lib/colors.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Deskflow -- mouse and keyboard sharing utility
-# Copyright (C) 2024 Symless Ltd.
-#
-# This package is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# found in the file LICENSE that should have accompanied this file.
-#
-# This package is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import colorama # type: ignore
-from colorama import Fore # type: ignore
-
-colorama.init()
-
-SUCCESS_TEXT = f"{Fore.LIGHTGREEN_EX}Success:{Fore.RESET}"
-ERROR_TEXT = f"{Fore.LIGHTRED_EX}Error:{Fore.RESET}"
-WARNING_TEXT = f"{Fore.LIGHTYELLOW_EX}Warning:{Fore.RESET}"
-HINT_TEXT = f"{Fore.LIGHTBLUE_EX}Hint:{Fore.RESET}"
diff --git a/scripts/lib/env.py b/scripts/lib/env.py
deleted file mode 100644
index 464be46c7..000000000
--- a/scripts/lib/env.py
+++ /dev/null
@@ -1,269 +0,0 @@
-# Deskflow -- mouse and keyboard sharing utility
-# Copyright (C) 2024 Symless Ltd.
-#
-# This package is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# found in the file LICENSE that should have accompanied this file.
-#
-# This package is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import os, sys, subprocess
-import lib.cmd_utils as cmd_utils
-
-# The `.venv` dir seems to be most common for virtual environments.
-VENV_DIR = ".venv"
-
-
-def check_module(module):
- try:
- __import__(module)
- return True
- except ImportError:
- print(f"Python is missing {module} module", file=sys.stderr)
- return False
-
-
-def get_os():
- """Detects the operating system."""
- if sys.platform == "win32":
- return "windows"
- elif sys.platform == "darwin":
- return "mac"
- elif sys.platform.startswith("linux"):
- return "linux"
- else:
- raise RuntimeError(f"Unsupported platform: {sys.platform}")
-
-
-def is_windows():
- return get_os() == "windows"
-
-
-def is_mac():
- return get_os() == "mac"
-
-
-def is_linux():
- return get_os() == "linux"
-
-
-def get_linux_distro():
- """Detects the Linux distro."""
- os_file = "/etc/os-release"
- name = None
- name_like = None
- version_id = None
- version_codename = None
-
- if os.path.isfile(os_file):
- with open(os_file) as f:
- for line in f:
- if line.startswith("ID="):
- name = line.strip().split("=")[1].strip('"')
- elif line.startswith("ID_LIKE="):
- name_like = line.strip().split("=")[1].strip('"')
- elif line.startswith("VERSION_ID="):
- version_id = line.strip().split("=")[1].strip('"')
- elif line.startswith("VERSION_CODENAME="):
- version_codename = line.strip().split("=")[1].strip('"')
-
- return name, name_like, version_id or version_codename
-
-
-def get_env(name, required=True, default=None):
- """
- Returns an env var (stripped) or optionally raises an error if not set.
-
- If `default` is set, it will be returned even if `required` is True.
- """
- value = os.getenv(name)
- if value:
- value = value.strip()
-
- if not value:
- if default:
- return default
- elif required:
- raise ValueError(f"Required env var not set: {name}")
-
- return value
-
-
-def get_env_bool(name, default=False):
- """Returns a boolean value from an env var (stripped)."""
- value = os.getenv(name)
- if value:
- value = value.strip()
-
- if value is None:
- return default
-
- return value.lower() in ["true", "1", "yes"]
-
-
-def get_venv_executable(binary="python"):
- if sys.platform == "win32":
- return os.path.join(VENV_DIR, "Scripts", binary)
- else:
- return os.path.join(VENV_DIR, "bin", binary)
-
-
-def in_venv():
- """Returns True if the script is running in a Python virtual environment."""
- return sys.prefix != sys.base_prefix
-
-
-def ensure_in_venv(script_file, create_venv=False):
- """
- Ensures the script is running in a Python virtual environment (venv).
- If the script is not running in a venv, it will create one and re-run the script in the venv.
- """
-
- check_dependencies(raise_error=True)
- import venv
-
- if in_venv():
- print(f"Running in venv, executable: {sys.executable}", flush=True)
- return
-
- if create_venv and not os.path.exists(VENV_DIR):
- print(f"Creating virtual environment at {VENV_DIR}")
- venv.create(VENV_DIR, with_pip=True)
-
- if os.path.exists(VENV_DIR):
- script_file_abs = os.path.abspath(script_file)
- print(f"Using virtual environment for: {script_file_abs}", flush=True)
- python_executable = get_venv_executable()
- result = subprocess.run([python_executable, script_file_abs] + sys.argv[1:])
- sys.exit(result.returncode)
- else:
- print(
- "The Python virtual environment (.venv) needs to be created before you can "
- "run this script.\n"
- "Please run: scripts/setup_venv.py"
- )
- sys.exit(1)
-
-
-def install_requirements():
- """
- Uses `pip` to install required Python modules from the `requirements.txt` file.
- """
-
- check_dependencies(raise_error=True)
-
- print("Updating pip...")
- cmd_utils.run(
- [sys.executable, "-m", "pip", "install", "--upgrade", "pip"],
- shell=False,
- print_cmd=True,
- )
-
- print("Installing required modules...")
- cmd_utils.run(
- [sys.executable, "-m", "pip", "install", "-e", "scripts"],
- shell=False,
- print_cmd=True,
- )
-
-
-def check_dependencies(raise_error=False):
- """
- Returns True if pip and venv are available.
- """
-
- has_pip = check_module("pip")
- has_venv = check_module("venv")
-
- if raise_error:
- if not has_pip:
- raise RuntimeError("Python is missing pip")
- if not has_venv:
- raise RuntimeError("Python is missing venv")
- else:
- return has_pip and has_venv
-
-
-def ensure_dependencies():
- """
- Ensures that pip and venv are available, and installs them if they are not.
- This is normally only installs on Linux, as Windows and Mac usually come with pip and venv.
- """
-
- if check_dependencies():
- return
-
- print("Installing Python dependencies...")
-
- os = get_os()
- if os != "linux":
- # should not be a problem, since windows and mac come with pip and venv
- raise RuntimeError(f"Unable to install Python dependencies on {os}")
-
- has_sudo = cmd_utils.has_command("sudo")
- sudo = "sudo" if has_sudo else ""
-
- distro, distro_like, _version = get_linux_distro()
- if not distro_like:
- distro_like = distro
-
- update_cmd = None
- install_cmd = None
- if distro == "rhel" or "rhel" in distro_like:
- update_cmd = "yum check-update"
- install_cmd = "yum install -y python3-pip" # rhel-like has venv already
- elif "debian" in distro_like:
- update_cmd = "apt update"
- install_cmd = "apt install -y python3-pip python3-venv"
- elif "fedora" in distro_like:
- update_cmd = "dnf check-update"
- install_cmd = "dnf install -y python3-pip python3-virtualenv"
- elif "arch" in distro_like:
- install_cmd = "pacman -Syu --noconfirm python-pip python-virtualenv"
- elif "opensuse" in distro_like:
- update_cmd = "zypper refresh"
- install_cmd = "zypper install -y python3-pip python3-virtualenv"
- else:
- raise RuntimeError(f"Unable to install Python dependencies on {distro}")
-
- if update_cmd:
- # don't check the return code, as some package managers return non-zero exit codes
- # under normal circumstances (e.g. dnf check-update returns 100 when there are
- # updates available).
- cmd_utils.run(
- f"{sudo} {update_cmd}".strip(), check=False, shell=True, print_cmd=True
- )
-
- cmd_utils.run(f"{sudo} {install_cmd}".strip(), shell=True, print_cmd=True)
-
-
-def import_colors():
- import lib.colors as colors
-
- return colors
-
-
-def persist_lock_file(path):
- """
- Persists a lock file and ensures the directory part of the path exists.
- """
- dir_path = os.path.dirname(path)
- if not os.path.exists(dir_path):
- os.makedirs(dir_path, exist_ok=True)
-
- with open(path, "w") as f:
- f.write(str(os.getpid()))
-
-
-def remove_lock_file(path):
- """
- Removes a lock file if it exists.
- """
- if os.path.exists(path):
- os.remove(path)
diff --git a/scripts/lib/fs.py b/scripts/lib/fs.py
deleted file mode 100644
index 0fde6e1f5..000000000
--- a/scripts/lib/fs.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Deskflow -- mouse and keyboard sharing utility
-# Copyright (C) 2024 Symless Ltd.
-#
-# This package is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# found in the file LICENSE that should have accompanied this file.
-#
-# This package is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import os, fnmatch
-
-
-def find_files(search_dirs, include_files, exclude_dirs=[]):
- """Recursively find files, excluding specified directories"""
- matches = []
- for dir in search_dirs:
- for root, dirnames, filenames in os.walk(dir):
- dirnames[:] = [d for d in dirnames if d not in exclude_dirs]
-
- for pattern in include_files:
- for filename in fnmatch.filter(filenames, pattern):
- matches.append(os.path.join(root, filename))
- return matches
diff --git a/scripts/lint_clang.py b/scripts/lint_clang.py
deleted file mode 100755
index a2f31d5c5..000000000
--- a/scripts/lint_clang.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env python3
-
-# Deskflow -- mouse and keyboard sharing utility
-# Copyright (C) 2024 Symless Ltd.
-#
-# This package is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# found in the file LICENSE that should have accompanied this file.
-#
-# This package is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import lib.env as env
-
-env.ensure_in_venv(__file__)
-
-import argparse, sys
-import lib.fs as fs
-from clang_format import clang_format # type: ignore
-
-include_files = [
- "*.h",
- "*.c",
- "*.hpp",
- "*.cpp",
- "*.m",
- "*.mm",
-]
-
-dirs = ["src"]
-
-
-def main():
- """
- Cross-platform equivalent of using find and xargs with clang-format.
- Lints by performing a dry run (--dry-run) which fails when formatting is needed.
- """
- parser = argparse.ArgumentParser()
- parser.add_argument(
- "-f",
- "--format",
- action="store_true",
- help="In-place format all files",
- )
- args = parser.parse_args()
-
- cmd_args = ["-i"] if args.format else ["--dry-run", "--Werror"]
- files_recursive = fs.find_files(dirs, include_files)
-
- if args.format:
- print("Formatting files with Clang formatter:")
- else:
- print("Checking files with Clang formatter:")
-
- for file in files_recursive:
- print(file)
-
- if files_recursive:
- sys.argv = [""] + cmd_args + files_recursive
- result = clang_format()
- if result == 0:
- print("Clang lint passed")
-
- sys.exit(result)
- else:
- print("No files for Clang to process", file=sys.stderr)
- sys.exit(0)
-
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/pyproject.toml b/scripts/pyproject.toml
deleted file mode 100644
index 12fc4fad6..000000000
--- a/scripts/pyproject.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[project]
-name = "scripts"
-version = "0.0.1"
-description = "Scripts to assist with development of Deskflow"
-requires-python = ">=3.9"
-dependencies = [
- "clang-format",
- "python-dotenv",
- "colorama",
-]
diff --git a/scripts/setup_venv.py b/scripts/setup_venv.py
deleted file mode 100755
index d298b2954..000000000
--- a/scripts/setup_venv.py
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python3
-
-# Deskflow -- mouse and keyboard sharing utility
-# Copyright (C) 2024 Symless Ltd.
-#
-# This package is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# found in the file LICENSE that should have accompanied this file.
-#
-# This package is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-import lib.env as env
-
-env.ensure_in_venv(__file__, create_venv=True)
-env.install_requirements()
-
-import lib.colors as colors
-
-print(colors.SUCCESS_TEXT, "Python virtual environment is ready.")
diff --git a/sonar-project.properties b/sonar-project.properties
index 835098274..138720a59 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,9 +1,9 @@
sonar.organization=deskflow
sonar.projectKey=deskflow_deskflow
-sonar.sources=scripts,src/apps,src/lib
+sonar.sources=src/apps,src/lib
sonar.tests=src/test
sonar.exclusions=subprojects/**,build/**
-sonar.coverage.exclusions=subprojects/**,scripts/**,src/test/**,src/apps/deskflow-gui/**,src/apps/res/**
+sonar.coverage.exclusions=subprojects/**,src/test/**,src/apps/deskflow-gui/**,src/apps/res/**
sonar.cpd.exclusions=**/*Test*.cpp
sonar.host.url=https://sonarcloud.io
sonar.coverageReportPaths=${{ steps.coverage-paths.outputs.csv }}