# Contributing to TuxMate ## Quick Navigation 1. [Project Overview](#project-overview) 2. [Development Workflow](#development-workflow) * [Initial Setup](#initial-setup) * [Running with Docker](#running-with-docker) * [Quality Assurance](#quality-assurance) * [Verifying Install Scripts](#verifying-install-scripts) 3. [Adding Applications](#adding-applications) * [Research Protocol](#1-mandatory-research-protocol) * [Entry Structure](#2-entry-structure) * [Unavailable Reason](#3-unavailable-reason-guidelines) * [Platform Rules](#4-platform-specific-rules) * [Arch Linux](#arch-linux) * [NixOS](#nixos) * [Ubuntu/Debian](#ubuntudebian) * [Flatpak](#flatpak) * [Snap](#snap) * [Homebrew](#homebrew) * [Universal Targets](#universal-targets-npm--script) * [Icon System](#5-icon-system) * [Valid Categories](#6-valid-categories) 4. [Adding Distributions](#adding-distributions) 5. [Pull Request Checklist](#pull-request-checklist) * [Core Principles](#core-principles) * [Verification Steps](#verification-steps) 6. [Templates](#templates) * [Pull Request](#pull-request-template) * [Bug Report](#issue-template-bug-report) --- ## Project Overview * `src/lib/apps/*.json`: Main registry for applications (split by category). * `src/lib/data.ts`: Main registry for distributions, categories, and Typescript types. * `src/lib/aur-packages.json`: Whitelist for AUR packages that lack standard suffixes. * `src/lib/nix-unfree.json`: Registry for unfree Nix packages. * `src/lib/verified-flatpaks.json`: Auto-generated list of verified Flathub apps. **Do not edit.** * `src/lib/verified-snaps.json`: Manual whitelist of verified Snap publishers. --- ## Development Workflow ### Initial Setup ```bash git clone https://github.com/abusoww/tuxmate.git cd tuxmate npm install npm run dev ``` The app will be available at `http://localhost:3000`.
Running with Docker You can run the full application using the official Docker image: ```bash docker run -p 3000:80 ghcr.io/abusoww/tuxmate:latest ```
Quality Assurance Always run these before pushing: * `npm run lint`: Check for code style issues. * `npm run test`: Run unit test suite. * `npm run build`: Verify production build.
Verifying Install Scripts To verify that the *scripts generated by TuxMate* work correctly, run them in a clean container environment. This prevents messing up your local system. 1. Generate a script in the TuxMate UI (Dev mode). 2. Copy the script. 3. Run the corresponding distro container commands below and paste the script. ```bash # Arch Linux docker run -it --rm archlinux:latest bash -c "pacman -Sy && bash" # Ubuntu docker run -it --rm ubuntu:latest bash -c "apt update && bash" # Fedora docker run -it --rm fedora:latest bash -c "dnf check-update; bash" ```
--- ## Adding Applications All applications are defined in category-specific JSON files within [`src/lib/apps/`](src/lib/apps/). ### 1. Mandatory Research Protocol **You MUST verify every package on these official sources before submitting:** | Source | Scope | URL | | :--- | :--- | :--- | | **Repology** | Global Index | [repology.org](https://repology.org/) | | **Arch Linux** | Official Repos | [archlinux.org/packages](https://archlinux.org/packages/) | | **AUR** | User Repo | [aur.archlinux.org](https://aur.archlinux.org/) | | **Debian** | Official Repos | [packages.debian.org](https://packages.debian.org/) | | **Ubuntu** | Official Repos | [packages.ubuntu.com](https://packages.ubuntu.com/) | | **Fedora** | Official Repos | [packages.fedoraproject.org](https://packages.fedoraproject.org/) | | **OpenSUSE** | Official Repos | [software.opensuse.org](https://software.opensuse.org/) | | **Nix** | Nixpkgs | [search.nixos.org](https://search.nixos.org/packages) | | **Flathub** | Flatpaks | [flathub.org](https://flathub.org/) | | **Snapcraft** | Snaps | [snapcraft.io](https://snapcraft.io/) | | **Homebrew** | CLI & Casks | [formulae.brew.sh](https://formulae.brew.sh/) | ### 2. Entry Structure ```json { "id": "app-id", // Unique, lowercase, kebab-case "name": "App Name", // Official display name "description": "Short description", // Max ~60 characters "category": "Category", // Must match valid categories "icon": { // See Icon System section "type": "iconify", "set": "simple-icons", "name": "python", "color": "#3776AB" }, "targets": { "ubuntu": "exact-package-name", // apt package (official repos ONLY) "arch": "exact-package-name", // pacman OR AUR package name "flatpak": "com.vendor.AppId", // FULL Flatpak App ID (reverse DNS) "snap": "snap-name", // Add --classic if needed "homebrew": "formula-name", // Formula (CLI) or '--cask name' (GUI) "npm": "@scope/package-name", // Global npm install (universal fallback) "script": "curl -fsSL ... | bash" // Custom install script (universal fallback) }, "note": "Context for universal targets", // Shown on hover for universal-fallback apps "unavailableReason": "Markdown install instructions" } ``` > [!NOTE] > **Priority system**: Native distro targets (e.g., `arch`, `ubuntu`) always take precedence over universal targets (`npm`, `script`). If an app has both `arch: "ollama"` and `script: "curl ..."`, the script is only used when the user selects a distro where no native package exists. ### 3. Unavailable Reason Guidelines This field renders Markdown when a target is missing. It serves as the manual fallback instruction. **Requirements:** 1. **Actionable**: Avoid "Not available". State the alternative (e.g., "Install via Flatpak"). 2. **Linked**: Hard-link the solution (Flathub page, upstream .deb URL, or Wiki guide). 3. **Format**: Use standard Markdown for links `[text](url)` and code `` `cmd` ``. **Examples:** | Status | Message | Why? | | :--- | :--- | :--- | | ❌ **Bad** | `'Not available.'` | Dead end. No solution provided. | | ❌ **Bad** | `'Download from website.'` | Dead end. No solution provided. | | ✅ **Good** | `'Not in official repos. Use the [Flatpak version](https://flathub.org/apps/com.spotify.Client) instead.'` | Directs to the preferred supported alternative. | | ✅ **Good** | ```'Arch requires [multilib](https://wiki.archlinux.org/title/Official_repositories#multilib) enabled: uncomment `[multilib]` in `/etc/pacman.conf`, run `sudo pacman -Syu`, then `sudo pacman -S steam`.'``` | Exact steps to enable the required repo. | ### 4. Platform Specific Rules #### Arch Linux * **Official Packages**: Use the package name directly if found in `core` or `extra` (e.g., `firefox`). * **Automatic AUR Detection**: `src/lib/aur.ts` automatically detects suffixes `-bin`, `-git`, or `-appimage`. Use the name directly (e.g., `brave-bin`). * **Manual AUR Detection**: For AUR packages without suffixes (e.g., `google-chrome`), **you must add the name to** `src/lib/aur-packages.json`. * Prefer `-bin` suffix packages in AUR (pre-built, faster install) #### NixOS Nixpkgs requires explicit user consent for unfree software. 1. Check the license on [search.nixos.org](https://search.nixos.org/packages). 2. If the license is unfree, add it to `src/lib/nix-unfree.json` if missing. 3. Add the package to the appropriate JSON file in `src/lib/apps/` normally. #### Ubuntu/Debian **Strict Repository Policy**: The generation scripts do **not** enable extra repositories (like PPAs or `non-free` by default). Packages must be available in the standard enabled repositories. * **Allowed**: * **Ubuntu**: Main, Restricted * **Debian**: Main * **Prohibited**: * PPAs (Personal Package Archives). * External `.deb` URLs. * Packages requiring manual `sources.list` modification (unless detailed in `unavailableReason`). #### Flatpak * **ID Format**: Always use the full **Application ID** (reverse-DNS style). * ✅ Correct: `org.mozilla.firefox` * ❌ Wrong: `firefox` * **Verification**: Find the exact ID at the bottom of the app's [Flathub page](https://flathub.org/). * **Note**: `verified-flatpaks.json` is auto-generated; do not edit it manually. #### Snap * **Classic Confinement**: If the snap requires classic confinement (access to host system files), append `--classic`. * Example: `code --classic` * Check the install command on [snapcraft.io](https://snapcraft.io/) to confirm. * **Verification**: If the publisher has a **"Verified" badge** on [Snapcraft](https://snapcraft.io/): * Add the **publisher name** to `src/lib/verified-snaps.json`. * This enables the "Verified" badge in the TuxMate UI. #### Homebrew Homebrew (macOS/Linux) has two package types. Check [formulae.brew.sh](https://formulae.brew.sh/) to find the correct one. * **Formula**: Standard CLI tools and libraries. * Usage: Use the package name directly. * Example: `'wget'`, `'node'`, `'python@3.12'` * **Cask**: GUI applications and large binaries (macOS only). * Usage: Prefix with `--cask ` (note the space). * Example: `'--cask firefox'`, `'--cask visual-studio-code'` * **Validation**: * Run `brew search ` locally to confirm type. * We skip `--cask` targets on Linux installs automatically. #### Universal Targets (npm & script) Universal targets provide cross-distro installation via package managers or custom scripts. They serve as **fallbacks** — only used when no native distro target exists for the selected distribution. * **`npm`**: Install via `npm install -g`. Requires Node.js runtime on the system. * **`script`**: Raw shell command (typically a `curl | bash` installer). Runs directly. > [!IMPORTANT] > **Native targets always take priority.** If an app defines `arch: "ollama"` alongside `script: "curl ..."`, the script target is completely ignored when Arch is selected. The fallback only activates for distros without a native package. **When to use each:** | Target | Use Case | Example | | :--- | :--- | :--- | | `npm` | CLI tools distributed via npmjs.com | `"npm": "@google/gemini-cli"` | | `script` | Apps with official install scripts | `"script": "curl -fsSL https://ollama.com/install.sh \| sh"` | **Real examples from the codebase:** ```json // Ollama: native on arch/fedora/nix/homebrew, falls back to script on ubuntu/debian { "id": "ollama", "targets": { "fedora": "ollama", "arch": "ollama", "nix": "ollama", "homebrew": "ollama", "script": "curl -fsSL https://ollama.com/install.sh | sh" }, "note": "Falls back to official installer (ollama.com/install.sh) on distros without a native package." } // Gemini CLI: npm-only (plus homebrew) { "id": "gemini-cli", "targets": { "npm": "@google/gemini-cli", "homebrew": "gemini-cli" }, "note": "Requires Node.js runtime. Installed globally via npm where native packages are unavailable." } ``` **The `note` field:** * Used to explain the universal target behavior to users (shown on hover). * **Required** when using `npm` or `script` targets. * Keep concise and professional. Examples: * ✅ `"Falls back to official installer (ollama.com/install.sh) on distros without a native package."` * ✅ `"Requires Node.js runtime. Installed globally via npm where native packages are unavailable."` * ❌ `"Installed globally via npm."` *(too terse, doesn't explain when or why)* **Script safety rules:** 1. Only use **official** installer scripts from the app's own domain. 2. Always use `curl -fsSL` flags (fail silently on errors, follow redirects, show errors). 3. Never combine multiple pipes or add `sudo` — the install script handles privileges itself. 4. Verify the script URL is stable and maintained by the upstream project. ### 5. Icon System Every app needs an icon! Our JSON format makes it super simple to add icons using [Iconify](https://iconify.design/). We store icons as structured objects in the JSON. You just need to provide the set, name, and color: ```json "icon": { "type": "iconify", "set": "simple-icons", "name": "python", "color": "#3776AB" } ``` #### The 3 Main Icon Sets We Use: 1. **Simple Icons** (`"set": "simple-icons"`) * Used for major brands and single-color logos (like Discord or Python). * *You must provide a hex `"color"` for these.* 2. **Logos** (`"set": "logos"`) * Used when an app has a multi-colored, official logo. * *You don't need a `"color"` for these.* 3. **Material Design** (`"set": "mdi"`) * Used for generic utilities (like a terminal or a magnifying glass). * *You must provide a hex `"color"` for these.* #### Can't find it on Iconify? If an app's icon isn't on Iconify, you can use a direct link to an image (SVG preferred, or a high-res PNG). Just change the `"type"` to `"url"`: ```json "icon": { "type": "url", "url": "https://raw.githubusercontent.com/..." } ``` * ✅ **DO:** Use a stable link (like the app's official GitHub repo raw image, or Wikimedia). * ❌ **DON'T:** Use temporary image hosts (like Imgur) or hotlink-protected websites. ### 6. Valid Categories Use **exactly** one of these: * Web Browsers • Communication • Media • Creative • Gaming • Office * Dev: Languages • Dev: Editors • Dev: Tools * Terminal • CLI Tools • AI Tools • VPN & Network • Security • File Sharing • System --- ## Adding Distributions Adding a new distribution involves three main steps: ### 1. Register the Distribution Edit [`src/lib/data.ts`](src/lib/data.ts): 1. Add the new ID to the `DistroId` type definition. 2. Add a new object to the `distros` array with: * `id`: unique identifier. * `name`: Display name. * `iconUrl`: Icon for the distro selector. * `color`: Brand color. * `installPrefix`: The default command prefix (e.g., `sudo dnf install -y`). ### 2. Create the Generator Script Create a new file `src/lib/scripts/.ts`. This file must export a function (e.g., `generateFedoraScript`) that takes `PackageInfo[]` and returns the generated shell script string. * Use helpers from `shared.ts` like `generateAsciiHeader` and `PackageInfo`. * Implement logic to check if a package is already installed. * Implement the installation command loop using `with_retry` for robustness. * See `src/lib/scripts/fedora.ts` or `ubuntu.ts` for clean examples. ### 3. Integrate the Generator 1. Export your new function in [`src/lib/scripts/index.ts`](src/lib/scripts/index.ts). 2. Import it in [`src/lib/generateInstallScript.ts`](src/lib/generateInstallScript.ts). 3. Add a case for your `distroId` in the `switch` statement inside `generateInstallScript`. 4. Also add the simple one-liner command logic in `generateSimpleCommand` within the same file. --- ## 🔀 Pull Request Checklist ### Core Principles > [!IMPORTANT] > **Your PR will be rejected if you violate these rules:** > > 1. **Verify Everything**: Submit only verified package names. Guessing is prohibited. > 2. **Official First**: Use official repository packages over third-party options. > 3. **No Unofficial Repos**: Do not include PPAs, COPRs, or unofficial repositories. > 4. **Full IDs**: Use full IDs for Flatpaks (e.g., `org.mozilla.firefox`). > 5. **Strict Casing**: Package names are case-sensitive. > 6. **Link Integrity**: Ensure all links in `unavailableReason` are direct and working. ### Verification Steps **Verify before submitting:** - [ ] Package names verified on official search pages (Repology, Arch, etc). - [ ] Case sensitivity checked (especially openSUSE). - [ ] Arch packages verified (Official vs AUR). - [ ] No PPAs used for Debian/Ubuntu; Main/Universe only. - [ ] Flatpak IDs are full reverse-DNS style. - [ ] Snap `--classic` flag verification. - [ ] Nix unfree packages added to JSON. - [ ] Homebrew Casks prefixed correctly. - [ ] Universal targets: `npm`/`script` only used as fallbacks, `note` field provided. - [ ] Script URLs verified as official, stable endpoints. - [ ] `npm run lint` & `npm run test` passed. --- ## 📝 Templates ### Pull Request Template ```markdown ## Summary Brief description of changes. ## Changes | App Name | Category | Sources | |----------|----------|---------| | Example | Dev: Tool| apt, pacman | ## Verification > Package names verified against official sources. | Source | Link | |--------|------| | Repology | [Link](...) | | Arch | [Link](...) | | ... | ... | ## Testing - [ ] `npm run dev` working - [ ] `npm run build` passed - [ ] `npm run test` passed - [ ] `npm run lint` passed ## Screenshots (if applicable) ``` ### Issue Template (Bug Report) ```markdown ## 🐛 Bug Report **Environment**: - OS: [e.g. Arch Linux] - Browser: [e.g. Firefox 120] **Steps to Reproduce**: 1. ... 2. ... **Details**: **Logs/Screenshots**: [Paste console logs or attach screenshots] ```