17 KiB
Contributing to TuxMate
Quick Navigation
- Project Overview
- Development Workflow
- Adding Applications
- Adding Distributions
- Pull Request Checklist
- Templates
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
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:
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.
- Generate a script in the TuxMate UI (Dev mode).
- Copy the script.
- Run the corresponding distro container commands below and paste the script.
# 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/.
1. Mandatory Research Protocol
You MUST verify every package on these official sources before submitting:
| Source | Scope | URL |
|---|---|---|
| Repology | Global Index | repology.org |
| Arch Linux | Official Repos | archlinux.org/packages |
| AUR | User Repo | aur.archlinux.org |
| Debian | Official Repos | packages.debian.org |
| Ubuntu | Official Repos | packages.ubuntu.com |
| Fedora | Official Repos | packages.fedoraproject.org |
| OpenSUSE | Official Repos | software.opensuse.org |
| Nix | Nixpkgs | search.nixos.org |
| Flathub | Flatpaks | flathub.org |
| Snapcraft | Snaps | snapcraft.io |
| Homebrew | CLI & Casks | formulae.brew.sh |
2. Entry Structure
{
"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 botharch: "ollama"andscript: "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:
- Actionable: Avoid "Not available". State the alternative (e.g., "Install via Flatpak").
- Linked: Hard-link the solution (Flathub page, upstream .deb URL, or Wiki guide).
- 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
coreorextra(e.g.,firefox). - Automatic AUR Detection:
src/lib/aur.tsautomatically 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 tosrc/lib/aur-packages.json. - Prefer
-binsuffix packages in AUR (pre-built, faster install)
NixOS
Nixpkgs requires explicit user consent for unfree software.
- Check the license on search.nixos.org.
- If the license is unfree, add it to
src/lib/nix-unfree.jsonif missing. - 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
.debURLs. - Packages requiring manual
sources.listmodification (unless detailed inunavailableReason).
Flatpak
- ID Format: Always use the full Application ID (reverse-DNS style).
- ✅ Correct:
org.mozilla.firefox - ❌ Wrong:
firefox
- ✅ Correct:
- Verification: Find the exact ID at the bottom of the app's Flathub page.
- Note:
verified-flatpaks.jsonis 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 to confirm.
- Example:
- Verification: If the publisher has a "Verified" badge on Snapcraft:
- Add the publisher name to
src/lib/verified-snaps.json. - This enables the "Verified" badge in the TuxMate UI.
- Add the publisher name to
Homebrew
Homebrew (macOS/Linux) has two package types. Check 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'
- Usage: Prefix with
- Validation:
- Run
brew search <name>locally to confirm type. - We skip
--casktargets on Linux installs automatically.
- Run
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 vianpm install -g. Requires Node.js runtime on the system.script: Raw shell command (typically acurl | bashinstaller). Runs directly.
Important
Native targets always take priority. If an app defines
arch: "ollama"alongsidescript: "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:
// 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
npmorscripttargets. - 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:
- Only use official installer scripts from the app's own domain.
- Always use
curl -fsSLflags (fail silently on errors, follow redirects, show errors). - Never combine multiple pipes or add
sudo— the install script handles privileges itself. - 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.
We store icons as structured objects in the JSON. You just need to provide the set, name, and color:
"icon": {
"type": "iconify",
"set": "simple-icons",
"name": "python",
"color": "#3776AB"
}
The 3 Main Icon Sets We Use:
- 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.
- Logos (
"set": "logos")- Used when an app has a multi-colored, official logo.
- You don't need a
"color"for these.
- 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":
"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:
- Add the new ID to the
DistroIdtype definition. - Add a new object to the
distrosarray 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/<distroId>.ts. This file must export a function (e.g., generateFedoraScript) that takes PackageInfo[] and returns the generated shell script string.
- Use helpers from
shared.tslikegenerateAsciiHeaderandPackageInfo. - Implement logic to check if a package is already installed.
- Implement the installation command loop using
with_retryfor robustness. - See
src/lib/scripts/fedora.tsorubuntu.tsfor clean examples.
3. Integrate the Generator
- Export your new function in
src/lib/scripts/index.ts. - Import it in
src/lib/generateInstallScript.ts. - Add a case for your
distroIdin theswitchstatement insidegenerateInstallScript. - Also add the simple one-liner command logic in
generateSimpleCommandwithin the same file.
🔀 Pull Request Checklist
Core Principles
Important
Your PR will be rejected if you violate these rules:
- Verify Everything: Submit only verified package names. Guessing is prohibited.
- Official First: Use official repository packages over third-party options.
- No Unofficial Repos: Do not include PPAs, COPRs, or unofficial repositories.
- Full IDs: Use full IDs for Flatpaks (e.g.,
org.mozilla.firefox).- Strict Casing: Package names are case-sensitive.
- Link Integrity: Ensure all links in
unavailableReasonare 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
--classicflag verification. - Nix unfree packages added to JSON.
- Homebrew Casks prefixed correctly.
- Universal targets:
npm/scriptonly used as fallbacks,notefield provided. - Script URLs verified as official, stable endpoints.
npm run lint&npm run testpassed.
📝 Templates
Pull Request Template
## 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)
<!-- Add screenshots for UI changes -->
Issue Template (Bug Report)
## 🐛 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]