Add README, runbook, per-script docs, CHANGELOG and LICENSE.
Documents the organise-isos and generate-ventoy-json workflow end-to-end: quick start in the README, full procedure with troubleshooting and recovery in docs/RUNBOOK.md, and reference docs for each script covering parameters, parsing rules, category groupings, and extensibility. Ships a YOLO licence (no warranty) and seeds CHANGELOG.md for the v0.1.0 release.
This commit is contained in:
parent
0c06a1483c
commit
74b210a702
7 changed files with 718 additions and 0 deletions
28
CHANGELOG.md
Normal file
28
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project are documented in this file.
|
||||||
|
|
||||||
|
The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||||
|
|
||||||
|
## [0.1.0] - 2026-05-21
|
||||||
|
|
||||||
|
First tagged release. Bundles the existing scripts with a documentation set.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `organise-isos.ps1` — filename-driven sorter that moves ISOs into
|
||||||
|
`iso-library/<os>/<version>/<arch>/`. Covers Windows, RHEL family, Fedora
|
||||||
|
family, SUSE, Debian/Ubuntu and derivatives, Arch family, Gentoo, Slackware,
|
||||||
|
independent Linux distros, hypervisors, BSDs, Solaris/illumos, exotic OSes,
|
||||||
|
and rescue tools.
|
||||||
|
- `generate-ventoy-json.ps1` — generates a categorised `ventoy.json` menu
|
||||||
|
from the sorted tree, with curated display names and per-family submenus.
|
||||||
|
- `README.md` — project overview and quick start.
|
||||||
|
- `docs/RUNBOOK.md` — end-to-end build/refresh procedure with troubleshooting
|
||||||
|
and recovery sections.
|
||||||
|
- `docs/organise-isos.md` — parameter reference, parsing rules, extensibility
|
||||||
|
guide.
|
||||||
|
- `docs/generate-ventoy-json.md` — parameter reference, category groupings,
|
||||||
|
menu output shape.
|
||||||
|
- `docs/directory-layout.md` — on-disk structure for the local working copy
|
||||||
|
and the Ventoy USB.
|
||||||
|
- `LICENSE` — YOLO licence, no warranty.
|
||||||
18
LICENSE
Normal file
18
LICENSE
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
YOLO LICENSE
|
||||||
|
============
|
||||||
|
|
||||||
|
Copyright (c) 2026 Laurence Horrocks-Barlow
|
||||||
|
|
||||||
|
Do whatever you want with this software. Use it, fork it, sell it, set it on
|
||||||
|
fire, print it out and use it as wallpaper. No attribution required, no
|
||||||
|
restrictions, no obligations.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
If things blow up: you keep both pieces.
|
||||||
72
README.md
Normal file
72
README.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# ventoy-extras
|
||||||
|
|
||||||
|
PowerShell helpers for keeping a tidy [Ventoy](https://www.ventoy.net/) USB
|
||||||
|
drive. Drops a pile of randomly-named ISOs into a sensible directory tree, then
|
||||||
|
generates a categorised `ventoy.json` so the boot menu groups them by OS family
|
||||||
|
instead of vomiting one flat list.
|
||||||
|
|
||||||
|
Two scripts, no dependencies beyond Windows PowerShell 5.1 (or PowerShell 7+).
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
| Script | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| [`organise-isos.ps1`](organise-isos.ps1) | Parse ISO filenames, infer `os/version/arch`, move into `iso-library\<os>\<version>\<arch>\` |
|
||||||
|
| [`generate-ventoy-json.ps1`](generate-ventoy-json.ps1) | Walk that tree and emit `ventoy.json` with a per-family submenu structure |
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Drop all your downloaded ISOs into a working directory, then:
|
||||||
|
.\organise-isos.ps1 -SourceDir .\downloads -DestDir .\iso-library -DryRun
|
||||||
|
# Review the planned moves, then re-run without -DryRun
|
||||||
|
.\organise-isos.ps1 -SourceDir .\downloads -DestDir .\iso-library
|
||||||
|
|
||||||
|
# 2. Generate the ventoy menu config:
|
||||||
|
.\generate-ventoy-json.ps1 -IsoRoot .\iso-library -OutFile .\ventoy.json
|
||||||
|
|
||||||
|
# 3. Copy iso-library\ to the root of your Ventoy USB, and copy
|
||||||
|
# ventoy.json into the ventoy\ folder on that same drive.
|
||||||
|
```
|
||||||
|
|
||||||
|
That's the whole workflow. See [`docs/RUNBOOK.md`](docs/RUNBOOK.md) for the
|
||||||
|
detailed operational walkthrough, and the per-script docs for the gory bits.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [`docs/RUNBOOK.md`](docs/RUNBOOK.md) — end-to-end procedure, troubleshooting, recovery
|
||||||
|
- [`docs/organise-isos.md`](docs/organise-isos.md) — parameters, naming rules, OS detection map
|
||||||
|
- [`docs/generate-ventoy-json.md`](docs/generate-ventoy-json.md) — parameters, category groupings, menu layout
|
||||||
|
- [`docs/directory-layout.md`](docs/directory-layout.md) — what the on-disk tree looks like and why
|
||||||
|
|
||||||
|
## Directory layout produced
|
||||||
|
|
||||||
|
```
|
||||||
|
iso-library\
|
||||||
|
ubuntu\
|
||||||
|
24.04\
|
||||||
|
x86_64\
|
||||||
|
ubuntu-24.04.1-desktop-amd64.iso
|
||||||
|
debian\
|
||||||
|
12\
|
||||||
|
x86_64\
|
||||||
|
debian-12.5.0-amd64-netinst.iso
|
||||||
|
rocky\
|
||||||
|
9\
|
||||||
|
aarch64\
|
||||||
|
Rocky-9.3-aarch64-minimal.iso
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Three levels: OS slug, version, architecture. Ventoy follows directories
|
||||||
|
transparently, so the same tree is what ends up on the USB.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Windows PowerShell 5.1 or PowerShell 7+
|
||||||
|
- A Ventoy USB drive (set up separately — see [ventoy.net](https://www.ventoy.net/))
|
||||||
|
- Enough disk space to hold your ISO collection twice during reorganisation
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[YOLO LICENSE](LICENSE) — do whatever you want with it, no warranty, things may blow up.
|
||||||
223
docs/RUNBOOK.md
Normal file
223
docs/RUNBOOK.md
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
# Runbook: Building and refreshing a Ventoy USB
|
||||||
|
|
||||||
|
End-to-end procedure for taking a pile of downloaded ISOs and turning them into
|
||||||
|
a categorised Ventoy boot stick. Designed to be safe to run repeatedly — adding
|
||||||
|
new ISOs later is the same flow.
|
||||||
|
|
||||||
|
## Audience and assumptions
|
||||||
|
|
||||||
|
- You already have a Ventoy-formatted USB drive (Ventoy installed via
|
||||||
|
`Ventoy2Disk` or similar). If not, do that first at
|
||||||
|
[ventoy.net](https://www.ventoy.net/) — these scripts only manage the
|
||||||
|
payload, not the bootloader.
|
||||||
|
- You're on Windows with PowerShell 5.1+ or PowerShell 7+.
|
||||||
|
- You have all the ISOs you want to deploy in a single staging directory.
|
||||||
|
|
||||||
|
## Drive layout this runbook produces
|
||||||
|
|
||||||
|
Two locations on the Ventoy USB matter:
|
||||||
|
|
||||||
|
| Path on USB | What it holds |
|
||||||
|
| --- | --- |
|
||||||
|
| `\iso-library\<os>\<version>\<arch>\*.iso` | The actual ISO payload |
|
||||||
|
| `\ventoy\ventoy.json` | The menu definition Ventoy reads at boot |
|
||||||
|
|
||||||
|
Ventoy itself only requires that ISOs exist somewhere on the data partition.
|
||||||
|
The `iso-library\` tree and the `ventoy.json` menu definition are layered on
|
||||||
|
top to give a navigable boot menu.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Procedure
|
||||||
|
|
||||||
|
### Step 1 — Stage the ISOs
|
||||||
|
|
||||||
|
Put every ISO you want into one directory. Filenames don't need to be
|
||||||
|
"clean"; the parser handles vendor-supplied names like
|
||||||
|
`Win11_24H2_English_x64.iso`, `ubuntu-24.04.1-desktop-amd64.iso`,
|
||||||
|
`Rocky-9.3-x86_64-minimal.iso`, etc.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Example staging area:
|
||||||
|
dir D:\iso-staging\
|
||||||
|
# Win11_24H2_English_x64.iso
|
||||||
|
# ubuntu-24.04.1-desktop-amd64.iso
|
||||||
|
# debian-12.5.0-amd64-netinst.iso
|
||||||
|
# Rocky-9.3-x86_64-minimal.iso
|
||||||
|
# clonezilla-live-3.1.2-22-amd64.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2 — Dry-run the organiser
|
||||||
|
|
||||||
|
Always start with `-DryRun` to confirm the parser inferred the right
|
||||||
|
`os/version/arch` for each file. Nothing is moved.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\Projects\own\forgejo\ventoy-extras
|
||||||
|
.\organise-isos.ps1 -SourceDir D:\iso-staging -DestDir D:\iso-library -DryRun
|
||||||
|
```
|
||||||
|
|
||||||
|
Output shows the planned target for every ISO. Look for anything that
|
||||||
|
parsed as `unknown` or landed in a surprising slot — those are the cases
|
||||||
|
to triage before committing to a move.
|
||||||
|
|
||||||
|
### Step 3 — Run the organiser for real
|
||||||
|
|
||||||
|
Once the dry-run looks right:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\organise-isos.ps1 -SourceDir D:\iso-staging -DestDir D:\iso-library
|
||||||
|
```
|
||||||
|
|
||||||
|
This **moves** (not copies) ISOs into `iso-library\<os>\<version>\<arch>\`.
|
||||||
|
Source directory will be left empty (modulo non-ISO files). If you want
|
||||||
|
copies kept, copy the staging directory aside first.
|
||||||
|
|
||||||
|
### Step 4 — Generate `ventoy.json`
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\generate-ventoy-json.ps1 -IsoRoot D:\iso-library -OutFile D:\ventoy.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The script prints the menu tree it built. Skim it to confirm sensible
|
||||||
|
category groupings and labels.
|
||||||
|
|
||||||
|
### Step 5 — Deploy to the USB
|
||||||
|
|
||||||
|
Plug in the Ventoy USB and note its drive letter (assume `E:` below).
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Mirror the iso-library tree onto the stick. Robocopy is safest because
|
||||||
|
# it handles long paths and resumes gracefully.
|
||||||
|
robocopy D:\iso-library E:\iso-library /MIR /R:1 /W:1
|
||||||
|
|
||||||
|
# Drop the menu file into the ventoy\ folder on the stick.
|
||||||
|
Copy-Item D:\ventoy.json E:\ventoy\ventoy.json -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
`E:\ventoy\` should already exist — Ventoy creates it when it installs the
|
||||||
|
bootloader. If it doesn't, the Ventoy install is incomplete.
|
||||||
|
|
||||||
|
### Step 6 — Verify the boot menu
|
||||||
|
|
||||||
|
Boot a machine (or a VM with USB passthrough) from the stick. The Ventoy
|
||||||
|
menu should show category submenus rather than one flat list. If categories
|
||||||
|
are missing, see [Troubleshooting](#troubleshooting).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding ISOs later
|
||||||
|
|
||||||
|
You don't have to redo the whole flow. To add a single new ISO:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Option A — let the organiser handle it
|
||||||
|
.\organise-isos.ps1 -SourceDir C:\downloads -DestDir D:\iso-library
|
||||||
|
.\generate-ventoy-json.ps1 -IsoRoot D:\iso-library -OutFile D:\ventoy.json
|
||||||
|
robocopy D:\iso-library E:\iso-library /MIR /R:1 /W:1
|
||||||
|
Copy-Item D:\ventoy.json E:\ventoy\ventoy.json -Force
|
||||||
|
|
||||||
|
# Option B — manually drop the ISO into the right tree, then just regenerate
|
||||||
|
Copy-Item C:\downloads\new.iso D:\iso-library\foo\1.2\x86_64\
|
||||||
|
.\generate-ventoy-json.ps1 -IsoRoot D:\iso-library -OutFile D:\ventoy.json
|
||||||
|
Copy-Item D:\ventoy.json E:\ventoy\ventoy.json -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
`robocopy /MIR` will also **remove** ISOs from the stick that no longer exist
|
||||||
|
in the local library — useful for retiring old versions, dangerous if your
|
||||||
|
local copy is incomplete. Skip `/MIR` and use `/E` instead if you only want
|
||||||
|
additive sync.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### An ISO landed in `unknown\unknown\noarch\`
|
||||||
|
|
||||||
|
The filename didn't match any pattern in the OS map. Fix one of:
|
||||||
|
|
||||||
|
1. Rename the ISO to start with a recognised slug
|
||||||
|
(e.g. `myweirdspin-2024.iso` becomes `arch-2024-myweirdspin.iso`).
|
||||||
|
2. Add a pattern to `$osNormMap` in
|
||||||
|
[`organise-isos.ps1`](../organise-isos.ps1) — the regex matches against
|
||||||
|
the lowercased first token of the cleaned filename.
|
||||||
|
|
||||||
|
After fixing, move the file back to staging and re-run.
|
||||||
|
|
||||||
|
### Version parsed as `unknown`
|
||||||
|
|
||||||
|
Filename didn't contain a recognisable version. Either rename the file to
|
||||||
|
include a `\d+\.\d+` style version, or move it manually into the right
|
||||||
|
`<version>` folder and regenerate.
|
||||||
|
|
||||||
|
### Wrong architecture detected
|
||||||
|
|
||||||
|
`organise-isos.ps1` checks arch tokens in priority order (see
|
||||||
|
[`docs/organise-isos.md`](organise-isos.md)). If a token in the filename
|
||||||
|
collides (e.g. a date that looks like an arch), rename the file or move
|
||||||
|
it manually. Common gotcha: macOS-style x64 in the version string.
|
||||||
|
|
||||||
|
### Ventoy boots but shows the old flat list
|
||||||
|
|
||||||
|
`ventoy.json` is in the wrong place. It must be at the **root** of the
|
||||||
|
Ventoy data partition's `ventoy\` folder, exactly: `\ventoy\ventoy.json`.
|
||||||
|
Not `\ventoy\config\`, not anywhere else.
|
||||||
|
|
||||||
|
### Ventoy reports JSON parse error on boot
|
||||||
|
|
||||||
|
Open `ventoy.json` in any editor and confirm it's valid JSON. The
|
||||||
|
generator writes UTF-8 with no BOM, which Ventoy accepts. If you've
|
||||||
|
hand-edited it, double-check for trailing commas (not valid JSON) or
|
||||||
|
unbalanced brackets.
|
||||||
|
|
||||||
|
### A category I want is missing
|
||||||
|
|
||||||
|
Add the slug to `$categoryMap` in
|
||||||
|
[`generate-ventoy-json.ps1`](../generate-ventoy-json.ps1), then re-run.
|
||||||
|
Unmapped slugs land in `Other`.
|
||||||
|
|
||||||
|
### `Access denied` when moving ISOs
|
||||||
|
|
||||||
|
The source ISO is probably open in another process (mount, antivirus
|
||||||
|
scan, browser download still finalising). Close it and retry. If on a
|
||||||
|
removable drive, eject and reattach.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recovery
|
||||||
|
|
||||||
|
### "I ran organise-isos and now my ISOs are scattered, how do I get them back?"
|
||||||
|
|
||||||
|
The organiser only moves files into `<DestDir>\<os>\<version>\<arch>\`. To
|
||||||
|
collapse everything back into one directory:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ChildItem -Path D:\iso-library -Recurse -Filter *.iso |
|
||||||
|
Move-Item -Destination D:\iso-staging
|
||||||
|
```
|
||||||
|
|
||||||
|
Then delete the empty tree:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ChildItem -Path D:\iso-library -Recurse -Directory |
|
||||||
|
Where-Object { -not (Get-ChildItem $_.FullName -Recurse -File) } |
|
||||||
|
Remove-Item -Recurse
|
||||||
|
```
|
||||||
|
|
||||||
|
### "My USB is borked"
|
||||||
|
|
||||||
|
These scripts don't touch the Ventoy bootloader — only the data partition
|
||||||
|
content. To recover the bootloader, reinstall Ventoy with `Ventoy2Disk.exe`
|
||||||
|
using the **upgrade** option (preserves the data partition) or the
|
||||||
|
**install** option (wipes it). If you only modified `ventoy.json` and the
|
||||||
|
boot menu now errors, just delete `\ventoy\ventoy.json` from the stick;
|
||||||
|
Ventoy falls back to its default flat menu.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Smoke test before deploying
|
||||||
|
|
||||||
|
Want to confirm the menu before copying to a real USB? Mount the
|
||||||
|
`iso-library` and `ventoy.json` into a VM with Ventoy installed, or use
|
||||||
|
[VBoxVentoy](https://www.ventoy.net/en/doc_vbox.html) to boot from a
|
||||||
|
VirtualBox USB device. Saves wear on physical sticks.
|
||||||
96
docs/directory-layout.md
Normal file
96
docs/directory-layout.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# Directory layout
|
||||||
|
|
||||||
|
A description of the on-disk shape these scripts work with — both the local
|
||||||
|
working copy and what ends up on the Ventoy USB.
|
||||||
|
|
||||||
|
## Local working copy
|
||||||
|
|
||||||
|
```
|
||||||
|
ventoy-extras/ # this repo
|
||||||
|
├─ organise-isos.ps1
|
||||||
|
├─ generate-ventoy-json.ps1
|
||||||
|
├─ ventoy.json # generated, gitignored — your menu
|
||||||
|
├─ iso-library/ # generated, gitignored — your ISOs
|
||||||
|
│ ├─ ubuntu/
|
||||||
|
│ │ └─ 24.04/
|
||||||
|
│ │ └─ x86_64/
|
||||||
|
│ │ └─ ubuntu-24.04.1-desktop-amd64.iso
|
||||||
|
│ ├─ debian/
|
||||||
|
│ │ └─ 12/
|
||||||
|
│ │ └─ x86_64/
|
||||||
|
│ │ └─ debian-12.5.0-amd64-netinst.iso
|
||||||
|
│ ├─ rocky/
|
||||||
|
│ │ └─ 9.3/
|
||||||
|
│ │ ├─ x86_64/
|
||||||
|
│ │ │ └─ Rocky-9.3-x86_64-minimal.iso
|
||||||
|
│ │ └─ aarch64/
|
||||||
|
│ │ └─ Rocky-9.3-aarch64-minimal.iso
|
||||||
|
│ ├─ proxmox-ve/
|
||||||
|
│ │ └─ 8.2/
|
||||||
|
│ │ └─ noarch/
|
||||||
|
│ │ └─ proxmox-ve_8.2-1.iso
|
||||||
|
│ └─ ...
|
||||||
|
└─ docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
You can keep `iso-library/` and `ventoy.json` anywhere — pass `-IsoRoot`
|
||||||
|
and `-OutFile` to point at them. Keeping them outside the repo is the
|
||||||
|
sensible default for non-trivial ISO collections.
|
||||||
|
|
||||||
|
## On the Ventoy USB
|
||||||
|
|
||||||
|
```
|
||||||
|
E:\ # Ventoy data partition root
|
||||||
|
├─ ventoy\
|
||||||
|
│ ├─ ventoy.json # copied from the generated file
|
||||||
|
│ ├─ theme\ # optional, not managed by these scripts
|
||||||
|
│ └─ ... # other Ventoy config
|
||||||
|
├─ iso-library\
|
||||||
|
│ ├─ ubuntu\
|
||||||
|
│ │ └─ 24.04\
|
||||||
|
│ │ └─ x86_64\
|
||||||
|
│ │ └─ ubuntu-24.04.1-desktop-amd64.iso
|
||||||
|
│ └─ ...
|
||||||
|
└─ (anything else you want on the stick)
|
||||||
|
```
|
||||||
|
|
||||||
|
Ventoy treats the data partition as a regular FAT/exFAT/NTFS filesystem.
|
||||||
|
The bootloader lives on a separate hidden ESP partition that these scripts
|
||||||
|
do not touch.
|
||||||
|
|
||||||
|
## Why three levels (`os/version/arch`)
|
||||||
|
|
||||||
|
- **`os/`** — keeps the same OS together regardless of version churn. Easier
|
||||||
|
to clean up when retiring an EOL distro.
|
||||||
|
- **`version/`** — lets you have multiple versions of the same OS without
|
||||||
|
filename collisions, and gives Ventoy a clean per-version label.
|
||||||
|
- **`arch/`** — multi-arch ISOs (typically x86_64 + aarch64 for RHEL family,
|
||||||
|
Debian, Ubuntu) sort cleanly side by side.
|
||||||
|
|
||||||
|
When arch isn't applicable (hypervisor appliances, BSD distros that ship
|
||||||
|
multi-arch in one image), the slot becomes `noarch\` and the generator omits
|
||||||
|
the `[<arch>]` suffix from menu labels.
|
||||||
|
|
||||||
|
## Path constraints
|
||||||
|
|
||||||
|
- **Forward vs backslash**: the local Windows paths use `\`. `ventoy.json`
|
||||||
|
paths use `/` (Ventoy is essentially Linux-bootstrapped and expects POSIX
|
||||||
|
paths). The generator handles the translation.
|
||||||
|
- **Leading slash**: `ventoy.json` paths start with `/` and are rooted at
|
||||||
|
the **Ventoy data partition root**, not the filesystem root.
|
||||||
|
- **Long paths**: deeply-nested distros (`opensuse-tumbleweed/...`) plus
|
||||||
|
long ISO filenames can exceed 260 chars on Windows. PowerShell 7+ handles
|
||||||
|
this transparently; on 5.1 you may need `\\?\` prefixes if you hit it.
|
||||||
|
Robocopy handles long paths regardless of PS version.
|
||||||
|
|
||||||
|
## Gitignore recommendation
|
||||||
|
|
||||||
|
A `.gitignore` for this repo (not yet committed):
|
||||||
|
|
||||||
|
```
|
||||||
|
iso-library/
|
||||||
|
ventoy.json
|
||||||
|
*.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
ISOs are large and frequently rotated. Don't commit them.
|
||||||
146
docs/generate-ventoy-json.md
Normal file
146
docs/generate-ventoy-json.md
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
# `generate-ventoy-json.ps1`
|
||||||
|
|
||||||
|
Walks an `iso-library` directory tree and emits a `ventoy.json` with
|
||||||
|
per-category submenus. Output is UTF-8, no BOM, ready to drop into
|
||||||
|
`\ventoy\ventoy.json` on the Ventoy USB.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `-IsoRoot` | `.\iso-library` | Root of the ISO tree to scan (recursively, for `*.iso`). |
|
||||||
|
| `-OutFile` | `.\ventoy.json` | Where to write the generated JSON. |
|
||||||
|
| `-VentoyDrive` | `E` | Cosmetic — currently unused at runtime; kept for forward compat. |
|
||||||
|
|
||||||
|
## Expected input tree
|
||||||
|
|
||||||
|
```
|
||||||
|
<IsoRoot>\
|
||||||
|
<os-slug>\
|
||||||
|
<version>\
|
||||||
|
<arch>\
|
||||||
|
*.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
This is exactly what [`organise-isos.ps1`](organise-isos.md) produces.
|
||||||
|
Files that don't fit this layout still get included, but with a best-effort
|
||||||
|
fallback:
|
||||||
|
|
||||||
|
- 3 path segments deep → arch defaults to `noarch`.
|
||||||
|
- < 3 segments → `os` and `version` both default to `unknown`.
|
||||||
|
|
||||||
|
## Output structure
|
||||||
|
|
||||||
|
Each top-level entry in `ventoy.json` is a category submenu, containing one
|
||||||
|
entry per ISO:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"menu_alias": [
|
||||||
|
{
|
||||||
|
"name": "Ubuntu Family",
|
||||||
|
"image": [
|
||||||
|
{ "image": "/ubuntu/24.04/x86_64/ubuntu-24.04.1-desktop-amd64.iso",
|
||||||
|
"menu_alias": "Ubuntu 24.04 [x86_64]" },
|
||||||
|
{ "image": "/lubuntu/24.04/x86_64/lubuntu-24.04-desktop-amd64.iso",
|
||||||
|
"menu_alias": "Lubuntu 24.04 [x86_64]" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Rescue & Tools",
|
||||||
|
"image": [
|
||||||
|
{ "image": "/clonezilla/3.1/x86_64/clonezilla-live-3.1.2-22-amd64.iso",
|
||||||
|
"menu_alias": "Clonezilla 3.1 [x86_64]" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Menu labels
|
||||||
|
|
||||||
|
For each ISO, the label is built as:
|
||||||
|
|
||||||
|
```
|
||||||
|
<display name> <version>[ [<arch>]]
|
||||||
|
```
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
- `<display name>` is looked up in `$displayNames` (e.g. `ubuntu` → `Ubuntu`,
|
||||||
|
`proxmox-ve` → `Proxmox VE`). Unmapped slugs are title-cased as a fallback.
|
||||||
|
- The arch suffix `[<arch>]` is omitted when arch is `noarch`.
|
||||||
|
|
||||||
|
Examples: `Ubuntu 24.04 [x86_64]`, `Proxmox VE 8.2`, `Memtest86+ 6.20 [x86_64]`.
|
||||||
|
|
||||||
|
## Category groupings
|
||||||
|
|
||||||
|
Slugs are mapped to category names via `$categoryMap`:
|
||||||
|
|
||||||
|
| Category | Slugs included |
|
||||||
|
| --- | --- |
|
||||||
|
| Windows | `windows`, `windows-server`, `ms-dos`, `reactos` |
|
||||||
|
| RHEL Family | `rhel`, `centos`, `rocky`, `almalinux`, `oraclelinux`, `scientific`, `eurolinux`, `navylinux` |
|
||||||
|
| Fedora Family | `fedora`, `qubes`, `coreos` |
|
||||||
|
| SUSE Family | `opensuse*`, `sles`, `geckolinux` |
|
||||||
|
| Debian Family | `debian`, `raspios`, `armbian`, `devuan`, `mx`, `antix`, `deepin`, `pureos`, `grml`, `lmde` |
|
||||||
|
| Ubuntu Family | `ubuntu*`, `lubuntu`, `kubuntu`, `xubuntu`, `linuxmint`, `pop_os`, `elementary`, `zorin` |
|
||||||
|
| Arch Family | `arch`, `manjaro`, `endeavouros`, `garuda`, `artix`, `cachyos`, `arcolinux` |
|
||||||
|
| Gentoo Family | `gentoo`, `funtoo`, `calculate` |
|
||||||
|
| Slackware Family | `slackware`, `salix`, `porteus` |
|
||||||
|
| Independent Linux | `void`, `nixos`, `alpine`, `solus`, `clearlinux`, `flatcar` |
|
||||||
|
| Security | `kali`, `parrot`, `backbox`, `tails`, `whonix`, `blackarch` |
|
||||||
|
| Network & Firewall | `pfsense`, `opnsense`, `ipfire`, `vyos`, `openwrt`, `ddwrt` |
|
||||||
|
| Virtualisation | `proxmox-*`, `esxi`, `vsphere`, `xcp-ng`, `xenserver`, `harvester` |
|
||||||
|
| BSD Family | `freebsd`, `openbsd`, `netbsd`, `dragonflybsd`, `truenas*`, `ghostbsd` |
|
||||||
|
| Solaris & illumos | `openindiana`, `omnios`, `smartos`, `solaris` |
|
||||||
|
| Media | `libreelec`, `osmc` |
|
||||||
|
| Exotic | `haiku`, `freedos`, `reactos` |
|
||||||
|
| Rescue & Tools | `clonezilla`, `systemrescue`, `gparted`, `memtest`, `memtest86`, `rescuezilla`, `hirens`, `winpe`, `dban`, `ubcd`, vendor rescue disks |
|
||||||
|
| Other | anything unmapped — sorted alphabetically at the end |
|
||||||
|
|
||||||
|
Matching is exact-first, then prefix. So `opensuse-leap` matches the exact
|
||||||
|
`opensuse-leap` entry if present, otherwise falls through to the `opensuse`
|
||||||
|
prefix.
|
||||||
|
|
||||||
|
Category order in the output menu is fixed (see `$categoryOrder` in the
|
||||||
|
script). Unlisted categories — including any new ones you add — append
|
||||||
|
alphabetically after the predefined order.
|
||||||
|
|
||||||
|
## Adding a category
|
||||||
|
|
||||||
|
Edit [`generate-ventoy-json.ps1`](../generate-ventoy-json.ps1):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$categoryMap = [ordered]@{
|
||||||
|
# ...existing...
|
||||||
|
'mycoolos' = 'Hobbyist OSes' # new category name appears as-is in the menu
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optional — place it explicitly in the menu order:
|
||||||
|
$categoryOrder = @(
|
||||||
|
'Windows',
|
||||||
|
# ...
|
||||||
|
'Hobbyist OSes',
|
||||||
|
# ...
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output details
|
||||||
|
|
||||||
|
- **Encoding**: UTF-8 without BOM. Ventoy accepts this.
|
||||||
|
- **Indentation**: `ConvertTo-Json -Depth 10` default formatting.
|
||||||
|
- **Path separators**: forward slashes, leading `/`. Ventoy paths are
|
||||||
|
rooted at the Ventoy data partition root, not at filesystem root.
|
||||||
|
|
||||||
|
## Exit codes
|
||||||
|
|
||||||
|
- `0` — always. The script prints to stderr but does not exit non-zero
|
||||||
|
on parsing edge cases; "unknown" categories are silently bucketed into
|
||||||
|
`Other`.
|
||||||
|
|
||||||
|
## Idempotency
|
||||||
|
|
||||||
|
Pure function of the directory tree. Re-running over the same tree always
|
||||||
|
produces byte-identical output (modulo line endings if you edited the
|
||||||
|
script). Safe to run on every Ventoy refresh.
|
||||||
135
docs/organise-isos.md
Normal file
135
docs/organise-isos.md
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
# `organise-isos.ps1`
|
||||||
|
|
||||||
|
Parses ISO filenames, infers `os`, `version`, and `arch`, and moves each file
|
||||||
|
into `<DestDir>\<os>\<version>\<arch>\`. Pure filename parsing — the ISO
|
||||||
|
itself is never opened.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `-SourceDir` | `.` | Directory to scan for `*.iso` files (non-recursive). |
|
||||||
|
| `-DestDir` | `.\iso-library` | Target root. Subdirectories are created as needed. |
|
||||||
|
| `-DryRun` | `false` | Print the moves it would make; touch nothing. |
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
1. Enumerates `*.iso` directly under `-SourceDir` (does not recurse).
|
||||||
|
2. For each file, calls `Get-IsoMeta` to derive `os`, `version`, `arch`.
|
||||||
|
3. Creates `<DestDir>\<os>\<version>\<arch>\` if missing.
|
||||||
|
4. Moves the ISO in.
|
||||||
|
5. Prints a table of the results.
|
||||||
|
|
||||||
|
## Parsing rules
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
Checked in priority order against the **raw** filename. First match wins:
|
||||||
|
|
||||||
|
| Pattern | Canonical |
|
||||||
|
| --- | --- |
|
||||||
|
| `aarch64`, `arm64` | `aarch64` |
|
||||||
|
| `arm` (not followed by `64`) | `arm` |
|
||||||
|
| `x86[_-]64`, `amd64`, `_x64`, `64bit` | `x86_64` |
|
||||||
|
| `x86` (not followed by `_64`), `i[3-6]86`, `32bit` | `x86` |
|
||||||
|
| `riscv64`, `riscv32` | `riscv64` / `riscv32` |
|
||||||
|
| `ppc64le`, `ppc64`, `ppc` | matching |
|
||||||
|
| `s390x`, `mips64`, `mips`, `loong64`, `ia64`, `sparc64`, `alpha` | matching |
|
||||||
|
|
||||||
|
If nothing matches, arch is `noarch`.
|
||||||
|
|
||||||
|
### Edition noise stripping
|
||||||
|
|
||||||
|
Tokens like `live`, `server`, `desktop`, `dvd`, `netinstall`, `workstation`,
|
||||||
|
`kde`, `gnome`, `xfce`, language codes (`en_us`, `en_gb`, `international`),
|
||||||
|
release tags (`lts`, `beta`, `rc1`, `sp2`, `u3`), and similar are stripped
|
||||||
|
**after** the arch check so they don't pollute the OS-name match. See the
|
||||||
|
`$noisePattern` regex in the script for the full list.
|
||||||
|
|
||||||
|
### OS detection
|
||||||
|
|
||||||
|
The cleaned filename is split on `-_.` and the first token (lowercased) is
|
||||||
|
matched against `$osNormMap`. First regex match wins. If no first-token match
|
||||||
|
hits, the first **two** tokens joined with `-` are tried (catches `linux-mint`,
|
||||||
|
`pop-os`, `ms-dos`).
|
||||||
|
|
||||||
|
If still no match, the first token is sanitised (`[^a-z0-9-]` removed) and
|
||||||
|
used as-is, producing a slug like `myweirdspin`.
|
||||||
|
|
||||||
|
The map covers Windows, RHEL family, Fedora family, SUSE, Debian/Ubuntu and
|
||||||
|
derivatives, Arch family, Gentoo family, Slackware, independent Linux (Void,
|
||||||
|
NixOS, Alpine, etc.), hypervisors (Proxmox, ESXi, XCP-ng), BSD family,
|
||||||
|
Solaris/illumos, exotic OSes (Haiku, FreeDOS, ReactOS, Plan9), and rescue
|
||||||
|
tools (Clonezilla, SystemRescue, GParted, Memtest, Hiren's, DBAN, vendor
|
||||||
|
rescue disks). Full list is in `$osNormMap` in
|
||||||
|
[`organise-isos.ps1`](../organise-isos.ps1).
|
||||||
|
|
||||||
|
### Version detection
|
||||||
|
|
||||||
|
Checked in this order, first match wins:
|
||||||
|
|
||||||
|
1. **Windows H-series** — `25H2`, `24H2`, etc. on the raw filename.
|
||||||
|
2. **Full date stamp** — `YYYYMMDD` → year only (so `20240315` → `2024`).
|
||||||
|
3. **Semver / partial semver** — `\d+\.\d+(\.\d+)?` on the cleaned name.
|
||||||
|
Three-part versions are truncated to `major.minor` (so `9.3.1` → `9.3`).
|
||||||
|
4. **Bare integer** — final fallback, captures things like `debian-12-...`.
|
||||||
|
|
||||||
|
If nothing matches, version is `unknown`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
| Input filename | OS | Version | Arch |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ubuntu-24.04.1-desktop-amd64.iso` | `ubuntu` | `24.04` | `x86_64` |
|
||||||
|
| `Win11_24H2_English_x64.iso` | `windows` | `24H2` | `x86_64` |
|
||||||
|
| `debian-12.5.0-amd64-netinst.iso` | `debian` | `12.5` | `x86_64` |
|
||||||
|
| `Rocky-9.3-aarch64-minimal.iso` | `rocky` | `9.3` | `aarch64` |
|
||||||
|
| `archlinux-2024.03.01-x86_64.iso` | `arch` | `2024.03` | `x86_64` |
|
||||||
|
| `clonezilla-live-3.1.2-22-amd64.iso` | `clonezilla` | `3.1` | `x86_64` |
|
||||||
|
| `proxmox-ve_8.2-1.iso` | `proxmox-ve` | `8.2` | `noarch` |
|
||||||
|
| `systemrescue-11.00-amd64.iso` | `systemrescue` | `11` | `x86_64` |
|
||||||
|
|
||||||
|
## Adding support for a new OS
|
||||||
|
|
||||||
|
Edit [`organise-isos.ps1`](../organise-isos.ps1):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$osNormMap = [ordered]@{
|
||||||
|
# ...existing entries...
|
||||||
|
'^mycoolos|^my.cool.os' = 'mycoolos'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add a display name and category in
|
||||||
|
[`generate-ventoy-json.ps1`](../generate-ventoy-json.ps1):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$displayNames = @{
|
||||||
|
'mycoolos' = 'My Cool OS'
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryMap = [ordered]@{
|
||||||
|
'mycoolos' = 'Independent Linux' # or any existing/new category
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `$osNormMap` is **ordered** — earlier patterns win, so put more specific
|
||||||
|
patterns above more general ones (e.g. `^centos.stream` must come before
|
||||||
|
`^centos`).
|
||||||
|
|
||||||
|
## Exit codes
|
||||||
|
|
||||||
|
- `0` — success (including dry-run).
|
||||||
|
- `1` — no ISO files found in `-SourceDir`.
|
||||||
|
|
||||||
|
## Idempotency and re-runs
|
||||||
|
|
||||||
|
Safe to re-run. The first time moves all ISOs out of `-SourceDir`; subsequent
|
||||||
|
runs find nothing to move (because the source directory is empty) and exit
|
||||||
|
with code 1. If you want to add ISOs later, drop them into the same source
|
||||||
|
directory and run again.
|
||||||
|
|
||||||
|
The script does **not** detect duplicates. If you move the same ISO twice
|
||||||
|
from different sources and they parse to the same target slot, the second
|
||||||
|
move fails with a `Move-Item` error — by design, since silently
|
||||||
|
overwriting an ISO would be a footgun.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue