sysetup 1.1.0__tar.gz → 1.2.0__tar.gz
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.
- {sysetup-1.1.0/src/sysetup.egg-info → sysetup-1.2.0}/PKG-INFO +3 -3
- {sysetup-1.1.0 → sysetup-1.2.0}/README.md +1 -1
- {sysetup-1.1.0 → sysetup-1.2.0}/pyproject.toml +10 -9
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/cli/entry_point.py +1 -2
- sysetup-1.2.0/src/sysetup/context/context.py +31 -0
- sysetup-1.2.0/src/sysetup/main/environment.py +14 -0
- sysetup-1.2.0/src/sysetup/main/files/__init__.py +1 -0
- sysetup-1.2.0/src/sysetup/main/files/assets.py +40 -0
- sysetup-1.2.0/src/sysetup/main/files/permissions.py +33 -0
- sysetup-1.2.0/src/sysetup/main/files/settings.py +45 -0
- sysetup-1.2.0/src/sysetup/main/files/setup.py +8 -0
- sysetup-1.2.0/src/sysetup/main/installations.py +67 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/main/main.py +11 -7
- sysetup-1.2.0/src/sysetup/main/packages.py +60 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/models/__init__.py +0 -2
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/models/action.py +1 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/models/path.py +1 -1
- sysetup-1.2.0/src/sysetup/utils/__init__.py +1 -0
- sysetup-1.2.0/src/sysetup/utils/download.py +12 -0
- {sysetup-1.1.0 → sysetup-1.2.0/src/sysetup.egg-info}/PKG-INFO +3 -3
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup.egg-info/SOURCES.txt +9 -4
- sysetup-1.2.0/src/sysetup.egg-info/entry_points.txt +3 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup.egg-info/requires.txt +1 -1
- {sysetup-1.1.0 → sysetup-1.2.0}/tests/test_background.py +12 -7
- {sysetup-1.1.0 → sysetup-1.2.0}/tests/test_installer.py +1 -1
- sysetup-1.1.0/src/sysetup/context/context.py +0 -5
- sysetup-1.1.0/src/sysetup/main/environment.py +0 -12
- sysetup-1.1.0/src/sysetup/main/files.py +0 -96
- sysetup-1.1.0/src/sysetup/main/installer.py +0 -124
- sysetup-1.1.0/src/sysetup/models/config.py +0 -9
- sysetup-1.1.0/src/sysetup/models/secrets_.py +0 -11
- sysetup-1.1.0/src/sysetup.egg-info/entry_points.txt +0 -3
- {sysetup-1.1.0 → sysetup-1.2.0}/LICENSE +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/bin/pw +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/bin/pw-askpass +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/setup.cfg +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/__init__.py +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/cli/__init__.py +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/context/__init__.py +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/main/__init__.py +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/models/options.py +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup/py.typed +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup.egg-info/dependency_links.txt +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/src/sysetup.egg-info/top_level.txt +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/tests/test_cli_entry_point.py +0 -0
- {sysetup-1.1.0 → sysetup-1.2.0}/tests/test_main.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sysetup
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Personal system setup
|
|
5
5
|
Author-email: Quinten Roets <qdr2104@columbia.edu>
|
|
6
6
|
License: MIT
|
|
@@ -8,7 +8,7 @@ Project-URL: Source Code, https://github.com/quintenroets/sysetup
|
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: backupmaster<2,>=1.2.
|
|
11
|
+
Requires-Dist: backupmaster<2,>=1.2.6
|
|
12
12
|
Requires-Dist: dbus-next<1,>=0.2.3
|
|
13
13
|
Requires-Dist: package-utils[context]<1,>=0.6.1
|
|
14
14
|
Requires-Dist: powercli<1,>=0.3.1
|
|
@@ -42,11 +42,11 @@ Requires-Dist: package-dev-utils<1,>=0.1.6; extra == "dev"
|
|
|
42
42
|
* Enable GPU acceleration in chrome://flags
|
|
43
43
|
* PDF viewer: set page zoom to page fit
|
|
44
44
|
* Import certificate from Scripts/assets/sysetup/certificates/certificate.crt
|
|
45
|
+
* Click experimental: enable tab groups save and sync
|
|
45
46
|
5) Login to
|
|
46
47
|
* Pycharm professional
|
|
47
48
|
* VNC Server
|
|
48
49
|
6) For new device: set touchpad scroll direction and click on touch
|
|
49
|
-
## Usage
|
|
50
50
|
|
|
51
51
|
## Installation
|
|
52
52
|
```shell
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
* Enable GPU acceleration in chrome://flags
|
|
22
22
|
* PDF viewer: set page zoom to page fit
|
|
23
23
|
* Import certificate from Scripts/assets/sysetup/certificates/certificate.crt
|
|
24
|
+
* Click experimental: enable tab groups save and sync
|
|
24
25
|
5) Login to
|
|
25
26
|
* Pycharm professional
|
|
26
27
|
* VNC Server
|
|
27
28
|
6) For new device: set touchpad scroll direction and click on touch
|
|
28
|
-
## Usage
|
|
29
29
|
|
|
30
30
|
## Installation
|
|
31
31
|
```shell
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sysetup"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.2.0"
|
|
4
4
|
description = "Personal system setup"
|
|
5
5
|
authors = [{name = "Quinten Roets", email = "qdr2104@columbia.edu"}]
|
|
6
6
|
license = {text = "MIT"}
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"backupmaster >=1.2.
|
|
10
|
+
"backupmaster >=1.2.6, <2",
|
|
11
11
|
"dbus-next >=0.2.3, <1",
|
|
12
12
|
"package-utils[context] >=0.6.1, <1",
|
|
13
13
|
"powercli >=0.3.1, <1",
|
|
@@ -27,7 +27,7 @@ dev = [
|
|
|
27
27
|
|
|
28
28
|
[project.scripts]
|
|
29
29
|
sysetup = "sysetup.cli:entry_point"
|
|
30
|
-
exportcrontab = "sysetup.main.files:move_crontab"
|
|
30
|
+
exportcrontab = "sysetup.main.files.assets:move_crontab"
|
|
31
31
|
|
|
32
32
|
[build-system]
|
|
33
33
|
requires = ["setuptools"]
|
|
@@ -53,12 +53,13 @@ pythonpath = [
|
|
|
53
53
|
fix = true
|
|
54
54
|
|
|
55
55
|
[tool.ruff.lint]
|
|
56
|
-
select = [
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
56
|
+
select = ["ALL"]
|
|
57
|
+
ignore = [
|
|
58
|
+
"ANN101", # annotate self
|
|
59
|
+
"ANN102", # annotate cls
|
|
60
|
+
"ANN401", # annotated with Any
|
|
61
|
+
"D", # docstrings
|
|
62
|
+
"S101", # assert used
|
|
62
63
|
]
|
|
63
64
|
|
|
64
65
|
[tool.ruff.lint.isort]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections.abc import Iterator
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
|
|
5
|
+
import cli
|
|
6
|
+
from package_utils.context import Context as Context_
|
|
7
|
+
|
|
8
|
+
from sysetup.models import Options
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Context(Context_[Options, None, None]):
|
|
12
|
+
@cached_property
|
|
13
|
+
def package_manager(self) -> str:
|
|
14
|
+
def generate_package_manager() -> Iterator[str]:
|
|
15
|
+
package_managers = "apt-get", "pacman"
|
|
16
|
+
for package_manager in package_managers:
|
|
17
|
+
if cli.completes_successfully("which", package_manager):
|
|
18
|
+
yield package_manager
|
|
19
|
+
|
|
20
|
+
return next(generate_package_manager())
|
|
21
|
+
|
|
22
|
+
@cached_property
|
|
23
|
+
def apt_is_installed(self) -> bool:
|
|
24
|
+
return self.package_manager == "apt-get"
|
|
25
|
+
|
|
26
|
+
@cached_property
|
|
27
|
+
def is_running_in_test(self) -> bool:
|
|
28
|
+
return "DISPLAY" not in os.environ
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
context = Context(Options)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
from sysetup.models import Path
|
|
6
|
+
from sysetup.utils import download_file
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def setup() -> None:
|
|
10
|
+
download_file(Path("etc") / "environment")
|
|
11
|
+
if "SUDO_ASKPASS" not in os.environ:
|
|
12
|
+
path = Path.HOME / ".bask_profile"
|
|
13
|
+
download_file(Path.HOME / ".bask_profile")
|
|
14
|
+
load_dotenv(dotenv_path=Path.HOME / path)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .setup import setup
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import cli
|
|
2
|
+
from backup.backups.cache import cache
|
|
3
|
+
|
|
4
|
+
from sysetup.models import Path
|
|
5
|
+
from sysetup.utils import download_directory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def setup() -> None:
|
|
9
|
+
download_directory(Path.assets)
|
|
10
|
+
move_crontab()
|
|
11
|
+
move_setup_files()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def move_crontab() -> None:
|
|
15
|
+
src = Path.assets / "crontab" / "crontab"
|
|
16
|
+
cli.run("crontab -", input=src.text)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def move_setup_files() -> None:
|
|
20
|
+
setup_files_root = Path.assets / "files"
|
|
21
|
+
setup_files = []
|
|
22
|
+
archived_setup_files = []
|
|
23
|
+
for path in setup_files_root.rglob("*"):
|
|
24
|
+
if path.is_file():
|
|
25
|
+
if path.archive_format is None:
|
|
26
|
+
setup_files.append(path)
|
|
27
|
+
else:
|
|
28
|
+
archived_setup_files.append(path)
|
|
29
|
+
|
|
30
|
+
backup = cache.Backup(paths=setup_files)
|
|
31
|
+
backup.pull()
|
|
32
|
+
|
|
33
|
+
for path in archived_setup_files:
|
|
34
|
+
dest = (backup.source / path.relative_to(setup_files_root)).parent
|
|
35
|
+
if dest.is_root and not dest.exists():
|
|
36
|
+
cli.run("mkdir -p", dest, root=True)
|
|
37
|
+
else:
|
|
38
|
+
dest.create_parent()
|
|
39
|
+
with cli.status(f"Unpacking {path.relative_to(setup_files_root)}"):
|
|
40
|
+
cli.capture_output("unzip -o", path, "-d", dest, root=dest.is_root)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import stat
|
|
2
|
+
|
|
3
|
+
import cli
|
|
4
|
+
|
|
5
|
+
from sysetup.models import Path
|
|
6
|
+
from sysetup.utils import download_directory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def setup() -> None:
|
|
10
|
+
set_git_permissions()
|
|
11
|
+
set_ssh_permissions()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def set_git_permissions() -> None:
|
|
15
|
+
git_hooks_folder = Path.HOME / ".config" / "git" / "hooks"
|
|
16
|
+
download_directory(git_hooks_folder)
|
|
17
|
+
for path in git_hooks_folder.iterdir():
|
|
18
|
+
cli.run("chmod +x", path)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def set_ssh_permissions() -> None:
|
|
22
|
+
directory = Path.HOME / ".ssh"
|
|
23
|
+
download_directory(directory)
|
|
24
|
+
for path in directory.glob("id_*"):
|
|
25
|
+
if path.suffix != ".pub":
|
|
26
|
+
check_permissions(path)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def check_permissions(path: Path) -> None:
|
|
30
|
+
permissions = path.stat().st_mode
|
|
31
|
+
other_users_can_read = permissions & (stat.S_IRGRP | stat.S_IROTH)
|
|
32
|
+
if other_users_can_read:
|
|
33
|
+
path.chmod(33152)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import cli
|
|
2
|
+
|
|
3
|
+
from sysetup.context import context
|
|
4
|
+
from sysetup.models import Path
|
|
5
|
+
from sysetup.utils import download_directory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def remove_clutter() -> None:
|
|
9
|
+
names = (
|
|
10
|
+
"Desktop",
|
|
11
|
+
"Downloads",
|
|
12
|
+
"Music",
|
|
13
|
+
"Pictures",
|
|
14
|
+
"Public",
|
|
15
|
+
"Templates",
|
|
16
|
+
"Videos",
|
|
17
|
+
)
|
|
18
|
+
for name in names:
|
|
19
|
+
path = Path.HOME / name
|
|
20
|
+
path.rmtree(missing_ok=True)
|
|
21
|
+
|
|
22
|
+
nginx_path = Path("/") / "etc" / "nginx" / "sites-enabled" / "default"
|
|
23
|
+
if nginx_path.exists():
|
|
24
|
+
cli.run("rm", nginx_path, root=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def set_background() -> None: # pragma: nocover
|
|
28
|
+
wallpaper_path = (
|
|
29
|
+
Path.HOME / ".local" / "share" / "wallpapers" / "Qwallpapers" / "background.jpg"
|
|
30
|
+
)
|
|
31
|
+
download_directory(wallpaper_path.parent)
|
|
32
|
+
wallpaper_uri = wallpaper_path.as_uri()
|
|
33
|
+
script = Path.update_wallpaper_script.text.replace(
|
|
34
|
+
"__wallpaper_uri__",
|
|
35
|
+
wallpaper_uri,
|
|
36
|
+
)
|
|
37
|
+
run_kde_script(script)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run_kde_script(script: str) -> None: # pragma: nocover
|
|
41
|
+
command = (
|
|
42
|
+
"qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript"
|
|
43
|
+
)
|
|
44
|
+
if not context.is_running_in_test:
|
|
45
|
+
cli.run(command, script)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import cli
|
|
4
|
+
|
|
5
|
+
from sysetup.context import context
|
|
6
|
+
from sysetup.models import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def setup() -> None:
|
|
10
|
+
install_chromium()
|
|
11
|
+
install_jumpapp()
|
|
12
|
+
install_language_support()
|
|
13
|
+
install_linter_env()
|
|
14
|
+
install_personal_git_repositories()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def install_personal_git_repositories() -> None:
|
|
18
|
+
base_url = "https://github.com/quintenroets"
|
|
19
|
+
token = os.getenv("GITHUB", None)
|
|
20
|
+
if token is not None:
|
|
21
|
+
base_url = base_url.replace("github.com", f"{token}@github.com")
|
|
22
|
+
if not Path.extensions.exists():
|
|
23
|
+
command = f"git clone {base_url}/extensions.git"
|
|
24
|
+
cli.run(command, Path.extensions)
|
|
25
|
+
cli.run(f"pip install git+{base_url}/system.git")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def install_language_support() -> None:
|
|
29
|
+
try:
|
|
30
|
+
packages = cli.capture_output("check-language-support")
|
|
31
|
+
except FileNotFoundError:
|
|
32
|
+
pass
|
|
33
|
+
else:
|
|
34
|
+
if packages:
|
|
35
|
+
cli.install(packages)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def install_chromium() -> None:
|
|
39
|
+
if not cli.capture_output("which chromium-browser", check=False):
|
|
40
|
+
_install_chromium()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _install_chromium() -> None:
|
|
44
|
+
release_name = cli.capture_output_lines("lsb_release -sc")[-1]
|
|
45
|
+
repo_url = f"https://freeshell.de/phd/chromium/{release_name}"
|
|
46
|
+
commands = (
|
|
47
|
+
f'echo "deb {repo_url} /" | sudo tee /etc/apt/sources.list.d/phd-chromium.list',
|
|
48
|
+
"apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 869689FE09306074",
|
|
49
|
+
"apt-get update",
|
|
50
|
+
"apt-get install -y chromium",
|
|
51
|
+
)
|
|
52
|
+
check = not context.is_running_in_test
|
|
53
|
+
cli.run_commands(*commands, shell=True, root=True, check=check) # noqa: S604
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def install_linter_env() -> None:
|
|
57
|
+
if not Path.linter_env.exists():
|
|
58
|
+
cli.run("python -m venv", Path.linter_env.name, cwd=Path.linter_env.parent)
|
|
59
|
+
python_path = Path.linter_env / "bin" / "python"
|
|
60
|
+
cli.run(f"{python_path} -m pip install autoimport powertrace-hooks")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def install_jumpapp() -> None:
|
|
64
|
+
if not cli.capture_output("which jumpapp", check=False):
|
|
65
|
+
with Path.tempdir() as directory:
|
|
66
|
+
cli.run("git clone https://github.com/mkropat/jumpapp", directory)
|
|
67
|
+
cli.run_commands("make", "sudo make install", cwd=directory)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import cli
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
|
|
3
|
+
from sysetup.context import context
|
|
4
|
+
from sysetup.models import Action
|
|
5
|
+
|
|
6
|
+
from . import environment, files, installations, packages
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def main() -> None:
|
|
@@ -11,9 +12,10 @@ def main() -> None:
|
|
|
11
12
|
"""
|
|
12
13
|
action_mapper = {
|
|
13
14
|
Action.all.value: setup,
|
|
14
|
-
Action.files.value: files.setup,
|
|
15
|
-
Action.install.value: installer.setup,
|
|
16
15
|
Action.env.value: environment.setup,
|
|
16
|
+
Action.files.value: files.setup,
|
|
17
|
+
Action.install.value: installations.setup,
|
|
18
|
+
Action.packages.value: packages.setup,
|
|
17
19
|
}
|
|
18
20
|
action = action_mapper[context.options.action.value]
|
|
19
21
|
action()
|
|
@@ -21,6 +23,8 @@ def main() -> None:
|
|
|
21
23
|
|
|
22
24
|
def setup() -> None:
|
|
23
25
|
environment.setup()
|
|
26
|
+
packages.setup()
|
|
24
27
|
files.setup()
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
installations.setup()
|
|
29
|
+
if not context.is_running_in_test:
|
|
30
|
+
cli.run("reboot now", root=True)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import cli
|
|
2
|
+
|
|
3
|
+
from sysetup.context import context
|
|
4
|
+
from sysetup.models import Path
|
|
5
|
+
from sysetup.utils import download_directory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def setup() -> None:
|
|
9
|
+
update_package_manager()
|
|
10
|
+
install_packages()
|
|
11
|
+
cleanup_after_install()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def install_packages() -> None:
|
|
15
|
+
download_directory(Path.packages)
|
|
16
|
+
installations = {"packages": None, "snap": "snap install"}
|
|
17
|
+
for name, command in installations.items():
|
|
18
|
+
path = (Path.packages / name).with_suffix(".yaml")
|
|
19
|
+
packages: list[str] = path.yaml
|
|
20
|
+
cli.install(*packages, install_command=command)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def update_package_manager() -> None:
|
|
24
|
+
if context.apt_is_installed:
|
|
25
|
+
update_apt()
|
|
26
|
+
else:
|
|
27
|
+
cli.run("pacman -Syy", root=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def update_apt() -> None:
|
|
31
|
+
agree_eula_command = (
|
|
32
|
+
"echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula"
|
|
33
|
+
"select true | sudo debconf-set-selections"
|
|
34
|
+
)
|
|
35
|
+
commands = ["sudo apt-get update", agree_eula_command]
|
|
36
|
+
if not context.is_running_in_test:
|
|
37
|
+
# snap currently doesn't work on arm
|
|
38
|
+
commands.append("sudo systemctl enable --now snapd.socket")
|
|
39
|
+
cli.run_commands_in_shell(*commands)
|
|
40
|
+
if not Path("/snap").exists():
|
|
41
|
+
cli.run("ln -s /var/lib/snapd/snap /snap", root=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def cleanup_after_install() -> None:
|
|
45
|
+
after_install_command = (
|
|
46
|
+
"sudo apt-get autoremove -y"
|
|
47
|
+
if context.apt_is_installed
|
|
48
|
+
else (
|
|
49
|
+
"sudo pacman -S --noconfirm python-pip; sudo pacman -S --noconfirm"
|
|
50
|
+
" base-devel; pip install wheel"
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
cli.run_commands_in_shell(after_install_command, "sudo tlp start")
|
|
54
|
+
cli.run("systemctl enable ssh", root=True) # start ssh server before log in
|
|
55
|
+
delete = "apt purge -y" if context.apt_is_installed else "pacman -R --noconfirm"
|
|
56
|
+
commands = (
|
|
57
|
+
"auto-cpufreq --install", # Fails on VM
|
|
58
|
+
f"{delete} firefox", # fails if firefox not installed
|
|
59
|
+
)
|
|
60
|
+
cli.run_commands(*commands, check=False, root=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .download import download_directory, download_file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from backup.backups import Backup
|
|
2
|
+
from backup.models import Path as BackupPath
|
|
3
|
+
|
|
4
|
+
from sysetup.models import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def download_directory(path: Path) -> None:
|
|
8
|
+
Backup(sub_check_path=BackupPath(path), confirm=False).pull()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def download_file(path: Path) -> None:
|
|
12
|
+
Backup(path=BackupPath(path), confirm=False).pull()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sysetup
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Personal system setup
|
|
5
5
|
Author-email: Quinten Roets <qdr2104@columbia.edu>
|
|
6
6
|
License: MIT
|
|
@@ -8,7 +8,7 @@ Project-URL: Source Code, https://github.com/quintenroets/sysetup
|
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: backupmaster<2,>=1.2.
|
|
11
|
+
Requires-Dist: backupmaster<2,>=1.2.6
|
|
12
12
|
Requires-Dist: dbus-next<1,>=0.2.3
|
|
13
13
|
Requires-Dist: package-utils[context]<1,>=0.6.1
|
|
14
14
|
Requires-Dist: powercli<1,>=0.3.1
|
|
@@ -42,11 +42,11 @@ Requires-Dist: package-dev-utils<1,>=0.1.6; extra == "dev"
|
|
|
42
42
|
* Enable GPU acceleration in chrome://flags
|
|
43
43
|
* PDF viewer: set page zoom to page fit
|
|
44
44
|
* Import certificate from Scripts/assets/sysetup/certificates/certificate.crt
|
|
45
|
+
* Click experimental: enable tab groups save and sync
|
|
45
46
|
5) Login to
|
|
46
47
|
* Pycharm professional
|
|
47
48
|
* VNC Server
|
|
48
49
|
6) For new device: set touchpad scroll direction and click on touch
|
|
49
|
-
## Usage
|
|
50
50
|
|
|
51
51
|
## Installation
|
|
52
52
|
```shell
|
|
@@ -17,15 +17,20 @@ src/sysetup/context/__init__.py
|
|
|
17
17
|
src/sysetup/context/context.py
|
|
18
18
|
src/sysetup/main/__init__.py
|
|
19
19
|
src/sysetup/main/environment.py
|
|
20
|
-
src/sysetup/main/
|
|
21
|
-
src/sysetup/main/installer.py
|
|
20
|
+
src/sysetup/main/installations.py
|
|
22
21
|
src/sysetup/main/main.py
|
|
22
|
+
src/sysetup/main/packages.py
|
|
23
|
+
src/sysetup/main/files/__init__.py
|
|
24
|
+
src/sysetup/main/files/assets.py
|
|
25
|
+
src/sysetup/main/files/permissions.py
|
|
26
|
+
src/sysetup/main/files/settings.py
|
|
27
|
+
src/sysetup/main/files/setup.py
|
|
23
28
|
src/sysetup/models/__init__.py
|
|
24
29
|
src/sysetup/models/action.py
|
|
25
|
-
src/sysetup/models/config.py
|
|
26
30
|
src/sysetup/models/options.py
|
|
27
31
|
src/sysetup/models/path.py
|
|
28
|
-
src/sysetup/
|
|
32
|
+
src/sysetup/utils/__init__.py
|
|
33
|
+
src/sysetup/utils/download.py
|
|
29
34
|
tests/test_background.py
|
|
30
35
|
tests/test_cli_entry_point.py
|
|
31
36
|
tests/test_installer.py
|
|
@@ -5,13 +5,13 @@ import cli
|
|
|
5
5
|
import pytest
|
|
6
6
|
from backup.context import context
|
|
7
7
|
from backup.utils import setup
|
|
8
|
-
from sysetup.main.files import set_background
|
|
8
|
+
from sysetup.main.files.settings import set_background
|
|
9
9
|
from sysetup.models import Path
|
|
10
10
|
|
|
11
11
|
plasma_config_path = Path.HOME / ".config" / "plasma-org.kde.plasma.desktop-appletsrc"
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
@pytest.fixture
|
|
14
|
+
@pytest.fixture()
|
|
15
15
|
def restore(path: Path) -> Callable[[Path], Iterator[None]]:
|
|
16
16
|
def _restore(restored_path: Path) -> Iterator[None]:
|
|
17
17
|
exists = restored_path.exists()
|
|
@@ -24,9 +24,9 @@ def restore(path: Path) -> Callable[[Path], Iterator[None]]:
|
|
|
24
24
|
return _restore
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
@pytest.fixture
|
|
27
|
+
@pytest.fixture()
|
|
28
28
|
def restore_and_check(
|
|
29
|
-
|
|
29
|
+
restore: Callable[[Path], Iterator[None]],
|
|
30
30
|
) -> Callable[[Path], Iterator[None]]:
|
|
31
31
|
setup.check_setup()
|
|
32
32
|
env = os.environ | {"RCLONE_CONFIG_PASS": context.secrets.rclone}
|
|
@@ -35,7 +35,10 @@ def restore_and_check(
|
|
|
35
35
|
def _restore_and_check(restored_path: Path) -> Iterator[None]:
|
|
36
36
|
def extract_content_hash() -> str:
|
|
37
37
|
return cli.capture_output(
|
|
38
|
-
"rclone hashsum MD5",
|
|
38
|
+
"rclone hashsum MD5",
|
|
39
|
+
restored_path,
|
|
40
|
+
env=env,
|
|
41
|
+
check=False,
|
|
39
42
|
)
|
|
40
43
|
|
|
41
44
|
content_hash = extract_content_hash()
|
|
@@ -45,14 +48,16 @@ def restore_and_check(
|
|
|
45
48
|
return _restore_and_check
|
|
46
49
|
|
|
47
50
|
|
|
48
|
-
@pytest.fixture
|
|
51
|
+
@pytest.fixture()
|
|
49
52
|
def restore_config_path(
|
|
50
53
|
restore_and_check: Callable[[Path], Iterator[None]],
|
|
51
54
|
) -> Iterator[None]:
|
|
52
55
|
yield from restore_and_check(plasma_config_path)
|
|
53
56
|
|
|
54
57
|
|
|
55
|
-
def test_wallpaper(
|
|
58
|
+
def test_wallpaper(
|
|
59
|
+
restore_config_path: Callable[[Path], Iterator[None]], # noqa: ARG001
|
|
60
|
+
) -> None:
|
|
56
61
|
# "org.kde.PlasmaShell.evaluateScript missing in GITHUB_ACTIONS"
|
|
57
62
|
# still run existing test case to maximize code under coverage
|
|
58
63
|
if "GITHUB_ACTIONS" not in os.environ:
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from backup.backups.remote import Backup
|
|
4
|
-
from backup.models import Path
|
|
5
|
-
from dotenv import load_dotenv
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def setup() -> None:
|
|
9
|
-
if "SUDO_ASKPASS" not in os.environ:
|
|
10
|
-
path = Path(".bash_profile")
|
|
11
|
-
Backup(path=path).pull()
|
|
12
|
-
load_dotenv(dotenv_path=Path.HOME / path)
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import cli
|
|
2
|
-
from backup.backups import Backup
|
|
3
|
-
from backup.backups.cache import cache
|
|
4
|
-
from backup.models import Path as BackupPath
|
|
5
|
-
|
|
6
|
-
from ..models import Path
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def setup() -> None:
|
|
10
|
-
kwargs_mapper = {
|
|
11
|
-
"Script assets": dict(sub_check_path=BackupPath.script_assets),
|
|
12
|
-
"environment": dict(
|
|
13
|
-
sub_check_path=BackupPath("/") / "etc",
|
|
14
|
-
filter_rules=["+ /environment", "- *"],
|
|
15
|
-
),
|
|
16
|
-
"setup files": dict(
|
|
17
|
-
sub_check_path=BackupPath.script_assets / "sysetup" / "files"
|
|
18
|
-
),
|
|
19
|
-
}
|
|
20
|
-
for name, kwargs in kwargs_mapper.items():
|
|
21
|
-
print(f"Downloading {name}..")
|
|
22
|
-
Backup(confirm=False, **kwargs).pull()
|
|
23
|
-
|
|
24
|
-
move_crontab()
|
|
25
|
-
move_setup_files()
|
|
26
|
-
set_permissions()
|
|
27
|
-
set_background()
|
|
28
|
-
remove_clutter()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def remove_clutter() -> None:
|
|
32
|
-
names = (
|
|
33
|
-
"Desktop",
|
|
34
|
-
"Downloads",
|
|
35
|
-
"Music",
|
|
36
|
-
"Pictures",
|
|
37
|
-
"Public",
|
|
38
|
-
"Templates",
|
|
39
|
-
"Videos",
|
|
40
|
-
)
|
|
41
|
-
for name in names:
|
|
42
|
-
path = Path.HOME / name
|
|
43
|
-
path.rmtree()
|
|
44
|
-
|
|
45
|
-
nginx_path = Path("/") / "etc" / "nginx" / "sites-enabled" / "default"
|
|
46
|
-
if nginx_path.exists():
|
|
47
|
-
cli.run("rm", nginx_path, root=True)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def set_background() -> None: # pragma: nocover
|
|
51
|
-
wallpaper_path = (
|
|
52
|
-
Path.HOME / ".local" / "share" / "wallpapers" / "Qwallpapers" / "background.jpg"
|
|
53
|
-
)
|
|
54
|
-
wallpaper_uri = wallpaper_path.as_uri()
|
|
55
|
-
script = Path.update_wallpaper_script.text.replace(
|
|
56
|
-
"__wallpaper_uri__", wallpaper_uri
|
|
57
|
-
)
|
|
58
|
-
run_kde_script(script)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def run_kde_script(script: str) -> None: # pragma: nocover
|
|
62
|
-
command = (
|
|
63
|
-
"qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript"
|
|
64
|
-
)
|
|
65
|
-
cli.run(command, script)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def set_permissions() -> None:
|
|
69
|
-
git_hooks_folder = Path.HOME / ".config" / "git" / "hooks"
|
|
70
|
-
for path in git_hooks_folder.iterdir():
|
|
71
|
-
cli.run("chmod +x", path)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def move_crontab() -> None:
|
|
75
|
-
src = Path.assets / "crontab" / "crontab"
|
|
76
|
-
cli.run("crontab -", input=src.text)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def move_setup_files() -> None:
|
|
80
|
-
setup_files_root = BackupPath.script_assets / "sysetup" / "files"
|
|
81
|
-
setup_files = []
|
|
82
|
-
archived_setup_files = []
|
|
83
|
-
for path in setup_files_root.rglob("*"):
|
|
84
|
-
if path.is_file():
|
|
85
|
-
if path.archive_format is None:
|
|
86
|
-
setup_files.append(path)
|
|
87
|
-
else:
|
|
88
|
-
archived_setup_files.append(path)
|
|
89
|
-
|
|
90
|
-
backup = cache.Backup(paths=setup_files)
|
|
91
|
-
backup.pull()
|
|
92
|
-
|
|
93
|
-
for path in archived_setup_files:
|
|
94
|
-
dest = (backup.source / path.relative_to(setup_files_root)).parent
|
|
95
|
-
with cli.status(f"Unpacking {path.relative_to(setup_files_root)}"):
|
|
96
|
-
cli.capture_output("unzip -o", path, "-d", dest, root=dest.is_root)
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import cli
|
|
2
|
-
from cli.commands.install import extract_package_manager_command
|
|
3
|
-
|
|
4
|
-
from ..models import Path
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def setup() -> None:
|
|
8
|
-
package_manager = "apt" if "apt" in extract_package_manager_command() else "pacman"
|
|
9
|
-
update_package_manager(package_manager)
|
|
10
|
-
install()
|
|
11
|
-
install_chromium()
|
|
12
|
-
install_extensions()
|
|
13
|
-
install_jumpapp()
|
|
14
|
-
install_language_support()
|
|
15
|
-
install_linter_env()
|
|
16
|
-
after_install(package_manager)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def install_extensions() -> None:
|
|
20
|
-
if not Path.extensions.exists():
|
|
21
|
-
command = "git clone https://github.com/quintenroets/extensions"
|
|
22
|
-
cli.run(command, cwd=Path.extensions.parent)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def install_language_support() -> None:
|
|
26
|
-
packages = cli.capture_output("check-language-support")
|
|
27
|
-
cli.install(packages)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def install_chromium() -> None:
|
|
31
|
-
if not cli.capture_output("which chromium-browser", check=False):
|
|
32
|
-
_install_chromium()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _install_chromium() -> None:
|
|
36
|
-
release_name = cli.capture_output_lines("lsb_release -sc")[-1]
|
|
37
|
-
repo_url = f"https://freeshell.de/phd/chromium/{release_name}"
|
|
38
|
-
commands = (
|
|
39
|
-
f'echo "deb {repo_url} /" | sudo tee /etc/apt/sources.list.d/phd-chromium.list',
|
|
40
|
-
"apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 869689FE09306074",
|
|
41
|
-
"apt-get update",
|
|
42
|
-
"apt-get install -y chromium",
|
|
43
|
-
)
|
|
44
|
-
cli.run_commands(*commands, shell=True, root=True)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def install_linter_env() -> None:
|
|
48
|
-
if not Path.linter_env.exists():
|
|
49
|
-
cli.run("python -m venv", Path.linter_env.name, cwd=Path.linter_env.parent)
|
|
50
|
-
python_path = Path.linter_env / "bin" / "python"
|
|
51
|
-
cli.run(f"{python_path} -m pip install autoimport powertrace-hooks")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def install() -> None:
|
|
55
|
-
installations = {
|
|
56
|
-
"packages": None,
|
|
57
|
-
"snap": "snap install",
|
|
58
|
-
}
|
|
59
|
-
for name, command in installations.items():
|
|
60
|
-
path = (Path.packages / name).with_suffix(".yaml")
|
|
61
|
-
packages: list[str] = path.yaml
|
|
62
|
-
cli.install(*packages, install_command=command)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def update_package_manager(package_manager: str) -> None:
|
|
66
|
-
if package_manager == "apt":
|
|
67
|
-
cli.run_commands_in_shell(
|
|
68
|
-
"sudo apt update",
|
|
69
|
-
# agree eula
|
|
70
|
-
(
|
|
71
|
-
"echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula"
|
|
72
|
-
"select true | sudo debconf-set-selections"
|
|
73
|
-
),
|
|
74
|
-
"sudo systemctl enable --now snapd.socket",
|
|
75
|
-
# snap currently doesnt work on arm
|
|
76
|
-
)
|
|
77
|
-
if not Path("/snap").exists():
|
|
78
|
-
cli.run("ln -s /var/lib/snapd/snap /snap", root=True)
|
|
79
|
-
elif package_manager == "pacman":
|
|
80
|
-
cli.run("pacman -Syy", root=True)
|
|
81
|
-
else:
|
|
82
|
-
raise Exception("No package manager found")
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def after_install(package_manager: str) -> None:
|
|
86
|
-
after_install_command = (
|
|
87
|
-
"sudo apt autoremove -y"
|
|
88
|
-
if package_manager == "apt"
|
|
89
|
-
else (
|
|
90
|
-
"sudo pacman -S --noconfirm python-pip; sudo pacman -S --noconfirm"
|
|
91
|
-
" base-devel; pip install wheel"
|
|
92
|
-
)
|
|
93
|
-
)
|
|
94
|
-
cli.run_commands_in_shell(after_install_command, "sudo tlp start")
|
|
95
|
-
cli.run("systemctl enable ssh", root=True) # start ssh server before log in
|
|
96
|
-
|
|
97
|
-
delete = "apt purge -y" if package_manager == "apt" else "pacman -R --noconfirm"
|
|
98
|
-
cli.run_commands(
|
|
99
|
-
"auto-cpufreq --install", # Fails on VM
|
|
100
|
-
f"{delete} firefox", # fails if firefox not installed
|
|
101
|
-
check=False,
|
|
102
|
-
root=True,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def install_jumpapp() -> None:
|
|
107
|
-
if not cli.capture_output("which jumpapp", check=False):
|
|
108
|
-
cli.run("git clone https://github.com/mkropat/jumpapp")
|
|
109
|
-
cli.run_commands("make", "sudo make install", cwd="jumpapp")
|
|
110
|
-
cli.run("rm -rf jumpapp")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def install_vpn() -> None:
|
|
114
|
-
add_source_command = (
|
|
115
|
-
'echo "deb https://repo.windscribe.com/ubuntu bionic main" | sudo tee'
|
|
116
|
-
" /etc/apt/sources.list.d/windscribe-repo.list"
|
|
117
|
-
)
|
|
118
|
-
commands = (
|
|
119
|
-
"sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key FDC247B7",
|
|
120
|
-
add_source_command,
|
|
121
|
-
"install windscribe-cli",
|
|
122
|
-
)
|
|
123
|
-
cli.run_commands_in_shell(*commands)
|
|
124
|
-
# login: $email2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|