From 2d99886cfd4869ef16810a4d7f02b21041240b2a Mon Sep 17 00:00:00 2001 From: Ven0m0 <82972344+Ven0m0@users.noreply.github.com> Date: Sun, 28 Jun 2026 18:18:51 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20improve=20snap-mem.py=20tests=20?= =?UTF-8?q?&=20fix=20lint=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Consolidate snap-mem tests into Cachyos/Scripts/WIP/test_snap_mem.py - Add test cases for build_base_name (whitespace, invalid dates) - Add test cases for make_unique_name (collisions, empty sets) - Fix CI failure by implementing missing lint-format.sh script - Apply project-wide formatting via lint-format.sh Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .github/workflows/jules-weekly-cleanup.yml | 4 +- .github/workflows/lint-format.yml | 2 +- AGENTS.md | 104 +++++++++++---------- Cachyos/Scripts/Android/BACKLOG.md | 10 +- Cachyos/Scripts/Android/README.MD | 22 +++++ Cachyos/Scripts/Android/webview-flags.md | 6 +- Cachyos/Scripts/WIP/gh/git-fetch-guide.md | 52 ++++++----- Cachyos/Scripts/WIP/test_snap_mem.py | 43 +++++++++ Cachyos/Scripts/WIP/test_snap_mem_logic.py | 40 -------- Cachyos/cachyos-autoinstall-plan.md | 92 ++++++++++-------- Cachyos/rate-mirrors/README.md | 1 + PLAN.md | 36 +++++-- README.md | 66 ++++++------- RaspberryPi/EXAMPLES.md | 26 ++++-- RaspberryPi/QUICKSTART.md | 31 +++--- RaspberryPi/README.md | 11 ++- RaspberryPi/docs/REFERENCES.md | 8 +- docs/DIETPI_F2FS_GUIDE.md | 26 +++++- docs/PERFORMANCE.md | 16 ++-- docs/Shell-book.md | 68 +++++++------- docs/USEFUL.MD | 16 ++-- lint-format.sh | 77 +++++++++++++++ 22 files changed, 472 insertions(+), 285 deletions(-) delete mode 100644 Cachyos/Scripts/WIP/test_snap_mem_logic.py create mode 100755 lint-format.sh diff --git a/.github/workflows/jules-weekly-cleanup.yml b/.github/workflows/jules-weekly-cleanup.yml index 3c71648f..834f4aa3 100644 --- a/.github/workflows/jules-weekly-cleanup.yml +++ b/.github/workflows/jules-weekly-cleanup.yml @@ -3,7 +3,7 @@ name: Weekly Codebase Cleanup on: schedule: # Run every Monday at 2 AM UTC - - cron: '0 2 * * 1' + - cron: "0 2 * * 1" workflow_dispatch: # Allow manual triggers jobs: @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - + steps: - name: Invoke Jules for Cleanup uses: BeksOmega/jules-action@v1.0.0 diff --git a/.github/workflows/lint-format.yml b/.github/workflows/lint-format.yml index ba37de56..ef87dbd4 100644 --- a/.github/workflows/lint-format.yml +++ b/.github/workflows/lint-format.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: '3.14' + python-version: "3.14" - name: Install core tools shell: bash run: | diff --git a/AGENTS.md b/AGENTS.md index 2ca38788..3e3660f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,11 +21,11 @@ ### Target Systems -| **Primary** | **Secondary** | **Tertiary** | -|:------------|:--------------|:-------------| -| Arch Linux | Debian | Termux | -| CachyOS | Raspbian | EndeavourOS | -| Wayland | Raspberry Pi OS | Gentoo | +| **Primary** | **Secondary** | **Tertiary** | +| :---------- | :-------------- | :----------- | +| Arch Linux | Debian | Termux | +| CachyOS | Raspbian | EndeavourOS | +| Wayland | Raspberry Pi OS | Gentoo | ### Repository Structure @@ -135,16 +135,16 @@ Always quote variables unless intentional glob/split. Exception: `$*` in printf ## Tool Hierarchy (Fallbacks Required) -| Task | Primary | Fallback Chain | -|:-----|:--------|:---------------| -| Find | `fd` | `fdfind`→`find` | -| Grep | `rg` | `grep -E` (prefer `-F` for literals) | -| View | `bat` | `cat` | -| Edit | `sd` | `sed -E` | -| Nav | `zoxide` | `cd` | -| Web | `aria2c` | `curl`→`wget2`→`wget` | -| JSON | `jaq` | `jq` | -| Parallel | `rust-parallel` | `parallel`→`xargs -r -P$(nproc)` | +| Task | Primary | Fallback Chain | +| :------- | :-------------- | :----------------------------------- | +| Find | `fd` | `fdfind`→`find` | +| Grep | `rg` | `grep -E` (prefer `-F` for literals) | +| View | `bat` | `cat` | +| Edit | `sd` | `sed -E` | +| Nav | `zoxide` | `cd` | +| Web | `aria2c` | `curl`→`wget2`→`wget` | +| JSON | `jaq` | `jq` | +| Parallel | `rust-parallel` | `parallel`→`xargs -r -P$(nproc)` | --- @@ -179,14 +179,14 @@ BLD=$'\e[1m' # Bold ### Command Costs (Relative) -| Operation | Cost | Alternative | Cost | -|:----------|:-----|:------------|:-----| -| `$(command)` | 100x | `${var//pattern/}` | 1x | -| `tr` | 50x | `${var,,}` | 1x | -| `basename` | 30x | `${var##*/}` | 1x | -| `dirname` | 30x | `${var%/*}` | 1x | -| `cat file` | 20x | `$(and add GraphicsMagick support | Complex | +| Priority | Description | Complexity | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| Medium | Fork and optimize [charwasp/modify](https://github.com/charwasp/modify/) — merge into a single Python script
and add GraphicsMagick support | Complex | ## Resources to Explore **Modules & Frameworks:** + - [LSPosed Modules Repository](https://modules.lsposed.org/) - [VirtualXposed](https://github.com/android-hacker/VirtualXposed) **UI/Navigation Fixes:** + - [Fullscreen immersive gesture script](https://github.com/trinhnv1205/optimize/blob/main/menu/fullscreen_immersive_getsure.sh) — may fix 3-button navigation bar **Modified Apps:** + - [SpotifyPlus](https://github.com/LeNerd46/SpotifyPlus) - [NPatch](https://github.com/7723mod/NPatch) - [InstaEclipse](https://github.com/ReSo7200/InstaEclipse) - [NagramX](https://github.com/risin42/NagramX) **Development Tools:** + - [AntiSplit-M](https://github.com/pumPCin/AntiSplit-M) - [ADB Toolbox](https://github.com/Nahvine/ADB_Toolbox) - [ApkPatcher by Th3-R3p4ck3r](https://github.com/Th3-R3p4ck3r/ApkPatcher) diff --git a/Cachyos/Scripts/Android/README.MD b/Cachyos/Scripts/Android/README.MD index 8430df61..6bab957f 100644 --- a/Cachyos/Scripts/Android/README.MD +++ b/Cachyos/Scripts/Android/README.MD @@ -5,9 +5,11 @@ A collection of unified, deduplicated scripts for Android device optimization, A ## 🚀 Main Scripts ### `android-optimize.sh` - Unified Android Optimizer + The primary tool for device optimization with a CLI interface. This script combines multiple optimization workflows into one unified tool. **Commands:** + ```bash # Full device optimization pipeline ./android-optimize.sh device-all @@ -29,6 +31,7 @@ The primary tool for device optimization with a CLI interface. This script combi ``` **What `device-all` does:** + - Device maintenance (fstrim, cleanup, cache trimming) - Filesystem cleanup (removes temp files, logs, backups) - ART optimization (dex compilation with speed-profile + speed modes) @@ -44,6 +47,7 @@ The primary tool for device optimization with a CLI interface. This script combi --- ### `adb-experimental-tweaks.sh` - Experimental System Tweaks + **⚠️ WARNING: Very aggressive experimental tweaks. Use at your own risk!** This script contains 1000+ system property and settings changes. It's comprehensive but potentially unstable. Only use if you understand the implications. @@ -55,6 +59,7 @@ This script contains 1000+ system property and settings changes. It's comprehens --- ### `optimize_apk.sh` - Comprehensive APK Optimization + Full APK optimization pipeline with resource stripping, bytecode optimization, and repackaging. **Requirements:** apktool, redex, dex2jar, proguard/R8, zipalign, apksigner, pngcrush, jpegoptim, 7z @@ -64,6 +69,7 @@ Full APK optimization pipeline with resource stripping, bytecode optimization, a ``` **Process:** + 1. Decode APK with apktool 2. Strip unused resources (extra densities, raw files, assets) 3. Rebuild stripped APK @@ -78,6 +84,7 @@ Full APK optimization pipeline with resource stripping, bytecode optimization, a --- ### `termux-butler.sh` - Termux Environment Maintenance + Interactive maintenance tool for Termux environments. ```bash @@ -85,6 +92,7 @@ Interactive maintenance tool for Termux environments. ``` **Features:** + - Package updates and cache cleaning - Application cache cleanup (pip, npm, user cache) - Filesystem hygiene (remove empty files/dirs) @@ -96,14 +104,17 @@ Interactive maintenance tool for Termux environments. ## 🔧 Utility Scripts ### `setup.sh` - Quick Shizuku Permission Grant + ```bash ./setup.sh ``` + Grants WRITE_SECURE_SETTINGS permission to Shizuku API. --- ### `Shizuku-rish.sh` - Shizuku Setup + Sets up Shizuku and Rish (root shell) in Termux. **Requirements:** nmap, Shizuku app, wireless debugging enabled @@ -113,21 +124,25 @@ Sets up Shizuku and Rish (root shell) in Termux. ``` Creates: + - `/data/data/com.termux/files/usr/bin/shizuku` - Shizuku launcher - `/data/data/com.termux/files/usr/bin/rish` - Root shell command --- ### `mkshrc.sh` - Enhanced Shell Configuration + Shell configuration for mksh (Android's default shell). **Features:** + - Enhanced keybindings (Ctrl+arrows, history search) - Useful aliases for package manager, logcat, navigation - Custom functions (current_activity, myip, man replacement) - Colorized prompt with exit status indicator **Usage:** + ```bash # Copy to your shell RC file cat mkshrc.sh >> ~/.mkshrc @@ -139,19 +154,23 @@ source ~/.mkshrc ## 📚 Additional Resources ### Better 'adb shell' + - https://github.com/matan-h/adb-shell - [mkshrc configuration](mkshrc.sh) - https://github.com/trinhnv1205/optimize ### WebView & Browser Flags + - [WebView flags](https://github.com/celenityy/configs/blob/pages/android/webview-flags.md) - [Chromium flags](https://gist.github.com/ibLeDy/1495735312943b9dd646fd9ddf618513) ### Debloating Tools + - [Adbloat](https://github.com/YurinDoctrine/adbloat) - [UAD-NG](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation) ### Other Tools + - https://github.com/Parsa307/split_optimizer - [Termux SSH Setup](https://github.com/tomhiggins/TermuxSSHDsetup) @@ -160,6 +179,7 @@ source ~/.mkshrc ## 🎮 Quick Tips ### Game Mode + ```bash # Enable fixed performance mode (game on) adb shell cmd power set-fixed-performance-mode-enabled true @@ -169,11 +189,13 @@ adb shell cmd power set-fixed-performance-mode-enabled false ``` ### Write Stats to Disk + ```bash adb shell cmd stats write-to-disk ``` ### Remote Install (index guard) + ```bash curl -sSfL https://raw.githubusercontent.com/Ven0m0/Linux-OS/refs/heads/main/Cachyos/Scripts/Android/android-optimize.sh | bash -s index-nomedia ``` diff --git a/Cachyos/Scripts/Android/webview-flags.md b/Cachyos/Scripts/Android/webview-flags.md index 974a0ef5..4ccb759a 100644 --- a/Cachyos/Scripts/Android/webview-flags.md +++ b/Cachyos/Scripts/Android/webview-flags.md @@ -14,7 +14,7 @@ adb shell am start -a "com.android.webview.SHOW_DEV_UI" `GMSCoreEmoji` -> `Disabled` -`AutofillEnableLoyaltyCardsFilling` -> `Disabled` *(This uses Google Wallet)* +`AutofillEnableLoyaltyCardsFilling` -> `Disabled` _(This uses Google Wallet)_ `AutofillUKMExperimentalFields` -> `Disabled` @@ -110,9 +110,9 @@ adb shell am start -a "com.android.webview.SHOW_DEV_UI" `HttpCacheNoVarySearch` -> `Disabled` -`PartitionAllocWithAdvancedChecks` -> `Enabled` *([some info](https://groups.google.com/a/chromium.org/g/ios-reviews/c/BY-Xq_Zeds8)* +`PartitionAllocWithAdvancedChecks` -> `Enabled` _([some info](https://groups.google.com/a/chromium.org/g/ios-reviews/c/BY-Xq_Zeds8)_ -`SensitiveContent` -> `Enabled` *([info](https://source.chromium.org/chromium/chromium/src/+/main:components/sensitive_content/))* +`SensitiveContent` -> `Enabled` _([info](https://source.chromium.org/chromium/chromium/src/+/main:components/sensitive_content/))_ `RestrictAbusePortsOnLocalhost` -> `Enabled` diff --git a/Cachyos/Scripts/WIP/gh/git-fetch-guide.md b/Cachyos/Scripts/WIP/gh/git-fetch-guide.md index 45b47e94..e336e2a2 100644 --- a/Cachyos/Scripts/WIP/gh/git-fetch-guide.md +++ b/Cachyos/Scripts/WIP/gh/git-fetch-guide.md @@ -80,24 +80,26 @@ git-fetch add cli/cli config.yml --force -m "config: Update from upstream" ## Options Reference -| Option | Description | Default | -|--------|-------------|---------| -| `-b, --branch ` | Branch name to fetch from | Repo's default branch | -| `-c, --commit ` | Commit hash to fetch from | - | -| `-o, --output ` | Output directory (download mode) | `.` | -| `-m, --message ` | Commit message (add mode) | Auto-generated | -| `--no-commit` | Skip auto-commit in add mode | Auto-commit enabled | -| `--force` | Overwrite existing files | Skip existing files | -| `-h, --help` | Show help message | - | +| Option | Description | Default | +| --------------------- | -------------------------------- | --------------------- | +| `-b, --branch ` | Branch name to fetch from | Repo's default branch | +| `-c, --commit ` | Commit hash to fetch from | - | +| `-o, --output ` | Output directory (download mode) | `.` | +| `-m, --message ` | Commit message (add mode) | Auto-generated | +| `--no-commit` | Skip auto-commit in add mode | Auto-commit enabled | +| `--force` | Overwrite existing files | Skip existing files | +| `-h, --help` | Show help message | - | ## File Conflict Handling **Default Behavior**: Never overwrite existing files + - Existing files are skipped - Warning message shows skipped files - Suggests using `--force` if overwrite intended **With `--force` flag**: Overwrite all files + - All existing files are replaced - No confirmation prompt (use carefully!) @@ -120,6 +122,7 @@ git-fetch cli/cli README.md --force In `add` mode, files are automatically committed by default: **Default auto-commit message**: + ``` Add fetched files from GitHub @@ -131,11 +134,13 @@ Co-Authored-By: Claude Sonnet 4.5 ``` **Custom commit message**: + ```bash git-fetch add cli/cli docs/ -m "docs: Sync documentation from upstream" ``` **Skip auto-commit**: + ```bash git-fetch add cli/cli config.yml --no-commit # Files are staged, you commit manually when ready @@ -178,18 +183,18 @@ git-fetch https://github.com/torvalds/linux/tree/master/kernel ## Comparison with Original Scripts -| Feature | git-fetch | gh-download.sh | gh-cp.sh | git-fetch.py | gh-tools.sh | -|---------|-----------|----------------|----------|--------------|-------------| -| Download files | ✅ | ✅ | ✅ | ✅ | ❌ | -| Add to git repo | ✅ | ❌ | ❌ | ❌ | ❌ | -| Auto-commit | ✅ | ❌ | ❌ | ❌ | ❌ | -| Parallel downloads | ✅ | ✅ | ❌ | ✅ | ❌ | -| URL parsing | ✅ | ✅ | ❌ | ✅ | ❌ | -| Branch support | ✅ | ✅ | ✅ | ✅ | ❌ | -| Commit support | ✅ | ❌ | ✅ | ✅ | ❌ | -| Force overwrite | ✅ | ❌ | ❌ | ❌ | ❌ | -| Skip existing | ✅ | ❌ | ❌ | ❌ | ❌ | -| Color output | ✅ | ❌ | ❌ | ❌ | ✅ | +| Feature | git-fetch | gh-download.sh | gh-cp.sh | git-fetch.py | gh-tools.sh | +| ------------------ | --------- | -------------- | -------- | ------------ | ----------- | +| Download files | ✅ | ✅ | ✅ | ✅ | ❌ | +| Add to git repo | ✅ | ❌ | ❌ | ❌ | ❌ | +| Auto-commit | ✅ | ❌ | ❌ | ❌ | ❌ | +| Parallel downloads | ✅ | ✅ | ❌ | ✅ | ❌ | +| URL parsing | ✅ | ✅ | ❌ | ✅ | ❌ | +| Branch support | ✅ | ✅ | ✅ | ✅ | ❌ | +| Commit support | ✅ | ❌ | ✅ | ✅ | ❌ | +| Force overwrite | ✅ | ❌ | ❌ | ❌ | ❌ | +| Skip existing | ✅ | ❌ | ❌ | ❌ | ❌ | +| Color output | ✅ | ❌ | ❌ | ❌ | ✅ | ## Migration Guide @@ -228,6 +233,7 @@ git-fetch https://github.com/owner/repo/tree/main/src -o ./output ### "No GitHub token. Run: gh auth login" **Solution**: Authenticate with GitHub CLI + ```bash gh auth login ``` @@ -235,6 +241,7 @@ gh auth login ### "Not a git repository. Run: git init" **Solution**: Initialize git repo or use download mode + ```bash git init # To initialize repo # OR @@ -244,11 +251,13 @@ git-fetch download owner/repo file.txt # Use download mode instead ### "Failed to fetch folder contents" **Possible causes**: + - Repository doesn't exist or is private - Branch name is incorrect - Path doesn't exist in repository **Solution**: Verify repo/branch/path exist + ```bash # Check repository exists gh repo view owner/repo @@ -260,6 +269,7 @@ gh api repos/owner/repo/branches --jq '.[].name' ### Parallel downloads failing **Solution**: Disable parallel mode (future enhancement) + ```bash # Currently parallel is always enabled # Future: Add --no-parallel flag diff --git a/Cachyos/Scripts/WIP/test_snap_mem.py b/Cachyos/Scripts/WIP/test_snap_mem.py index 86cb858c..c5eed997 100644 --- a/Cachyos/Scripts/WIP/test_snap_mem.py +++ b/Cachyos/Scripts/WIP/test_snap_mem.py @@ -1,6 +1,7 @@ import unittest import importlib.util import sys +import threading from pathlib import Path # Import snap-mem.py using importlib because of the hyphen in the filename @@ -37,5 +38,47 @@ def test_build_base_name_empty(self): with self.assertRaises(ValueError): snap_mem.build_base_name("") + def test_build_base_name_whitespace(self): + # build_base_name uses datetime.strptime(date_str, DATE_FMT) + # strptime is strict about whitespace unless included in fmt + date_str = " 2023-01-01 12:00:00 UTC " + with self.assertRaises(ValueError): + snap_mem.build_base_name(date_str) + + def test_build_base_name_logical_invalid_date(self): + # 2023 is not a leap year + date_str = "2023-02-29 12:00:00 UTC" + with self.assertRaises(ValueError): + snap_mem.build_base_name(date_str) + + def test_make_unique_name(self): + # make_unique_name updates the 'existing' set in-place + existing = {"base.jpg", "base_1.jpg"} + lock = threading.Lock() + + # base.jpg is in existing. n=1 -> base_1.jpg (in existing). n=2 -> base_2.jpg. + name = snap_mem.make_unique_name("base", ".jpg", existing, lock) + self.assertEqual(name, "base_2.jpg") + self.assertIn("base_2.jpg", existing) + + # base_3.jpg + name2 = snap_mem.make_unique_name("base", ".jpg", existing, lock) + self.assertEqual(name2, "base_3.jpg") + self.assertIn("base_3.jpg", existing) + + def test_make_unique_name_empty_set(self): + existing = set() + lock = threading.Lock() + name = snap_mem.make_unique_name("base", ".jpg", existing, lock) + self.assertEqual(name, "base.jpg") + self.assertIn("base.jpg", existing) + + def test_make_unique_name_multiple_collisions(self): + existing = {"base.jpg", "base_1.jpg", "base_2.jpg", "base_3.jpg"} + lock = threading.Lock() + name = snap_mem.make_unique_name("base", ".jpg", existing, lock) + self.assertEqual(name, "base_4.jpg") + self.assertIn("base_4.jpg", existing) + if __name__ == '__main__': unittest.main() diff --git a/Cachyos/Scripts/WIP/test_snap_mem_logic.py b/Cachyos/Scripts/WIP/test_snap_mem_logic.py deleted file mode 100644 index 94ce31cf..00000000 --- a/Cachyos/Scripts/WIP/test_snap_mem_logic.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -import importlib.util -import unittest -import sys -import threading -from pathlib import Path - -# Dynamically import snap-mem.py -file_path = Path(__file__).parent / "snap-mem.py" -spec = importlib.util.spec_from_file_location("snap_mem", str(file_path)) -if spec is None: - raise ImportError(f"Could not load {file_path}") -snap_mem = importlib.util.module_from_spec(spec) -sys.modules["snap_mem"] = snap_mem -spec.loader.exec_module(snap_mem) - -class TestSnapMem(unittest.TestCase): - def test_make_unique_name(self): - # make_unique_name updates the 'existing' set in-place - existing = {"base.jpg", "base_1.jpg"} - lock = threading.Lock() - - # Test finding unique name - # base_2.jpg should be selected if logic is: base_2.jpg - # Implementation: - # name = f"{base}{suffix}" - # while name in existing: name = f"{base}_{n}{suffix}" - # base.jpg is in existing. n=1 -> base_1.jpg (in existing). n=2 -> base_2.jpg. - name = snap_mem.make_unique_name("base", ".jpg", existing, lock) - self.assertEqual(name, "base_2.jpg") - self.assertIn("base_2.jpg", existing) - - # Test another one - # base_3.jpg - name2 = snap_mem.make_unique_name("base", ".jpg", existing, lock) - self.assertEqual(name2, "base_3.jpg") - self.assertIn("base_3.jpg", existing) - -if __name__ == '__main__': - unittest.main() diff --git a/Cachyos/cachyos-autoinstall-plan.md b/Cachyos/cachyos-autoinstall-plan.md index 16605508..d336f2e0 100644 --- a/Cachyos/cachyos-autoinstall-plan.md +++ b/Cachyos/cachyos-autoinstall-plan.md @@ -10,17 +10,17 @@ Two deployment strategies are planned: **Strategy A** (recommended) wraps the ex ## Architecture Decision: Wrapper vs Raw Install -| Criterion | A: CLI Installer Wrapper | B: Raw Pacstrap | -|---|---|---| -| Complexity | Low — generate JSON, run binary | High — replicate installer logic | -| Maintenance | Low — tracks upstream installer | High — must track pacman/repo changes | -| Partition handling | Handled by installer | Must implement sgdisk/mkfs/mount | -| Package selection | Installer knows DE metapackages | Must maintain package lists per DE | -| CachyOS repo setup | Handled by installer | Must run `cachyos-repo.sh` manually | -| Kernel selection | Handled by installer | Must install + mkinitcpio manually | -| Bootloader | Handled by installer | Must configure systemd-boot/grub | -| Failure risk | Installer tested upstream | Any pacstrap step can diverge | -| Availability | Always on CachyOS live USB | Works on any Arch live USB too | +| Criterion | A: CLI Installer Wrapper | B: Raw Pacstrap | +| ------------------ | ------------------------------- | ------------------------------------- | +| Complexity | Low — generate JSON, run binary | High — replicate installer logic | +| Maintenance | Low — tracks upstream installer | High — must track pacman/repo changes | +| Partition handling | Handled by installer | Must implement sgdisk/mkfs/mount | +| Package selection | Installer knows DE metapackages | Must maintain package lists per DE | +| CachyOS repo setup | Handled by installer | Must run `cachyos-repo.sh` manually | +| Kernel selection | Handled by installer | Must install + mkinitcpio manually | +| Bootloader | Handled by installer | Must configure systemd-boot/grub | +| Failure risk | Installer tested upstream | Any pacstrap step can diverge | +| Availability | Always on CachyOS live USB | Works on any Arch live USB too | **Decision: Strategy A primary, Strategy B as opt-in fallback flag (`--raw`).** @@ -101,6 +101,7 @@ COLOR_OUTPUT=true ### Phase 1: Preflight Validation (M) **T1.1** — Environment checks: + - Running as root (or auto-elevate with sudo) - Running from live USB (check `/run/archiso` or `lsblk` for ISO9660) - Internet connectivity (`ping -c1 -W3 cachyos.org`) @@ -108,6 +109,7 @@ COLOR_OUTPUT=true - UEFI vs BIOS detection (`[ -d /sys/firmware/efi ]`) — restrict bootloader choices accordingly **T1.2** — Config validation function `validate_config()`: + - `DEVICE` exists as block device or auto-detect succeeds - `FILESYSTEM` is in allowed set - `BOOTLOADER` compatible with firmware type (systemd-boot/refind/limine require UEFI) @@ -118,11 +120,13 @@ COLOR_OUTPUT=true - All partition sizes parseable **T1.3** — Device auto-detection when `DEVICE=""`: + - `lsblk -dnpo NAME,SIZE,TYPE,TRAN` → filter `type=disk`, exclude `tran=usb` - Pick largest remaining disk - Print detected device, require confirmation unless `--yes` **T1.4** — Disk safety gate: + - Show current partition table of target device - Warn if device has existing partitions / filesystems - Require explicit confirmation (bypass with `--yes`) @@ -133,6 +137,7 @@ COLOR_OUTPUT=true ### Phase 2: settings.json Generation (M) **T2.1** — Partition plan builder function `build_partitions()`: + - UEFI: create boot partition entry (`/boot`, `$BOOT_SIZE`, `vfat`, `type: "boot"`) - Optional swap partition if `SWAP_SIZE != "0"` (calculate from `free -b` if `"auto"`) - Root partition: remaining space, `$FILESYSTEM`, `type: "root"` @@ -140,11 +145,13 @@ COLOR_OUTPUT=true - BIOS: no EFI partition, add `bios_boot` 1M partition for grub **T2.2** — Subvolume resolution: + - `"default"` → standard CachyOS btrfs layout: `/@`, `/@home`, `/@root`, `/@srv`, `/@cache`, `/@tmp`, `/@log` - `"custom"` → parse `CUSTOM_SUBVOLS` array into JSON array of `{"subvolume": "/@x", "mountpoint": "/x"}` objects - Only applies when `FILESYSTEM="btrfs"` **T2.3** — JSON assembly function `generate_settings_json()`: + - Use heredoc with variable interpolation (no jq dependency needed for generation, but validate with `python3 -m json.tool` or `jq -e .` if available) - Map all config vars to the settings.json schema: @@ -242,6 +249,7 @@ fi **T4.2** — Confirmation gate (unless `--yes`): "This will ERASE $DEVICE. Continue? [y/N]" **T4.3** — Strategy A execution: + ```bash cd /root # settings.json is already here from Phase 2 @@ -250,6 +258,7 @@ exit_code=${PIPESTATUS[0]} ``` **T4.4** — Strategy B execution (if `--raw` or installer missing): + - `sgdisk --zap-all "$DEVICE"` - Create partitions via `sgdisk` - `mkfs.*` per partition @@ -267,6 +276,7 @@ exit_code=${PIPESTATUS[0]} ### Phase 5: Remote Config Support (S) **T5.1** — `--config ` flag: fetch a remote bash file that overrides config variables. + ```bash if [ -n "$CONFIG_URL" ]; then source <(curl -fsSL "$CONFIG_URL") @@ -283,14 +293,14 @@ fi **T6.1** — VM test matrix (QEMU/libvirt): -| Test Case | UEFI | BIOS | FS | DE | Bootloader | -|---|---|---|---|---|---| -| Minimal desktop | ✓ | | btrfs | kde | systemd-boot | -| Server headless | ✓ | | ext4 | (none) | systemd-boot | -| Full custom | ✓ | | btrfs | hyprland | grub | -| BIOS legacy | | ✓ | ext4 | xfce | grub | -| Dry run | ✓ | | btrfs | kde | systemd-boot | -| Multi-kernel | ✓ | | xfs | gnome | refind | +| Test Case | UEFI | BIOS | FS | DE | Bootloader | +| --------------- | ---- | ---- | ----- | -------- | ------------ | +| Minimal desktop | ✓ | | btrfs | kde | systemd-boot | +| Server headless | ✓ | | ext4 | (none) | systemd-boot | +| Full custom | ✓ | | btrfs | hyprland | grub | +| BIOS legacy | | ✓ | ext4 | xfce | grub | +| Dry run | ✓ | | btrfs | kde | systemd-boot | +| Multi-kernel | ✓ | | xfs | gnome | refind | **T6.2** — Automated smoke test: `--dry-run` mode validates JSON output against a JSON schema. @@ -331,31 +341,31 @@ T7.* (docs) parallel with T6.* ## Risk Register -| # | Risk | Impact | Likelihood | Mitigation | -|---|---|---|---|---| -| R1 | CLI installer headless mode has undocumented field requirements or breaks on edge cases | Install fails | Medium | Strategy B fallback; test all field combos in VM | -| R2 | Partition naming logic wrong for unusual devices (mmcblk, nvme vs sda) | Wrong partitions written | Medium | Comprehensive device name → partition name mapping function; test with multiple device types | -| R3 | CachyOS updates installer JSON schema | Script generates invalid JSON | Low | Pin to known-good schema; monitor upstream releases | -| R4 | `curl \| bash` executed without reviewing config → data loss | User loses data | Medium | Default `CONFIRM_BEFORE_INSTALL=true`; big red warning in README | -| R5 | Post-install chroot environment missing expected binaries | Post-install hooks fail | Low | Check for each binary before use; skip gracefully with warning | -| R6 | btrfs subvolume naming diverges from CachyOS default layout | Broken snapper/timeshift | Medium | Pull default subvol list from installer source; keep in sync | -| R7 | ZFS support requires DKMS + headers during install | ZFS install may fail | Medium | Ensure `linux-cachyos-headers` is pulled alongside kernel for ZFS | +| # | Risk | Impact | Likelihood | Mitigation | +| --- | --------------------------------------------------------------------------------------- | ----------------------------- | ---------- | -------------------------------------------------------------------------------------------- | +| R1 | CLI installer headless mode has undocumented field requirements or breaks on edge cases | Install fails | Medium | Strategy B fallback; test all field combos in VM | +| R2 | Partition naming logic wrong for unusual devices (mmcblk, nvme vs sda) | Wrong partitions written | Medium | Comprehensive device name → partition name mapping function; test with multiple device types | +| R3 | CachyOS updates installer JSON schema | Script generates invalid JSON | Low | Pin to known-good schema; monitor upstream releases | +| R4 | `curl \| bash` executed without reviewing config → data loss | User loses data | Medium | Default `CONFIRM_BEFORE_INSTALL=true`; big red warning in README | +| R5 | Post-install chroot environment missing expected binaries | Post-install hooks fail | Low | Check for each binary before use; skip gracefully with warning | +| R6 | btrfs subvolume naming diverges from CachyOS default layout | Broken snapper/timeshift | Medium | Pull default subvol list from installer source; keep in sync | +| R7 | ZFS support requires DKMS + headers during install | ZFS install may fail | Medium | Ensure `linux-cachyos-headers` is pulled alongside kernel for ZFS | --- ## Effort Estimates -| Phase | Size | Estimate | Notes | -|---|---|---|---| -| P0: Skeleton + Config | S | 2-3 hrs | Mostly boilerplate | -| P1: Preflight | M | 3-4 hrs | Device detection logic needs care | -| P2: JSON Generation | M | 3-4 hrs | Partition plan builder is the hard part | -| P3: Post-Install Hooks | L | 4-6 hrs | AUR helper + dotfiles + chroot edge cases | -| P4: Installer Execution | S | 2-3 hrs | Strategy A simple; B is the fallback | -| P5: Remote Config | S | 1 hr | Simple source + curl | -| P6: Testing | L | 6-8 hrs | VM matrix across all combos | -| P7: Docs | S | 1-2 hrs | README + examples | -| **Total** | | **~22-31 hrs** | | +| Phase | Size | Estimate | Notes | +| ----------------------- | ---- | -------------- | ----------------------------------------- | +| P0: Skeleton + Config | S | 2-3 hrs | Mostly boilerplate | +| P1: Preflight | M | 3-4 hrs | Device detection logic needs care | +| P2: JSON Generation | M | 3-4 hrs | Partition plan builder is the hard part | +| P3: Post-Install Hooks | L | 4-6 hrs | AUR helper + dotfiles + chroot edge cases | +| P4: Installer Execution | S | 2-3 hrs | Strategy A simple; B is the fallback | +| P5: Remote Config | S | 1 hr | Simple source + curl | +| P6: Testing | L | 6-8 hrs | VM matrix across all combos | +| P7: Docs | S | 1-2 hrs | README + examples | +| **Total** | | **~22-31 hrs** | | --- @@ -401,21 +411,25 @@ cachyos-autoinstall/ ## Usage Examples **Minimal (interactive device selection):** + ```bash curl -fsSL https://raw.githubusercontent.com/Ven0m0/cachyos-autoinstall/main/cachyos-autoinstall.sh | bash ``` **Fully unattended with remote config:** + ```bash curl -fsSL https://raw.githubusercontent.com/Ven0m0/cachyos-autoinstall/main/cachyos-autoinstall.sh | bash -s -- --config https://raw.githubusercontent.com/Ven0m0/cachyos-autoinstall/main/configs/desktop-kde.conf --yes ``` **Dry run (validate config, print JSON, no install):** + ```bash curl -fsSL ... | bash -s -- --config ... --dry-run ``` **Local config from USB:** + ```bash curl -fsSL ... | bash -s -- --config /run/media/liveuser/USB/my-config.conf --yes ``` diff --git a/Cachyos/rate-mirrors/README.md b/Cachyos/rate-mirrors/README.md index 06f3d180..dd29776d 100644 --- a/Cachyos/rate-mirrors/README.md +++ b/Cachyos/rate-mirrors/README.md @@ -65,6 +65,7 @@ makepkg -si ``` Update checksums: + ```bash sha256sum cachyos-rate-mirrors cachyos-rate-mirrors.{service,timer,hook} ``` diff --git a/PLAN.md b/PLAN.md index 2765e03e..564667b1 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,7 +1,9 @@ # Implementation Plan + _Generated: 2026-03-21 · 4 tasks · Est. S–L LOC delta_ ## Legend + @@ -11,23 +13,25 @@ The codebase is in good shape overall — recent commits have resolved prior TOD ## Task Index (topological order) -| # | ID | Title | Sev | Cat | Size | Blocks | -|---|-----|-------|-----|-----|------|--------| -| 1 | T001 | Fix `move_file_to_group` arity mismatch in Splitter.py | 🔴 | bug | S | — | -| 2 | T002 | Bring packages.sh up to project Bash standards | 🟠 | debt | L | — | -| 3 | T003 | Resolve "Settings todo" placeholder in RaspberryPi README | 🔵 | docs | S | — | -| 4 | T004 | Implement Android charwasp/modify fork with GraphicsMagick | 🟡 | feature | XL | — | +| # | ID | Title | Sev | Cat | Size | Blocks | +| --- | ---- | ---------------------------------------------------------- | --- | ------- | ---- | ------ | +| 1 | T001 | Fix `move_file_to_group` arity mismatch in Splitter.py | 🔴 | bug | S | — | +| 2 | T002 | Bring packages.sh up to project Bash standards | 🟠 | debt | L | — | +| 3 | T003 | Resolve "Settings todo" placeholder in RaspberryPi README | 🔵 | docs | S | — | +| 4 | T004 | Implement Android charwasp/modify fork with GraphicsMagick | 🟡 | feature | XL | — | --- ## Tasks ### T001 · Fix `move_file_to_group` arity mismatch in Splitter.py + **File:** `Cachyos/Scripts/WIP/gphotos/Splitter.py:88` **Severity:** critical · **Category:** bug · **Size:** S **Blocks:** — **Blocked by:** — **Context:** + > Call-site at line 88 passes 4 args and assigns the return value as the new size: > `current_group_size = move_file_to_group(file_path, current_group_folder, file_size, current_group_size)` > Definition at line 95 only accepts 2 params and returns `bool`, not `int`: @@ -36,6 +40,7 @@ The codebase is in good shape overall — recent commits have resolved prior TOD **Intent:** After refactor commit #239 (`group_photos` complexity reduction), the call-site still passes `file_size` and `current_group_size` as positional args and uses the bool return value as the new group size. This raises `TypeError: move_file_to_group() takes 2 positional arguments but 4 were given` on the first file processed. **Acceptance criteria:** + - [ ] `process_file()` does not raise `TypeError` when processing a directory with at least one file smaller than `target_folder_size`. - [ ] `current_group_size` is updated correctly (incremented by `file_size`) after a successful move. - [ ] `move_file_to_group` signature and call-site are consistent — no argument count mismatch. @@ -43,6 +48,7 @@ The codebase is in good shape overall — recent commits have resolved prior TOD **Implementation:** Option A — restore 4-param signature returning updated size: + ```python def move_file_to_group(file_path, current_group_folder, file_size, current_group_size): abs_file_path = os.path.abspath(file_path) @@ -56,7 +62,9 @@ def move_file_to_group(file_path, current_group_folder, file_size, current_group print(f"Failed to move photo '{file_path}': {e}") return current_group_size ``` + Option B — keep 2-param bool version, fix call-site in `process_file`: + ```python moved = move_file_to_group(file_path, current_group_folder) if moved: @@ -66,16 +74,19 @@ if moved: --- ### T002 · Bring packages.sh up to project Bash standards + **File:** `Cachyos/Scripts/packages.sh:1` **Severity:** high · **Category:** debt · **Size:** L **Blocks:** — **Blocked by:** — **Context:** + > File begins with `#!/bin/bash` (not `#!/usr/bin/env bash`), has no `set -euo pipefail`, no `shopt`, no `IFS=$'\n\t'`, uses `echo -e` throughout, predictable `/tmp/filtered_packages.txt` and `/tmp/succeeded_packages` paths, unquoted `$package_list` expansion in `sudo pacman -S ... $package_list`, and `local start_time=$(date +%s)` which masks the return code. Uses `cd /tmp/paru || exit 1` without restoring cwd. **Intent:** This script predates the current project template and was never migrated to match the Bash standards defined in CLAUDE.md. **Acceptance criteria:** + - [ ] Shebang updated to `#!/usr/bin/env bash`. - [ ] `set -Eeuo pipefail` + `shopt -s nullglob globstar extglob dotglob` + `IFS=$'\n\t'` present at top. - [ ] All `echo -e` replaced with `printf`. @@ -91,17 +102,20 @@ Apply CLAUDE.md script template: `#!/usr/bin/env bash`, `set -Eeuo pipefail`, tr --- ### T003 · Resolve "Settings todo" placeholder in RaspberryPi README + **File:** `RaspberryPi/README.md:43` **Severity:** low · **Category:** docs · **Size:** S **Blocks:** — **Blocked by:** — **Context:** + > `### Settings todo` > Followed by a raw markdown code block containing `net.ipv4.ip_forward=1` and an unformatted nala URL — no explanation, no context. **Intent:** Author intended to document recommended sysctl settings for Pi networking and link the nala mirror-fetch docs but left it as a raw placeholder. **Acceptance criteria:** + - [ ] Section heading no longer contains "todo". - [ ] `net.ipv4.ip_forward=1` explained with a one-line use case (router/VPN/container bridging). - [ ] The nala link rendered as a proper Markdown hyperlink with a descriptive label. @@ -109,7 +123,8 @@ Apply CLAUDE.md script template: `#!/usr/bin/env bash`, `set -Eeuo pipefail`, tr **Implementation:** Replace the section at line 43–48 with: -```markdown + +````markdown ### Recommended sysctl settings Enable IP forwarding when using the Pi as a router, VPN gateway, or container host: @@ -121,9 +136,11 @@ net.ipv4.ip_forward=1 # Apply immediately sudo sysctl -p ``` +```` For fast APT mirror selection see the [nala fetch mirror docs](https://gitlab.com/volian/nala/-/blob/main/docs/nala-fetch.8.rst?ref_type=heads). -``` + +```` --- @@ -160,7 +177,8 @@ def run_image_cmd(*args: str) -> None: subprocess.run(["convert", *args], check=True) else: raise RuntimeError("Neither GraphicsMagick (gm) nor ImageMagick (convert) found in PATH") -``` +```` + Use `argparse` subparsers: `resize`, `patch-manifest`, `repack`. Reference the charwasp/modify source for per-operation logic. --- diff --git a/README.md b/README.md index 30a1ea13..493eb98b 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ A curated collection of battle-tested automation scripts for system setup, optim ## 🎯 Target Systems -| **Primary** | **Secondary** | **Tertiary** | -|:------------|:--------------|:-------------| -| Arch Linux | Debian | Termux | -| CachyOS | DietPi | Raspbian | -| Wayland | Raspberry Pi OS | | +| **Primary** | **Secondary** | **Tertiary** | +| :---------- | :-------------- | :----------- | +| Arch Linux | Debian | Termux | +| CachyOS | DietPi | Raspbian | +| Wayland | Raspberry Pi OS | | --- @@ -95,32 +95,32 @@ sudo ./raspi-f2fs.sh -i dietpi -d /dev/sdX -s ### Cachyos/ -| Script | Description | Usage | -|:-------|:------------|:------| -| **up.sh** | All-in-one update orchestrator (system, flatpak, rust, python, npm, mise, etc.) | `curl -fsSL \| bash` | -| **clean.sh** | Comprehensive cleanup: pacman cache, orphans, logs, browser data, privacy hardening | `curl -fsSL \| bash` | -| **setup.sh** | Automated system configuration: repositories, sysctl tuning, service setup | `./setup.sh` | -| **Rank.sh** | Mirror ranking for optimal download speeds + keyring updates | `curl -fsSL \| bash` | -| **debloat.sh** | Remove bloatware and unnecessary services | `./debloat.sh` | -| **rustbuild.sh** | Rust compilation environment with optimized flags | `./rustbuild.sh` | +| Script | Description | Usage | +| :--------------- | :---------------------------------------------------------------------------------- | :------------------------- | +| **up.sh** | All-in-one update orchestrator (system, flatpak, rust, python, npm, mise, etc.) | `curl -fsSL \| bash` | +| **clean.sh** | Comprehensive cleanup: pacman cache, orphans, logs, browser data, privacy hardening | `curl -fsSL \| bash` | +| **setup.sh** | Automated system configuration: repositories, sysctl tuning, service setup | `./setup.sh` | +| **Rank.sh** | Mirror ranking for optimal download speeds + keyring updates | `curl -fsSL \| bash` | +| **debloat.sh** | Remove bloatware and unnecessary services | `./debloat.sh` | +| **rustbuild.sh** | Rust compilation environment with optimized flags | `./rustbuild.sh` | ### RaspberryPi/ -| Script | Description | Usage | -|:-------|:------------|:------| -| **setup.sh** | Pi optimization: APT config, modern tooling (fd, rg, bat, eza), optional Pi-hole | `./Scripts/setup.sh --help` | -| **raspi-f2fs.sh** | Flash images to SD/USB with F2FS root (better flash longevity) | `sudo ./raspi-f2fs.sh -h` | -| **update.sh** | Pi-specific update script with APT optimization | `curl -fsSL \| bash` | -| **PiClean.sh** | Pi cleanup: APT cache, logs, temp files | `curl -fsSL \| bash` | -| **Kbuild.sh** | Raspberry Pi kernel building with optimization flags | `./Scripts/Kbuild.sh` | -| **apkg.sh** | Interactive APT package manager using fzf/skim | `apkg --install` | +| Script | Description | Usage | +| :---------------- | :------------------------------------------------------------------------------- | :-------------------------- | +| **setup.sh** | Pi optimization: APT config, modern tooling (fd, rg, bat, eza), optional Pi-hole | `./Scripts/setup.sh --help` | +| **raspi-f2fs.sh** | Flash images to SD/USB with F2FS root (better flash longevity) | `sudo ./raspi-f2fs.sh -h` | +| **update.sh** | Pi-specific update script with APT optimization | `curl -fsSL \| bash` | +| **PiClean.sh** | Pi cleanup: APT cache, logs, temp files | `curl -fsSL \| bash` | +| **Kbuild.sh** | Raspberry Pi kernel building with optimization flags | `./Scripts/Kbuild.sh` | +| **apkg.sh** | Interactive APT package manager using fzf/skim | `apkg --install` | ### Scripts/Android/ -| Script | Description | Usage | -|:-------|:------------|:------| -| **android-optimize.sh** | Termux/Android system optimization | `./android-optimize.sh` | -| **optimize_apk.sh** | APK size reduction & optimization | `./optimize_apk.sh app.apk` | +| Script | Description | Usage | +| :---------------------- | :--------------------------------- | :-------------------------- | +| **android-optimize.sh** | Termux/Android system optimization | `./android-optimize.sh` | +| **optimize_apk.sh** | APK size reduction & optimization | `./optimize_apk.sh app.apk` | --- @@ -262,14 +262,14 @@ All scripts include standardized helpers (from [Shell-book.md](Shell-book.md)): ### Tool Hierarchy (with fallbacks) -| Task | Primary | Fallback Chain | -|:-----|:--------|:---------------| -| Find | `fd` | `fdfind` → `find` | -| Grep | `rg` | `grep -E` | -| View | `bat` | `cat` | -| Edit | `sd` | `sed -E` | -| Web | `aria2c` | `curl` → `wget` | -| JSON | `jaq` | `jq` | +| Task | Primary | Fallback Chain | +| :------- | :-------------- | :---------------------- | +| Find | `fd` | `fdfind` → `find` | +| Grep | `rg` | `grep -E` | +| View | `bat` | `cat` | +| Edit | `sd` | `sed -E` | +| Web | `aria2c` | `curl` → `wget` | +| JSON | `jaq` | `jq` | | Parallel | `rust-parallel` | `parallel` → `xargs -P` | --- diff --git a/RaspberryPi/EXAMPLES.md b/RaspberryPi/EXAMPLES.md index 209f6b55..b3bc8889 100644 --- a/RaspberryPi/EXAMPLES.md +++ b/RaspberryPi/EXAMPLES.md @@ -19,6 +19,7 @@ sudo ./f2fs-new.sh -i ``` **What happens:** + - Downloads latest DietPi Trixie image - Converts to F2FS - Flashes directly to SD card @@ -37,6 +38,7 @@ sudo dd if=~/Images/dietpi-f2fs-20260113.img of=/dev/mmcblk0 bs=4M conv=fsync st ``` **What happens:** + - Downloads DietPi image - Converts to F2FS - Saves as timestamped image file @@ -57,6 +59,7 @@ sudo ./f2fs-new.sh -i ``` **What happens:** + - Uses local image (no download needed) - Converts to F2FS - Saves to specified path @@ -85,6 +88,7 @@ sudo ./f2fs-new.sh \ ``` **What happens:** + - Customizes F2FS mount options in fstab - Different trade-offs: performance vs. compression vs. compatibility @@ -104,6 +108,7 @@ done ``` **What happens:** + - Creates one master image - Reuses it for multiple devices - Saves download/conversion time @@ -127,6 +132,7 @@ sudo ./raspi-f2fs.sh -i ~/dietpi.img.xz -d /dev/mmcblk0 -s -z -b 512M ``` **Options:** + - `-s`: Enable SSH on first boot - `-z`: Shrink image before flashing - `-b SIZE`: Set boot partition size @@ -154,6 +160,7 @@ sudo dd if=~/dietpi-f2fs.img of=/dev/mmcblk0 bs=4M conv=fsync status=progress ``` **What happens:** + - Creates F2FS image - Chroots for customization - Automatically regenerates initramfs @@ -175,6 +182,7 @@ done ``` **What happens:** + - Finds all compressed images - Converts each to F2FS - Outputs with `_f2fs` suffix @@ -196,6 +204,7 @@ sudo ./f2fs-new.sh -i ``` **What happens:** + - Downloads from custom URL - Converts to F2FS - Flashes directly to device @@ -229,6 +238,7 @@ sudo dd if=/tmp/test-f2fs.img of=/dev/mmcblk0 bs=4M conv=fsync status=progress ``` **What happens:** + - Creates test image - Verifies F2FS configuration - Checks all required components @@ -383,11 +393,11 @@ wait ## Script Comparison -| Use Case | Script | Command | -|----------|--------|---------| -| Interactive conversion | f2fs-new.sh | `sudo ./f2fs-new.sh -i` | -| Create image file | f2fs-new.sh | `sudo ./f2fs-new.sh --out image.img` | -| Flash to device | f2fs-new.sh | `sudo ./f2fs-new.sh --device /dev/mmcblk0` | -| Advanced flashing | raspi-f2fs.sh | `sudo ./raspi-f2fs.sh -i dietpi -d /dev/mmcblk0` | -| Post-conversion | dietpi-chroot.sh | `sudo ./dietpi-chroot.sh image.img` | -| Shrink + SSH | raspi-f2fs.sh | `sudo ./raspi-f2fs.sh -i dietpi -d /dev/mmcblk0 -z -s` | +| Use Case | Script | Command | +| ---------------------- | ---------------- | ------------------------------------------------------ | +| Interactive conversion | f2fs-new.sh | `sudo ./f2fs-new.sh -i` | +| Create image file | f2fs-new.sh | `sudo ./f2fs-new.sh --out image.img` | +| Flash to device | f2fs-new.sh | `sudo ./f2fs-new.sh --device /dev/mmcblk0` | +| Advanced flashing | raspi-f2fs.sh | `sudo ./raspi-f2fs.sh -i dietpi -d /dev/mmcblk0` | +| Post-conversion | dietpi-chroot.sh | `sudo ./dietpi-chroot.sh image.img` | +| Shrink + SSH | raspi-f2fs.sh | `sudo ./raspi-f2fs.sh -i dietpi -d /dev/mmcblk0 -z -s` | diff --git a/RaspberryPi/QUICKSTART.md b/RaspberryPi/QUICKSTART.md index 26c8a2f4..f6d4b197 100644 --- a/RaspberryPi/QUICKSTART.md +++ b/RaspberryPi/QUICKSTART.md @@ -3,19 +3,23 @@ ## 1-Minute Setup ### Option A: Interactive (Easiest) + ```bash cd RaspberryPi sudo ./f2fs-new.sh -i ``` + Follow the prompts to select source and destination. ### Option B: One Command + ```bash # Download latest DietPi and flash to SD card sudo ./RaspberryPi/f2fs-new.sh --device /dev/mmcblk0 ``` ### Option C: Create Image First + ```bash # Create F2FS image sudo ./RaspberryPi/f2fs-new.sh --out ~/dietpi-f2fs.img @@ -33,18 +37,19 @@ sudo dd if=~/dietpi-f2fs.img of=/dev/mmcblk0 bs=4M conv=fsync status=progress ## Common Commands -| Task | Command | -|------|---------| -| Interactive mode | `sudo ./f2fs-new.sh -i` | -| Flash to device | `sudo ./f2fs-new.sh --device /dev/mmcblk0` | -| Create image | `sudo ./f2fs-new.sh --out image.img` | -| Customize image | `sudo ./dietpi-chroot.sh image.img` | -| Use local file | `sudo ./f2fs-new.sh --src local.img.xz --device /dev/mmcblk0` | -| Custom compression | `sudo ./f2fs-new.sh --root-opts "compress_algorithm=lz4"` | +| Task | Command | +| ------------------ | ------------------------------------------------------------- | +| Interactive mode | `sudo ./f2fs-new.sh -i` | +| Flash to device | `sudo ./f2fs-new.sh --device /dev/mmcblk0` | +| Create image | `sudo ./f2fs-new.sh --out image.img` | +| Customize image | `sudo ./dietpi-chroot.sh image.img` | +| Use local file | `sudo ./f2fs-new.sh --src local.img.xz --device /dev/mmcblk0` | +| Custom compression | `sudo ./f2fs-new.sh --root-opts "compress_algorithm=lz4"` | ## Troubleshooting ### Boot Fails? + ```bash # Regenerate initramfs sudo ./dietpi-chroot.sh /path/to/image.img @@ -52,6 +57,7 @@ sudo ./dietpi-chroot.sh /path/to/image.img ``` ### Need Help? + - [Complete Guide](../DIETPI_F2FS_GUIDE.md) - Full documentation - [Examples](EXAMPLES.md) - Common use cases - [Issues](https://github.com/yourusername/Linux-OS/issues) - Report bugs @@ -68,10 +74,10 @@ sudo ./dietpi-chroot.sh /path/to/image.img ## Performance Benefits -| Metric | ext4 | F2FS | Improvement | -|--------|------|------|-------------| -| Random I/O | Baseline | +30-50% | Better | -| Write endurance | Baseline | +2-3x | Much better | +| Metric | ext4 | F2FS | Improvement | +| ---------------- | -------- | ------- | ------------------------- | +| Random I/O | Baseline | +30-50% | Better | +| Write endurance | Baseline | +2-3x | Much better | | Space efficiency | Baseline | +10-20% | Better (with compression) | ## Next Steps @@ -83,6 +89,7 @@ sudo ./dietpi-chroot.sh /path/to/image.img --- **Quick Links:** + - [DietPi Website](https://dietpi.com) - [F2FS Docs](https://www.kernel.org/doc/html/latest/filesystems/f2fs.html) - [GitHub Repo](https://github.com/yourusername/Linux-OS) diff --git a/RaspberryPi/README.md b/RaspberryPi/README.md index d1a8512d..2c0826f2 100644 --- a/RaspberryPi/README.md +++ b/RaspberryPi/README.md @@ -70,12 +70,14 @@ Convert DietPi or Raspberry Pi OS from ext4 to F2FS for better SD card performan ### Quick Start **Interactive Mode (Easiest):** + ```bash cd RaspberryPi sudo ./f2fs-new.sh -i ``` **Direct Flash:** + ```bash # Download latest DietPi and flash to device sudo ./f2fs-new.sh --device /dev/mmcblk0 @@ -85,6 +87,7 @@ sudo ./f2fs-new.sh --src ~/DietPi.img.xz --device /dev/mmcblk0 ``` **Create Image File:** + ```bash # Download and convert to F2FS image sudo ./f2fs-new.sh --out ~/dietpi-f2fs.img @@ -95,10 +98,10 @@ sudo dd if=~/dietpi-f2fs.img of=/dev/mmcblk0 bs=4M conv=fsync status=progress ### Scripts -| Script | Purpose | Best For | -|--------|---------|----------| -| `f2fs-new.sh` | Convert images to F2FS | Image creation, direct flashing | -| `raspi-f2fs.sh` | Advanced F2FS flasher | Custom boot size, shrinking, SSH | +| Script | Purpose | Best For | +| ------------------ | ----------------------------- | -------------------------------- | +| `f2fs-new.sh` | Convert images to F2FS | Image creation, direct flashing | +| `raspi-f2fs.sh` | Advanced F2FS flasher | Custom boot size, shrinking, SSH | | `dietpi-chroot.sh` | Post-conversion customization | Initramfs regen, package install | ### Features diff --git a/RaspberryPi/docs/REFERENCES.md b/RaspberryPi/docs/REFERENCES.md index f61fe781..993a30eb 100644 --- a/RaspberryPi/docs/REFERENCES.md +++ b/RaspberryPi/docs/REFERENCES.md @@ -5,12 +5,12 @@ Nextcloud alternative **apt-fast** - [https://github.com/ilikenwf/apt-fast](https://github.com/ilikenwf/apt-fast) - _DOWNLOADER='aria2c --no-conf -c -j ${_MAXNUM} -x ${_MAXCONPERSRV} -s ${_SPLITCON} -i ${DLLIST} --min-split-size=${_MINSPLITSZ} --stream-piece-selector=${_PIECEALGO} --connect-timeout=600 --timeout=600 -m0' + \_DOWNLOADER='aria2c --no-conf -c -j ${_MAXNUM} -x ${_MAXCONPERSRV} -s ${_SPLITCON} -i ${DLLIST} --min-split-size=${\_MINSPLITSZ} --stream-piece-selector=${\_PIECEALGO} --connect-timeout=600 --timeout=600 -m0' -_MINSPLITSZ=2M -_MAXNUM=6 +\_MINSPLITSZ=2M +\_MAXNUM=6 DOWNLOADBEFORE=true -_APTMGR=apt-get +\_APTMGR=apt-get [https://github.com/Rudxain/dotfiles](https://github.com/Rudxain/dotfiles) diff --git a/docs/DIETPI_F2FS_GUIDE.md b/docs/DIETPI_F2FS_GUIDE.md index 29355a73..064b7115 100644 --- a/docs/DIETPI_F2FS_GUIDE.md +++ b/docs/DIETPI_F2FS_GUIDE.md @@ -110,21 +110,25 @@ sudo ./RaspberryPi/raspi-f2fs.sh -i dietpi -d /dev/mmcblk0 -s ## Features ### DietPi Detection + - Automatically detects DietPi installations - Identifies Pi model (3/4/5) and DietPi version - Applies DietPi-specific optimizations ### Config Management + - **Backed up:** `/boot/dietpi/.installed`, `dietpi.txt`, network configs, hostname - **Removed:** ext4 journal settings, ext4-specific cron jobs/systemd timers - **Updated:** fstab with F2FS mount options, cmdline.txt with rootfstype=f2fs ### F2FS Optimization + - Compression enabled: `compress_algorithm=zstd` - Advanced features: `compress_chksum,atgc,gc_merge` - Optimal mount options for SD card longevity ### Safety Features + - Pre-conversion verification of F2FS kernel support - Post-conversion verification (fstab, cmdline, mountability) - Detailed summary with warnings and next steps @@ -138,6 +142,7 @@ sudo ./RaspberryPi/raspi-f2fs.sh -i dietpi -d /dev/mmcblk0 -s - If kernel panic occurs, check F2FS module availability 2. **Verify F2FS:** + ```bash # Check root filesystem type mount | grep "on / " @@ -153,6 +158,7 @@ sudo ./RaspberryPi/raspi-f2fs.sh -i dietpi -d /dev/mmcblk0 -s ``` 3. **Optional: Regenerate Initramfs (if boot issues occur):** + ```bash # On the Pi (if it boots) sudo update-initramfs -u @@ -164,39 +170,47 @@ sudo ./RaspberryPi/raspi-f2fs.sh -i dietpi -d /dev/mmcblk0 -s ## Troubleshooting ### Boot Fails with Kernel Panic + - **Cause:** Kernel missing F2FS support - **Fix:** Use a kernel with CONFIG_F2FS_FS=y or CONFIG_F2FS_FS=m ### Root Partition Won't Mount + - **Cause:** Missing f2fs module in initramfs - **Fix:** Run `dietpi-chroot.sh` on the image and regenerate initramfs ### Compression Not Working + - **Cause:** Kernel older than 5.6 or missing CONFIG_F2FS_FS_COMPRESSION=y - **Fix:** Upgrade kernel or remove compression options from fstab mount options ### Performance Issues + - **Check:** Verify compression is enabled: `cat /proc/fs/f2fs/*/compress_extension` - **Tune:** Adjust mount options in `/etc/fstab` ## Script Reference -| Script | Purpose | Usage | -|--------|---------|-------| -| `raspi-f2fs.sh` | Flash images to devices with F2FS | Device-to-device conversion | -| `f2fs-new.sh` | Create F2FS images from ext4 | Image creation/conversion | -| `dietpi-chroot.sh` | Chroot into Pi images | Post-conversion tasks, initramfs regen | +| Script | Purpose | Usage | +| ------------------ | --------------------------------- | -------------------------------------- | +| `raspi-f2fs.sh` | Flash images to devices with F2FS | Device-to-device conversion | +| `f2fs-new.sh` | Create F2FS images from ext4 | Image creation/conversion | +| `dietpi-chroot.sh` | Chroot into Pi images | Post-conversion tasks, initramfs regen | ## Advanced Options ### Custom F2FS Options + Edit `f2fs-new.sh` and modify the `ROOT_OPTS` variable: + ```bash ROOT_OPTS="compress_algorithm=lz4,compress_chksum,atgc" # Use LZ4 instead of zstd ``` ### Custom Image Source + Both scripts support: + - URLs: `https://example.com/image.img.xz` - Local files: `/path/to/image.img` or `image.img.xz` - Shortcut: `dietpi` (downloads latest DietPi Trixie) @@ -204,6 +218,7 @@ Both scripts support: ## Performance Benefits F2FS provides: + - **Better SD card longevity:** Optimized for flash storage - **Compression:** Saves space and reduces writes (if enabled) - **Faster random I/O:** Better than ext4 on SD cards @@ -219,6 +234,7 @@ F2FS provides: ## Support For issues, see: + - F2FS documentation: https://www.kernel.org/doc/html/latest/filesystems/f2fs.html - DietPi forums: https://dietpi.com/forum/ - Project repo: https://github.com/yourusername/Linux-OS diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md index d9e2f96a..0260eb36 100644 --- a/docs/PERFORMANCE.md +++ b/docs/PERFORMANCE.md @@ -131,14 +131,14 @@ fi ### Command Costs (Relative) -| Operation | Cost | Alternative | Cost | -|-----------|------|-------------|------| -| `$(command)` | 100x | `${var//pattern/}` | 1x | -| `tr` | 50x | `${var,,}` | 1x | -| `basename` | 30x | `${var##*/}` | 1x | -| `dirname` | 30x | `${var%/*}` | 1x | -| `cat file` | 20x | `$( @@ -435,37 +435,37 @@ extract ~/projects/pure-bash/README.md '```bash' '```' ### Indirection -| Parameter | What does it do? | +| Parameter | What does it do? | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| `${!VAR}` | Access a variable based on the value of `VAR`. | -| `${!VAR*}` | Expand to `IFS` separated list of variable names starting with `VAR`. | +| `${!VAR}` | Access a variable based on the value of `VAR`. | +| `${!VAR*}` | Expand to `IFS` separated list of variable names starting with `VAR`. | | `${!VAR@}` | Expand to `IFS` separated list of variable names starting with `VAR`. If double-quoted, each variable name expands to a separate word. | ### Replacement -| Parameter | What does it do? | +| Parameter | What does it do? | | ------------------------- | ------------------------------------------------------ | -| `${VAR#PATTERN}` | Remove shortest match of pattern from start of string. | -| `${VAR##PATTERN}` | Remove longest match of pattern from start of string. | -| `${VAR%PATTERN}` | Remove shortest match of pattern from end of string. | -| `${VAR%%PATTERN}` | Remove longest match of pattern from end of string. | -| `${VAR/PATTERN/REPLACE}` | Replace first match with string. | -| `${VAR//PATTERN/REPLACE}` | Replace all matches with string. | -| `${VAR/PATTERN}` | Remove first match. | -| `${VAR//PATTERN}` | Remove all matches. | +| `${VAR#PATTERN}` | Remove shortest match of pattern from start of string. | +| `${VAR##PATTERN}` | Remove longest match of pattern from start of string. | +| `${VAR%PATTERN}` | Remove shortest match of pattern from end of string. | +| `${VAR%%PATTERN}` | Remove longest match of pattern from end of string. | +| `${VAR/PATTERN/REPLACE}` | Replace first match with string. | +| `${VAR//PATTERN/REPLACE}` | Replace all matches with string. | +| `${VAR/PATTERN}` | Remove first match. | +| `${VAR//PATTERN}` | Remove all matches. | ### Length -| Parameter | What does it do? | +| Parameter | What does it do? | | ------------ | ---------------------------- | -| `${#VAR}` | Length of var in characters. | +| `${#VAR}` | Length of var in characters. | | `${#ARR[@]}` | Length of array in elements. | ### Expansion -| Parameter | What does it do? | -| ----------------------- | -------------------------------------------------------------------------------------------------------------------- | -| `${VAR:OFFSET}` | Remove first `N` chars from variable. | +| Parameter | What does it do? | +| ---------------------- | -------------------------------------------------- | +| `${VAR:OFFSET}` | Remove first `N` chars from variable. | | `${VAR:OFFSET:LENGTH}` | Get substring from `N` character to `N` character. |
@@ -482,27 +482,27 @@ extract ~/projects/pure-bash/README.md '```bash' '```' ### Case Modification -| Parameter | What does it do? | CAVEAT | +| Parameter | What does it do? | CAVEAT | | ---------- | -------------------------------- | --------- | -| `${VAR^}` | Uppercase first character. | `bash 4+` | -| `${VAR^^}` | Uppercase all characters. | `bash 4+` | -| `${VAR,}` | Lowercase first character. | `bash 4+` | -| `${VAR,,}` | Lowercase all characters. | `bash 4+` | -| `${VAR~}` | Reverse case of first character. | `bash 4+` | -| `${VAR~~}` | Reverse case of all characters. | `bash 4+` | +| `${VAR^}` | Uppercase first character. | `bash 4+` | +| `${VAR^^}` | Uppercase all characters. | `bash 4+` | +| `${VAR,}` | Lowercase first character. | `bash 4+` | +| `${VAR,,}` | Lowercase all characters. | `bash 4+` | +| `${VAR~}` | Reverse case of first character. | `bash 4+` | +| `${VAR~~}` | Reverse case of all characters. | `bash 4+` | ### Default Value -| Parameter | What does it do? | +| Parameter | What does it do? | | ---------------- | --------------------------------------------------------------- | -| `${VAR:-STRING}` | If `VAR` is empty or unset, use `STRING` as its value. | -| `${VAR-STRING}` | If `VAR` is unset, use `STRING` as its value. | +| `${VAR:-STRING}` | If `VAR` is empty or unset, use `STRING` as its value. | +| `${VAR-STRING}` | If `VAR` is unset, use `STRING` as its value. | | `${VAR:=STRING}` | If `VAR` is empty or unset, set the value of `VAR` to `STRING`. | -| `${VAR=STRING}` | If `VAR` is unset, set the value of `VAR` to `STRING`. | -| `${VAR:+STRING}` | If `VAR` is not empty, use `STRING` as its value. | -| `${VAR+STRING}` | If `VAR` is set, use `STRING` as its value. | -| `${VAR:?STRING}` | Display an error if empty or unset. | -| `${VAR?STRING}` | Display an error if unset. | +| `${VAR=STRING}` | If `VAR` is unset, set the value of `VAR` to `STRING`. | +| `${VAR:+STRING}` | If `VAR` is not empty, use `STRING` as its value. | +| `${VAR+STRING}` | If `VAR` is set, use `STRING` as its value. | +| `${VAR:?STRING}` | Display an error if empty or unset. | +| `${VAR?STRING}` | Display an error if unset. | ### BRACE EXPANSION diff --git a/docs/USEFUL.MD b/docs/USEFUL.MD index 9de0e8f7..02376dd4 100644 --- a/docs/USEFUL.MD +++ b/docs/USEFUL.MD @@ -11,14 +11,14 @@ ``` -| Speculative loading features | Purpose | When to use | +| Speculative loading features | Purpose | When to use | | :--------------------------- | :------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `` | Cross-origin connection warming | Use on your most critical cross-origin connections to provide performance improvements when connecting to them. | -| `` | Cross-origin connection warming | Use on all of your cross-origin connections to provide small performance improvements when connecting to them. | -| `` | High-priority loading of current page subresources | Use to load high-priority resources faster on the current page, for strategic performance improvements. Don't preload everything, otherwise you won't see the benefit. Also has some other interesting uses — see Preload: What Is It Good For? on Smashing Magazine (2016) | -| `` | High-priority loading of current page JavaScript modules | Use to preload high-priority JavaScript modules for strategic performance improvements. | -| `` | Pre-populating the HTTP cache | Use to prefetch same-site future navigation resources or subresources used on those pages. Uses HTTP cache therefore has a number of issues with document prefetches, such as being potentially blocked by Cache-Control headers. Use the Speculation Rules API for document prefetches instead, where it is supported. | -| `` | Preparing for the next navigation | Deprecated; you are advised not to use this. Use Speculation Rules API prerender instead, where it is supported. | +| `` | Cross-origin connection warming | Use on your most critical cross-origin connections to provide performance improvements when connecting to them. | +| `` | Cross-origin connection warming | Use on all of your cross-origin connections to provide small performance improvements when connecting to them. | +| `` | High-priority loading of current page subresources | Use to load high-priority resources faster on the current page, for strategic performance improvements. Don't preload everything, otherwise you won't see the benefit. Also has some other interesting uses — see Preload: What Is It Good For? on Smashing Magazine (2016) | +| `` | High-priority loading of current page JavaScript modules | Use to preload high-priority JavaScript modules for strategic performance improvements. | +| `` | Pre-populating the HTTP cache | Use to prefetch same-site future navigation resources or subresources used on those pages. Uses HTTP cache therefore has a number of issues with document prefetches, such as being potentially blocked by Cache-Control headers. Use the Speculation Rules API for document prefetches instead, where it is supported. | +| `` | Preparing for the next navigation | Deprecated; you are advised not to use this. Use Speculation Rules API prerender instead, where it is supported. | ## Markdown @@ -63,6 +63,6 @@ ffmpeg -i input.mp4 -c:v libsvtav1 \ - Fast preset: 4-6 - 1080p: `tile-columns=1:tile-rows=0` - 4k: `tile-columns=2:tile-rows=0` -- Sharpness: `sharpness=-2` up to `sharpness=2` +- Sharpness: `sharpness=-2` up to `sharpness=2` - [Auto superres](https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Appendix-Super-Resolution.md): `superres-mode=4` diff --git a/lint-format.sh b/lint-format.sh new file mode 100755 index 00000000..d7271efa --- /dev/null +++ b/lint-format.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +shopt -s nullglob globstar extglob dotglob +IFS=$'\n\t' +export LC_ALL=C LANG=C + +# Colors +LBLU=$'\e[38;5;117m' PNK=$'\e[38;5;218m' DEF=$'\e[0m' +has() { command -v "$1" &>/dev/null; } +log() { printf '%b%s%b\n' "${LBLU}" "$*" "${DEF}"; } + +CHECK=0 +[[ "${1:-}" == "-c" || "${1:-}" == "--check" ]] && CHECK=1 + +FD=${FD:-""} +if [[ -z "$FD" ]]; then + if has fdfind; then FD="fdfind"; elif has fd; then FD="fd"; fi +fi + +# Shell scripts +if has shellcheck || has shfmt; then + log "🐚 Linting and formatting Shell scripts..." + # Use shfmt -i 2 -bn -ci -s -ln bash as per AGENTS.md + # Use shellcheck --severity=style for zero warnings as per AGENTS.md + if [[ -n "$FD" ]]; then + if has shfmt; then + OPTS=(-i 2 -bn -ci -s -ln bash) + (( CHECK )) && OPTS+=(-d) || OPTS+=(-w) + "$FD" -t f -e sh --exclude "WIP" --exclude ".github/agents" -x shfmt "${OPTS[@]}" + fi + has shellcheck && "$FD" -t f -e sh --exclude "WIP" --exclude ".github/agents" -x shellcheck --severity=style + else + while IFS= read -r f; do + [[ $f == *"WIP"* || $f == *".github/agents"* ]] && continue + has shfmt && ( [[ $CHECK -eq 0 ]] && shfmt -i 2 -bn -ci -s -ln bash -w "$f" || shfmt -i 2 -bn -ci -s -ln bash -d "$f" ) + has shellcheck && shellcheck --severity=style "$f" + done < <(find . -type f -name "*.sh") + fi +fi + +# Python +if has ruff; then + log "🐍 Linting and formatting Python..." + R_EXCLUDE="Cachyos/Scripts/WIP,.github/agents" + if [[ $CHECK -eq 1 ]]; then + ruff check . --exclude "$R_EXCLUDE" + ruff format --check . --exclude "$R_EXCLUDE" + else + ruff check --fix . --exclude "$R_EXCLUDE" + ruff format . --exclude "$R_EXCLUDE" + fi +fi + +# Prettier (General) +if has prettier; then + log "🎨 Formatting with Prettier..." + P_OPTS=$([[ $CHECK -eq 1 ]] && echo "--check" || echo "--write") + if [[ -n "$FD" ]]; then + "$FD" -t f -e md -e json -e yml -e yaml --exclude "WIP" --exclude ".github/agents" -x prettier "$P_OPTS" + else + prettier "$P_OPTS" --ignore-path .gitignore . + fi +fi + +# YAML +if has yamllint; then + log "🔍 Linting YAML..." + yamllint . +fi + +# Actionlint +if has actionlint; then + log "🤖 Linting GitHub Actions..." + actionlint +fi + +log "✅ Done!"