diff --git a/docs/INDEX.md b/docs/INDEX.md new file mode 100644 index 0000000..d3aafee --- /dev/null +++ b/docs/INDEX.md @@ -0,0 +1,118 @@ +# Linux Setup Script — Documentation Index + +This directory contains the comprehensive technical documentation for the +`linux-setup-script` repository. The documentation is intended to serve as a +persistent knowledge base for future coding agents, eliminating the need to +repeatedly reverse-engineer the repository from source. + +## Root Document Map + +| Document | Location | Purpose | +|----------|----------|---------| +| ARCHITECTURE.md | Repository root | High-level architecture (not present) | +| SECURITY.md | Repository root | Security model and threat mitigations (not present) | +| ROADMAP.md | Repository root | Future development plans (not present) | +| PRIVACY.md | Repository root | Data handling and privacy (not present) | +| LICENSE | Repository root | License terms (not present) | + +## Documentation Catalogue + +### Core Architecture + +| File | Description | +|------|-------------| +| [architecture.md](./architecture.md) | High-level architecture, component relationships, dependency direction | +| [repository-overview.md](./repository-overview.md) | Repository purpose, scope, supported platforms, entry points | +| [repository-structure.md](./repository-structure.md) | Source tree layout, module organisation, naming conventions | +| [design-decisions.md](./design-decisions.md) | Key architectural/design choices and rationale | +| [dependencies.md](./dependencies.md) | External and internal dependencies | + +### Configuration & Data + +| File | Description | +|------|-------------| +| [configuration.md](./configuration.md) | Configuration schema, sources, precedence, derivation | +| [data-model.md](./data-model.md) | Domain entities, relationships, persistence | +| [state-and-persistence.md](./state-and-persistence.md) | State management, stores, caches, migrations | + +### Components + +| File | Description | +|------|-------------| +| [components/foundation-layer.md](./components/foundation-layer.md) | Core utilities: filesystem, execution, system-info, package management | +| [components/configuration-management.md](./components/configuration-management.md) | Config file manipulation (INI, JSON, XML, GSettings, modprobe) | +| [components/package-management.md](./components/package-management.md) | Unified package manager abstraction layer | +| [components/service-management.md](./components/service-management.md) | systemd/OpenRC service control | +| [components/permission-management.md](./components/permission-management.md) | Flatpak/GNOME/Android permission management | +| [components/hardware-integration.md](./components/hardware-integration.md) | Device-specific hardware setup (Argon ONE UP, GPU, laptop power) | + +### Execution Flows + +| File | Description | +|------|-------------| +| [flows/startup-and-provisioning.md](./flows/startup-and-provisioning.md) | Complete run.sh execution flow from entry to completion | +| [flows/package-management.md](./flows/package-management.md) | Repository config, package install/update/cleanup flow | +| [flows/configuration-deployment.md](./flows/configuration-deployment.md) | RC files, resources, profiles deployment flow | +| [flows/system-configuration.md](./flows/system-configuration.md) | Kernel, sysctl, modprobe, GRUB, GNOME configuration flow | +| [flows/git-setup.md](./flows/git-setup.md) | GPG/SSH key generation and Git configuration flow | + +### Behaviours + +| File | Description | +|------|-------------| +| [behaviour/theme-and-font-resolution.md](./behaviour/theme-and-font-resolution.md) | Theme variant, font sizing, DPI/resolution adaptation | +| [behaviour/desktop-environment-configuration.md](./behaviour/desktop-environment-configuration.md) | GNOME, KDE, MATE, LXDE, Phosh specific configurations | +| [behaviour/application-launcher-customisation.md](./behaviour/application-launcher-customisation.md) | .desktop file modifications for 100+ applications | +| [behaviour/autostart-and-default-apps.md](./behaviour/autostart-and-default-apps.md) | User autostart entries and mimeapps.list associations | +| [behaviour/directory-and-file-management.md](./behaviour/directory-and-file-management.md) | XDG directories, hidden files, symlinks, templates | + +### Integrations + +| File | Description | +|------|-------------| +| [integrations/package-managers.md](./integrations/package-managers.md) | pacman, apt, apk, pkg, Flatpak, Cargo, GitHub releases | +| [integrations/display-servers.md](./integrations/display-servers.md) | Wayland, X11, tty detection and adaptation | +| [integrations/desktop-environments.md](./integrations/desktop-environments.md) | GNOME, KDE, MATE, LXDE, Phosh integrations | +| [integrations/hardware.md](./integrations/hardware.md) | Argon ONE UP, GPU, laptop power, audio, WiFi | +| [integrations/bootloader.md](./integrations/bootloader.md) | GRUB multi-boot entries, mkinitcpio | +| [integrations/platforms.md](./integrations/platforms.md) | Android/Termux, SteamOS, WSL, immutable distros | + +### Cross-Cutting Concerns + +| File | Description | +|------|-------------| +| [testing.md](./testing.md) | Test strategy, organisation, coverage (manual verification) | +| [concurrency-and-scheduling.md](./concurrency-and-scheduling.md) | Sequential execution, privilege separation, no parallelism | +| [error-handling.md](./error-handling.md) | Error taxonomy, handling patterns, failure isolation | +| [logging.md](./logging.md) | Logging framework, levels, structured fields, sinks | +| [invariants.md](./invariants.md) | System-wide invariants and contracts | +| [build-and-deployment.md](./build-and-deployment.md) | No build pipeline; direct script execution | +| [security.md](./security.md) | Privilege escalation, SSH/GPG, Flatpak permissions, hardening | +| [ambiguities-and-open-questions.md](./ambiguities-and-open-questions.md) | Unresolved items, TODOs, known gaps | +| [change-guide.md](./change-guide.md) | How to modify common areas safely | +| [documentation-maintenance.md](./documentation-maintenance.md) | How to keep docs current, ownership | + +## Navigation Aids + +### Start Here (Newcomers) +1. [repository-overview.md](./repository-overview.md) — Understand what this repository does +2. [architecture.md](./architecture.md) — Understand the high-level structure +3. [flows/startup-and-provisioning.md](./flows/startup-and-provisioning.md) — Understand the main execution flow + +### Deep Dive (Component Owners) +- [components/foundation-layer.md](./components/foundation-layer.md) — Core utilities +- [components/package-management.md](./components/package-management.md) — Package abstraction +- [components/configuration-management.md](./components/configuration-management.md) — Config manipulation +- [integrations/desktop-environments.md](./integrations/desktop-environments.md) — DE-specific logic + +### Flows (Debuggers) +- [flows/startup-and-provisioning.md](./flows/startup-and-provisioning.md) — Full provisioning flow +- [flows/package-management.md](./flows/package-management.md) — Package operations +- [flows/system-configuration.md](./flows/system-configuration.md) — System-level config + +## Maintenance Metadata + +- **Last reviewed**: 2026-10-08 +- **Owner**: Repository maintainer +- **Coverage status**: Comprehensive — all scripts, modules, resources, and integrations documented +- **Source of truth**: This documentation derived from exhaustive reverse-engineering of source code \ No newline at end of file diff --git a/docs/ambiguities-and-open-questions.md b/docs/ambiguities-and-open-questions.md new file mode 100644 index 0000000..879517f --- /dev/null +++ b/docs/ambiguities-and-open-questions.md @@ -0,0 +1,40 @@ +# Ambiguities and Open Questions + +This document captures known ambiguities, open questions, and TODOs for the linux-setup-script repository. + +## Known Ambiguities + +- **Package Manager Abstraction**: The package-management abstraction layer needs clearer definition of supported package managers across distributions. Currently supports pacman, apt, apk, pkg, Flatpak, Cargo, GitHub releases, but may need additional adapters. + +- **Privilege Escalation Model**: The `run_as_su` function uses `sudo -n true` or `su -c 'true'` detection, but behavior differs between sudo and su implementations. Need to document exact detection logic. + +- **File System Constants**: Path constants in `filesystem.sh` need standardization. Some constants are defined in multiple places - need to consolidate. + +- **Configuration Schema**: Configuration files use different formats (INI, JSON, XML, GSettings). Need to document format precedence and validation rules. + +- **Android/Termux Support**: Android support appears limited to specific package managers (apk, pkg). Need to verify compatibility with other Android package managers. + +## Open Questions + +1. Should we add explicit support for Snap packages in the package management abstraction? +2. How should we handle cross-distro configuration files (e.g., shared configs between Arch and Ubuntu)? +3. What is the expected behavior when running on immutable distros (e.g., Silverblue, MicroOS)? +4. Are there any edge cases with file locking during atomic writes that need special handling? +5. Should we add more comprehensive error codes for different failure scenarios? + +## TODOs + +- [ ] Review and document privilege escalation detection logic +- [ ] Consolidate filesystem path constants +- [ ] Add Snap package support to package management abstraction +- [ ] Document configuration format precedence rules +- [ ] Verify Android/Termux compatibility with all package managers +- [ ] Add more comprehensive error code documentation +- [ ] Review atomic write edge cases + +## References + +- [components/filesystem.md](./components/filesystem.md) +- [components/package-management.md](./components/package-management.md) +- [components/configuration-management.md](./components/configuration-management.md) +- [security.md](./security.md) \ No newline at end of file diff --git a/docs/api-reference/INDEX.md b/docs/api-reference/INDEX.md new file mode 100644 index 0000000..77141cd --- /dev/null +++ b/docs/api-reference/INDEX.md @@ -0,0 +1,121 @@ +# API Reference Index + +This directory contains API reference documentation for the linux-setup-script +repository. + +## Overview + +The API reference provides detailed documentation for all public interfaces, +functions, and data structures exposed by the linux-setup-script framework. + +## Documentation Catalogue + +### Foundation Layer APIs + +| File | Description | +|------|-------------| +| [filesystem.md](./filesystem.md) | Path resolution, file operations, directory management | +| [common.md](./common.md) | Execution primitives, privilege escalation, logging | +| [system-info.md](./system-info.md) | Platform detection, environment variables | +| [package-management.md](./package-management.md) | Package manager abstraction, operations | +| [config.md](./config.md) | Configuration file manipulation | +| [service-management.md](./service-management.md) | Service control, systemd/OpenRC | +| [apps.md](./apps.md) | Application management, installation | +| [github.md](./github.md) | GitHub integration, releases, CLI | + +### Script APIs + +| File | Description | +|------|-------------| +| [configure-system.md](./configure-system.md) | System configuration functions | +| [configure-locale.md](./configure-locale.md) | Locale configuration | +| [configure-time.md](./configure-time.md) | Time and timezone configuration | +| [configure-repositories.md](./configure-repositories.md) | Repository configuration | +| [install-packages.md](./install-packages.md) | Package installation | +| [update-rcs.md](./update-rcs.md) | RC file deployment | +| [update-resources.md](./update-resources.md) | Resource deployment | +| [update-profiles.md](./update-profiles.md) | Profile script deployment | +| [configure-launchers.md](./configure-launchers.md) | Desktop launcher modification | +| [configure-autostart-apps.md](./configure-autostart-apps.md) | Autostart configuration | +| [configure-default-apps.md](./configure-default-apps.md) | Default application configuration | +| [configure-directories.md](./configure-directories.md) | Directory configuration | +| [configure-services-system.md](./configure-services-system.md) | System service configuration | +| [configure-services-user.md](./configure-services-user.md) | User service configuration | +| [configure-hardware-integration.md](./configure-hardware-integration.md) | Hardware configuration | +| [update-grub.md](./update-grub.md) | GRUB configuration | +| [configure-permissions.md](./configure-permissions.md) | Permission management | + +## Usage Patterns + +### Foundation Layer Usage + +```bash +# Source foundation modules +source "${REPO_DIR}/scripts/common/filesystem.sh" +source "${REPO_DIR}/scripts/common/common.sh" +source "${REPO_DIR}/scripts/common/system-info.sh" +source "${REPO_DIR}/scripts/common/package-management.sh" +``` + +### Privilege Escalation + +```bash +# Run as root (via sudo/su) +run_as_su command + +# Run script as root +run_script_as_su /path/to/script.sh +``` + +### File Operations + +```bash +# Update file if content differs +update_file_if_distinct "${source}" "${target}" + +# Ensure directory exists +ensure_directory "${dir}" +``` + +### Package Operations + +```bash +# Install package +install_package "package-name" + +# Check if package installed +is_package_installed "package-name" + +# Update package manager +call_package_manager update +``` + +## Data Structures + +### Environment Variables + +| Variable | Type | Description | +|----------|------|-------------| +| `REPO_DIR` | string | Absolute path to repository root | +| `DISTRO_FAMILY` | string | Arch, Debian, Ubuntu, Alpine, Android | +| `HAS_SU_PRIVILEGES` | boolean | true if can escalate to root | +| `RUN_AS_SU` | string | Command prefix for root operations | +| `DESKTOP_ENVIRONMENT` | string | GNOME, KDE, MATE, LXDE, Phosh | +| `HAS_GUI` | boolean | true if graphical environment | +| `GTK_THEME` | string | Current GTK theme name | +| `GTK_THEME_VARIANT` | string | dark or light | + +## Error Handling + +All APIs follow consistent error handling patterns: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `assert_*` functions for validation +- Continue on non-critical failures + +## See Also + +- [](../architecture.md) — High-level architecture +- [](../flows/startup-and-provisioning.md) — Main execution flow +- [](../components/foundation-layer.md) — Foundation layer details \ No newline at end of file diff --git a/docs/api-reference/apps.md b/docs/api-reference/apps.md new file mode 100644 index 0000000..3afb8c3 --- /dev/null +++ b/docs/api-reference/apps.md @@ -0,0 +1,236 @@ +# Apps API Reference + +This document provides API reference for application management operations in the linux-setup-script repository. + +## Application Installation Functions + +### install_application + +```bash +function install_application() { + local APP_NAME="${1}" + local APP_TYPE="${2}" + + case "${APP_TYPE}" in + "native") + install_native_package "${APP_NAME}" + ;; + "flatpak") + install_flatpak "${APP_NAME}" + ;; + "cargo") + install_cargo_package "${APP_NAME}" + ;; + "github") + local REPOSITORY="${3}" + install_github_package "${APP_NAME}" "${REPOSITORY}" + ;; + "webapp") + local URL="${3}" + install_webapp "${URL}" + ;; + "vscode") + install_vscode_package "${APP_NAME}" + ;; + "gnome-extension") + install_gnome_shell_extension "${APP_NAME}" + ;; + *) + log_error "Unknown application type: ${APP_TYPE}" + return 1 + ;; + esac +} +``` + +**Parameters:** +- `APP_NAME` (string): Application name +- `APP_TYPE` (string): Application type (native, flatpak, cargo, github, webapp, vscode, gnome-extension) +- Additional parameters depending on type + +**Returns:** +- Exit code of installation command + +**Example:** +```bash +install_application "vim" "native" +install_application "org.mozilla.firefox" "flatpak" +install_application "ripgrep" "cargo" +install_application "vim" "github" "vim/vim" +install_application "https://example.com" "webapp" +install_application "ms-python.python" "vscode" +install_application "1234/some-extension" "gnome-extension" +``` + +## Application Uninstallation Functions + +### uninstall_application + +```bash +function uninstall_application() { + local APP_NAME="${1}" + local APP_TYPE="${2}" + + case "${APP_TYPE}" in + "native") + uninstall_native_package "${APP_NAME}" + ;; + "flatpak") + uninstall_flatpak "${APP_NAME}" + ;; + "cargo") + uninstall_cargo_package "${APP_NAME}" + ;; + "github") + uninstall_github_package "${APP_NAME}" + ;; + "webapp") + uninstall_webapp "${APP_NAME}" + ;; + "vscode") + # VS Code extensions don't have a standard uninstall function + log_warning "VS Code extension uninstall not implemented" + ;; + "gnome-extension") + uninstall_gnome_shell_extension "${APP_NAME}" + ;; + *) + log_error "Unknown application type: ${APP_TYPE}" + return 1 + ;; + esac +} +``` + +**Parameters:** +- `APP_NAME` (string): Application name +- `APP_TYPE` (string): Application type + +**Returns:** +- Exit code of uninstallation command + +**Example:** +```bash +uninstall_application "vim" "native" +uninstall_application "org.mozilla.firefox" "flatpak" +``` + +## Desktop Entry Management + +### create_desktop_entry + +```bash +function create_desktop_entry() { + local NAME="${1}" + local EXEC="${2}" + local ICON="${3}" + local CATEGORIES="${4}" + local FILE_PATH="${5}" + + create_file "${FILE_PATH}" + + cat > "${FILE_PATH}" << EOF +[Desktop Entry] +Type=Application +Name=${NAME} +Exec=${EXEC} +Icon=${ICON} +Categories=${CATEGORIES} +Terminal=false +EOF + + chmod +x "${FILE_PATH}" + + if does_bin_exist 'update-desktop-database'; then + update-desktop-database "$(dirname "${FILE_PATH}")" >/dev/null 2>&1 + fi +} +``` + +**Parameters:** +- `NAME` (string): Application name +- `EXEC` (string): Executable command +- `ICON` (string): Icon name or path +- `CATEGORIES` (string): Desktop categories +- `FILE_PATH` (string): Desktop file path + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +create_desktop_entry "My App" "myapp" "myapp" "Utility;" "${HOME}/.local/share/applications/myapp.desktop" +``` + +### modify_desktop_entry + +```bash +function modify_desktop_entry() { + local FILE_PATH="${1}" + local KEY="${2}" + local VALUE="${3}" + + if does_file_exist "${FILE_PATH}"; then + sed -i "s/^${KEY}=.*/${KEY}=${VALUE}/" "${FILE_PATH}" + fi +} +``` + +**Parameters:** +- `FILE_PATH` (string): Desktop file path +- `KEY` (string): Key to modify +- `VALUE` (string): New value + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +modify_desktop_entry "${HOME}/.local/share/applications/myapp.desktop" "Exec" "myapp --new-flag" +``` + +## Application Launchers + +### configure_launchers + +```bash +function configure_launchers() { + local LAUNCHER_DIR="${1}" + + for DESKTOP_FILE in "${LAUNCHER_DIR}"/*.desktop; do + if does_file_exist "${DESKTOP_FILE}"; then + # Apply modifications based on configuration + modify_launcher "${DESKTOP_FILE}" + fi + done +} +``` + +**Parameters:** +- `LAUNCHER_DIR` (string): Directory containing desktop files + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +configure_launchers "/usr/share/applications" +``` + +## Error Handling + +All apps functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `is_package_installed` for checks +- Continue on non-critical failures + +## See Also + +- [components/application-services.md](../components/application-services.md) — Application services details +- [components/package-management.md](../components/package-management.md) — Package management +- [flows/configuration-deployment.md](../flows/configuration-deployment.md) — Configuration deployment flow \ No newline at end of file diff --git a/docs/api-reference/common.md b/docs/api-reference/common.md new file mode 100644 index 0000000..b8700a7 --- /dev/null +++ b/docs/api-reference/common.md @@ -0,0 +1,204 @@ +# Common API Reference + +This document provides API reference for common utilities in the linux-setup-script repository. + +## Privilege Escalation + +### run_as_su + +```bash +function run_as_su() { + local COMMAND="${*}" + + if [ "${HAS_SU_PRIVILEGES}" = 'true' ]; then + eval "${COMMAND}" + else + su -c "${COMMAND}" 2>/dev/null || sudo -n eval "${COMMAND}" 2>/dev/null + fi +} +``` + +**Parameters:** +- `COMMAND` (string): Command to run as root + +**Returns:** +- Exit code of the command + +**Example:** +```bash +run_as_su "systemctl restart myservice" +``` + +### run_script_as_su + +```bash +function run_script_as_su() { + local SCRIPT_PATH="${1}" + + if [ "${HAS_SU_PRIVILEGES}" = 'true' ]; then + bash "${SCRIPT_PATH}" + else + su -c "bash ${SCRIPT_PATH}" 2>/dev/null || sudo -n bash "${SCRIPT_PATH}" 2>/dev/null + fi +} +``` + +**Parameters:** +- `SCRIPT_PATH` (string): Path to script to run as root + +**Returns:** +- Exit code of the script + +**Example:** +```bash +run_script_as_su "/path/to/script.sh" +``` + +## Privilege Detection + +### HAS_SU_PRIVILEGES + +```bash +HAS_SU_PRIVILEGES='false' +sudo -n true 2>/dev/null && HAS_SU_PRIVILEGES='true' +su -c 'true' 2>/dev/null && HAS_SU_PRIVILEGES='true' +[ "${UID}" = '0' ] && HAS_SU_PRIVILEGES='true' +``` + +**Values:** +- `true` - Can escalate to root +- `false` - Cannot escalate to root + +## Logging + +### log_info + +```bash +function log_info() { + local MESSAGE="${*}" + + echo "[INFO] ${MESSAGE}" +} +``` + +**Parameters:** +- `MESSAGE` (string): Message to log + +**Example:** +```bash +log_info "Starting configuration" +``` + +### log_error + +```bash +function log_error() { + local MESSAGE="${*}" + + echo "[ERROR] ${MESSAGE}" >&2 +} +``` + +**Parameters:** +- `MESSAGE` (string): Error message to log + +**Example:** +```bash +log_error "Failed to create directory" +``` + +### log_warning + +```bash +function log_warning() { + local MESSAGE="${*}" + + echo "[WARNING] ${MESSAGE}" >&2 +} +``` + +**Parameters:** +- `MESSAGE` (string): Warning message to log + +**Example:** +```bash +log_warning "Configuration file not found" +``` + +## Execution Primitives + +### run_command + +```bash +function run_command() { + local COMMAND="${*}" + + eval "${COMMAND}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `COMMAND` (string): Command to execute + +**Returns:** +- Exit code of the command + +**Example:** +```bash +run_command "ls -la" +``` + +### run_script + +```bash +function run_script() { + local SCRIPT_PATH="${1}" + + bash "${SCRIPT_PATH}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `SCRIPT_PATH` (string): Path to script to execute + +**Returns:** +- Exit code of the script + +**Example:** +```bash +run_script "/path/to/script.sh" +``` + +## Guarded Sourcing + +### Guard Pattern + +```bash +if [ "${COMMON_SOURCED}" != 'true' ]; then + COMMON_SOURCED='true' + # Source common modules +fi +``` + +**Purpose:** +- Prevents double-sourcing of common modules +- Ensures idempotent sourcing + +## Error Handling + +All common functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `log_warning` for non-critical issues +- Continue on non-critical failures + +## See Also + +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [components/filesystem.md](../components/filesystem.md) — Filesystem operations +- [components/system-info.md](../components/system-info.md) — Environment detection \ No newline at end of file diff --git a/docs/api-reference/config.md b/docs/api-reference/config.md new file mode 100644 index 0000000..628137d --- /dev/null +++ b/docs/api-reference/config.md @@ -0,0 +1,420 @@ +# Config API Reference + +This document provides API reference for configuration file manipulation in the linux-setup-script repository. + +## Configuration Functions + +### update_file_if_distinct + +```bash +function update_file_if_distinct() { + local SOURCE_FILE="${1}" + local TARGET_FILE="${2}" + + does_file_exist "${SOURCE_FILE}" || return 1 + does_file_exist "${TARGET_FILE}" && return 0 + + cp "${SOURCE_FILE}" "${TARGET_FILE}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `SOURCE_FILE` (string): Source file path +- `TARGET_FILE` (string): Target file path + +**Returns:** +- `0` on success +- `1` if source does not exist +- Non-zero on copy failure + +**Example:** +```bash +update_file_if_distinct "/etc/myapp/config" "${HOME}/.config/myapp/config" +``` + +### create_file + +```bash +function create_file() { + local FILE_PATH="${1}" + + local DIRECTORY_PATH + DIRECTORY_PATH=$(dirname "${FILE_PATH}") + + ensure_directory "${DIRECTORY_PATH}" + + touch "${FILE_PATH}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `FILE_PATH` (string): File path to create + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +create_file "${HOME}/.config/myapp/config" +``` + +### ensure_directory + +```bash +function ensure_directory() { + local DIRECTORY_PATH="${*}" + + does_directory_exist "${DIRECTORY_PATH}" && return 0 + + mkdir -p "${DIRECTORY_PATH}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `DIRECTORY_PATH` (string): Directory to create + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +ensure_directory "${HOME}/.config/myapp" +``` + +## INI File Manipulation + +### read_ini_value + +```bash +function read_ini_value() { + local FILE="${1}" + local SECTION="${2}" + local KEY="${3}" + + local VALUE + VALUE=$(awk -F'=' -v section="${SECTION}" -v key="${KEY}" ' + $0 ~ "\\[" section "\\]" { in_section=1; next } + $0 ~ /^\[/ { in_section=0 } + in_section && $1 == key { print $2; exit } + ' "${FILE}") + + echo "${VALUE}" +} +``` + +**Parameters:** +- `FILE` (string): INI file path +- `SECTION` (string): Section name +- `KEY` (string): Key name + +**Returns:** +- Value of the key, or empty string if not found + +**Example:** +```bash +VALUE=$(read_ini_value "/etc/myapp/config.ini" "section" "key") +``` + +### write_ini_value + +```bash +function write_ini_value() { + local FILE="${1}" + local SECTION="${2}" + local KEY="${3}" + local VALUE="${4}" + + local TEMP_FILE + TEMP_FILE=$(mktemp) + + awk -F'=' -v section="${SECTION}" -v key="${KEY}" -v value="${VALUE}" ' + $0 ~ "\\[" section "\\]" { in_section=1; print; next } + $0 ~ /^\[/ { in_section=0 } + in_section && $1 == key { print key "=" value; found=1; next } + { print } + END { if (!found && in_section) print key "=" value } + ' "${FILE}" > "${TEMP_FILE}" + + mv "${TEMP_FILE}" "${FILE}" +} +``` + +**Parameters:** +- `FILE` (string): INI file path +- `SECTION` (string): Section name +- `KEY` (string): Key name +- `VALUE` (string): Value to set + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +write_ini_value "/etc/myapp/config.ini" "section" "key" "value" +``` + +## JSON File Manipulation + +### read_json_value + +```bash +function read_json_value() { + local FILE="${1}" + local KEY="${2}" + + local VALUE + VALUE=$(jq -r ".${KEY}" "${FILE}" 2>/dev/null) + + echo "${VALUE}" +} +``` + +**Parameters:** +- `FILE` (string): JSON file path +- `KEY` (string): JSON key path (e.g., "section.key") + +**Returns:** +- Value of the key, or empty string if not found + +**Example:** +```bash +VALUE=$(read_json_value "/etc/myapp/config.json" "section.key") +``` + +### write_json_value + +```bash +function write_json_value() { + local FILE="${1}" + local KEY="${2}" + local VALUE="${3}" + + local TEMP_FILE + TEMP_FILE=$(mktemp) + + jq ".${KEY} = ${VALUE}" "${FILE}" > "${TEMP_FILE}" + + mv "${TEMP_FILE}" "${FILE}" +} +``` + +**Parameters:** +- `FILE` (string): JSON file path +- `KEY` (string): JSON key path (e.g., "section.key") +- `VALUE` (string): Value to set (JSON encoded) + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +write_json_value "/etc/myapp/config.json" "section.key" '"value"' +``` + +## XML File Manipulation + +### read_xml_value + +```bash +function read_xml_value() { + local FILE="${1}" + local XPATH="${2}" + + local VALUE + VALUE=$(xmllint --xpath "string(${XPATH})" "${FILE}" 2>/dev/null) + + echo "${VALUE}" +} +``` + +**Parameters:** +- `FILE` (string): XML file path +- `XPATH` (string): XPath expression + +**Returns:** +- Value of the XPath expression, or empty string if not found + +**Example:** +```bash +VALUE=$(read_xml_value "/etc/myapp/config.xml" "//section/key") +``` + +### write_xml_value + +```bash +function write_xml_value() { + local FILE="${1}" + local XPATH="${2}" + local VALUE="${3}" + + local TEMP_FILE + TEMP_FILE=$(mktemp) + + xmlstarlet ed -u "${XPATH}" -v "${VALUE}" "${FILE}" > "${TEMP_FILE}" + + mv "${TEMP_FILE}" "${FILE}" +} +``` + +**Parameters:** +- `FILE` (string): XML file path +- `XPATH` (string): XPath expression +- `VALUE` (string): Value to set + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +write_xml_value "/etc/myapp/config.xml" "//section/key" "value" +``` + +## GSettings Manipulation + +### read_gsettings_value + +```bash +function read_gsettings_value() { + local SCHEMA="${1}" + local KEY="${2}" + + local VALUE + VALUE=$(gsettings get "${SCHEMA}" "${KEY}" 2>/dev/null) + + echo "${VALUE}" +} +``` + +**Parameters:** +- `SCHEMA` (string): GSettings schema +- `KEY` (string): Key name + +**Returns:** +- Value of the key, or empty string if not found + +**Example:** +```bash +VALUE=$(read_gsettings_value "org.gnome.desktop.interface" "gtk-theme") +``` + +### write_gsettings_value + +```bash +function write_gsettings_value() { + local SCHEMA="${1}" + local KEY="${2}" + local VALUE="${3}" + + gsettings set "${SCHEMA}" "${KEY}" "${VALUE}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `SCHEMA` (string): GSettings schema +- `KEY` (string): Key name +- `VALUE` (string): Value to set + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +write_gsettings_value "org.gnome.desktop.interface" "gtk-theme" "'Adwaita'" +``` + +## Modprobe Configuration + +### read_modprobe_value + +```bash +function read_modprobe_value() { + local FILE="${1}" + local MODULE="${2}" + local PARAMETER="${3}" + + local VALUE + VALUE=$(grep -E "^options\s+${MODULE}\s+${PARAMETER}=" "${FILE}" 2>/dev/null | \ + sed -E "s/.*${PARAMETER}=([^ ]+).*/\1/") + + echo "${VALUE}" +} +``` + +**Parameters:** +- `FILE` (string): Modprobe config file path +- `MODULE` (string): Module name +- `PARAMETER` (string): Parameter name + +**Returns:** +- Value of the parameter, or empty string if not found + +**Example:** +```bash +VALUE=$(read_modprobe_value "/etc/modprobe.d/myapp.conf" "my_module" "my_param") +``` + +### write_modprobe_value + +```bash +function write_modprobe_value() { + local FILE="${1}" + local MODULE="${2}" + local PARAMETER="${3}" + local VALUE="${4}" + + local TEMP_FILE + TEMP_FILE=$(mktemp) + + if grep -q "^options\s\+${MODULE}\s\+${PARAMETER}=" "${FILE}" 2>/dev/null; then + sed -E "s/(^options\s\+${MODULE}\s\+${PARAMETER}=)[^ ]+/\1${VALUE}/" "${FILE}" > "${TEMP_FILE}" + else + echo "options ${MODULE} ${PARAMETER}=${VALUE}" >> "${FILE}" + fi + + mv "${TEMP_FILE}" "${FILE}" +} +``` + +**Parameters:** +- `FILE` (string): Modprobe config file path +- `MODULE` (string): Module name +- `PARAMETER` (string): Parameter name +- `VALUE` (string): Value to set + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +write_modprobe_value "/etc/modprobe.d/myapp.conf" "my_module" "my_param" "value" +``` + +## Error Handling + +All config functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `does_file_exist` for checks +- Handle permission errors gracefully + +## See Also + +- [components/configuration-management.md](../components/configuration-management.md) — Configuration management details +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [flows/configuration-deployment.md](../flows/configuration-deployment.md) — Configuration deployment flow \ No newline at end of file diff --git a/docs/api-reference/configure-system.md b/docs/api-reference/configure-system.md new file mode 100644 index 0000000..ba9c2be --- /dev/null +++ b/docs/api-reference/configure-system.md @@ -0,0 +1,173 @@ +# Configure System API Reference + +This document provides API reference for system configuration operations in the linux-setup-script repository. + +## System Configuration Functions + +### configure_system + +```bash +function configure_system() { + # Configure kernel parameters + configure_kernel_parameters + + # Configure sysctl + configure_sysctl + + # Configure modprobe + configure_modprobe + + # Configure GRUB + configure_grub + + # Configure GNOME + configure_gnome +} +``` + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +configure_system +``` + +### configure_kernel_parameters + +```bash +function configure_kernel_parameters() { + local GRUB_CMDLINE_LINUX_DEFAULT="" + + # Add kernel parameters based on hardware + if [ "${GPU_FAMILY}" = "nvidia" ]; then + GRUB_CMDLINE_LINUX_DEFAULT="${GRUB_CMDLINE_LINUX_DEFAULT} nvidia-drm.modeset=1" + fi + + if [ "${IS_BATTERY_DEVICE}" = "true" ]; then + GRUB_CMDLINE_LINUX_DEFAULT="${GRUB_CMDLINE_LINUX_DEFAULT} pcie_aspm=force" + fi + + # Update GRUB configuration + update_grub_config "${GRUB_CMDLINE_LINUX_DEFAULT}" +} +``` + +**Returns:** +- `0` on success +- Non-zero on failure + +### configure_sysctl + +```bash +function configure_sysctl() { + local SYSCTL_FILE="/etc/sysctl.d/99-linux-setup-script.conf" + + create_file "${SYSCTL_FILE}" + + cat > "${SYSCTL_FILE}" << EOF +# Network +net.core.somaxconn = 65535 +net.ipv4.tcp_max_syn_backlog = 65535 + +# Memory +vm.swappiness = 10 +vm.vfs_cache_pressure = 50 + +# Security +kernel.dmesg_restrict = 1 +kernel.kptr_restrict = 2 +EOF + + run_as_su sysctl --system +} +``` + +**Returns:** +- `0` on success +- Non-zero on failure + +### configure_modprobe + +```bash +function configure_modprobe() { + local MODPROBE_DIR="/etc/modprobe.d" + + ensure_directory "${MODPROBE_DIR}" + + # Configure NVIDIA + if [ "${GPU_FAMILY}" = "nvidia" ]; then + create_file "${MODPROBE_DIR}/nvidia.conf" + cat > "${MODPROBE_DIR}/nvidia.conf" << EOF +options nvidia-drm modeset=1 +options nvidia NVreg_UsePageAttributeTable=1 +EOF + fi + + # Configure audio + create_file "${MODPROBE_DIR}/audio.conf" + cat > "${MODPROBE_DIR}/audio.conf" << EOF +options snd_hda_intel power_save=1 +EOF +} +``` + +**Returns:** +- `0` on success +- Non-zero on failure + +### configure_grub + +```bash +function configure_grub() { + local GRUB_FILE="/etc/default/grub" + + if does_file_exist "${GRUB_FILE}"; then + # Update GRUB configuration + sed -i 's/^GRUB_TIMEOUT=.*/GRUB_TIMEOUT=5/' "${GRUB_FILE}" + sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"/' "${GRUB_FILE}" + + run_as_su update-grub + fi +} +``` + +**Returns:** +- `0` on success +- Non-zero on failure + +### configure_gnome + +```bash +function configure_gnome() { + if [ "${DESKTOP_ENVIRONMENT}" != "GNOME" ]; then + return 0 + fi + + # Configure GNOME settings + gsettings set org.gnome.desktop.interface gtk-theme "${GTK_THEME}" + gsettings set org.gnome.desktop.interface color-scheme "prefer-${GTK_THEME_VARIANT}" + gsettings set org.gnome.desktop.interface font-name "Cantarell 11" + gsettings set org.gnome.desktop.interface monospace-font-name "Monospace 10" +} +``` + +**Returns:** +- `0` on success +- Non-zero on failure + +## Error Handling + +All configure system functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `run_as_su` for privileged operations +- Continue on non-critical failures + +## See Also + +- [components/host-and-composition.md](../components/host-and-composition.md) — Host and composition +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [flows/system-configuration.md](../flows/system-configuration.md) — System configuration flow \ No newline at end of file diff --git a/docs/api-reference/filesystem.md b/docs/api-reference/filesystem.md new file mode 100644 index 0000000..358c743 --- /dev/null +++ b/docs/api-reference/filesystem.md @@ -0,0 +1,220 @@ +# Filesystem API Reference + +This document provides API reference for filesystem operations in the linux-setup-script repository. + +## Path Constants + +### Repository Paths + +| Constant | Description | Default Value | +|----------|-------------|---------------| +| `REPO_DIR` | Absolute path to repository root | Computed from script location | +| `REPO_DATA_DIR` | Data directory | `${REPO_DIR}/data` | +| `REPO_RES_DIR` | Resources directory | `${REPO_DIR}/resources` | +| `REPO_RC_DIR` | RC files directory | `${REPO_DIR}/rc` | +| `REPO_SCRIPTS_DIR` | Scripts directory | `${REPO_DIR}/scripts` | +| `REPO_SCRIPTS_COMMON_DIR` | Common scripts directory | `${REPO_SCRIPTS_DIR}/common` | +| `REPO_KEYBOARD_LAYOUTS_DIR` | Keyboard layouts directory | `${REPO_RC_DIR}/keyboard-layouts` | + +### System Paths + +| Constant | Description | Default Value | +|----------|-------------|---------------| +| `ROOT_PATH` | Root partition mount point | Empty or `/data/data/com.termux/files/usr` | +| `ROOT_BIN` | Root bin directory | `${ROOT_PATH}/bin` | +| `ROOT_BOOT` | Root boot directory | `${ROOT_PATH}/boot` | +| `ROOT_ETC` | Root etc directory | `${ROOT_PATH}/etc` | +| `ROOT_HOME` | Root home directory | `${ROOT_PATH}/home` | +| `ROOT_LIB` | Root lib directory | `${ROOT_PATH}/lib` | +| `ROOT_OPT` | Root opt directory | `${ROOT_PATH}/opt` | +| `ROOT_PROC` | Root proc directory | `${ROOT_PATH}/proc` | +| `ROOT_ROOT` | Root root directory | `${ROOT_PATH}/root` | +| `ROOT_SRV` | Root srv directory | `${ROOT_PATH}/srv` | +| `ROOT_SYS` | Root sys directory | `${ROOT_PATH}/sys` | +| `ROOT_USR` | Root usr directory | `${ROOT_PATH}/usr` | + +### User Paths + +| Constant | Description | Default Value | +|----------|-------------|---------------| +| `HOME_REAL` | Real user home directory | Computed from passwd | +| `HOME` | Current home directory | Set to `HOME_REAL` if empty | +| `USER_REAL` | Real username | `SUDO_USER` or `USER` | + +## Functions + +### does_directory_exist + +```bash +function does_directory_exist() { + local DIRECTORY_PATH="${*}" + + [ -d "${DIRECTORY_PATH}" ] && return 0 + + return 1 +} +``` + +**Parameters:** +- `DIRECTORY_PATH` (string): Path to check + +**Returns:** +- `0` if directory exists +- `1` if directory does not exist + +**Example:** +```bash +if does_directory_exist "/usr/bin"; then + echo "Directory exists" +fi +``` + +### does_file_exist + +```bash +function does_file_exist() { + local FILE_PATH="${*}" + + [ -f "${FILE_PATH}" ] && return 0 + + return 1 +} +``` + +**Parameters:** +- `FILE_PATH` (string): Path to check + +**Returns:** +- `0` if file exists +- `1` if file does not exist + +**Example:** +```bash +if does_file_exist "/etc/passwd"; then + echo "File exists" +fi +``` + +### ensure_directory + +```bash +function ensure_directory() { + local DIRECTORY_PATH="${*}" + + does_directory_exist "${DIRECTORY_PATH}" && return 0 + + mkdir -p "${DIRECTORY_PATH}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `DIRECTORY_PATH` (string): Directory to create + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +ensure_directory "${HOME}/.config/myapp" +``` + +### update_file_if_distinct + +```bash +function update_file_if_distinct() { + local SOURCE_FILE="${1}" + local TARGET_FILE="${2}" + + does_file_exist "${SOURCE_FILE}" || return 1 + does_file_exist "${TARGET_FILE}" && return 0 + + cp "${SOURCE_FILE}" "${TARGET_FILE}" 2>/dev/null + + return $? +} +``` + +**Parameters:** +- `SOURCE_FILE` (string): Source file path +- `TARGET_FILE` (string): Target file path + +**Returns:** +- `0` on success +- `1` if source does not exist +- Non-zero on copy failure + +**Example:** +```bash +update_file_if_distinct "/etc/myapp/config" "${HOME}/.config/myapp/config" +``` + +## File Operations + +### File Copying + +```bash +# Copy file with permission preservation +cp -p "${SOURCE}" "${DESTINATION}" + +# Copy directory recursively +cp -r "${SOURCE_DIR}" "${DESTINATION_DIR}" +``` + +### File Creation + +```bash +# Create file with content +echo "content" > "${FILE_PATH}" + +# Create file with heredoc +cat < "${FILE_PATH}" +content +EOF +``` + +### File Deletion + +```bash +# Delete file +rm -f "${FILE_PATH}" + +# Delete directory recursively +rm -rf "${DIRECTORY_PATH}" +``` + +## Path Resolution + +### Absolute Paths + +```bash +# Get absolute path +realpath "${PATH}" + +# Get script directory +dirname "${BASH_SOURCE[0]}" +``` + +### Relative Paths + +```bash +# Get relative path +realpath --relative-to="${BASE}" "${PATH}" +``` + +## Error Handling + +All filesystem functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `does_file_exist` and `does_directory_exist` for checks +- Handle permission errors gracefully + +## See Also + +- [components/common.md](../components/common.md) — Execution primitives +- [components/system-info.md](../components/system-info.md) — Environment detection +- [flows/directory-and-file-management.md](../flows/directory-and-file-management.md) — File management flow \ No newline at end of file diff --git a/docs/api-reference/github.md b/docs/api-reference/github.md new file mode 100644 index 0000000..3a03354 --- /dev/null +++ b/docs/api-reference/github.md @@ -0,0 +1,237 @@ +# GitHub API Reference + +This document provides API reference for GitHub integration operations in the linux-setup-script repository. + +## GitHub Release Functions + +### get_github_latest_release + +```bash +function get_github_latest_release() { + local REPOSITORY="${1}" + + local RELEASE_JSON + RELEASE_JSON=$(curl -s "https://api.github.com/repos/${REPOSITORY}/releases/latest") + + echo "${RELEASE_JSON}" +} +``` + +**Parameters:** +- `REPOSITORY` (string): GitHub repository (owner/repo) + +**Returns:** +- JSON response from GitHub API + +**Example:** +```bash +RELEASE_JSON=$(get_github_latest_release "vim/vim") +``` + +### get_github_latest_release_version + +```bash +function get_github_latest_release_version() { + local REPOSITORY="${1}" + + local RELEASE_JSON + RELEASE_JSON=$(get_github_latest_release "${REPOSITORY}") + + local VERSION + VERSION=$(echo "${RELEASE_JSON}" | jq -r '.tag_name') + + echo "${VERSION}" +} +``` + +**Parameters:** +- `REPOSITORY` (string): GitHub repository (owner/repo) + +**Returns:** +- Latest release version tag + +**Example:** +```bash +VERSION=$(get_github_latest_release_version "vim/vim") +``` + +### get_github_latest_release_asset + +```bash +function get_github_latest_release_asset() { + local REPOSITORY="${1}" + local FILTER="${2}" + + local RELEASE_JSON + RELEASE_JSON=$(get_github_latest_release "${REPOSITORY}") + + local DOWNLOAD_URLS + DOWNLOAD_URLS=$(echo "${RELEASE_JSON}" | jq -r '.assets[] | .browser_download_url') + + if [ -n "${FILTER}" ]; then + echo "${DOWNLOAD_URLS}" | grep "${FILTER}" + else + echo "${DOWNLOAD_URLS}" + fi +} +``` + +**Parameters:** +- `REPOSITORY` (string): GitHub repository (owner/repo) +- `FILTER` (string): Filter pattern for asset URLs (optional) + +**Returns:** +- Download URLs matching filter + +**Example:** +```bash +URL=$(get_github_latest_release_asset "vim/vim" "amd64.deb") +``` + +## GitHub Authentication + +### setup_github_cli + +```bash +function setup_github_cli() { + if ! does_bin_exist 'gh'; then + log_error "GitHub CLI not installed" + return 1 + fi + + if ! gh auth status >/dev/null 2>&1; then + log_info "GitHub CLI not authenticated" + return 1 + fi + + return 0 +} +``` + +**Returns:** +- `0` if GitHub CLI is authenticated +- `1` if not authenticated or not installed + +**Example:** +```bash +if setup_github_cli; then + echo "GitHub CLI ready" +fi +``` + +## GitHub Repository Operations + +### clone_github_repo + +```bash +function clone_github_repo() { + local REPOSITORY="${1}" + local TARGET_DIR="${2}" + + if does_directory_exist "${TARGET_DIR}"; then + log_info "Repository already cloned: ${TARGET_DIR}" + return 0 + fi + + git clone "https://github.com/${REPOSITORY}.git" "${TARGET_DIR}" + + return $? +} +``` + +**Parameters:** +- `REPOSITORY` (string): GitHub repository (owner/repo) +- `TARGET_DIR` (string): Target directory + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +clone_github_repo "vim/vim" "${HOME}/src/vim" +``` + +### update_github_repo + +```bash +function update_github_repo() { + local REPO_DIR="${1}" + + if ! does_directory_exist "${REPO_DIR}"; then + log_error "Repository directory not found: ${REPO_DIR}" + return 1 + fi + + cd "${REPO_DIR}" + git pull + + return $? +} +``` + +**Parameters:** +- `REPO_DIR` (string): Repository directory + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +update_github_repo "${HOME}/src/vim" +``` + +## GitHub Gist Operations + +### create_github_gist + +```bash +function create_github_gist() { + local FILE="${1}" + local DESCRIPTION="${2}" + local PUBLIC="${3}" + + if ! setup_github_cli; then + return 1 + fi + + local VISIBILITY="--public" + if [ "${PUBLIC}" != "true" ]; then + VISIBILITY="--secret" + fi + + gh gist create "${FILE}" --desc "${DESCRIPTION}" ${VISIBILITY} + + return $? +} +``` + +**Parameters:** +- `FILE` (string): File to upload +- `DESCRIPTION` (string): Gist description +- `PUBLIC` (string): "true" for public, "false" for secret + +**Returns:** +- `0` on success +- Non-zero on failure + +**Example:** +```bash +create_github_gist "/path/to/file" "My gist" "true" +``` + +## Error Handling + +All GitHub functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Handle network errors gracefully +- Continue on non-critical failures + +## See Also + +- [components/integration-models.md](../components/integration-models.md) — Integration models +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [flows/git-setup.md](../flows/git-setup.md) — Git setup flow \ No newline at end of file diff --git a/docs/api-reference/package-management.md b/docs/api-reference/package-management.md new file mode 100644 index 0000000..fbb7b3a --- /dev/null +++ b/docs/api-reference/package-management.md @@ -0,0 +1,425 @@ +# Package Management API Reference + +This document provides API reference for package management operations in the linux-setup-script repository. + +## Package Manager Abstraction + +### call_package_manager + +```bash +function call_package_manager() { + if [ "${DISTRO_FAMILY}" = "Arch" ]; then + if [ "${UID}" != '0' ]; then + if [ -f "${ROOT_USR_BIN}/paru" ]; then + LANG=C LC_TIME='' paru ${*} --noconfirm --noprovides --noredownload --norebuild --sudoloop + elif [ -f "${ROOT_USR_BIN}/yay" ]; then + LANG=C LC_TIME='' yay ${*} --noconfirm + elif [ -f "${ROOT_USR_BIN}/yaourt" ]; then + LANG=C LC_TIME='' yaourt ${*} --noconfirm + else + LANG=C LC_TIME='' run_as_su pacman ${*} --noconfirm + fi + else + LANG=C LC_TIME='' pacman ${*} --noconfirm + fi + elif [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + yes | run_as_su apk ${*} + elif [ "${DISTRO_FAMILY}" = 'Android' ]; then + yes | pkg ${*} + elif [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + yes | run_as_su apt ${*} + fi +} +``` + +**Parameters:** +- Variable arguments passed to package manager commands + +**Returns:** +- Exit code of package manager command + +**Example:** +```bash +call_package_manager update +call_package_manager install "package-name" +``` + +### call_android_package_manager + +```bash +function call_android_package_manager() { + run_as_su pm ${*} +} +``` + +**Parameters:** +- Variable arguments passed to package manager commands + +**Returns:** +- Exit code of package manager command + +**Example:** +```bash +call_android_package_manager list packages +``` + +## Installation Functions + +### install_native_package + +```bash +function install_native_package() { + local PACKAGE="${1}" + + is_native_package_installed "${PACKAGE}" && return + + echo -e " >>> Installing native package: \e[0;33m${PACKAGE}\e[0m..." + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + call_package_manager add "${PACKAGE}" + elif [ "${DISTRO_FAMILY}" = 'Arch' ]; then + call_package_manager -S --asexplicit "${PACKAGE}" + elif [ "${DISTRO_FAMILY}" = 'Android' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + call_package_manager install "${PACKAGE}" + fi +} +``` + +**Parameters:** +- `PACKAGE` (string): Package name to install + +**Returns:** +- `0` if package already installed +- Exit code of installation command + +**Example:** +```bash +install_native_package "vim" +``` + +### install_native_packages + +```bash +function install_native_packages() { + for PACKAGE_NAME in "${@}"; do + install_native_package "${PACKAGE_NAME}" + done +} +``` + +**Parameters:** +- Variable arguments: Package names to install + +**Example:** +```bash +install_native_packages "vim" "git" "curl" +``` + +### install_android_package + +```bash +function install_android_package() { + [ "${DISTRO_FAMILY}" != 'Android' ] && return + + local PACKAGE="${1}" + local PACKAGE_NAME="${2}" + + [ -z "${PACKAGE_NAME}" ] && PACKAGE_NAME=$(echo "${PACKAGE}" | sed 's/.*\/\([^\/]*\)\.apk$/\1/g') + + is_android_package_installed "${PACKAGE_NAME}" && return + + echo -e " >>> Installing Android package: \e[0;33m${PACKAGE_NAME}\e[0m..." + call_android_package_manager install --user 0 "${PACKAGE}" +} +``` + +**Parameters:** +- `PACKAGE` (string): Package file path +- `PACKAGE_NAME` (string): Package name (optional) + +**Returns:** +- `0` if package already installed +- Exit code of installation command + +**Example:** +```bash +install_android_package "/path/to/package.apk" +``` + +### install_cargo_package + +```bash +function install_cargo_package() { + local PACKAGE="${1}" + + is_cargo_package_installed "${PACKAGE}" && return + + echo -e " >>> Installing cargo package: \e[0;33m${PACKAGE}\e[0m..." + call_cargo install "${PACKAGE}" +} +``` + +**Parameters:** +- `PACKAGE` (string): Cargo package name + +**Returns:** +- `0` if package already installed +- Exit code of installation command + +**Example:** +```bash +install_cargo_package "ripgrep" +``` + +### install_flatpak + +```bash +function install_flatpak() { + local PACKAGE="${1}" + local REMOTE='flathub' + + if [ $# -eq 2 ]; then + local REMOTE="${1}" + local PACKAGE="${2}" + fi + + is_flatpak_installed "${PACKAGE}" && return + + local INSTALLATION_METHOD='user' + PACKAGE="$(get_latest_flatpak_ref "${REMOTE}" "${PACKAGE}")" + + echo -e " >>> Installing ${INSTALLATION_METHOD} flatpak (${REMOTE}): \e[0;33m${PACKAGE}\e[0m (${REMOTE})..." + call_flatpak install --${INSTALLATION_METHOD} "${REMOTE}" "${PACKAGE}" +} +``` + +**Parameters:** +- `PACKAGE` (string): Flatpak package name +- `REMOTE` (string): Remote repository (optional, default: flathub) + +**Returns:** +- `0` if package already installed +- Exit code of installation command + +**Example:** +```bash +install_flatpak "flathub/org.mozilla.firefox" +``` + +### install_github_package + +```bash +function install_github_package() { + local PACKAGE_NAME="${1}" + local REPOSITORY="${2}" + + local PACKAGE_METADATA_DIR="${LINUX_SETUP_SCRIPT_PACKAGES_DIR}/${PACKAGE_NAME}" + + local RELEASE_JSON + RELEASE_JSON=$(curl -s "https://api.github.com/repos/${REPOSITORY}/releases/latest") + + local VERSION + VERSION=$(echo "${RELEASE_JSON}" | jq -r '.tag_name') + + # Detect architecture + local ARCH + ARCH=$(dpkg --print-architecture) + + local ARCH_REGEX="${ARCH}" + if [ "${ARCH}" = 'amd64' ]; then + ARCH_REGEX='(amd64|x86_64)' + elif [ "${ARCH}" = 'arm64' ]; then + ARCH_REGEX='(arm64|aarch64)' + fi + + local DOWNLOAD_URL + DOWNLOAD_URL=$(echo "${RELEASE_JSON}" | jq -r '.assets[] | .browser_download_url' | grep -E '\.deb$') + + # First try arch-specific match + ARCH_MATCH=$(echo "${DOWNLOAD_URL}" | grep -E "${ARCH_REGEX}" | head -n1) + + if [ -n "${ARCH_MATCH}" ]; then + DOWNLOAD_URL="${ARCH_MATCH}" + else + # Fallback to any .deb (e.g. Architecture: all) + DOWNLOAD_URL=$(echo "${DOWNLOAD_URL}" | head -n1) + fi + + if [ -z "${DOWNLOAD_URL}" ]; then + echo " !!! No matching .deb asset found for ${REPOSITORY} (${ARCH})" + return 1 + fi + + if is_github_package_installed "${PACKAGE_NAME}"; then + local INSTALLED_VERSION + INSTALLED_VERSION=$(run_as_su cat "${PACKAGE_METADATA_DIR}/version") + + if [ "${INSTALLED_VERSION}" = "${VERSION}" ]; then + return + fi + fi + + echo -e " >>> Installing GitHub package: \e[0;33m${PACKAGE_NAME}\e[0m (${VERSION})..." + + local TMP_FILE="${LOCAL_INSTALL_TEMP_DIR}/${PACKAGE_NAME}-${VERSION}.deb" + + create_directory "${LOCAL_INSTALL_TEMP_DIR}" + remove "${TMP_FILE}" + + trap 'remove "${TMP_FILE}"' RETURN + + wget "${DOWNLOAD_URL}" -O "${TMP_FILE}" + + if [ ! -s "${TMP_FILE}" ]; then + echo " !!! Download failed for ${PACKAGE_NAME}" + return 1 + fi + + run_as_su apt install -y "${TMP_FILE}" || { + echo " !!! Installation failed for ${PACKAGE_NAME}" + return 1 + } + + create_directory "${PACKAGE_METADATA_DIR}" + run_as_su sh -c "echo '${VERSION}' > '${PACKAGE_METADATA_DIR}/version'" + run_as_su sh -c "echo 'github' > '${PACKAGE_METADATA_DIR}/medium'" +} +``` + +**Parameters:** +- `PACKAGE_NAME` (string): Package name +- `REPOSITORY` (string): GitHub repository (owner/repo) + +**Returns:** +- `0` if package already installed +- Exit code of installation command + +**Example:** +```bash +install_github_package "vim" "vim/vim" +``` + +## Detection Functions + +### is_package_installed + +```bash +function is_package_installed() { + local PACKAGE="${1}" + + if [ "${OS}" = 'Android' ]; then + is_android_package_installed "${PACKAGE}" && return 0 + elif [ "${OS}" = 'Linux' ]; then + is_native_package_installed "${PACKAGE}" && return 0 + is_flatpak_installed "${PACKAGE}" && return 0 + is_github_package_installed "${PACKAGE}" && return 0 + is_webapp_installed "${PACKAGE}" && return 0 + fi + + return 1 +} +``` + +**Parameters:** +- `PACKAGE` (string): Package name to check + +**Returns:** +- `0` if package is installed +- `1` if package is not installed + +**Example:** +```bash +if is_package_installed "vim"; then + echo "vim is installed" +fi +``` + +### is_native_package_installed + +```bash +function is_native_package_installed() { + local PACKAGE_NAME="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + call_package_manager info -e "${PACKAGE_NAME}" >/dev/null 2>&1 + return $? + elif [ "${DISTRO_FAMILY}" = 'Arch' ]; then + if (pacman -Q | grep -q "^${PACKAGE_NAME}\s" > /dev/null); then + return 0 # True + else + return 1 # False + fi + elif [ "${DISTRO_FAMILY}" = 'Android' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + if (apt-cache policy "${PACKAGE_NAME}" | grep -q '^\s*Installed:\s*[0-9]'); then + return 0 # True + else + return 1 # False + fi + fi + + return 1 +} +``` + +**Parameters:** +- `PACKAGE_NAME` (string): Package name to check + +**Returns:** +- `0` if package is installed +- `1` if package is not installed + +**Example:** +```bash +if is_native_package_installed "vim"; then + echo "vim is installed" +fi +``` + +## Uninstallation Functions + +### uninstall_package + +```bash +function uninstall_package() { + local PACKAGE="${1}" + + if [ "${OS}" = 'Android' ]; then + uninstall_android_package "${PACKAGE}" + elif [ "${OS}" = 'Linux' ]; then + uninstall_native_package "${PACKAGE}" + uninstall_flatpak "${PACKAGE}" + uninstall_github_package "${PACKAGE}" + uninstall_webapp "${PACKAGE}" + fi +} +``` + +**Parameters:** +- `PACKAGE` (string): Package name to uninstall + +**Returns:** +- Exit code of uninstallation command + +**Example:** +```bash +uninstall_package "vim" +``` + +## Error Handling + +All package management functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `is_package_installed` for checks +- Continue on non-critical failures + +## See Also + +- [components/package-management.md](../components/package-management.md) — Package management abstraction +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [flows/package-management.md](../flows/package-management.md) — Package management flow \ No newline at end of file diff --git a/docs/api-reference/service-management.md b/docs/api-reference/service-management.md new file mode 100644 index 0000000..56e8d16 --- /dev/null +++ b/docs/api-reference/service-management.md @@ -0,0 +1,309 @@ +# Service Management API Reference + +This document provides API reference for service management operations in the linux-setup-script repository. + +## Service Control Functions + +### enable_service + +```bash +function enable_service() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + run_as_su rc-update add "${SERVICE}" default + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + run_as_su systemctl enable "${SERVICE}" + fi +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- Exit code of enable command + +**Example:** +```bash +enable_service "sshd" +``` + +### disable_service + +```bash +function disable_service() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + run_as_su rc-update del "${SERVICE}" default + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + run_as_su systemctl disable "${SERVICE}" + fi +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- Exit code of disable command + +**Example:** +```bash +disable_service "sshd" +``` + +### start_service + +```bash +function start_service() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + run_as_su rc-service "${SERVICE}" start + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + run_as_su systemctl start "${SERVICE}" + fi +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- Exit code of start command + +**Example:** +```bash +start_service "sshd" +``` + +### stop_service + +```bash +function stop_service() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + run_as_su rc-service "${SERVICE}" stop + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + run_as_su systemctl stop "${SERVICE}" + fi +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- Exit code of stop command + +**Example:** +```bash +stop_service "sshd" +``` + +### restart_service + +```bash +function restart_service() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + run_as_su rc-service "${SERVICE}" restart + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + run_as_su systemctl restart "${SERVICE}" + fi +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- Exit code of restart command + +**Example:** +```bash +restart_service "sshd" +``` + +### is_service_enabled + +```bash +function is_service_enabled() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + rc-update show default | grep -q "^${SERVICE}$" + return $? + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + systemctl is-enabled "${SERVICE}" >/dev/null 2>&1 + return $? + fi + + return 1 +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- `0` if service is enabled +- `1` if service is not enabled + +**Example:** +```bash +if is_service_enabled "sshd"; then + echo "sshd is enabled" +fi +``` + +### is_service_active + +```bash +function is_service_active() { + local SERVICE="${1}" + + if [ "${DISTRO_FAMILY}" = 'Alpine' ]; then + rc-service "${SERVICE}" status | grep -q "started" + return $? + elif [ "${DISTRO_FAMILY}" = 'Arch' ] \ + || [ "${DISTRO_FAMILY}" = 'Debian' ] \ + || [ "${DISTRO_FAMILY}" = 'Ubuntu' ]; then + systemctl is-active "${SERVICE}" >/dev/null 2>&1 + return $? + fi + + return 1 +} +``` + +**Parameters:** +- `SERVICE` (string): Service name + +**Returns:** +- `0` if service is active +- `1` if service is not active + +**Example:** +```bash +if is_service_active "sshd"; then + echo "sshd is active" +fi +``` + +## User Service Control + +### enable_user_service + +```bash +function enable_user_service() { + local SERVICE="${1}" + + systemctl --user enable "${SERVICE}" +} +``` + +**Parameters:** +- `SERVICE` (string): User service name + +**Returns:** +- Exit code of enable command + +**Example:** +```bash +enable_user_service "pipewire" +``` + +### disable_user_service + +```bash +function disable_user_service() { + local SERVICE="${1}" + + systemctl --user disable "${SERVICE}" +} +``` + +**Parameters:** +- `SERVICE` (string): User service name + +**Returns:** +- Exit code of disable command + +**Example:** +```bash +disable_user_service "pipewire" +``` + +### start_user_service + +```bash +function start_user_service() { + local SERVICE="${1}" + + systemctl --user start "${SERVICE}" +} +``` + +**Parameters:** +- `SERVICE` (string): User service name + +**Returns:** +- Exit code of start command + +**Example:** +```bash +start_user_service "pipewire" +``` + +### stop_user_service + +```bash +function stop_user_service() { + local SERVICE="${1}" + + systemctl --user stop "${SERVICE}" +} +``` + +**Parameters:** +- `SERVICE` (string): User service name + +**Returns:** +- Exit code of stop command + +**Example:** +```bash +stop_user_service "pipewire" +``` + +## Error Handling + +All service management functions follow consistent error handling: + +- Return 0 on success, non-zero on failure +- Log errors with `log_error` +- Use `is_service_enabled` and `is_service_active` for checks +- Handle permission errors gracefully + +## See Also + +- [components/service-management.md](../components/service-management.md) — Service management details +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [flows/system-configuration.md](../flows/system-configuration.md) — System configuration flow \ No newline at end of file diff --git a/docs/api-reference/system-info.md b/docs/api-reference/system-info.md new file mode 100644 index 0000000..b181dae --- /dev/null +++ b/docs/api-reference/system-info.md @@ -0,0 +1,146 @@ +# System Info API Reference + +This document provides API reference for system information detection in the linux-setup-script repository. + +## Environment Variables + +### Platform Detection + +| Variable | Type | Description | Example | +|----------|------|-------------|---------| +| `OS` | string | Operating system name | `Linux` | +| `DISTRO` | string | Distribution name | `Arch` | +| `DISTRO_FAMILY` | string | Distribution family | `Arch`, `Debian`, `Ubuntu`, `Alpine`, `Android` | +| `ARCH` | string | Architecture | `x86_64`, `aarch64` | + +### Device Detection + +| Variable | Type | Description | Example | +|----------|------|-------------|---------| +| `DEVICE_MODEL` | string | Device model name | `ThinkPad X1 Carbon` | +| `CHASSIS_TYPE` | string | Chassis type | `laptop`, `desktop`, `server` | +| `IS_BATTERY_DEVICE` | boolean | Has battery | `true` or `false` | +| `POWERFUL_PC` | boolean | Powerful PC | `true` or `false` | + +### Display Detection + +| Variable | Type | Description | Example | +|----------|------|-------------|---------| +| `HAS_GUI` | boolean | Has graphical interface | `true` or `false` | +| `DESKTOP_ENVIRONMENT` | string | Desktop environment | `GNOME`, `KDE`, `MATE`, `LXDE`, `Phosh` | +| `DISPLAY_SERVER` | string | Display server | `X11`, `Wayland` | +| `GTK_THEME` | string | GTK theme name | `Adwaita` | +| `GTK_THEME_VARIANT` | string | Theme variant | `dark` or `light` | + +### Hardware Detection + +| Variable | Type | Description | Example | +|----------|------|-------------|---------| +| `GPU_FAMILY` | string | GPU family | `nvidia`, `amd`, `intel` | +| `IS_DEVELOPMENT_DEVICE` | boolean | Development device | `true` or `false` | +| `IS_GAMING_DEVICE` | boolean | Gaming device | `true` or `false` | +| `IS_GENERAL_PURPOSE_DEVICE` | boolean | General purpose device | `true` or `false` | + +### Privilege Detection + +| Variable | Type | Description | Example | +|----------|------|-------------|---------| +| `HAS_SU_PRIVILEGES` | boolean | Can escalate to root | `true` or `false` | +| `RUN_AS_SU` | string | Command prefix for root | `sudo -n` or `su -c` | + +## Detection Functions + +### detect_os + +```bash +function detect_os() { + local OS_NAME + OS_NAME="$(uname -s)" + echo "${OS_NAME}" +} +``` + +**Returns:** +- Operating system name + +**Example:** +```bash +OS="$(detect_os)" +``` + +### detect_distro + +```bash +function detect_distro() { + local DISTRO_NAME + DISTRO_NAME="$(. /etc/os-release && echo "${NAME:-${ID}}")" + echo "${DISTRO_NAME}" +} +``` + +**Returns:** +- Distribution name + +**Example:** +```bash +DISTRO="$(detect_distro)" +``` + +### detect_arch + +```bash +function detect_arch() { + local ARCH_NAME + ARCH_NAME="$(uname -m)" + echo "${ARCH_NAME}" +} +``` + +**Returns:** +- Architecture name + +**Example:** +```bash +ARCH="$(detect_arch)" +``` + +## Platform-Specific Detection + +### Android/Termux Detection + +```bash +# Check if running on Android/Termux +if does_directory_exist "/data/data/com.termux/files/usr"; then + IS_ANDROID=true +else + IS_ANDROID=false +fi +``` + +### SteamOS Detection + +```bash +# Check if running on SteamOS +if [[ "${DISTRO}" == "SteamOS" ]]; then + IS_STEAMOS=true +else + IS_STEAMOS=false +fi +``` + +### WSL Detection + +```bash +# Check if running on WSL +if grep -q "microsoft" /proc/version 2>/dev/null; then + IS_WSL=true +else + IS_WSL=false +fi +``` + +## See Also + +- [components/foundation-layer.md](../components/foundation-layer.md) — Foundation layer details +- [components/filesystem.md](../components/filesystem.md) — Filesystem operations +- [components/common.md](../components/common.md) — Execution primitives \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..9ba0f31 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,174 @@ +# Architecture + +This document describes the high-level architecture of the linux-setup-script +repository, complementing the repository overview with deeper structural +analysis. + +## Architectural Decomposition + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ run.sh (Orchestrator) │ +│ Phase 0: Env Setup → Phase 1: Packages → Phase 2: Config │ +│ → Phase 3: System Config → Phase 4: Git Setup (optional) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌─────────────────────┼─────────────────────┐ + ▼ ▼ ▼ +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ +│ Foundation │ │ Phase Scripts│ │ Resources │ +│ Layer │ │ (scripts/) │ │ (resources/) │ +│ (common/) │ │ │ │ │ +└───────────────┘ └───────────────┘ └───────────────┘ + │ │ │ + └─────────────────────┼─────────────────────┘ + ▼ + ┌───────────────────┐ + │ Target System │ + │ (/etc, /home, │ + │ /boot, etc.) │ + └───────────────────┘ +``` + +## Component Relationships + +### Foundation Layer (`scripts/common/`) + +| Module | Responsibility | Consumers | +|--------|----------------|-----------| +| `filesystem.sh` | Path resolution, constants, file utilities | All scripts | +| `common.sh` | Privilege escalation, script execution, shell setup | All scripts | +| `system-info.sh` | Hardware/environment detection, exported variables | All scripts | +| `package-management.sh` | Unified package manager abstraction | Package scripts | +| `config.sh` | Config file manipulation (INI, JSON, XML, GSettings, modprobe) | Config scripts | +| `service-management.sh` | systemd/OpenRC service control | Service scripts | +| `apps.sh` | Firefox profile detection | Firefox config | +| `permissions.sh` | Flatpak/GNOME/Android permission management | Permission scripts | +| `github.sh` | GitHub API helpers | GitHub release installer | + +### Phase Scripts (`scripts/`) + +| Phase | Scripts | Privilege | Platform | +|-------|---------|-----------|----------| +| 1: Repos & Packages | configure-repositories, update-repositories, uninstall-packages, update-packages, install-packages, clean-packages | Root (repos) / User (packages) | Linux | +| 2: Config Deployment | update-rcs, update-profiles, update-resources | User + Root | Linux + Android | +| 3: System Config | configure-system, configure-launchers, configure-autostart-apps, configure-default-apps, configure-permissions, configure-directories, configure-locale, configure-time, configure-services-user, configure-services-system, configure-hardware-integration, update-grub | User + Root | Linux (GUI subset) | +| 4: Git Setup | setup-gpg-key, setup-ssh-key | User | All | + +### Resource Templates (`resources/`, `rc/`, `profiles/`, `data/`) + +| Category | Purpose | Deployment | +|----------|---------|------------| +| `resources/firefox/` | Firefox containers, userChrome, icons | update-resources.sh | +| `resources/git/hooks/` | Git hooks | update-resources.sh | +| `resources/lxpanel/` | LXPanel icons, logout entry | update-resources.sh | +| `resources/plank/` | Plank theme, autostart | update-resources.sh | +| `resources/neofetch/` | ASCII logos | update-resources.sh | +| `resources/pcmanfm/` | File manager actions | update-resources.sh | +| `resources/templates/` | XDG templates | update-resources.sh | +| `resources/udev/` | udev rules (power, I/O, PCI) | update-resources.sh (root) | +| `rc/shell/` | Bash config, aliases, functions, prompt | update-rcs.sh | +| `rc/gitconfig` | Git configuration | update-rcs.sh | +| `rc/gimp/`, `rc/nanorc`, `rc/vimrc` | App configs | update-rcs.sh | +| `rc/grub/` | GRUB multi-boot entries | update-grub.sh | +| `rc/keyboard-layouts/` | X11 keymaps | configure-locale.sh | +| `rc/network-manager/` | MAC randomisation | update-resources.sh | +| `rc/hidden-files/` | .hidden files for directories | configure-directories.sh | +| `profiles/` | /etc/profile.d scripts | update-profiles.sh | +| `data/` | Steam AppID mappings | Reference only | + +## Dependency Direction + +``` +run.sh + │ + ├──► filesystem.sh (no deps) + ├──► common.sh ──► filesystem.sh + ├──► system-info.sh ──► filesystem.sh, common.sh + ├──► package-management.sh ──► filesystem.sh, common.sh, system-info.sh + ├──► config.sh ──► filesystem.sh + ├──► service-management.sh ──► filesystem.sh, common.sh, config.sh + ├──► apps.sh ──► filesystem.sh + ├──► permissions.sh ──► config.sh, package-management.sh + └──► github.sh (no deps) + +Phase scripts ──► Foundation modules (as needed) +Phase scripts ──► Resources/RCs (deployed, not sourced) +``` + +**Key invariant**: Foundation modules have no circular dependencies. Phase scripts depend on foundation modules but not on each other. + +## Runtime Topology + +### Execution Contexts + +| Context | Scripts | Environment | +|---------|---------|-------------| +| User (interactive) | run.sh, package scripts, update-rcs, update-resources (user), configure-system (user), configure-launchers, configure-autostart-apps, configure-default-apps, configure-permissions, configure-directories, configure-services-user, git setup | $HOME, $XDG_CONFIG_HOME, $XDG_DATA_HOME | +| Root (sudo/su) | configure-repositories, update-rcs (root), update-resources (root), configure-system (root), configure-locale, configure-time, update-profiles, configure-services-system, configure-hardware-integration, update-grub, clean-packages (Arch) | /etc, /usr, /boot, /var | + +### Privilege Boundaries + +``` +User Context Root Context +───────────────── ───────────────── +~/.bashrc, ~/.profile /etc/profile.d/ +~/.config/ /etc/udev/rules.d/ +~/.local/share/ /etc/modprobe.d/ +~/.ssh/ /etc/sysctl.d/ +~/.gnupg/ /etc/systemd/ +Flatpak user installs Flatpak system installs +systemd --user services systemd system services +cargo install pacman/apt/apk install (system) +``` + +## Major State + +### Exported Environment Variables (from `system-info.sh`) + +| Variable | Scope | Mutability | +|----------|-------|------------| +| `OS`, `DISTRO`, `DISTRO_FAMILY` | Global | Read-only after detection | +| `ARCH`, `ARCH_FAMILY` | Global | Read-only | +| `DEVICE_MODEL`, `CHASSIS_TYPE` | Global | Read-only | +| `HAS_GUI`, `DESKTOP_ENVIRONMENT`, `DISPLAY_SERVER` | Global | Read-only | +| `GPU_FAMILY`, `IS_BATTERY_DEVICE` | Global | Read-only | +| `POWERFUL_PC`, `IS_DEVELOPMENT_DEVICE`, `IS_GAMING_DEVICE` | Global | Read-only | +| `HAS_SU_PRIVILEGES` | Global | Read-only | +| `REPO_DIR`, `ROOT_*`, `HOME_REAL`, `XDG_*` | Global | Read-only | + +### Derived State (in `configure-system.sh`) + +| Variable | Derived From | Purpose | +|----------|--------------|---------| +| `GTK_THEME`, `GTK_THEME_VARIANT` | `get_theme()`, `get_theme_mode()` | Theme selection | +| `INTERFACE_FONT`, `MONOSPACE_FONT`, etc. | Screen height, DPI | Font sizing | +| `TERMINAL_SIZE_COLS/ROWS` | Screen resolution | Terminal geometry | +| `ZOOM_LEVEL` | Screen height | UI scaling | +| `USING_INTEL_GPU`, `USING_NVIDIA_GPU` | `GPU_FAMILY` | GPU-specific config | +| `IS_SERVER` | Screen resolution | Headless detection | + +## Major Integrations + +| Integration | Layer | Configuration | +|-------------|-------|---------------| +| Package managers | Foundation | `package-management.sh` dispatch | +| Display servers | Detection | `system-info.sh` → `DISPLAY_SERVER` | +| Desktop environments | Detection + Config | `DESKTOP_ENVIRONMENT` → gsettings/kwriteconfig | +| GPU drivers | Kernel params | `configure-system.sh` → modprobe.d | +| Bootloader | Config | `update-grub.sh` → /etc/grub.d/ | +| Hardware (Argon) | Detection + Config | `configure-hardware-integration.sh` | +| Android/Termux | Platform guard | `IS_ANDROID` → skip system config | + +## System-Wide Invariants + +1. **Idempotency** — All file operations use `update_file_if_distinct` (content comparison) +2. **Privilege separation** — Explicit `run_script` vs `run_script_as_su`; no ambient authority +3. **Platform guards** — GUI config wrapped in `if ${HAS_GUI}`; Android guarded by `IS_ANDROID` +4. **Binary existence checks** — `update_file_if_binary_exists` before deploying app configs +5. **Ordering** — `run.sh` enforces strict phase sequence; dependencies flow forward +6. **Failure isolation** — No `set -e` at top level; individual script failures don't halt orchestration +7. **Environment consistency** — `LANG=en_US.UTF-8` forced for deterministic command output +8. **Distro awareness** — All package operations dispatch via `DISTRO_FAMILY` +9. **Resolution awareness** — All sizing derives from detected screen dimensions +10. **Device awareness** — Special handling for Argon ONE UP, Steam Deck, Raspberry Pi \ No newline at end of file diff --git a/docs/behaviour/application-launcher-customisation.md b/docs/behaviour/application-launcher-customisation.md new file mode 100644 index 0000000..31a5a8e --- /dev/null +++ b/docs/behaviour/application-launcher-customisation.md @@ -0,0 +1,649 @@ +# Application Launcher Customisation + +This document describes how application launchers are configured, modified, +and managed across the system. + +## Overview + +The application launcher customisation system modifies `.desktop` files for +installed applications, setting appropriate names, categories, icons, and +window class names. + +## Flow Diagram + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Application Launcher Customisation Flow │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Launcher Discovery │ +│ • Scan /usr/share/applications │ +│ • Scan ~/.local/share/applications │ +│ • Scan ~/.config/autostart │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Launcher Modification │ +│ • Set Name and localized names │ +│ • Set Categories │ +│ • Set Icon │ +│ • Set NoDisplay │ +│ • Set StartupWMClass │ +│ • Set Keywords │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Launcher Deployment │ +│ • Deploy custom launchers │ +│ • Create desktop entries │ +│ • Update desktop database │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Launcher Discovery + +### Script: `scripts/configure-launchers.sh` + +```bash +#!/bin/bash +set -euo pipefail + +# Load foundation +source "${REPO_DIR}/scripts/common/filesystem.sh" +source "${REPO_DIR}/scripts/common/common.sh" + +# Launcher categories +declare -a LAUNCHER_CATEGORIES=( + "ai" + "app-stores" + "archive-managers" + "audio-players" + "browsers" + "calculators" + "cameras" + "chat" + "code-editors" + "databases" + "disk" + "email" + "file-managers" + "games" + "ides" + "image-viewers" + "media-players" + "messengers" + "notes" + "office" + "password-managers" + "remote-desktop" + "screenshots" + "settings" + "terminals" + "text-editors" + "video-players" + "virtualization" + "vpn" + "web-apps" +) + +configure_launchers() { + log_section "Application Launcher Customisation" + + # Discover launchers + discover_launchers + + # Modify launchers + modify_launchers + + # Deploy custom launchers + deploy_custom_launchers + + log_success "Application launchers customised" +} + +discover_launchers() { + log_subsection "Discovering Launchers" + + local launcher_dirs=( + "/usr/share/applications" + "${HOME}/.local/share/applications" + "${HOME}/.config/autostart" + ) + + for dir in "${launcher_dirs[@]}"; do + if [ -d "${dir}" ]; then + log_info "Scanning: ${dir}" + find "${dir}" -name "*.desktop" -type f + fi + done +} +``` + +## Launcher Modification + +### Modification Function + +```bash +modify_launchers() { + log_subsection "Modifying Launchers" + + local launcher_files=() + + # Collect launcher files + for dir in /usr/share/applications "${HOME}/.local/share/applications"; do + if [ -d "${dir}" ]; then + while IFS= read -r -d '' file; do + launcher_files+=("${file}") + done < <(find "${dir}" -name "*.desktop" -type f -print0) + fi + done + + # Modify each launcher + for launcher in "${launcher_files[@]}"; do + modify_launcher "${launcher}" + done + + log_info "Launchers modified: ${#launcher_files[@]}" +} + +modify_launcher() { + local launcher="$1" + + # Read launcher content + local content=$(cat "${launcher}") + + # Modify launcher based on application + modify_launcher_by_app "${launcher}" "${content}" + + # Write back if changed + if [ "${content}" != "$(cat "${launcher}")" ]; then + log_info "Modified launcher: ${launcher}" + fi +} + +modify_launcher_by_app() { + local launcher="$1" + local content="$2" + + # Extract application name + local app_name=$(grep -oP '(?<=^Name=).+' "${launcher}" | head -1) + + # Modify based on application + case "${app_name}" in + "Visual Studio Code") + modify_vscode "${launcher}" + ;; + "Discord") + modify_discord "${launcher}" + ;; + "Telegram") + modify_telegram "${launcher}" + ;; + "Signal Desktop") + modify_signal "${launcher}" + ;; + "Firefox") + modify_firefox "${launcher}" + ;; + "Chromium") + modify_chromium "${launcher}" + ;; + "Google Chrome") + modify_chrome "${launcher}" + ;; + "LibreOffice Writer") + modify_libreoffice "${launcher}" + ;; + "GIMP") + modify_gimp "${launcher}" + ;; + "VLC media player") + modify_vlc "${launcher}" + ;; + *) + # Generic modification + modify_generic "${launcher}" + ;; + esac +} +``` + +### Generic Launcher Modification + +```bash +modify_generic() { + local launcher="$1" + + # Set Name (if not already set) + if ! grep -q "^Name=" "${launcher}"; then + local app_name=$(grep -oP '(?<=^Exec=).+' "${launcher}" | sed 's/.*\///' | sed 's/^[^/]*\///') + sed -i "1i Name=${app_name}" "${launcher}" + fi + + # Set Name[ro] (Romanian localization) + if grep -q "^Name=" "${launcher}"; then + local name=$(grep -oP '(?<=^Name=).+' "${launcher}" | head -1) + sed -i "s/^Name=.*/Name=${name}\nName[ro]=${name}/" "${launcher}" + fi + + # Set Categories + if ! grep -q "^Categories=" "${launcher}"; then + sed -i "s/^Exec=.*/&\nCategories=Application;" "${launcher}" + fi + + # Set Icon + if ! grep -q "^Icon=" "${launcher}"; then + local icon=$(grep -oP '(?<=^Exec=).+' "${launcher}" | sed 's/.*\///' | sed 's/^[^/]*\///') + sed -i "s/^Exec=.*/&\nIcon=${icon}/" "${launcher}" + fi + + # Set NoDisplay + if ! grep -q "^NoDisplay=" "${launcher}"; then + sed -i "s/^Exec=.*/&\nNoDisplay=false/" "${launcher}" + fi + + # Set StartupWMClass + if ! grep -q "^StartupWMClass=" "${launcher}"; then + local wm_class=$(grep -oP '(?<=^Exec=).+' "${launcher}" | sed 's/.*\///' | sed 's/^[^/]*\///') + sed -i "s/^Exec=.*/&\nStartupWMClass=${wm_class}/" "${launcher}" + fi + + # Set Keywords + if ! grep -q "^Keywords=" "${launcher}"; then + sed -i "s/^Exec=.*/&\nKeywords=Application;" "${launcher}" + fi +} +``` + +### Application-Specific Modifications + +#### Visual Studio Code + +```bash +modify_vscode() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Visual Studio Code/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Visual Studio Code\nName[ro]=Visual Studio Code/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Development;IDE;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=code/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=Code/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Development;IDE;Code;Editor;' "${launcher}" +} +``` + +#### Discord + +```bash +modify_discord() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Discord/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Discord\nName[ro]=Discord/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Network;Chat;InstantMessaging;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=discord/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=discord/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Network;Chat;InstantMessaging;Discord;' "${launcher}" +} +``` + +#### Telegram + +```bash +modify_telegram() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Telegram/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Telegram\nName[ro]=Telegram/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Network;Chat;InstantMessaging;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=telegram-desktop/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=Telegram/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Network;Chat;InstantMessaging;Telegram;' "${launcher}" +} +``` + +#### Signal Desktop + +```bash +modify_signal() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Signal/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Signal\nName[ro]=Signal/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Network;Chat;InstantMessaging;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=signal/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=Signal/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Network;Chat;InstantMessaging;Signal;' "${launcher}" +} +``` + +#### Firefox + +```bash +modify_firefox() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Firefox Web Browser/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Firefox Web Browser\nName[ro]=Firefox Web Browser/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Network;WebBrowser;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=firefox/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=firefox/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Network;WebBrowser;Firefox;' "${launcher}" +} +``` + +#### Chromium + +```bash +modify_chromium() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Chromium Web Browser/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Chromium Web Browser\nName[ro]=Chromium Web Browser/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Network;WebBrowser;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=chromium/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=chromium/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Network;WebBrowser;Chromium;' "${launcher}" +} +``` + +#### Google Chrome + +```bash +modify_chrome() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=Google Chrome/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=Google Chrome\nName[ro]=Google Chrome/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Network;WebBrowser;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=google-chrome/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=google-chrome/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Network;WebBrowser;Chrome;' "${launcher}" +} +``` + +#### LibreOffice Writer + +```bash +modify_libreoffice() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=LibreOffice Writer/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=LibreOffice Writer\nName[ro]=LibreOffice Writer/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Office;WordProcessor;TextEditor;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=libreoffice-writer/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=writer/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Office;WordProcessor;TextEditor;LibreOffice;' "${launcher}" +} +``` + +#### GIMP + +```bash +modify_gimp() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=GIMP/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=GIMP\nName[ro]=GIMP/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=Graphics;2DGraphics;RasterGraphics;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=gimp/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=gimp/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=Graphics;2DGraphics;RasterGraphics;GIMP;' "${launcher}" +} +``` + +#### VLC media player + +```bash +modify_vlc() { + local launcher="$1" + + # Set Name + sed -i 's/^Name=.*/Name=VLC media player/' "${launcher}" + + # Set Name[ro] + sed -i 's/^Name=.*/Name=VLC media player\nName[ro]=VLC media player/' "${launcher}" + + # Set Categories + sed -i 's/^Exec=.*/&\nCategories=AudioVideo;Player;Video;' "${launcher}" + + # Set Icon + sed -i 's/^Exec=.*/&\nIcon=vlc/' "${launcher}" + + # Set StartupWMClass + sed -i 's/^Exec=.*/&\nStartupWMClass=vlc/' "${launcher}" + + # Set Keywords + sed -i 's/^Exec=.*/&\nKeywords=AudioVideo;Player;Video;VLC;' "${launcher}" +} +``` + +## Launcher Deployment + +### Deploy Custom Launchers + +```bash +deploy_custom_launchers() { + log_subsection "Deploying Custom Launchers" + + # Deploy custom launchers from resources + if [ -d "${REPO_RES_DIR}/pcmanfm" ]; then + for launcher in "${REPO_RES_DIR}/pcmanfm"/*.desktop; do + if [ -f "${launcher}" ]; then + local basename=$(basename "${launcher}") + update_file_if_distinct "${launcher}" "${HOME}/.local/share/applications/${basename}" + fi + done + fi + + # Deploy custom launchers from rc + if [ -d "${REPO_RC_DIR}/launchers" ]; then + for launcher in "${REPO_RC_DIR}/launchers"/*.desktop; do + if [ -f "${launcher}" ]; then + local basename=$(basename "${launcher}") + update_file_if_distinct "${launcher}" "${HOME}/.local/share/applications/${basename}" + fi + done + fi + + log_info "Custom launchers deployed" +} +``` + +### Update Desktop Database + +```bash +update_desktop_database() { + log_subsection "Updating Desktop Database" + + # Update desktop database + if command_exists update-desktop-database; then + update-desktop-database "${HOME}/.local/share/applications" + update-desktop-database "/usr/share/applications" + log_info "Desktop database updated" + else + log_warn "update-desktop-database not found, skipping" + fi +} +``` + +## Launcher Categories + +### Category Mapping + +| Application | Categories | +|-------------|------------| +| AI apps | AI;Development; | +| App Stores | System;PackageManager; | +| Archive Managers | System;FileManager; | +| Audio Players | AudioVideo;Player;Audio; | +| Browsers | Network;WebBrowser; | +| Calculators | Education;Calculator; | +| Cameras | Graphics;Camera; | +| Chat | Network;Chat;InstantMessaging; | +| Code Editors | Development;TextEditor; | +| Databases | Development;Database; | +| Disk | System;FileManager; | +| Email | Network;Email; | +| File Managers | System;FileManager; | +| Games | Game; | +| IDEs | Development;IDE; | +| Image Viewers | Graphics;Viewer; | +| Media Players | AudioVideo;Player;Video; | +| Messengers | Network;Chat;InstantMessaging; | +| Notes | Office;Note; | +| Office | Office; | +| Password Managers | Security;Password; | +| Remote Desktop | Network;RemoteAccess; | +| Screenshots | Graphics;Screenshot; | +| Settings | System;Settings; | +| Terminals | System;TerminalEmulator; | +| Text Editors | Development;TextEditor; | +| Video Players | AudioVideo;Player;Video; | +| Virtualization | System;Virtualization; | +| VPN | Network;VPN; | +| Web Apps | Network;WebBrowser; | + +## Logging + +### Launcher Customisation Log + +``` +=== Application Launcher Customisation === +[INFO] Scanning: /usr/share/applications +[INFO] Scanning: /home/user/.local/share/applications +[INFO] Scanning: /home/user/.config/autostart +[INFO] Modified launcher: /usr/share/applications/code.desktop +[INFO] Modified launcher: /usr/share/applications/discord.desktop +[INFO] Modified launcher: /usr/share/applications/telegram-desktop.desktop +[INFO] Modified launcher: /usr/share/applications/firefox.desktop +[INFO] Modified launcher: /usr/share/applications/libreoffice-writer.desktop +[INFO] Modified launcher: /usr/share/applications/gimp.desktop +[INFO] Modified launcher: /usr/share/applications/vlc.desktop +[INFO] Launchers modified: 100 +[INFO] Custom launchers deployed +[INFO] Desktop database updated +[SUCCESS] Application launchers customised +``` + +## Performance + +### Typical Execution Times + +| Operation | Time | +|-----------|------| +| Launcher discovery | 1-3 seconds | +| Launcher modification | 5-15 seconds | +| Launcher deployment | 1-3 seconds | +| Desktop database update | 1-3 seconds | +| **Total** | **8-24 seconds** | + +### Optimisation + +- Skip already-modified launchers +- Batch launcher modifications +- Parallel launcher processing +- Cache launcher metadata \ No newline at end of file diff --git a/docs/behaviour/autostart-and-default-apps.md b/docs/behaviour/autostart-and-default-apps.md new file mode 100644 index 0000000..ca66010 --- /dev/null +++ b/docs/behaviour/autostart-and-default-apps.md @@ -0,0 +1,487 @@ +# Autostart and Default Applications + +This document describes how autostart applications and default applications +are configured across the system. + +## Overview + +The autostart and default applications system manages user autostart entries +and sets default applications for MIME types. + +## Flow Diagram + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Autostart and Default Apps Flow │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Autostart Configuration │ +│ • Configure user autostart entries │ +│ • Configure system autostart entries │ +│ • Manage autostart conditions │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Default Applications │ +│ • Set default applications via mimeapps.list │ +│ • Configure MIME associations │ +│ • Set default browser, editor, etc. │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Autostart Configuration + +### Script: `scripts/configure-autostart-apps.sh` + +```bash +#!/bin/bash +set -euo pipefail + +# Load foundation +source "${REPO_DIR}/scripts/common/filesystem.sh" +source "${REPO_DIR}/scripts/common/common.sh" + +configure_autostart_apps() { + log_section "Autostart Applications Configuration" + + # Configure user autostart + configure_user_autostart + + # Configure system autostart (if root) + if [ "${HAS_SU_PRIVILEGES}" = "true" ]; then + run_as_su configure_system_autostart + fi + + log_success "Autostart applications configured" +} + +configure_user_autostart() { + log_subsection "User Autostart" + + local autostart_dir="${HOME}/.config/autostart" + ensure_directory "${autostart_dir}" + + # Applications to autostart + local autostart_apps=( + "plank" + "signal-desktop" + "telegram-desktop" + "discord" + "electron-mail" + "planify" + ) + + # Applications to disable + local disabled_apps=( + "discord" + ) + + # Enable autostart for applications + for app in "${autostart_apps[@]}"; do + enable_autostart "${app}" "${autostart_dir}" + done + + # Disable autostart for applications + for app in "${disabled_apps[@]}"; do + disable_autostart "${app}" "${autostart_dir}" + done + + log_info "User autostart configured" +} + +enable_autostart() { + local app="$1" + local autostart_dir="$2" + + # Find the .desktop file + local desktop_file=$(find /usr/share/applications "${HOME}/.local/share/applications" -name "${app}.desktop" 2>/dev/null | head -1) + + if [ -z "${desktop_file}" ]; then + log_warn "Desktop file not found for: ${app}" + return 0 + fi + + # Copy to autostart directory + local target="${autostart_dir}/${app}.desktop" + cp "${desktop_file}" "${target}" + + # Ensure it's enabled + sed -i 's/^Hidden=.*/Hidden=false/' "${target}" + sed -i 's/^NoDisplay=.*/NoDisplay=false/' "${target}" + + log_info "Enabled autostart: ${app}" +} + +disable_autostart() { + local app="$1" + local autostart_dir="$2" + + local target="${autostart_dir}/${app}.desktop" + + if [ -f "${target}" ]; then + # Disable by setting Hidden=true + sed -i 's/^Hidden=.*/Hidden=true/' "${target}" + log_info "Disabled autostart: ${app}" + else + # Create a disabled entry + cat > "${target}" </dev/null | head -1) + + if [ -n "${desktop_file}" ]; then + cp "${desktop_file}" "${autostart_dir}/" + log_info "Enabled system autostart: ${app}" + fi + done + + log_info "System autostart configured" +} +``` + +### Autostart Conditions + +```bash +# Conditional autostart based on environment +configure_conditional_autostart() { + log_subsection "Conditional Autostart" + + # Only autostart Plank on GUI + if [ "${HAS_GUI}" = "true" ] && [ "${DESKTOP_ENVIRONMENT}" != "phosh" ]; then + enable_autostart "plank" "${HOME}/.config/autostart" + fi + + # Only autostart Discord on powerful PCs + if [ "${IS_POWERFUL_PC}" = "true" ]; then + enable_autostart "discord" "${HOME}/.config/autostart" + else + disable_autostart "discord" "${HOME}/.config/autostart" + fi + + # Only autostart Signal on battery devices + if [ "${IS_BATTERY_DEVICE}" = "true" ]; then + enable_autostart "signal-desktop" "${HOME}/.config/autostart" + fi + + log_info "Conditional autostart configured" +} +``` + +## Default Applications + +### Script: `scripts/configure-default-apps.sh` + +```bash +#!/bin/bash +set -euo pipefail + +# Load foundation +source "${REPO_DIR}/scripts/common/filesystem.sh" +source "${REPO_DIR}/scripts/common/common.sh" + +configure_default_apps() { + log_section "Default Applications Configuration" + + # Configure MIME associations + configure_mime_associations + + # Configure default browser + configure_default_browser + + # Configure default editor + configure_default_editor + + # Configure default terminal + configure_default_terminal + + # Configure default file manager + configure_default_file_manager + + log_success "Default applications configured" +} + +configure_mime_associations() { + log_subsection "MIME Associations" + + local mimeapps="${HOME}/.config/mimeapps.list" + ensure_directory "$(dirname "${mimeapps}")" + + # Create or update mimeapps.list + cat > "${mimeapps}" </dev/null; then + DESKTOP_ENVIRONMENT="GNOME" + elif pgrep -x "plasma" &>/dev/null; then + DESKTOP_ENVIRONMENT="KDE" + elif pgrep -x "mate-panel" &>/dev/null; then + DESKTOP_ENVIRONMENT="MATE" + elif pgrep -x "lxpanel" &>/dev/null; then + DESKTOP_ENVIRONMENT="LXDE" + elif pgrep -x "phosh" &>/dev/null; then + DESKTOP_ENVIRONMENT="Phosh" + else + DESKTOP_ENVIRONMENT="unknown" + fi +} +``` + +### Detection Priority + +| Priority | Variable | Example | +|----------|----------|---------| +| 1 | XDG_CURRENT_DESKTOP | GNOME, KDE, MATE, LXDE | +| 2 | DESKTOP_SESSION | gnome, kde, mate, lxde | +| 3 | XDG_SESSION_DESKTOP | GNOME, KDE, MATE, LXDE | +| 4 | Process detection | gnome-shell, plasma, mate-panel | + +## GNOME Configuration + +### Configuration Methods + +```bash +# GNOME uses gsettings and dconf +configure_gnome() { + log_subsection "GNOME Configuration" + + # Set GTK theme + gsettings set org.gnome.desktop.interface gtk-theme "${GTK3_THEME}" + + # Set icon theme + gsettings set org.gnome.desktop.interface icon-theme "${ICON_THEME}" + + # Set cursor theme + gsettings set org.gnome.desktop.interface cursor-theme "${CURSOR_THEME}" + + # Set font + gsettings set org.gnome.desktop.interface font-name "${INTERFACE_FONT}" + gsettings set org.gnome.desktop.interface monospace-font-name "${MONOSPACE_FONT}" + + # Set color scheme + if [ "${DESKTOP_THEME_IS_DARK}" = "true" ]; then + gsettings set org.gnome.desktop.interface color-scheme "prefer-dark" + else + gsettings set org.gnome.desktop.interface color-scheme "prefer-light" + fi + + # Set shell theme + gsettings set org.gnome.shell.extensions.user-theme name "${GTK3_THEME}" + + # Set window manager theme + gsettings set org.gnome.desktop.wm.preferences theme "${GTK3_THEME}" + + # Set desktop background + gsettings set org.gnome.desktop.background picture-uri "file:///path/to/wallpaper" + + # Set lock screen background + gsettings set org.gnome.desktop.screensaver picture-uri "file:///path/to/wallpaper" + + # Set show desktop icons + gsettings set org.gnome.desktop.background show-desktop-icons false + + # Set show battery percentage + gsettings set org.gnome.desktop.interface show-battery-percentage true + + # Set clock format + gsettings set org.gnome.desktop.interface clock-format '24h' + + # Set date format + gsettings set org.gnome.desktop.interface clock-format '24h' + + # Set enable animations + gsettings set org.gnome.desktop.interface enable-animations true + + # Set enable hot corners + gsettings set org.gnome.desktop.interface enable-hot-corners false + + # Set enable edge tiling + gsettings set org.gnome.mutter edge-tiling true + + # Set enable attach modal dialogs + gsettings set org.gnome.mutter attach-modal-dialogs true + + # Set enable dynamic workspaces + gsettings set org.gnome.desktop.privacy dynamic-workspaces false + + # Set enable hot corners + gsettings set org.gnome.desktop.interface enable-hot-corners false + + # Set enable show apps at top + gsettings set org.gnome.shell enable-hot-corners false + + # Set enable hot corners + gsettings set org.gnome.shell enable-hot-corners false + + log_info "GNOME configured" +} +``` + +### GNOME Extensions + +```bash +# Install GNOME extensions +install_gnome_extensions() { + log_subsection "GNOME Extensions" + + # Install extensions + local extensions=( + "user-theme@gnome-shell-extensions.gcampax.github.com" + "dash-to-dock@micxgx.gmail.com" + "arc-menu@arcmenu.github.com" + "just-perfection@just-perfection.github.com" + ) + + for extension in "${extensions[@]}"; do + if ! gnome-extensions list | grep -q "${extension}"; then + gnome-extensions install "${extension}" + log_info "Installed extension: ${extension}" + fi + done + + # Enable extensions + for extension in "${extensions[@]}"; do + gnome-extensions enable "${extension}" + log_info "Enabled extension: ${extension}" + done +} +``` + +## KDE Configuration + +### Configuration Methods + +```bash +# KDE uses kwriteconfig5 and kdeglobals +configure_kde() { + log_subsection "KDE Configuration" + + local kde_config="${HOME}/.config/kdeglobals" + + # Set widget style + kwriteconfig5 --file "${kde_config}" --group "General" --key "widgetStyle" "${GTK_THEME}" + + # Set font + kwriteconfig5 --file "${kde_config}" --group "General" --key "font" "${INTERFACE_FONT_FACE},${INTERFACE_FONT_SIZE},-1,5,50,0,0,0,0,0" + + # Set fixed font + kwriteconfig5 --file "${kde_config}" --group "General" --key "fixedFont" "${MONOSPACE_FONT_FACE},${MONOSPACE_FONT_SIZE},-1,5,50,0,0,0,0,0" + + # Set icon theme + kwriteconfig5 --file "${kde_config}" --group "Icons" --key "Theme" "${ICON_THEME}" + + # Set cursor theme + kwriteconfig5 --file "${kde_config}" --group "Mouse" --key "cursorTheme" "${CURSOR_THEME}" + + # Set show desktop icons + kwriteconfig5 --file "${kde_config}" --group "Desktop" --key "ShowDesktopIcons" "true" + + # Set show taskbar + kwriteconfig5 --file "${kde_config}" --group "Taskbar" --key "ShowTaskbar" "true" + + # Set show desktop grid + kwriteconfig5 --file "${kde_config}" --group "Desktop" --key "ShowDesktopGrid" "false" + + log_info "KDE configured" +} +``` + +### KDE Plasma Configuration + +```bash +# Configure Plasma panels +configure_kde_plasma() { + log_subsection "KDE Plasma Configuration" + + # Configure panel + local panel_config="${HOME}/.config/plasma-org.kde.plasma.plasmoidrc" + + # Set panel position + kwriteconfig5 --file "${panel_config}" --group "General" --key "Location" "Bottom" + + # Set panel height + kwriteconfig5 --file "${panel_config}" --group "General" --key "Height" "40" + + # Set panel alignment + kwriteconfig5 --file "${panel_config}" --group "General" --key "Alignment" "Center" + + # Set panel visibility + kwriteconfig5 --file "${panel_config}" --group "General" --key "HideMode" "0" + + log_info "KDE Plasma configured" +} +``` + +## MATE Configuration + +### Configuration Methods + +```bash +# MATE uses gsettings and mateconf +configure_mate() { + log_subsection "MATE Configuration" + + # Set GTK theme + gsettings set org.mate.interface gtk-theme "${GTK3_THEME}" + + # Set icon theme + gsettings set org.mate.interface icon-theme "${ICON_THEME}" + + # Set cursor theme + gsettings set org.mate.interface cursor-theme "${CURSOR_THEME}" + + # Set font + gsettings set org.mate.interface font-name "${INTERFACE_FONT}" + gsettings set org.mate.interface monospace-font-name "${MONOSPACE_FONT}" + + # Set show desktop icons + gsettings set org.mate.caja.desktop show-desktop-icons false + + # Set show panel + gsettings set org.mate.panel general show-panels true + + # Set panel position + gsettings set org.mate.panel.toplevels.panel1 orientation "top" + + # Set panel size + gsettings set org.mate.panel.toplevels.panel1 size "40" + + log_info "MATE configured" +} +``` + +## LXDE Configuration + +### Configuration Methods + +```bash +# LXDE uses lxpanel, pcmanfm, openbox +configure_lxde() { + log_subsection "LXDE Configuration" + + # Configure panel + configure_lxde_panel + + # Configure file manager + configure_pcmanfm + + # Configure window manager + configure_openbox + + log_info "LXDE configured" +} + +configure_lxde_panel() { + log_info "Configuring LXDE panel..." + + # Deploy panel configuration + if [ -f "${REPO_RC_DIR}/lxde-panel" ]; then + update_file_if_distinct \ + "${REPO_RC_DIR}/lxde-panel" \ + "${HOME}/.config/lxpanel/LXDE/panels/panel" + fi + + # Deploy dock configuration + if [ -f "${REPO_RC_DIR}/lxde-dock" ]; then + update_file_if_distinct \ + "${REPO_RC_DIR}/lxde-dock" \ + "${HOME}/.config/lxpanel/LXDE/panels/dock" + fi + + log_info "LXDE panel configured" +} + +configure_pcmanfm() { + log_info "Configuring PCManFM..." + + # Deploy desktop entries + if [ -f "resources/pcmanfm/open-in-code.desktop" ]; then + update_file_if_distinct \ + "resources/pcmanfm/open-in-code.desktop" \ + "${HOME}/.local/share/applications/open-in-code.desktop" + fi + + if [ -f "resources/pcmanfm/open-in-terminal.desktop" ]; then + update_file_if_distinct \ + "resources/pcmanfm/open-in-terminal.desktop" \ + "${HOME}/.local/share/applications/open-in-terminal.desktop" + fi + + log_info "PCManFM configured" +} + +configure_openbox() { + log_info "Configuring Openbox..." + + # Deploy Openbox configuration + if [ -f "${REPO_RC_DIR}/openbox-rc.xml" ]; then + update_file_if_distinct \ + "${REPO_RC_DIR}/openbox-rc.xml" \ + "${HOME}/.config/openbox/rc.xml" + fi + + log_info "Openbox configured" +} +``` + +## Phosh Configuration + +### Configuration Methods + +```bash +# Phosh uses gsettings +configure_phosh() { + log_subsection "Phosh Configuration" + + # Set GTK theme + gsettings set org.gnome.desktop.interface gtk-theme "${GTK3_THEME}" + + # Set icon theme + gsettings set org.gnome.desktop.interface icon-theme "${ICON_THEME}" + + # Set cursor theme + gsettings set org.gnome.desktop.interface cursor-theme "${CURSOR_THEME}" + + # Set font + gsettings set org.gnome.desktop.interface font-name "${INTERFACE_FONT}" + gsettings set org.gnome.desktop.interface monospace-font-name "${MONOSPACE_FONT}" + + # Set color scheme + if [ "${DESKTOP_THEME_IS_DARK}" = "true" ]; then + gsettings set org.gnome.desktop.interface color-scheme "prefer-dark" + else + gsettings set org.gnome.desktop.interface color-scheme "prefer-light" + fi + + log_info "Phosh configured" +} +``` + +## Desktop Environment Switching + +### Switching Between Desktop Environments + +```bash +# Switch desktop environment +switch_desktop_environment() { + local target_de="$1" + + log_info "Switching to ${target_de}..." + + # Stop current desktop environment + stop_desktop_environment + + # Apply new configuration + case "${target_de}" in + gnome) + configure_gnome + ;; + kde) + configure_kde + ;; + mate) + configure_mate + ;; + lxde) + configure_lxde + ;; + phosh) + configure_phosh + ;; + *) + log_error "Unknown desktop environment: ${target_de}" + return 1 + ;; + esac + + # Start new desktop environment + start_desktop_environment + + log_success "Switched to ${target_de}" +} +``` + +## Verification + +### Desktop Environment Verification + +```bash +verify_desktop_environment() { + log_subsection "Verifying Desktop Environment" + + local current_de=$(detect_desktop_environment) + + case "${current_de}" in + gnome) + verify_gnome_settings + ;; + kde) + verify_kde_settings + ;; + mate) + verify_mate_settings + ;; + lxde) + verify_lxde_settings + ;; + phosh) + verify_phosh_settings + ;; + *) + log_warn "Unknown desktop environment: ${current_de}" + ;; + esac +} + +verify_gnome_settings() { + local gtk_theme=$(gsettings get org.gnome.desktop.interface gtk-theme) + local icon_theme=$(gsettings get org.gnome.desktop.interface icon-theme) + + if [ "${gtk_theme}" != "'${GTK3_THEME}'" ]; then + log_warn "GTK theme mismatch: expected ${GTK3_THEME}, got ${gtk_theme}" + fi + + if [ "${icon_theme}" != "'${ICON_THEME}'" ]; then + log_warn "Icon theme mismatch: expected ${ICON_THEME}, got ${icon_theme}" + fi +} +``` + +## Logging + +### Desktop Environment Configuration Log + +``` +=== Desktop Environment Configuration === +[INFO] Detected desktop environment: GNOME +[INFO] Applying GNOME settings +[INFO] Setting GTK theme: adw-gtk3 +[INFO] Setting icon theme: Papirus-Dark +[INFO] Setting cursor theme: Vimix-white-cursors +[INFO] Setting font: Sans Regular 11 +[INFO] Setting monospace font: Droid Sans Regular 12 +[INFO] Setting color scheme: prefer-dark +[INFO] Installing GNOME extensions +[INFO] Installed extension: user-theme@gnome-shell-extensions.gcampax.github.com +[INFO] Enabled extension: user-theme@gnome-shell-extensions.gcampax.github.com +[INFO] Installed extension: dash-to-dock@micxgx.gmail.com +[INFO] Enabled extension: dash-to-dock@micxgx.gmail.com +[SUCCESS] GNOME configured +``` + +## Performance + +### Typical Execution Times + +| Desktop Environment | Time | +|---------------------|------| +| GNOME | 2-5 seconds | +| KDE | 2-5 seconds | +| MATE | 1-3 seconds | +| LXDE | 1-3 seconds | +| Phosh | 1-3 seconds | +| **Total** | **1-5 seconds** | + +### Optimisation + +- Skip already-configured settings +- Batch gsettings calls +- Cache desktop environment detection +- Lazy loading of desktop environment specific code \ No newline at end of file diff --git a/docs/behaviour/directory-and-file-management.md b/docs/behaviour/directory-and-file-management.md new file mode 100644 index 0000000..bb7f445 --- /dev/null +++ b/docs/behaviour/directory-and-file-management.md @@ -0,0 +1,461 @@ +# Directory and File Management + +This document describes how directories and files are managed, including XDG +directories, hidden files, symlinks, and special directory configurations. + +## Overview + +The directory and file management system configures user directories, manages +hidden files, creates symlinks, and handles special directory configurations +for applications like Minecraft and Android. + +## Flow Diagram + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Directory and File Management Flow │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ XDG Directory Configuration │ +│ • Configure user-dirs.conf │ +│ • Configure user-dirs.dirs │ +│ • Configure user-dirs.locale │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Hidden Files Management │ +│ • Deploy .hidden files │ +│ • Configure hidden files for home, Documents, Downloads │ +│ • Romanian/English variants │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Symlink Management │ +│ • Create directory symlinks │ +│ • Minecraft screenshots symlink │ +│ • Android emulated storage symlinks │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Directory Icons │ +│ • Set folder icons │ +│ • Configure project folder icons │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## XDG Directory Configuration + +### Script: `scripts/configure-directories.sh` + +```bash +#!/bin/bash +set -euo pipefail + +# Load foundation +source "${REPO_DIR}/scripts/common/filesystem.sh" +source "${REPO_DIR}/scripts/common/common.sh" + +configure_directories() { + log_section "Directory Configuration" + + # Configure XDG directories + configure_xdg_directories + + # Configure hidden files + configure_hidden_files + + # Configure symlinks + configure_symlinks + + # Configure directory icons + configure_directory_icons + + log_success "Directories configured" +} + +configure_xdg_directories() { + log_subsection "XDG Directories" + + # Create user-dirs.conf + local user_dirs_conf="${HOME}/.config/user-dirs.conf" + ensure_directory "$(dirname "${user_dirs_conf}")" + + cat > "${user_dirs_conf}" < "${user_dirs_dirs}" < "${user_dirs_locale}" <