Restore earlier README.md version and remove DEV guides for now

This commit is contained in:
Lukas Greve
2026-03-30 20:02:58 +02:00
parent 2fd464dd5c
commit 5fcbdc2e9d
3 changed files with 148 additions and 799 deletions
-739
View File
@@ -1,739 +0,0 @@
# Phyllome OS Development Guide
This guide covers development workflows for contributors to Phyllome OS.
## Table of Contents
- [Architecture Overview](#architecture-overview)
- [Development Environment Setup](#development-environment-setup)
- [Ingredient Development](#ingredient-development)
- [Recipe Generation](#recipe-generation)
- [Testing](#testing)
- [CI/CD](#cicd)
- [Common Workflows](#common-workflows)
- [Migration Guide: Ingredient-Based Architecture](#migration-guide-ingredient-based-architecture)
---
## Architecture Overview
Phyllome OS uses a **ingredient-driven** kickstart generation system:
```
ingredients/ (54 .ks files)
↓ (modular snippets)
recipe-generator/generate_recipe.py
↓ (YAML templates + manifest)
recipes/ (16 auto-generated .cfg)
↓ (ksflatten)
dishes/ (28 flattened .cfg)
↓ (virt-install)
VMs and ISO images
```
### Directory Structure
| Path | Purpose | Contents |
|------|---------|----------|
| `ingredients/` | Modular kickstart snippets | 54 `.ks` files |
| `recipes/` | Generated recipes | Manifest-driven compositions |
| `dishes/` | Flattened kickstarts | Ready-to-deploy artifacts |
| `legacy/` | Legacy building blocks | 35 `.cfg` files (legacy) |
| `recipe-generator/` | Recipe generation | `generate_recipe.py`, YAML configs, Makefile |
| `deploy/` | Deployment scripts | Bash automation tools |
| `bin/` | Executables | Wrapper scripts (e.g., `generate-recipe`) |
### Data Flow
1. **Ingredients** (`ingredients/**/*.ks`) - Small, reusable kickstart snippets
2. **Templates** (`recipe-generator/recipe_templates.yaml`) - Define recipe structures
3. **Manifest** (`recipe-generator/recipes_manifest.yaml`) - Specify variants (version, desktop, storage, etc.)
4. **Generator** (`recipe-generator/generate_recipe.py`) - Composes ingredients via `%ksappend` directives
5. **Recipes** (`recipes/*.cfg`) - Generated kickstart files with ingredient references
6. **Flattening** (`ksflatten`) - Resolves `%ksappend` into single dish file
7. **Deployment** (`virt-install`) - Creates VMs from dish files
---
## Development Environment Setup
### Prerequisites
**System dependencies:**
```bash
# Fedora/RHEL-based systems
sudo dnf install qemu libvirt virt-install pykickstart
# Start libvirt
sudo systemctl start libvirtd
sudo systemctl enable libvirtd
```
**Python dependencies:**
```bash
cd scripts
pip install -r requirements.txt
# Required: PyYAML>=6.0, pytest>=7.0
# Optional: pykickstart (for validation)
```
### Verify Setup
```bash
# Generate all recipes from manifest
cd scripts
make generate-recipes
# Run all tests
make test
# Check recipe validation
make validate-recipes
```
---
## Fragment Development
Fragments are modular kickstart snippets stored in `ingredients/`. Each `.ks` file contains a single feature section.
### Creating a New Fragment
**Step 1: Choose location**
- `ingredients/shared/core/` - Base settings (security, services, networking)
- `ingredients/shared/storage/` - Partition layouts
- `ingredients/shared/packages/` - Package groups
- `ingredients/shared/desktop/` - Desktop environment configs
- `ingredients/shared/hypervisor/` - Virtualization hardware configs
- `ingredients/shared/live/` - Live system components
- `ingredients/shared/initial-setup/` - First-boot configuration
- `ingredients/platform/generic-43/` or `generic-rawhide/` - Version-specific
**Step 2: Create the fragment file**
```bash
# Example: Add Luanti game engine
cat > ingredients/shared/packages/luanti.ks << 'EOF'
%packages
luanti
%end
EOF
```
**Step 3: Validate with pykickstart**
```bash
python3 -c "
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, DEVEL
parser = KickstartParser(makeVersion(DEVEL))
try:
with open('ingredients/shared/packages/luanti.ks') as f:
parser.readKickstart(f.read())
print('✓ Validation passed')
except Exception as e:
print(f'✗ Validation failed: {e}')
"
```
### Fragment Naming Conventions
- **Pattern:** `<feature>/<subfeature>.ks`
- **Examples:**
- `ingredients/shared/core/security/enabled.ks`
- `ingredients/shared/desktop/gnome/packages.ks`
- `ingredients/platform/generic-43/repo/fedora-mirrors.ks`
### Common Fragment Types
**Packages:**
```bash
%packages
@base-graphical
package-name
%end
```
**Storage:**
```bash
part / --fstype="ext4" --grow
part /boot/efi --fstype="efi" --size=512
```
**Services:**
```bash
%services
--enabled=sshd,chronyd
EOF
```
### Fragment Validation Script
Create `deploy/validate-fragment.sh`:
```bash
#!/bin/bash
set -e
fragment="$1"
if [ -z "$fragment" ]; then
echo "Usage: $0 <fragment-path>"
exit 1
fi
python3 -c "
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, DEVEL
parser = KickstartParser(makeVersion(DEVEL))
try:
with open('$fragment') as f:
parser.readKickstart(f.read())
print('✓ Valid: $fragment')
except Exception as e:
print(f'✗ Invalid: $fragment - {e}')
exit(1)
"
```
---
## Recipe Generation
Recipes are generated from templates and the manifest file. This section covers modifying existing recipes and creating new ones.
### Manifest Editing
**File:** `recipe-generator/recipes_manifest.yaml`
The manifest defines all recipe variants using modifiers from templates.
**Structure:**
```yaml
recipes:
- name: virtual-desktop # Template name
variants: # Modifier combinations
- version: 43
desktop: gnome
storage: standard
security: secure
extras: true
- version: rawhide
desktop: labwc
storage: encrypted
security: devel
```
**Adding a new variant:**
```yaml
- name: virtual-server
variants:
- version: 43
security: secure
extras: true
post: true
# Add new variant below
- version: 43
security: secure
extras: false
post: false
```
**Valid modifiers** (per template):
- `version`: `43` or `rawhide`
- `desktop`: `gnome` or `labwc` (desktop templates)
- `storage`: `standard` or `encrypted`
- `security`: `secure` or `devel`
- `cpu`: `amdcpu`, `intelcpu` (hypervisor)
- `gpu`: `intelgpu` (hypervisor)
- `extras`: `true` or `false`
- `post`: `true` or `false`
### Template Editing
**File:** `recipe-generator/recipe_templates.yaml`
Templates define the structure and fragment composition for each recipe type.
**Structure:**
```yaml
templates:
virtual-desktop:
description: "A recipe for a virtual desktop"
base: core
required: # Always included ingredients
- core: ingredients/shared/core/base.ks
- storage: ingredients/shared/storage/standard.ks
optional: # Conditional ingredients
security:
secure: ingredients/shared/core/security/enabled.ks
devel: ingredients/shared/core/security/disabled.ks
modifiers: # Storage/bootloader alternatives
storage:
standard: ingredients/shared/storage/standard.ks
encrypted: ingredients/shared/storage/encrypted.ks
```
**Adding a new required fragment:**
```yaml
required:
- core: ingredients/shared/core/base.ks
- storage: ingredients/shared/storage/standard.ks
# New fragment
- packages: ingredients/shared/packages/hand-picked.ks
```
**Adding an optional modifier:**
```yaml
optional:
security:
secure: ingredients/shared/core/security/enabled.ks
devel: ingredients/shared/core/security/disabled.ks
# New optional - post-install scripts
post: ingredients/shared/section-data/post/base.ks
```
**Adding a modifier alternative:**
```yaml
modifiers:
storage:
standard: ingredients/shared/storage/standard.ks
encrypted: ingredients/shared/storage/encrypted.ks
# New storage option
btrfs: ingredients/shared/storage/btrfs.ks
```
### Generation Workflow
```bash
cd scripts
# Generate all recipes from manifest
make generate-recipes
# Validate generated recipes
make validate-recipes
# Validate with strict mode (checks fragment existence)
python3 generate_recipe.py --validate ../recipes/*.cfg --strict
```
**Output:**
```
Generating: virtual-desktop_43.cfg
Generating: virtual-desktop_rawhide_encrypted.cfg
Generating: virtual-server_43.cfg
...
✓ Generated 16 recipes
```
### Manual Recipe Creation
**Option 1: Copy existing recipe**
```bash
# Copy template
cp recipes/_list-of-ingredients.cfg recipes/my-custom.cfg
# Edit with your favorite editor
nano recipes/my-custom.cfg
# Add custom ingredient
echo "%include ../ingredients/extra-luanti.cfg" >> recipes/my-custom.cfg
# Flatten to dish
ksflatten -c recipes/my-custom.cfg -o dishes/my-custom.cfg
```
**Option 2: Create from scratch**
```bash
cat > recipes/my-distro.cfg << 'EOF'
# My custom Phyllome OS variant
# Installation method
%include ../ingredients/core.cfg
# Storage configuration
%include ../ingredients/core-storage.cfg
# Bootloader
%include ../ingredients/platform/generic-43/bootloader/grub.ks
# Network configuration
%include ../ingredients/shared/core/network.ks
# Desktop environment
%include ../ingredients/shared/desktop/gnome/packages.ks
# Additional packages
%packages
my-custom-package
@base-graphical
%end
EOF
```
### Flattening Recipes
Convert recipe (with `%ksappend`) to dish (flattened):
```bash
# Single recipe
ksflatten -c recipes/virtual-desktop_43.cfg -o dishes/virtual-desktop_43.cfg
# All recipes
cd recipes
for filename in *.cfg; do
ksflatten -c "$filename" -o "../dishes/$filename"
done
```
---
The project uses Gitea Actions for automated testing and building.
### Workflow Files
| File | Trigger | Purpose |
|------|---------|---------|
| `.gitea/workflows/validate-ingredients.yaml` | Push/PR ingredients | Validate all 54 .ks files |
| `.gitea/workflows/test-generation.yaml` | Push/PR scripts | Generate 16 recipes |
| `.gitea/workflows/validate-recipes.yaml` | Push PR main | Full validation suite |
| `.gitea/workflows/build-iso.yaml` | Push main, release | Build live ISO |
### Workflow: Validate Fragments
**File:** `.gitea/workflows/validate-ingredients.yaml`
**Triggers:**
- Push to any `ingredients/**/*.ks` file
- Pull request with fragment changes
**Steps:**
1. Checkout code
2. Install pykickstart
3. Validate each `.ks` file
4. Report errors
**Local equivalent:**
```bash
for fragment in $(find ingredients -name "*.ks"); do
python3 -c "
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, DEVEL
parser = KickstartParser(makeVersion(DEVEL))
parser.readKickstart(open('$fragment').read())
" || exit 1
done
```
### Workflow: Test Generation
**File:** `.gitea/workflows/test-generation.yaml`
**Triggers:**
- Push to `recipe-generator/**/*.py` or `recipe-generator/**/*.yaml`
- Pull request with script changes
**Steps:**
1. Pull request with manifest changes
**Steps:**
1. Checkout code
2. Install dependencies (PyYAML, pykickstart)
3. Generate all recipes
4. Verify 16 recipes created
5. Upload as artifact
### Workflow: Build ISO
**File:** `.gitea/workflows/build-iso.yaml`
**Triggers:**
- Push to main branch
- Release published
**Steps:**
1. Generate and validate recipes
2. Initialize mock build environment
3. Install build tools (lorax-lmc-novirt, livemedia-creator)
4. Copy recipe to mock
5. Build ISO with livemedia-creator
6. Upload ISO as artifact
---
## Common Workflows
### Quick Reference
| Task | Command | File |
|------|---------|------|
| Generate all recipes | `cd scripts && make generate-recipes` | - |
| Validate all ingredients | `for f in $(find ingredients -name "*.ks"); do python3 -c "from pykickstart.parser import KickstartParser; from pykickstart.version import makeVersion, DEVEL; parser = KickstartParser(makeVersion(DEVEL)); parser.readKickstart(open('$f').read())" && echo "✓ $f"; done` | - |
| Run all tests | `cd scripts && make test` | - |
| Flatten recipe to dish | `ksflatten -c recipes/X.cfg -o dishes/X.cfg` | - |
| Deploy VM from dish | `./deploy-vm.sh` | `deploy.sh`, `deploy-distro.sh` |
| Build ISO | See `.gitea/workflows/build-iso.yaml` | - |
| Update golden masters | `python3 -m pytest tests/integration/test_golden_masters.py --snapshot-update` | - |
### Workflow: Add New Package
**Example: Add Luanti game engine**
```bash
# Step 1: Create fragment
cat > ingredients/shared/packages/luanti.ks << 'EOF'
%packages
luanti
%end
EOF
# Step 2: Add to recipe template
# Edit recipe-generator/recipe_templates.yaml
# Add to 'required' section:
# - luanti: ingredients/shared/packages/luanti.ks
# Step 3: Regenerate recipes
cd recipe-generator
make generate-recipes
# Step 4: Validate
make validate-recipes
# Step 5: Flatten and test
ksflatten -c ../recipes/virtual-desktop_43.cfg -o ../dishes/virtual-desktop_43.cfg
```
### Workflow: Add New Desktop Environment
**Example: Add KDE Plasma**
```bash
# Step 1: Create desktop fragment
cat > ingredients/shared/desktop/kde/packages.ks << 'EOF'
%packages
@kde-desktop
plasma-workspace
EOF
# Step 2: Add to template
# Edit recipe-generator/recipe_templates.yaml
# Add to optional/desktop section:
# kde: ingredients/shared/desktop/kde/packages.ks
# Step 3: Add variant to manifest
# Edit recipe-generator/recipes_manifest.yaml
# Add variant:
# - version: 43
# desktop: kde
# storage: standard
# security: secure
# Step 4: Generate and test
make generate-recipes
make test
```
### Workflow: Create New Recipe Type
**Example: Create minimal-server recipe**
```bash
# Step 1: Add template to recipe_templates.yaml
cat >> recipe-generator/recipe_templates.yaml << 'EOF'
minimal-server:
description: "A minimal server recipe"
base: core
required:
- core: ingredients/shared/core/base.ks
- storage: ingredients/shared/storage/standard.ks
- bootloader: ingredients/platform/generic-43/bootloader/grub.ks
- packages: ingredients/shared/packages/core-group.ks
- fedora-remix: ingredients/shared/packages/fedora-remix.ks
optional:
security:
secure: ingredients/shared/core/security/enabled.ks
devel: ingredients/shared/core/security/disabled.ks
version:
"43": ingredients/platform/generic-43/repo/fedora-mirrors.ks
"rawhide": ingredients/platform/generic-rawhide/repo/rawhide-mirrors.ks
EOF
# Step 2: Add variant to recipes_manifest.yaml
cat >> recipe-generator/recipes_manifest.yaml << 'EOF'
- name: minimal-server
variants:
- version: 43
security: secure
EOF
# Step 3: Generate and validate
make generate-recipes
make validate-recipes
```
---
## Migration Guide: Fragment-Based Architecture
Phyllome OS migrated from a monolithic ingredient-based system to a modular fragment-based architecture (Phase 2).
### Before: Ingredient-Based
**File:** `ingredients/extra-luanti.cfg`
```bash
%packages
luanti
%end
```
**Recipe:** `recipes/virtual-desktop-luanti.cfg`
```bash
%include ../ingredients/core.cfg
%include ../ingredients/extra-luanti.cfg
```
**Issues:**
- Duplicate `%include` directives across recipes
- Hard to maintain common patterns
- No automatic version matrix
### After: Fragment-Based
**Fragment:** `ingredients/shared/packages/luanti.ks`
```bash
%packages
luanti
%end
```
**Template:** `recipe-generator/recipe_templates.yaml`
```yaml
templates:
virtual-desktop:
required:
- luanti: ingredients/shared/packages/luanti.ks
```
**Recipe:** `recipes/virtual-desktop_43.cfg`
```bash
# Generated automatically
%ksappend ingredients/shared/core/base.ks
%ksappend ingredients/shared/packages/luanti.ks
```
### Migration Benefits
| Aspect | Before | After |
|--------|--------|-------|
| Maintenance | Repetitive includes | Single source in template |
| Variants | Manual recipe creation | Manifest-driven generation |
| Testing | Per-recipe validation | Fragment-level validation |
| Code duplication | High | None (DRY principle) |
### Why `%ksappend` over `%include`?
- **`%include`** - Simple file inclusion (copied into result)
- **`%ksappend`** - References external file (keeps file path in flattened output)
**Benefits of `%ksappend`:**
1. Smaller dish files (no duplicate content)
2. Clear dependency tracking
3. Better validation (fragment existence checks)
4. Easier debugging (visible fragment References)
### Migration Checklist
- [ ] Review all `ingredients/*.cfg` files
- [ ] Identify reusable patterns
- [ ] Create `ingredients/shared/` for common components
- [ ] Update `recipe_templates.yaml` with new structure
- [ ] Update `recipes_manifest.yaml` for variants
- [ ] Regenerate recipes with `make generate-recipes`
- [ ] Validate with `make validate-recipes`
- [ ] Flatten to dishes with `ksflatten`
- [ ] Run tests with `make test`
---
## Troubleshooting
### Common Issues
**1. Duplicate %ksappend entries**
```bash
# Detect duplicates
grep "^%ksappend" recipes/*.cfg | sort | uniq -d
```
**Fix:** Check template for duplicate fragment references
**2. Missing fragment error**
```
ERROR: Missing fragment: ingredients/shared/unknown/missing.ks
```
**Fix:** Verify fragment exists in `ingredients/shared/`
**3. Deprecated command warning**
```
keyboard command is deprecated. Use keyboard --vckeymap instead.
```
**4. pykickstart validation fails**
```bash
# Check fragment with pykickstart
python3 -c "
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, DEVEL
parser = KickstartParser(makeVersion(DEVEL))
try:
with open('fragment.ks') as f:
parser.readKickstart(f.read())
print('Valid')
except Exception as e:
print(f'Error: {e}')
"
```
### Debugging Generator
```bash
# Verbose mode
python3 generate_recipe.py --manifest recipes_manifest.yaml --output-dir ../recipes/ --verbose
# Check template loading
python3 -c "
from pathlib import Path
import sys
sys.path.insert(0, '.')
from generate_recipe import RecipeGenerator
gen = RecipeGenerator(Path('recipe_templates.yaml'))
print('Templates:', list(gen.templates.keys()))
"
```
---
## Additional Resources
- **Kickstart Documentation:** https://pykickstart.readthedocs.io/
- **Fedora Kickstarts:** https://pagure.io/fedora-kickstarts
- **virt-install:** `man virt-install`
- **livemedia-creator:** `man livemedia-creator`
---
*Last updated: March 2026*
-59
View File
@@ -1,59 +0,0 @@
# Phyllome OS Development Workflow
This is a quick-reference guide for developers. For comprehensive coverage, see [DEVELOPMENT.md](./DEVELOPMENT.md).
## Quick Start
```bash
# Prerequisites
sudo dnf install qemu libvirt virt-install pykickstart
pip install PyYAML
# Verify setup
cd recipe-generator && make generate-recipes
```
## Core Workflows
### Add Fragment
```bash
# Create new kickstart snippet
cat > ingredients/shared/packages/new-package.ks << 'EOF'
%packages
new-package
%end
```
### Add to Recipe
```bash
# Edit recipe-generator/recipe_templates.yaml to include fragment
# Edit recipe-generator/recipes_manifest.yaml to add variant
# Regenerate
cd recipe-generator && make generate-recipes && make validate-recipes
```
```
### Validate Fragments
```bash
for f in $(find ingredients -name "*.ks"); do
python3 -c "
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, DEVEL
parser = KickstartParser(makeVersion(DEVEL))
parser.readKickstart(open('$f').read())
" || echo "✗ $f"
done
```
## Architecture
```
ingredients/ (54 .ks) → recipe-generator/generate_recipe.py → recipes/ (16 .cfg) → ksflatten → dishes/ (28 .cfg)
```
See `DEVELOPMENT.md` Section 1 for detailed architecture overview.
+148 -1
View File
@@ -4,9 +4,10 @@
Provided that some dependencies are met (`libvirt` is running on your computer, QEMU is installed, etc), one could run the following script to deploy virtual machines, including Phyllome OS itself.
- :
- Make the script executable:
```
chmod +x deploy-vm.sh
```
- Execute it and pick `virtual-desktop-hypervisor` when prompted:
@@ -48,3 +49,149 @@ Each ingredient represents a feature or a set of integrated features, such as a
- Ingredients prefixed with *live* such as `live-core.cfg` are to be used with live editions only
- *core* ingredients are meant be used in all their respective recipes, *base* ingredients, recommended but optional, and extra provides more stuff (sic)
## Development
Using a pull request, you can suggest a modification to an existing ingredient or create a new ingredient from scratch.
### Requirements
- `qemu`
- `libvirt`
- `virt-install`
- `pykickstart`
### Example 1: add a new package and include it into a recipe
- Add [Luanti](https://www.luanti.org/), a free and open-source sandbox video game engine formerly known as Minetest, as a standalone ingredient, using the `echo` command
```
echo "%packages --exclude-weakdeps # Beginning of the package section. Does not include weak dependencies
luanti # Multiplayer infinite-world block sandbox with survival mode
%end # End of the packages section" > ingredients/extra-luanti.cfg
```
Instead of creating a recipe from scratch, let's make a copy of the `virtual-desktop.cfg` recipe, which provide a Desktop environment necessary for *luanti* to function
```
cp recipes/virtual-desktop.cfg recipes/virtual-desktop-luanti.cfg
```
- Add the extra ingredient to the new recipe:
```
echo "%include ../ingredients/extra-luanti.cfg # Sandbox video game engine" >> recipes/virtual-desktop-luanti.cfg
```
#### Flatten
- Prepare the dish by following the recipe, a process called 'flattening'
```
ksflatten -c recipes/virtual-desktop-luanti.cfg -o dishes/virtual-desktop-luanti.cfg
```
> If any errors are detected, go back and fix them.
It is time to test the new dish!
#### Kickstart
- Make the `deploy-vm.sh` script executable
```
chmod +x deploy-vm.sh
```
- Execute the script
```
./deploy-vm.sh
```
- Select the new dish, *virtual-desktop-luanti*
```
[...]
Available files:
1. desktop-hypervisor-amdcpu
[...]
14. virtual-desktop-luanti
```
- When the installation is done, the machine will shut down
- Start it again, and ensure that Luanti has correctly been installed
That's it !
### Example 2: Create a new recipe from the existing list of ingredients
The file `recipes/_list-of-ingredients.cfg` can be copied and edited to create your own remix of Phyllome OS, which itself is a remix of Fedora.
```
cp recipes/_list-of-ingredients.cfg recipes/my-new-distro.cfg
```
Then edit the said file to include your favorite ingredient
```
nano recipes/my-new-distro.cfg
```
```
# __ ____ ____ _____
# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/
# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \
# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ /
# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/
# /_/ /____/
# The list of ingredients for composing Phyllome OS
# Uncomment lines with "%include" to enable ingredient
# Installation method
# Exactly one option has to be picked
# %include ../ingredients/core.cfg # Text mode
# %include ../ingredients/live-core.cfg # For live systems only
# Documentation: https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#graphical-or-text-or-cmdline
# Storage configuration
# Exactly one option has to be picked
# WARNING !!! Will erase local disks!
# %include ../ingredients/core-storage.cfg # Basic ext4 partition layout for UEFI-based systems
# %include ../ingredients/live-core-storage.cfg # For live systems only
# Documentation: https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#part-or-partition
[...]
```
- Once you are done, you can [flatten](#flatten) the file and [kickstart](#kickstart) it as explained in the previous section.
## FAQ
If multiple dishes are affected by your ingredient, you can flatten them all
- Navigate to the recipes' directory
```
cd recipes
```
- Then use the following
```
for filename in *.cfg; do ksflatten -c "$filename" -o "../dishes/$filename"; done
```
The following message can safetly be ignored:
```
/usr/lib/python3.13/site-packages/pykickstart/commands/partition.py:461: KickstartParseWarning: A partition with the mountpoint / has already been defined.
```
## Acknowledgement
Thanks to the main contributors of the official Fedora kickstart files repository, and related tools:
> Adam Miller, Bastien Nocera, Bruno Wolff III, Bryan Kearney, Chitlesh Goorah, Christoph Wickert, Colin Walters, Fabian Affolter, Igor Pires Soares, Jens Petersen, Jeremy Katz, Jeroen van Meeuwen Jesse Keating, Luya Tshimbalanga, Matthias Clasen, Pedro Silva, Rahul Sundaram, Sebastian Dziallas Sebastian Vahl, wart. More information here : https://pagure.io/fedora-kickstarts