Last active
September 7, 2026 08:59
-
-
Save birgersp/c7d84daca89d68daa9d68a65ec67e0f0 to your computer and use it in GitHub Desktop.
fedora-silverblue-setup.py
This file contains hidden or 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
| # NOTE: this utility script is a) very hastily put togheter and b) under constant modification. | |
| from os import environ, path | |
| import os | |
| import shutil | |
| import subprocess | |
| import sys | |
| from typing import Callable, Dict, List | |
| FEDORA_VERSION = 44 | |
| DOCKER_DESKTOP_RPM_URL = "https://desktop.docker.com/linux/main/amd64/docker-desktop-x86_64.rpm" | |
| EXTERNAL_RPM_PACKAGES: Dict[str, str] = { | |
| "google-chrome-stable": "https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm", | |
| "chatgpt": "https://persistent.oaistatic.com/codex-app-prod/linux/rpm/latest/chatgpt.x86_64.rpm", | |
| "docker-desktop": DOCKER_DESKTOP_RPM_URL, | |
| } | |
| RPM_PACKAGES = [ | |
| "akmods", | |
| "code", | |
| "dbus-tools", | |
| "git", | |
| "insync", | |
| "mosh", | |
| "pipx", | |
| "postgresql", | |
| "rpmdevtools", | |
| "sqlite3", | |
| "yaru-theme", | |
| "zsh", | |
| f"https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-{FEDORA_VERSION}.noarch.rpm", | |
| f"https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-{FEDORA_VERSION}.noarch.rpm", | |
| ] + list(EXTERNAL_RPM_PACKAGES.values()) | |
| FLATPAK_APPS = [ | |
| "com.github.git_cola.git-cola", | |
| "org.pgadmin.pgadmin4", | |
| "com.spotify.Client", | |
| "com.discordapp.Discord", | |
| "org.signal.Signal", | |
| "io.github.Faugus.faugus-launcher", | |
| "org.sqlitebrowser.sqlitebrowser", | |
| "com.valvesoftware.Steam", | |
| "page.tesk.Refine", | |
| "com.mattjakeman.ExtensionManager", | |
| "io.github.kolunmi.Bazaar", | |
| "io.github.pwr_solaar.solaar", | |
| ] | |
| GNOME_EXTENSIONS = [ | |
| "quicksettings-audio-devices-hider@marcinjahn.com", | |
| "appindicatorsupport@rgcjonas.gmail.com", | |
| "clipboard-indicator@tudmotu.com", | |
| "dash-to-panel@jderose9.github.com", | |
| "emoji-copy@felipeftn", | |
| "gnome-extensions-cli install", | |
| "switcher@landau.fi", | |
| "system-monitor@gnome-shell-extensions.gcampax.github.com", | |
| "window-calls@domandoman.xyz", | |
| ] | |
| HOME = os.environ["HOME"] | |
| CHROME_PROFILE_DIRECTORY = "Default" | |
| CHROME_DESKTOP_ENTRY_FILES = [ | |
| "google-chrome.desktop", | |
| "com.google.Chrome.desktop", | |
| ] | |
| SIGNAL_FLATPAK_APP_ID = "org.signal.Signal" | |
| STEAM_FLATPAK_APP_ID = "com.valvesoftware.Steam" | |
| # | |
| # | |
| # | |
| # repo files, installation urls, etc | |
| DOCKER_REPO_PATH = "/etc/yum.repos.d/docker-ce.repo" | |
| DOCKER_REPO_URL = "https://download.docker.com/linux/fedora/docker-ce.repo" | |
| VSCODE_REPO_PATH = "/etc/yum.repos.d/vscode.repo" | |
| VSCODE_REPO_FILE = """[vscode-yum] | |
| name=vscode-yum | |
| baseurl=https://packages.microsoft.com/yumrepos/vscode/ | |
| repo_gpgcheck=0 | |
| gpgcheck=0 | |
| enabled=1 | |
| gpgkey=https://packages.microsoft.com/yumrepos/vscode/repodata/repomd.xml.key | |
| """ | |
| NAUTILUS_TYPEAHEAD_REPO_PATH = f"/etc/yum.repos.d/nelsonaloysio-nautilus-typeahead-fedora-{FEDORA_VERSION}.repo" | |
| NAUTILUS_TYPEAHEAD_REPO_URL = f"https://copr.fedorainfracloud.org/coprs/nelsonaloysio/nautilus-typeahead/repo/fedora-44/nelsonaloysio-nautilus-typeahead-fedora-{FEDORA_VERSION}.repo" | |
| INSYNC_REPO_PATH = "/etc/yum.repos.d/insync.repo" | |
| INSYNC_REPO_FILE = """[insync] | |
| name=insync repo | |
| baseurl=http://yum.insync.io/fedora/$releasever/ | |
| gpgcheck=1 | |
| gpgkey=https://d2t3ff60b2tol4.cloudfront.net/repomd.xml.key | |
| enabled=1 | |
| metadata_expire=120m | |
| """ | |
| NVM_INSTALL_URL = "https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh" | |
| # | |
| # | |
| # | |
| # main | |
| def main() -> None: | |
| actions: Dict[str, Callable[[], None]] = { | |
| "all": doAllActions, | |
| "configure gnome": configureGnome, | |
| "configure chrome": configureChromeDesktopEntries, | |
| "configure steam autostart": configureSteamAutostart, | |
| "install flatpaks": installFlatpaks, | |
| "install nvm": installNvm, | |
| "install deno": installDeno, | |
| "install all system packages": installSystemPackages, | |
| "reinstall (upgrade) url-package": upgradeExternal, | |
| } | |
| args = sys.argv[2:] | |
| if len(sys.argv) > 1: | |
| action = sys.argv[1] | |
| else: | |
| action = promptChooseItem(list(actions.keys())) | |
| callback = actions.get(action) | |
| if callback is None: | |
| print(f"Unrecognized option: {action}") | |
| print("Pick one of these:") | |
| for action in list(actions.keys()): | |
| print(f" {action}") | |
| print("Or none to open the selector") | |
| exit(1) | |
| callback() | |
| def promptChooseItem(items: List[str]) -> str: | |
| print("Which one?") | |
| for number, action in enumerate(items, start=1): | |
| print(f" {number}. {action}") | |
| while True: | |
| try: | |
| selection = input(f"Choose [1-{len(items)}]: ").strip() | |
| except (EOFError, KeyboardInterrupt): | |
| print("\nCancelled.") | |
| raise SystemExit(0) | |
| if selection.isdigit(): | |
| selectionNumber = int(selection) | |
| if 1 <= selectionNumber <= len(items): | |
| return items[selectionNumber - 1] | |
| print(f"Please enter a number from 1 to {len(items)}.") | |
| def doAllActions() -> None: | |
| appState = AppState() | |
| if appState.state == "": | |
| appState.setState("1") | |
| if appState.state == "1": | |
| installSystemPackages() | |
| installNautilusTypeahead() | |
| appState.setState("2") | |
| print("you'll need to reboot to proceed. reboot and re-run this script") | |
| exit(0) | |
| if appState.state == "2": | |
| installFlatpaks() | |
| installGnomeExtensions() | |
| configureGnome() | |
| configureChatgptLogoutKill() | |
| configureChromeDesktopEntries() | |
| configureSteamAutostart() | |
| setDefaultShellToZsh() | |
| appState.setState("3") | |
| print("you'll need to log out") | |
| exit(0) | |
| # | |
| # | |
| # | |
| # helpers | |
| def assertCommandExists(command: str) -> None: | |
| if not checkCommandExists(command): | |
| print(f"command \"{command}\" not found. perhaps you need to reboot?") | |
| exit(1) | |
| def writeStateFile(fileName: str, content: str) -> None: | |
| stateFilePath = getStateFilePath(fileName) | |
| stateFileDir = os.path.dirname(stateFilePath) | |
| if not os.path.exists(stateFileDir): | |
| os.mkdir(stateFileDir) | |
| with open(stateFilePath, 'w', encoding='utf-8') as file: | |
| file.write(content) | |
| def readStateFile(fileName: str) -> str: | |
| try: | |
| with open(getStateFilePath(fileName)) as file: | |
| return file.read().strip() | |
| except: | |
| return "" | |
| def optionalBool() -> bool | None: | |
| return None | |
| def executeReturnOutput(command: str) -> str: | |
| print(f"> {command}") | |
| process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) | |
| if process.stdout is None: | |
| raise Exception() | |
| buffer = [] | |
| for line in process.stdout: | |
| buffer.append(line) | |
| process.wait() | |
| output = "".join(buffer).strip() | |
| if process.returncode != 0: | |
| raise subprocess.CalledProcessError(returncode=process.returncode, cmd=command, output=output) | |
| return output | |
| def executePrintOutput(command: str) -> None: | |
| print(f"> {command}") | |
| if subprocess.run(command, shell=True).returncode != 0: | |
| print("Command failed") | |
| exit(1) | |
| def checkInstallVscodeExtension(id: str) -> None: | |
| targetFolder = id.lower().replace("@", "-") | |
| for file in os.listdir(path.join(environ["HOME"], ".vscode", "extensions")): | |
| if file.startswith(targetFolder): | |
| # target folder already present | |
| return | |
| executePrintOutput(f"code --install-extension {id}") | |
| def checkCommandExists(cmd: str) -> bool: | |
| return shutil.which(cmd) != None | |
| def getStateFilePath(fileName: str) -> str: | |
| return path.join(environ["HOME"], ".local", "state", "birgersp-setup", fileName) | |
| def checkSystemIsFedora() -> bool: | |
| return executeReturnOutput("grep '^ID=fedora' /etc/os-release") == "ID=fedora" | |
| def writeFileAsSudo(filePath: str, content: str) -> None: | |
| if not content.endswith("\n"): | |
| content = content + "\n" | |
| subprocess.run(["sudo", "mkdir", "-p", os.path.dirname(filePath)]) | |
| subprocess.run(["sudo", "tee", filePath], input=content, text=True, capture_output=True) | |
| def checkFileExists(filePath: str) -> bool: | |
| return os.path.exists(filePath) | |
| class AppState: | |
| state = "" | |
| def __init__(self): | |
| self.state = readStateFile("state") | |
| def setState(self, newState: str) -> None: | |
| writeStateFile("state", newState) | |
| self.state = newState | |
| # | |
| # | |
| # | |
| # setup action functions | |
| def configureGnome() -> None: | |
| installGnomeExtensions() | |
| setGnomeSettings() | |
| configureChatgptLogoutKill() | |
| configureChromeDesktopEntries() | |
| configureSteamAutostart() | |
| def installSystemPackages() -> None: | |
| print("Installing system packages...") | |
| if not checkFileExists(VSCODE_REPO_PATH): | |
| writeFileAsSudo(VSCODE_REPO_PATH, VSCODE_REPO_FILE) | |
| if not checkFileExists(INSYNC_REPO_PATH): | |
| writeFileAsSudo(INSYNC_REPO_PATH, INSYNC_REPO_FILE) | |
| configureDockerRepo() | |
| rpmPackagesStr = " ".join(RPM_PACKAGES) | |
| executePrintOutput(f"sudo rpm-ostree install --idempotent -y {rpmPackagesStr}") | |
| def configureDockerRepo() -> None: | |
| if not checkFileExists(DOCKER_REPO_PATH): | |
| executePrintOutput(f"sudo wget \"{DOCKER_REPO_URL}\" -O \"{DOCKER_REPO_PATH}\"") | |
| def installDockerDesktop() -> None: | |
| print("Installing Docker Desktop...") | |
| configureDockerRepo() | |
| executePrintOutput(f"sudo rpm-ostree install -y {DOCKER_DESKTOP_RPM_URL}") | |
| def installNautilusTypeahead() -> None: | |
| if not checkFileExists(NAUTILUS_TYPEAHEAD_REPO_PATH): | |
| executePrintOutput(f"sudo wget \"{NAUTILUS_TYPEAHEAD_REPO_URL}\" -O \"{NAUTILUS_TYPEAHEAD_REPO_PATH}\"") | |
| executePrintOutput(f"sudo rpm-ostree override remove nautilus nautilus-extensions papers-nautilus --install nautilus-typeahead") | |
| def installFlatpaks() -> None: | |
| executePrintOutput("flatpak remote-add --system --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo") | |
| flatpaksStr = " ".join(FLATPAK_APPS) | |
| executePrintOutput(f"flatpak install --system -y flathub {flatpaksStr}") | |
| configureSignalFlatpak() | |
| def configureSignalFlatpak() -> None: | |
| executePrintOutput(f"flatpak override --user --env=SIGNAL_PASSWORD_STORE=gnome-libsecret {SIGNAL_FLATPAK_APP_ID}") | |
| def installNvm() -> None: | |
| if checkFileExists(path.join(HOME, ".nvm")): | |
| return | |
| executePrintOutput(f"wget -qO- {NVM_INSTALL_URL} | bash") | |
| def installDeno() -> None: | |
| if checkCommandExists("deno"): | |
| return | |
| executePrintOutput("curl -fsSL https://deno.land/install.sh | sh") | |
| def installGnomeExtensions() -> None: | |
| executePrintOutput("pipx install gnome-extensions-cli") | |
| executePrintOutput("pipx ensurepath") | |
| if not checkCommandExists("gnome-extensions-cli"): | |
| print("Command \"install gnome-extensions-cli\" not found. Try re-running in a new shell (where PATH is loaded)") | |
| exit(1) | |
| extensionsStr = " ".join(GNOME_EXTENSIONS) | |
| executePrintOutput(f"gnome-extensions-cli install {extensionsStr}") | |
| def setGnomeSettings() -> None: | |
| commands = [ | |
| "dconf write \"/org/gnome/desktop/interface/icon-theme\" \"'Yaru-blue'\"", | |
| "dconf write \"/org/gnome/desktop/wm/keybindings/switch-applications-backward\" \"@as []\"", | |
| "dconf write \"/org/gnome/desktop/wm/keybindings/switch-applications\" \"@as []\"", | |
| "dconf write \"/org/gnome/desktop/wm/keybindings/switch-windows-backward\" \"['<Shift><Alt>Tab']\"", | |
| "dconf write \"/org/gnome/desktop/wm/keybindings/switch-windows\" \"['<Alt>Tab']\"", | |
| "dconf write \"/org/gnome/desktop/wm/preferences/button-layout\" \"':minimize,maximize,close'\"", | |
| "dconf write \"/org/gnome/desktop/wm/preferences/resize-with-right-button\" \"true\"", | |
| "gsettings set org.gnome.desktop.wm.keybindings close \"['<Alt>F4']\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/binding\" \"'<Control><Alt>t'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/command\" \"'bash /var/home/birger/repo/tools/linux/hotkeys.sh --ctrl-alt-t'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/name\" \"'ctrl+alt+t'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/binding\" \"'<Super>m'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/command\" \"'bash /var/home/birger/repo/tools/linux/hotkeys.sh --super-m'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/name\" \"'super+m'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/binding\" \"'<Super>g'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/command\" \"'bash /var/home/birger/repo/tools/linux/hotkeys.sh --super-g'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/name\" \"'super+g'\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings\" \"['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/']\"", | |
| "dconf write \"/org/gnome/settings-daemon/plugins/media-keys/terminal\" \"@as []\"", | |
| "dconf write \"/org/gnome/shell/extensions/clipboard-indicator/cache-size\" \"20\"", | |
| "dconf write \"/org/gnome/shell/extensions/clipboard-indicator/history-size\" \"100\"", | |
| "dconf write \"/org/gnome/shell/extensions/clipboard-indicator/toggle-menu\" \"['<Control><Alt>h']\"", | |
| "dconf write \"/org/gnome/shell/extensions/dash-to-panel/dot-size\" \"0\"", | |
| "dconf write \"/org/gnome/shell/extensions/dash-to-panel/group-apps\" \"false\"", | |
| "dconf write \"/org/gnome/shell/extensions/dash-to-panel/trans-bg-color\" \"'#3d3846'\"", | |
| "dconf write \"/org/gnome/shell/extensions/dash-to-panel/trans-panel-opacity\" \"1.0\"", | |
| "dconf write \"/org/gnome/shell/extensions/dash-to-panel/trans-use-custom-bg\" \"true\"", | |
| "dconf write \"/org/gnome/shell/extensions/dash-to-panel/trans-use-custom-opacity\" \"false\"", | |
| "dconf write \"/org/gnome/shell/extensions/emoji-copy/always-show\" \"false\"", | |
| "dconf write \"/org/gnome/shell/extensions/emoji-copy/emoji-keybind\" \"['<Ctrl><Alt>period']\"", | |
| "dconf write \"/org/gnome/shell/extensions/switcher/fade-enable\" \"true\"", | |
| "dconf write \"/org/gnome/shell/extensions/switcher/font-size\" \"uint32 24\"", | |
| "dconf write \"/org/gnome/shell/extensions/switcher/max-width-percentage\" \"uint32 50\"", | |
| "dconf write \"/org/gnome/shell/extensions/system-monitor/show-cpu\" \"false\"", | |
| "dconf write \"/org/gnome/shell/extensions/system-monitor/show-download\" \"false\"", | |
| "dconf write \"/org/gnome/shell/extensions/system-monitor/show-swap\" \"false\"", | |
| "dconf write \"/org/gnome/shell/extensions/system-monitor/show-upload\" \"false\"", | |
| "dconf write \"/org/gnome/shell/keybindings/toggle-message-tray\" \"@as []\"", | |
| ] | |
| for command in commands: | |
| executePrintOutput(command) | |
| def configureChatgptLogoutKill() -> None: | |
| localBinDir = path.join(HOME, ".local", "bin") | |
| systemdUserDir = path.join(HOME, ".config", "systemd", "user") | |
| wantsDir = path.join(systemdUserDir, "graphical-session.target.wants") | |
| os.makedirs(localBinDir, exist_ok=True) | |
| os.makedirs(wantsDir, exist_ok=True) | |
| killScriptPath = path.join(localBinDir, "kill-chatgpt-app-now") | |
| servicePath = path.join(systemdUserDir, "kill-chatgpt-app-on-logout.service") | |
| serviceLinkPath = path.join(wantsDir, "kill-chatgpt-app-on-logout.service") | |
| oldKillScriptPath = path.join(localBinDir, "kill-codex-desktop-now") | |
| oldServicePath = path.join(systemdUserDir, "kill-codex-desktop-on-logout.service") | |
| oldServiceLinkPath = path.join(wantsDir, "kill-codex-desktop-on-logout.service") | |
| killScript = """#!/usr/bin/env bash | |
| set -u | |
| legacy_app_id="codex-desktop" | |
| legacy_state_dir="${XDG_STATE_HOME:-${HOME}/.local/state}/${legacy_app_id}" | |
| log_dir="${XDG_STATE_HOME:-${HOME}/.local/state}/chatgpt-app-logout-kill" | |
| log_file="${log_dir}/events.log" | |
| mkdir -p "${log_dir}" | |
| patterns=( | |
| "${HOME}/.local/share/codex-app/electron" | |
| "${HOME}/.local/share/codex-app/start.sh" | |
| "${HOME}/.local/share/codex-app/.codex-linux/webview-server.py" | |
| "--user-data-dir=${legacy_state_dir}/electron-user-data" | |
| "CODEX_LINUX_APP_ID=${legacy_app_id}" | |
| "/usr/lib/chatgpt/ChatGPT" | |
| "/usr/lib/chatgpt/resources/codex" | |
| "/usr/lib/chatgpt/resources/cua_node/bin/node" | |
| "/usr/lib/chatgpt/resources/cua_node/bin/node_repl" | |
| "--user-data-dir=${HOME}/.config/Codex" | |
| "--database=${HOME}/.config/Codex/Crashpad" | |
| ) | |
| log_pattern="codex-app/electron|codex-app/start.sh|codex-linux/webview-server.py|--user-data-dir=.*codex-desktop|/usr/lib/chatgpt/ChatGPT|/usr/lib/chatgpt/resources/codex|/usr/lib/chatgpt/resources/cua_node/bin/node|--user-data-dir=${HOME}/.config/Codex|--database=${HOME}/.config/Codex/Crashpad" | |
| log_matches() { | |
| pgrep -a -u "${USER}" -f "${log_pattern}" || true | |
| } | |
| read_pid_files() { | |
| find "${legacy_state_dir}" -type f \\( -name 'app.pid' -o -name 'webview.pid' \\) -print 2>/dev/null | | |
| while IFS= read -r pid_file; do | |
| sed -n '1p' "${pid_file}" 2>/dev/null | |
| done | | |
| awk '/^[0-9]+$/ { print }' | |
| } | |
| signal_pid_files() { | |
| local signal="$1" | |
| local pid | |
| while IFS= read -r pid; do | |
| kill "-${signal}" "${pid}" 2>/dev/null || true | |
| done < <(read_pid_files) | |
| } | |
| { | |
| printf '\\n[%s] killing ChatGPT app processes for logout\\n' "$(date --iso-8601=seconds)" | |
| log_matches | |
| } >> "${log_file}" 2>&1 | |
| signal_pid_files TERM | |
| for pattern in "${patterns[@]}"; do | |
| pkill -TERM -u "${USER}" -f "${pattern}" 2>/dev/null || true | |
| done | |
| sleep 1 | |
| signal_pid_files KILL | |
| for pattern in "${patterns[@]}"; do | |
| pkill -KILL -u "${USER}" -f "${pattern}" 2>/dev/null || true | |
| done | |
| { | |
| printf '[%s] remaining ChatGPT app matches after kill attempt\\n' "$(date --iso-8601=seconds)" | |
| log_matches | |
| } >> "${log_file}" 2>&1 | |
| """ | |
| service = """[Unit] | |
| Description=Kill ChatGPT app when the graphical session ends | |
| Documentation=man:systemd.special(7) | |
| PartOf=graphical-session.target | |
| After=graphical-session.target | |
| Before=shutdown.target | |
| [Service] | |
| Type=oneshot | |
| RemainAfterExit=yes | |
| ExecStart=/usr/bin/true | |
| ExecStop=%h/.local/bin/kill-chatgpt-app-now | |
| TimeoutStopSec=3s | |
| [Install] | |
| WantedBy=graphical-session.target | |
| """ | |
| for oldPath in [oldServiceLinkPath, oldServicePath, oldKillScriptPath]: | |
| if os.path.lexists(oldPath): | |
| os.remove(oldPath) | |
| with open(killScriptPath, "w", encoding="utf-8") as file: | |
| file.write(killScript) | |
| os.chmod(killScriptPath, 0o755) | |
| with open(servicePath, "w", encoding="utf-8") as file: | |
| file.write(service) | |
| if os.path.lexists(serviceLinkPath): | |
| os.remove(serviceLinkPath) | |
| os.symlink("../kill-chatgpt-app-on-logout.service", serviceLinkPath) | |
| executePrintOutput("systemctl --user daemon-reload") | |
| executePrintOutput("systemctl --user start kill-chatgpt-app-on-logout.service") | |
| def configureChromeDesktopEntries() -> None: | |
| applicationsDir = path.join(HOME, ".local", "share", "applications") | |
| os.makedirs(applicationsDir, exist_ok=True) | |
| for desktopEntryFile in CHROME_DESKTOP_ENTRY_FILES: | |
| srcPath = path.join("/usr/share/applications", desktopEntryFile) | |
| destPath = path.join(applicationsDir, desktopEntryFile) | |
| if not checkFileExists(srcPath): | |
| print(f"Skipping {desktopEntryFile}: {srcPath} does not exist") | |
| continue | |
| shutil.copyfile(srcPath, destPath) | |
| with open(destPath, encoding="utf-8") as file: | |
| lines = file.read().splitlines() | |
| replacements = { | |
| "Exec=/usr/bin/google-chrome-stable %U": f"Exec=/usr/bin/google-chrome-stable --profile-directory={CHROME_PROFILE_DIRECTORY} %U", | |
| "Exec=/usr/bin/google-chrome-stable": f"Exec=/usr/bin/google-chrome-stable --profile-directory={CHROME_PROFILE_DIRECTORY}", | |
| "Exec=/usr/bin/google-chrome-stable --incognito": f"Exec=/usr/bin/google-chrome-stable --profile-directory={CHROME_PROFILE_DIRECTORY} --incognito", | |
| } | |
| with open(destPath, "w", encoding="utf-8") as file: | |
| file.write("\n".join([replacements.get(line, line) for line in lines]) + "\n") | |
| if checkCommandExists("update-desktop-database"): | |
| executePrintOutput(f"update-desktop-database \"{applicationsDir}\"") | |
| def configureSteamAutostart() -> None: | |
| autostartDir = path.join(HOME, ".config", "autostart") | |
| os.makedirs(autostartDir, exist_ok=True) | |
| if checkCommandExists("steam"): | |
| execCommand = "steam -silent" | |
| tryExec = "steam" | |
| else: | |
| execCommand = f"flatpak run {STEAM_FLATPAK_APP_ID} -silent" | |
| tryExec = "flatpak" | |
| content = f"""[Desktop Entry] | |
| Type=Application | |
| Name=Steam | |
| Comment=Start Steam automatically on login | |
| Exec={execCommand} | |
| TryExec={tryExec} | |
| Icon=steam | |
| Terminal=false | |
| X-GNOME-Autostart-enabled=true | |
| X-GNOME-Autostart-Delay=10 | |
| """ | |
| with open(path.join(autostartDir, "steam.desktop"), "w", encoding="utf-8") as file: | |
| file.write(content) | |
| def setDefaultShellToZsh() -> None: | |
| zshPath = shutil.which("zsh") | |
| if zshPath is None: | |
| print("zsh is not found(?). you might wanna reboot or something") | |
| exit(1) | |
| if os.environ["SHELL"] == zshPath: | |
| # shell is zsh | |
| return | |
| executePrintOutput(f"chsh -s {zshPath}") | |
| def upgradeExternal(): | |
| packages = list(EXTERNAL_RPM_PACKAGES.keys()) | |
| item = promptChooseItem(packages + ["all"]) | |
| if item == "all": | |
| command = "sudo rpm-ostree install " + " ".join(list(EXTERNAL_RPM_PACKAGES.values())) | |
| for package in packages: | |
| command += f" --uninstall {package}" | |
| executePrintOutput(command) | |
| else: | |
| url = EXTERNAL_RPM_PACKAGES[item] | |
| executePrintOutput(f"sudo rpm-ostree install {url} --uninstall {item}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment