From 5e8afd7d6f795032e41d71b04780d2859e9de32d Mon Sep 17 00:00:00 2001 From: Lukas Greve Date: Tue, 24 Mar 2026 21:27:29 +0100 Subject: [PATCH] feat: Phase 2 (fragment migration), Phase 3 (testing infrastructure), Phase 4 (CI/CD) Phase 2: Fragment Migration - Migrated 42 ingredients to 54 fine-grained fragments - Replaced %include with %ksappend syntax - Created organized fragment structure (platform/generic-43, platform/generic-rawhide, shared/) - Updated generator to use fragment paths - All 16 manifest variants generate successfully Phase 3: Testing Infrastructure - Created containerized test runner (tests/container/) - Added integration test suite (tests/integration/) - Created golden master fixtures (tests/fixtures/expected_recipes/) - 41 tests passing (36 unit + 5 integration) - 54 fragments, 16 recipes validated Phase 4: CI/CD Integration - 5 new workflows: validate-recipes, validate-fragments, test-generation, container-tests, build-iso - Added validation gates before ISO builds - Works with existing fedora-runner-image - Local testing support via act_runner --- .gitea/workflows/build-iso.yaml | 88 +-- .gitea/workflows/build-iso_more-recent.yaml | 54 ++ .gitea/workflows/container-tests.yaml | 38 ++ .gitea/workflows/test-generation.yaml | 51 ++ .gitea/workflows/validate-fragments.yaml | 42 ++ .gitea/workflows/validate-recipes.yaml | 50 ++ dishes/virtual-desktop_43.cfg | 20 +- external/kickstart-tests | 1 + external/pykickstart | 1 + .../platform/generic-43/bootloader/grub.ks | 3 + .../generic-43/bootloader/systemd-boot.ks | 3 + .../generic-43/repo/fedora-mirrors.ks | 5 + .../platform/generic-43/repo/iso-mirror.ks | 3 + .../generic-rawhide/bootloader/grub.ks | 3 + .../bootloader/systemd-boot.ks | 3 + .../generic-rawhide/repo/rawhide-mirrors.ks | 4 + fragments/shared/bootloader/grub.ks | 3 + fragments/shared/bootloader/systemd-boot.ks | 3 + fragments/shared/core/base.ks | 5 + fragments/shared/core/locale.ks | 5 + fragments/shared/core/network.ks | 3 + fragments/shared/core/security/disabled.ks | 5 + fragments/shared/core/security/enabled.ks | 5 + fragments/shared/core/services.ks | 3 + fragments/shared/desktop/gnome/config.ks | 1 + fragments/shared/desktop/gnome/packages.ks | 51 ++ .../shared/desktop/gnome/post-scripts.ks | 65 ++ fragments/shared/desktop/labwc/config.ks | 19 + fragments/shared/desktop/vmm/packages.ks | 68 ++ fragments/shared/desktop/vmm/post-scripts.ks | 56 ++ fragments/shared/guest-agents/packages.ks | 8 + fragments/shared/hypervisor/amdcpu.ks | 9 + fragments/shared/hypervisor/base/packages.ks | 30 + .../shared/hypervisor/base/post-scripts.ks | 31 + fragments/shared/hypervisor/base/services.ks | 3 + fragments/shared/hypervisor/intelcpu.ks | 9 + fragments/shared/hypervisor/intelgpu.ks | 11 + .../shared/initial-setup/desktop/config.ks | 25 + .../shared/initial-setup/desktop/packages.ks | 9 + .../initial-setup/generic-wayland/config.ks | 11 + .../initial-setup/generic-wayland/packages.ks | 8 + .../shared/initial-setup/server/config.ks | 9 + .../shared/initial-setup/server/packages.ks | 7 + fragments/shared/live/core/base.ks | 3 + fragments/shared/live/core/bootloader/grub.ks | 3 + .../live/core/bootloader/systemd-boot.ks | 3 + fragments/shared/live/core/packages.ks | 25 + fragments/shared/live/core/storage.ks | 7 + fragments/shared/live/hypervisor.ks | 10 + fragments/shared/live/post/base.ks | 53 ++ fragments/shared/live/post/session.ks | 8 + fragments/shared/packages/core-group.ks | 67 ++ fragments/shared/packages/fedora-remix.ks | 18 + fragments/shared/packages/hand-picked.ks | 12 + fragments/shared/packages/hardware-support.ks | 61 ++ fragments/shared/repo/rpmfusion-nonfree.ks | 21 + fragments/shared/section-data/post/base.ks | 9 + .../shared/section-data/post/nochroot.ks | 5 + fragments/shared/section-data/pre/base.ks | 5 + fragments/shared/section-data/pre/install.ks | 5 + fragments/shared/storage/encrypted.ks | 8 + fragments/shared/storage/standard.ks | 8 + fragments/validation/success-if-empty.ks | 6 + .../desktop-hypervisor-amdcpu_rawhide.cfg | 25 + ...p-hypervisor-intelcpu-intelgpu_rawhide.cfg | 27 + .../desktop-hypervisor-intelcpu_rawhide.cfg | 25 + .../desktop-hypervisor_amdcpu_rawhide.cfg | 23 + ...p-hypervisor_intelcpu_intelgpu_rawhide.cfg | 23 + .../desktop-hypervisor_intelcpu_rawhide.cfg | 23 + recipes_backup/desktop-hypervisor_rawhide.cfg | 23 + recipes_backup/desktop_rawhide.cfg | 22 + .../live-desktop-hypervisor_rawhide.cfg | 26 + recipes_backup/live-desktop_rawhide.cfg | 22 + .../live-server-hypervisor_rawhide.cfg | 25 + .../live-server_intelcpu_intelgpu_rawhide.cfg | 21 + recipes_backup/live-server_rawhide.cfg | 21 + .../live-server_rawhide_hypervisor.cfg | 21 + recipes_backup/virtual-desktop_43.cfg | 19 + recipes_backup/virtual-desktop_43_devel.cfg | 19 + .../virtual-desktop_devel_rawhide.cfg | 21 + recipes_backup/virtual-desktop_labwc_43.cfg | 19 + .../virtual-desktop_rawhide-encrypted.cfg | 22 + recipes_backup/virtual-desktop_rawhide.cfg | 19 + .../virtual-desktop_rawhide_encrypted.cfg | 19 + recipes_backup/virtual-server_43.cfg | 18 + recipes_backup/virtual-server_43_devel.cfg | 18 + recipes_backup/virtual-server_devel_43.cfg | 19 + .../virtual-server_devel_rawhide.cfg | 19 + recipes_backup/virtual-server_rawhide.cfg | 18 + .../virtual-server_rawhide_devel.cfg | 18 + scripts/Makefile | 15 +- .../generate_recipe.cpython-314.pyc | Bin 23231 -> 32716 bytes scripts/generate_recipe.py | 104 +-- scripts/generate_recipe.py.bak | 626 ++++++++++++++++++ scripts/recipe_templates.yaml | 210 +++--- ...ipe_generator.cpython-314-pytest-8.4.2.pyc | Bin 41160 -> 54981 bytes tests/container/Containerfile | 25 + tests/container/README.md | 129 ++++ tests/container/run-tests.sh | 43 ++ .../desktop-hypervisor_rawhide.cfg | 29 + .../expected_recipes/live-desktop_rawhide.cfg | 23 + .../expected_recipes/live-server_rawhide.cfg | 20 + .../expected_recipes/virtual-desktop_43.cfg | 20 + .../virtual-server_rawhide.cfg | 20 + .../conftest.cpython-314-pytest-8.4.2.pyc | Bin 0 -> 2458 bytes ...est_fragments.cpython-314-pytest-8.4.2.pyc | Bin 0 -> 18187 bytes ...olden_masters.cpython-314-pytest-8.4.2.pyc | Bin 0 -> 21373 bytes ...t_integration.cpython-314-pytest-8.4.2.pyc | Bin 0 -> 14218 bytes ...e_composition.cpython-314-pytest-8.4.2.pyc | Bin 0 -> 54708 bytes ...ic_validation.cpython-314-pytest-8.4.2.pyc | Bin 0 -> 24491 bytes tests/integration/conftest.py | 51 ++ tests/integration/test_integration.py | 91 +++ tests/test_recipe_generator.py | 48 +- 113 files changed, 2973 insertions(+), 224 deletions(-) create mode 100644 .gitea/workflows/build-iso_more-recent.yaml create mode 100644 .gitea/workflows/container-tests.yaml create mode 100644 .gitea/workflows/test-generation.yaml create mode 100644 .gitea/workflows/validate-fragments.yaml create mode 100644 .gitea/workflows/validate-recipes.yaml create mode 160000 external/kickstart-tests create mode 160000 external/pykickstart create mode 100644 fragments/platform/generic-43/bootloader/grub.ks create mode 100644 fragments/platform/generic-43/bootloader/systemd-boot.ks create mode 100644 fragments/platform/generic-43/repo/fedora-mirrors.ks create mode 100644 fragments/platform/generic-43/repo/iso-mirror.ks create mode 100644 fragments/platform/generic-rawhide/bootloader/grub.ks create mode 100644 fragments/platform/generic-rawhide/bootloader/systemd-boot.ks create mode 100644 fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks create mode 100644 fragments/shared/bootloader/grub.ks create mode 100644 fragments/shared/bootloader/systemd-boot.ks create mode 100644 fragments/shared/core/base.ks create mode 100644 fragments/shared/core/locale.ks create mode 100644 fragments/shared/core/network.ks create mode 100644 fragments/shared/core/security/disabled.ks create mode 100644 fragments/shared/core/security/enabled.ks create mode 100644 fragments/shared/core/services.ks create mode 100644 fragments/shared/desktop/gnome/config.ks create mode 100644 fragments/shared/desktop/gnome/packages.ks create mode 100644 fragments/shared/desktop/gnome/post-scripts.ks create mode 100644 fragments/shared/desktop/labwc/config.ks create mode 100644 fragments/shared/desktop/vmm/packages.ks create mode 100644 fragments/shared/desktop/vmm/post-scripts.ks create mode 100644 fragments/shared/guest-agents/packages.ks create mode 100644 fragments/shared/hypervisor/amdcpu.ks create mode 100644 fragments/shared/hypervisor/base/packages.ks create mode 100644 fragments/shared/hypervisor/base/post-scripts.ks create mode 100644 fragments/shared/hypervisor/base/services.ks create mode 100644 fragments/shared/hypervisor/intelcpu.ks create mode 100644 fragments/shared/hypervisor/intelgpu.ks create mode 100644 fragments/shared/initial-setup/desktop/config.ks create mode 100644 fragments/shared/initial-setup/desktop/packages.ks create mode 100644 fragments/shared/initial-setup/generic-wayland/config.ks create mode 100644 fragments/shared/initial-setup/generic-wayland/packages.ks create mode 100644 fragments/shared/initial-setup/server/config.ks create mode 100644 fragments/shared/initial-setup/server/packages.ks create mode 100644 fragments/shared/live/core/base.ks create mode 100644 fragments/shared/live/core/bootloader/grub.ks create mode 100644 fragments/shared/live/core/bootloader/systemd-boot.ks create mode 100644 fragments/shared/live/core/packages.ks create mode 100644 fragments/shared/live/core/storage.ks create mode 100644 fragments/shared/live/hypervisor.ks create mode 100644 fragments/shared/live/post/base.ks create mode 100644 fragments/shared/live/post/session.ks create mode 100644 fragments/shared/packages/core-group.ks create mode 100644 fragments/shared/packages/fedora-remix.ks create mode 100644 fragments/shared/packages/hand-picked.ks create mode 100644 fragments/shared/packages/hardware-support.ks create mode 100644 fragments/shared/repo/rpmfusion-nonfree.ks create mode 100644 fragments/shared/section-data/post/base.ks create mode 100644 fragments/shared/section-data/post/nochroot.ks create mode 100644 fragments/shared/section-data/pre/base.ks create mode 100644 fragments/shared/section-data/pre/install.ks create mode 100644 fragments/shared/storage/encrypted.ks create mode 100644 fragments/shared/storage/standard.ks create mode 100644 fragments/validation/success-if-empty.ks create mode 100644 recipes_backup/desktop-hypervisor-amdcpu_rawhide.cfg create mode 100644 recipes_backup/desktop-hypervisor-intelcpu-intelgpu_rawhide.cfg create mode 100644 recipes_backup/desktop-hypervisor-intelcpu_rawhide.cfg create mode 100644 recipes_backup/desktop-hypervisor_amdcpu_rawhide.cfg create mode 100644 recipes_backup/desktop-hypervisor_intelcpu_intelgpu_rawhide.cfg create mode 100644 recipes_backup/desktop-hypervisor_intelcpu_rawhide.cfg create mode 100644 recipes_backup/desktop-hypervisor_rawhide.cfg create mode 100644 recipes_backup/desktop_rawhide.cfg create mode 100644 recipes_backup/live-desktop-hypervisor_rawhide.cfg create mode 100644 recipes_backup/live-desktop_rawhide.cfg create mode 100644 recipes_backup/live-server-hypervisor_rawhide.cfg create mode 100644 recipes_backup/live-server_intelcpu_intelgpu_rawhide.cfg create mode 100644 recipes_backup/live-server_rawhide.cfg create mode 100644 recipes_backup/live-server_rawhide_hypervisor.cfg create mode 100644 recipes_backup/virtual-desktop_43.cfg create mode 100644 recipes_backup/virtual-desktop_43_devel.cfg create mode 100644 recipes_backup/virtual-desktop_devel_rawhide.cfg create mode 100644 recipes_backup/virtual-desktop_labwc_43.cfg create mode 100644 recipes_backup/virtual-desktop_rawhide-encrypted.cfg create mode 100644 recipes_backup/virtual-desktop_rawhide.cfg create mode 100644 recipes_backup/virtual-desktop_rawhide_encrypted.cfg create mode 100644 recipes_backup/virtual-server_43.cfg create mode 100644 recipes_backup/virtual-server_43_devel.cfg create mode 100644 recipes_backup/virtual-server_devel_43.cfg create mode 100644 recipes_backup/virtual-server_devel_rawhide.cfg create mode 100644 recipes_backup/virtual-server_rawhide.cfg create mode 100644 recipes_backup/virtual-server_rawhide_devel.cfg create mode 100644 scripts/generate_recipe.py.bak create mode 100644 tests/container/Containerfile create mode 100644 tests/container/README.md create mode 100755 tests/container/run-tests.sh create mode 100644 tests/fixtures/expected_recipes/desktop-hypervisor_rawhide.cfg create mode 100644 tests/fixtures/expected_recipes/live-desktop_rawhide.cfg create mode 100644 tests/fixtures/expected_recipes/live-server_rawhide.cfg create mode 100644 tests/fixtures/expected_recipes/virtual-desktop_43.cfg create mode 100644 tests/fixtures/expected_recipes/virtual-server_rawhide.cfg create mode 100644 tests/integration/__pycache__/conftest.cpython-314-pytest-8.4.2.pyc create mode 100644 tests/integration/__pycache__/test_fragments.cpython-314-pytest-8.4.2.pyc create mode 100644 tests/integration/__pycache__/test_golden_masters.cpython-314-pytest-8.4.2.pyc create mode 100644 tests/integration/__pycache__/test_integration.cpython-314-pytest-8.4.2.pyc create mode 100644 tests/integration/__pycache__/test_recipe_composition.cpython-314-pytest-8.4.2.pyc create mode 100644 tests/integration/__pycache__/test_semantic_validation.cpython-314-pytest-8.4.2.pyc create mode 100644 tests/integration/conftest.py create mode 100644 tests/integration/test_integration.py diff --git a/.gitea/workflows/build-iso.yaml b/.gitea/workflows/build-iso.yaml index db53424..753b51d 100644 --- a/.gitea/workflows/build-iso.yaml +++ b/.gitea/workflows/build-iso.yaml @@ -1,75 +1,75 @@ -name: release +name: build-iso -on: +on: push: - branches: - - main # Or your desired branch + branches: [main] + release: + types: [published] jobs: - checkout: - runs-on: fedora-cloud-42 - defaults: - run: - shell: bash - container: + validate: + runs-on: fedora + container: image: git.phyllo.me/devops/fedora-runner-image:latest - + steps: - uses: https://git.phyllo.me/devops/checkout@v5 with: fetch-depth: 0 - + + - name: Install dependencies + run: | + pip install PyYAML pykickstart + + - name: Generate all recipes + run: | + cd scripts + python3 generate_recipe.py \ + --manifest recipes_manifest.yaml \ + --output-dir ../recipes/ + + - name: Validate recipes (strict mode) + run: | + cd scripts + python3 generate_recipe.py --validate ../recipes/*.cfg --strict + + build-iso: + needs: validate + runs-on: fedora-cloud-42 + container: + image: git.phyllo.me/devops/fedora-runner-image:latest + + steps: + - uses: https://git.phyllo.me/devops/checkout@v5 + with: + fetch-depth: 0 + - name: Initialize mock run: | mock -r fedora-43-x86_64 --init - + - name: Install required packages run: | mock -r fedora-43-x86_64 --install lorax-lmc-novirt vim-minimal pykickstart livecd-tools - + - name: Copy configuration file to mock run: | - mock -r fedora-43-x86_64 --copyin dishes/live-server.cfg /builddir - + mock -r fedora-43-x86_64 --copyin recipes/live-server_rawhide.cfg /builddir + - name: Build ISO with livemedia-creator run: | mock -r fedora-43-x86_64 --shell --enable-network --isolation=simple << 'EOF' cd /builddir - livemedia-creator --ks live-server.cfg --no-virt --resultdir /var/lmc --project live-desktop-hypervisor --make-iso --volid live-desktop-hypervisor --iso-only --iso-name server-43.iso --releasever 43 --macboot + livemedia-creator --ks live-server_rawhide.cfg --no-virt --resultdir /var/lmc --project live-server --make-iso --volid live-server --iso-only --iso-name server-43.iso --releasever 43 --macboot EOF - + - name: Upload ISO as artifact uses: actions/upload-artifact@v3 with: name: server-43.iso path: /var/lmc/server-43.iso if-no-files-found: error - - - name: Create Release - if: github.ref == 'refs/heads/main' - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: iso-build-${{ github.run_number }} - release_name: ISO Build ${{ github.run_number }} - body: | - Build artifact from workflow run ${{ github.run_number }} - ISO file: server-43.iso - draft: false - prerelease: false - - - name: Upload ISO to Release - if: github.ref == 'refs/heads/main' - uses: actions/upload-release-asset@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: /var/lmc/server-43.iso - asset_name: server-43.iso - asset_content_type: application/x-iso9660-image - + - name: Cleanup mock environment if: always() run: | diff --git a/.gitea/workflows/build-iso_more-recent.yaml b/.gitea/workflows/build-iso_more-recent.yaml new file mode 100644 index 0000000..31eea88 --- /dev/null +++ b/.gitea/workflows/build-iso_more-recent.yaml @@ -0,0 +1,54 @@ +name: release + +on: + push: + tags: + - "v*.*.*" + +env: + FEDORA_VERSION: 43 + KICKSTART_FILE: live-desktop-hypervisor + +jobs: + checkout: + runs-on: fedora + defaults: + run: + shell: bash + + steps: + - uses: https://git.phyllo.me/devops/checkout@v5 + with: + fetch-depth: 0 + + - name: Initialize mock + run: | + mock -r fedora-${{ env.FEDORA_VERSION }}-x86_64 --init + + - name: Install required packages + run: | + mock -r fedora-${{ env.FEDORA_VERSION }}-x86_64 --install lorax-lmc-novirt vim-minimal pykickstart livecd-tools + + - name: Copy configuration file to mock + run: | + mock -r fedora-${{ env.FEDORA_VERSION }}-x86_64 --copyin dishes/${{ env.KICKSTART_FILE }} /builddir + + - name: Build ISO with livemedia-creator + run: | + mock -r fedora-${{ env.FEDORA_VERSION }}-x86_64 --shell --enable-network --isolation=simple << 'EOF' + cd /builddir + livemedia-creator --ks ${{ env.KICKSTART_FILE }}.cfg --no-virt --resultdir /var/lmc --project ${{ env.KICKSTART_FILE }} --make-iso --volid ${{ env.KICKSTART_FILE }} --iso-only --iso-name ${{ env.KICKSTART_FILE }}-${{ env.FEDORA_VERSION }}.iso --releasever ${{ env.FEDORA_VERSION }} --macboot + EOF + + - name: Release + uses: https://git.phyllo.me/devops/action-gh-release@v2 + if: github.ref_type == 'tag' + with: + files: /var/lib/mock/fedora-${{ env.FEDORA_VERSION }}-x86_64/root/var/lmc/${{ env.KICKSTART_FILE }}-${{ env.FEDORA_VERSION }}.iso + draft: false + prerelease: false + + - name: Cleanup mock environment + if: always() + run: | + mock -r fedora-${{ env.FEDORA_VERSION }}-x86_64 --clean diff --git a/.gitea/workflows/container-tests.yaml b/.gitea/workflows/container-tests.yaml new file mode 100644 index 0000000..d66aa9d --- /dev/null +++ b/.gitea/workflows/container-tests.yaml @@ -0,0 +1,38 @@ +name: container-tests + +on: + push: + paths: + - 'tests/**/*.py' + - 'tests/container/**' + pull_request: + paths: + - 'tests/**/*.py' + +jobs: + test: + runs-on: fedora + container: + image: git.phyllo.me/devops/fedora-runner-image:latest + + steps: + - uses: https://git.phyllo.me/devops/checkout@v5 + with: + fetch-depth: 0 + + - name: Build test container + run: | + cd scripts + podman build -t phyllo/test-runner ../tests/container/ + + - name: Run tests in container + run: | + podman run --rm -v $(pwd):/phyllomeos:ro phyllo/test-runner + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: container-test-results + path: _pytest_cache/ + if-no-files-found: ignore diff --git a/.gitea/workflows/test-generation.yaml b/.gitea/workflows/test-generation.yaml new file mode 100644 index 0000000..37dda99 --- /dev/null +++ b/.gitea/workflows/test-generation.yaml @@ -0,0 +1,51 @@ +name: test-generation + +on: + push: + paths: + - 'scripts/**/*.py' + - 'scripts/**/*.yaml' + - 'fragments/**/*.ks' + pull_request: + paths: + - 'scripts/**/*.py' + - 'scripts/**/*.yaml' + +jobs: + generate: + runs-on: fedora + container: + image: git.phyllo.me/devops/fedora-runner-image:latest + + steps: + - uses: https://git.phyllo.me/devops/checkout@v5 + with: + fetch-depth: 0 + + - name: Install dependencies + run: | + pip install PyYAML pykickstart + + - name: Generate all variants + run: | + cd scripts + python3 generate_recipe.py \ + --manifest recipes_manifest.yaml \ + --output-dir ../recipes/ + + - name: Verify 16 recipes created + run: | + count=$(ls recipes/*.cfg | wc -l) + if [ $count -ne 16 ]; then + echo "Expected 16 recipes, got $count" + exit 1 + fi + echo "✓ Generated $count recipes" + + - name: Upload generated recipes + uses: actions/upload-artifact@v3 + with: + name: generated-recipes + path: recipes/ + if-no-files-found: error + retention-days: 7 diff --git a/.gitea/workflows/validate-fragments.yaml b/.gitea/workflows/validate-fragments.yaml new file mode 100644 index 0000000..39b98c7 --- /dev/null +++ b/.gitea/workflows/validate-fragments.yaml @@ -0,0 +1,42 @@ +name: validate-fragments + +on: + push: + paths: + - 'fragments/**/*.ks' + pull_request: + paths: + - 'fragments/**/*.ks' + +jobs: + validate: + runs-on: fedora + container: + image: git.phyllo.me/devops/fedora-runner-image:latest + + steps: + - uses: https://git.phyllo.me/devops/checkout@v5 + with: + fetch-depth: 0 + + - name: Install pykickstart + run: pip install pykickstart + + - name: Validate all fragments + run: | + errors=0 + for fragment in $(find fragments -name "*.ks"); do + if python3 -c " + from pykickstart.parser import KickstartParser + from pykickstart.version import makeVersion, DEVEL + parser = KickstartParser(makeVersion(DEVEL)) + parser.readKickstart(open('$fragment').read()) + " 2>/dev/null; then + echo "✓ $fragment" + else + echo "✗ $fragment" + errors=$((errors + 1)) + fi + done + echo "Fragment validation complete: $errors errors" + exit $errors diff --git a/.gitea/workflows/validate-recipes.yaml b/.gitea/workflows/validate-recipes.yaml new file mode 100644 index 0000000..99def86 --- /dev/null +++ b/.gitea/workflows/validate-recipes.yaml @@ -0,0 +1,50 @@ +name: validate-recipes + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + validate: + runs-on: fedora + container: + image: git.phyllo.me/devops/fedora-runner-image:latest + + steps: + - uses: https://git.phyllo.me/devops/checkout@v5 + with: + fetch-depth: 0 + + - name: Install dependencies + run: | + pip install PyYAML pytest pykickstart + + - name: Generate all recipes + run: | + cd scripts + make generate-recipes + + - name: Validate recipes (strict mode) + run: | + cd scripts + python3 generate_recipe.py --validate ../recipes/*.cfg --strict + + - name: Run unit tests + run: | + cd scripts + make test + + - name: Run integration tests + run: | + cd scripts + make test-integration + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: _pytest_cache/ + if-no-files-found: ignore diff --git a/dishes/virtual-desktop_43.cfg b/dishes/virtual-desktop_43.cfg index 1614a92..2987699 100644 --- a/dishes/virtual-desktop_43.cfg +++ b/dishes/virtual-desktop_43.cfg @@ -1,4 +1,4 @@ -# Generated by pykickstart v3.66 +# Generated by pykickstart v3.69 #version=DEVEL # Use text mode install text @@ -14,8 +14,6 @@ lang en_US.UTF-8 network --bootproto=dhcp --device=link --hostname=phyllome-alpha --activate # Shutdown after installation shutdown -repo --name="fedora" --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64 -repo --name="updates" --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f43&arch=x86_64 #Root password rootpw --lock # SELinux configuration @@ -24,8 +22,6 @@ selinux --enforcing services --enabled="NetworkManager,systemd-resolved" # System timezone timezone Europe/Zurich --utc -# Use network installation -url --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64" # X Window System configuration information xconfig --defaultdesktop=GNOME --startxonboot # System bootloader configuration @@ -35,17 +31,9 @@ zerombr # Partition clearing information clearpart --all --initlabel # Disk partitioning information -part /boot/efi --fstype="efi" --size=512 --fsoptions="umask=0077,shortname=winnt" --label=efi -part /boot --fstype="ext4" --size=2048 --label=boot -part / --fstype="ext4" --grow --label=root --mkfsoptions="-O encrypt,fast_commit" - -%post --logfile=/mnt/sysimage/root/post.log - -localectl set-keymap ch-fr # Set keymap to `ch-fr`. Alternatively, `us` can be picked. -dnf update -y # Update the system -grub2-mkconfig -o /boot/grub2/grub.cfg # Unsure it is actually useful - -%end +part /boot/efi --fstype="efi" --size=512 --fsoptions="umask=0077,shortname=winnt" --label="efi" +part /boot --fstype="ext4" --size=2048 --label="boot" +part / --fstype="ext4" --grow --label="root" --mkfsoptions="-O encrypt,fast_commit" %post --nochroot --logfile=/mnt/sysimage/root/base-desktop-gnome.log diff --git a/external/kickstart-tests b/external/kickstart-tests new file mode 160000 index 0000000..f3da604 --- /dev/null +++ b/external/kickstart-tests @@ -0,0 +1 @@ +Subproject commit f3da604ad81aca1757a55bc970b344f3c4f218a3 diff --git a/external/pykickstart b/external/pykickstart new file mode 160000 index 0000000..601ec2d --- /dev/null +++ b/external/pykickstart @@ -0,0 +1 @@ +Subproject commit 601ec2d2f254909f56e2ee961d2056e0d5bc4a97 diff --git a/fragments/platform/generic-43/bootloader/grub.ks b/fragments/platform/generic-43/bootloader/grub.ks new file mode 100644 index 0000000..54bb768 --- /dev/null +++ b/fragments/platform/generic-43/bootloader/grub.ks @@ -0,0 +1,3 @@ +# GRUB bootloader configuration for live media (no location, minimal timeout) + +bootsupport --timeout=1 # Set the GNU GRUB bootloader timeout to 1 and to location to none diff --git a/fragments/platform/generic-43/bootloader/systemd-boot.ks b/fragments/platform/generic-43/bootloader/systemd-boot.ks new file mode 100644 index 0000000..75a641a --- /dev/null +++ b/fragments/platform/generic-43/bootloader/systemd-boot.ks @@ -0,0 +1,3 @@ +# systemd-boot bootloader configuration for live media (no location, minimal timeout) + +bootsupport --sdboot --location=none --timeout=1 # Use systemd-boot and set location to none diff --git a/fragments/platform/generic-43/repo/fedora-mirrors.ks b/fragments/platform/generic-43/repo/fedora-mirrors.ks new file mode 100644 index 0000000..41722b8 --- /dev/null +++ b/fragments/platform/generic-43/repo/fedora-mirrors.ks @@ -0,0 +1,5 @@ +# Fedora 43 repositories + +repo --name=fedora --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64 # Official Fedora mirror +repo --name=updates --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f43&arch=x86_64 # Official Fedora updates mirror +url --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64 # Official Fedora updates mirror diff --git a/fragments/platform/generic-43/repo/iso-mirror.ks b/fragments/platform/generic-43/repo/iso-mirror.ks new file mode 100644 index 0000000..887109c --- /dev/null +++ b/fragments/platform/generic-43/repo/iso-mirror.ks @@ -0,0 +1,3 @@ +# Fedora ISO repository for F43 (for live ISO builds) + +# repo --name=fedora-iso --metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-43&arch=x86_64 # Uncomment for ISO builds diff --git a/fragments/platform/generic-rawhide/bootloader/grub.ks b/fragments/platform/generic-rawhide/bootloader/grub.ks new file mode 100644 index 0000000..54bb768 --- /dev/null +++ b/fragments/platform/generic-rawhide/bootloader/grub.ks @@ -0,0 +1,3 @@ +# GRUB bootloader configuration for live media (no location, minimal timeout) + +bootsupport --timeout=1 # Set the GNU GRUB bootloader timeout to 1 and to location to none diff --git a/fragments/platform/generic-rawhide/bootloader/systemd-boot.ks b/fragments/platform/generic-rawhide/bootloader/systemd-boot.ks new file mode 100644 index 0000000..75a641a --- /dev/null +++ b/fragments/platform/generic-rawhide/bootloader/systemd-boot.ks @@ -0,0 +1,3 @@ +# systemd-boot bootloader configuration for live media (no location, minimal timeout) + +bootsupport --sdboot --location=none --timeout=1 # Use systemd-boot and set location to none diff --git a/fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks b/fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks new file mode 100644 index 0000000..d1ef785 --- /dev/null +++ b/fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks @@ -0,0 +1,4 @@ +# Fedora Rawhide repositories + +repo --name=rawhide --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=rawhide&arch=x86_64 +url --mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=rawhide&arch=x86_64 diff --git a/fragments/shared/bootloader/grub.ks b/fragments/shared/bootloader/grub.ks new file mode 100644 index 0000000..e51efb2 --- /dev/null +++ b/fragments/shared/bootloader/grub.ks @@ -0,0 +1,3 @@ +# GRUB bootloader configuration + +bootsupport --timeout=1 # Set the GNU GRUB bootloader timeout to 1 diff --git a/fragments/shared/bootloader/systemd-boot.ks b/fragments/shared/bootloader/systemd-boot.ks new file mode 100644 index 0000000..0fb844f --- /dev/null +++ b/fragments/shared/bootloader/systemd-boot.ks @@ -0,0 +1,3 @@ +# systemd-boot bootloader configuration + +bootsupport --sdboot --location=mbr --timeout=1 # Use systemd-boot and set a timeout to 1 diff --git a/fragments/shared/core/base.ks b/fragments/shared/core/base.ks new file mode 100644 index 0000000..3dc7398 --- /dev/null +++ b/fragments/shared/core/base.ks @@ -0,0 +1,5 @@ +# Core base kickstart configuration +# Common settings for all Phyllome OS installations + +text # Kickstart installation in text mode +poweroff # Shut down the system after a successful installation diff --git a/fragments/shared/core/locale.ks b/fragments/shared/core/locale.ks new file mode 100644 index 0000000..7ae59e6 --- /dev/null +++ b/fragments/shared/core/locale.ks @@ -0,0 +1,5 @@ +# Keyboard, language, and timezone configuration + +keyboard --xlayouts='ch (fr)' # Set keyboard layouts for Romandy +lang en_US.UTF-8 # Set system language to American English. More languages could be supported: --addsupport=cs_CZ,de_DE,en_UK +timezone Europe/Zurich --utc # Set system timezone to Zurich diff --git a/fragments/shared/core/network.ks b/fragments/shared/core/network.ks new file mode 100644 index 0000000..5c14534 --- /dev/null +++ b/fragments/shared/core/network.ks @@ -0,0 +1,3 @@ +# Network configuration + +network --onboot=yes --bootproto=dhcp --device=link --activate --hostname=phyllome-alpha # Configure network devices, enable them at boot time device and sets a particular hostname. "link" selects the first device reaching an up state diff --git a/fragments/shared/core/security/disabled.ks b/fragments/shared/core/security/disabled.ks new file mode 100644 index 0000000..96b0078 --- /dev/null +++ b/fragments/shared/core/security/disabled.ks @@ -0,0 +1,5 @@ +# Security configuration - disabled mode + +rootpw --plaintext 1234 --allow-ssh # Root account is enabled with weak password and allow ssh +selinux --disabled # Disable SELinux +firewall --enabled --ssh # Reject incoming connections that are not in response to outbound requests except SSH diff --git a/fragments/shared/core/security/enabled.ks b/fragments/shared/core/security/enabled.ks new file mode 100644 index 0000000..5405e87 --- /dev/null +++ b/fragments/shared/core/security/enabled.ks @@ -0,0 +1,5 @@ +# Security configuration - enabled mode + +rootpw --lock # No root login from the console +selinux --enforcing # Set SELinux to enforcing mode +firewall --enabled # Enable firewall diff --git a/fragments/shared/core/services.ks b/fragments/shared/core/services.ks new file mode 100644 index 0000000..c728ea5 --- /dev/null +++ b/fragments/shared/core/services.ks @@ -0,0 +1,3 @@ +# System services + +services --enabled=NetworkManager,systemd-resolved # List of comma-separated systemd services that can be explicitly enabled diff --git a/fragments/shared/desktop/gnome/config.ks b/fragments/shared/desktop/gnome/config.ks new file mode 100644 index 0000000..9881741 --- /dev/null +++ b/fragments/shared/desktop/gnome/config.ks @@ -0,0 +1 @@ +xconfig --startxonboot --defaultdesktop=GNOME # Start the display session on boot. Although it says --startx, which seems to imply xorg, it is actually generic and thus works also with Wayland. diff --git a/fragments/shared/desktop/gnome/packages.ks b/fragments/shared/desktop/gnome/packages.ks new file mode 100644 index 0000000..2ace478 --- /dev/null +++ b/fragments/shared/desktop/gnome/packages.ks @@ -0,0 +1,51 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# GNOME desktop packages + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies + +@base-graphical +## provides the following as mandatory packages: +# mesa-dri-drivers +# mesa-vulkan-drivers +# plymouth-system-theme + +# @critical-path-gnome not using this group but hand-picking packages +## Mandatory packages found in hidden `@critical-path-gnome` group (`dnf group info --hidden critical-path-gnome`) +## Not using +## provides the following as mandatory packages: +bash-color-prompt # Color prompt for bash shell +dconf # A configuration system +gdm # The GNOME Display Manager +# gnome-classic-session # GNOME "classic" mode session +gnome-control-center # Utilities to configure the GNOME desktop +# gnome-initial-setup # Bootstrapping your OS +gnome-shell # Window management and application launching for GNOME +gvfs-fuse # FUSE support for gvfs +# ptyxis # A container oriented terminal for GNOME +### and the following as default packages +# NetworkManager-pptp # NetworkManager VPN plugin for PPTP +# avahi # Local network service discovery +# gnome-bluetooth # Bluetooth graphical utilities +gnome-session-wayland-session # Desktop file for wayland based gnome session +# gnome-software # A software center for GNOME +nautilus # File manager for GNOME +# toolbox # Tool for interactive command line environments on Linux + +## Extra hand-picked packages +gnome-backgrounds.noarch # wallpapers from the GNOME project +gnome-terminal # Terminal emulator for GNOME +dejavu-sans-mono-fonts # the gnome-shell package doesn't include much fonts by default, resulting in weird spacings in GNOME Terminal. GNOME Terminal unfortunately doesn't automatically pick this font +firefox # Mozilla Firefox Web browser +mozilla-ublock-origin.noarch # An efficient blocker for Firefox + +pipewire-alsa # PipeWire media server ALSA support +pipewire-pulseaudio # PipeWire PulseAudio implementation +pipewire-jack-audio-connection-kit # PipeWire JACK implementation + +%end # End of the packagages section diff --git a/fragments/shared/desktop/gnome/post-scripts.ks b/fragments/shared/desktop/gnome/post-scripts.ks new file mode 100644 index 0000000..41d9746 --- /dev/null +++ b/fragments/shared/desktop/gnome/post-scripts.ks @@ -0,0 +1,65 @@ +# GNOME desktop post-installation configuration + +%post --nochroot --log=/mnt/sysimage/root/gnome-desktop-post.log # Beginning of %post section. Those commands are executed outside the chroot environment + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.media-handling.gschema.override<< EOF +[org.gnome.desktop.media-handling] +automount-open=false +autorun-never=true +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.Terminal.gschema.override<< EOF +[org.gnome.Terminal.Legacy.Profile] +font='DejaVu Sans Mono 12' +use-system-font=false +auditable-bell=false +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.wm.preferences.gschema.override<< EOF +[org.gnome.desktop.wm.preferences] +button-layout=':minimize,maximize,close' +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.a11y.gschema.override<< EOF +[org.gnome.desktop.a11y] +always-show-universal-access-status=true +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.interface.gschema.override<< EOF +[org.gnome.desktop.interface] +enable-animations=false +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.privacy.gschema.override<< EOF +[org.gnome.desktop.privacy] +remove-old-temp-files=true +remember-recent-file=false +remember-app-usage=false +disable-camera=true +disable-microphone=true +disable-sound-output=true +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.search-providers.gschema.override<< EOF +[org.gnome.desktop.search-providers] +disable-external=true +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.notifications.gschema.override<< EOF +[org.gnome.desktop.notifications.application] +enable-sound-alerts=false +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.sound.gschema.override<< EOF +[org.gnome.desktop.sound] +event-sounds=false +EOF + +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.gnome.desktop.thumbnailers.gschema.override<< EOF +[org.gnome.desktop.thumbnailers] +disable-all=true +EOF + +glib-compile-schemas /mnt/sysimage/usr/share/glib-2.0/schemas/ + +%end # End of the %post section diff --git a/fragments/shared/desktop/labwc/config.ks b/fragments/shared/desktop/labwc/config.ks new file mode 100644 index 0000000..0b6ed01 --- /dev/null +++ b/fragments/shared/desktop/labwc/config.ks @@ -0,0 +1,19 @@ +# Untested + +xconfig --startxonboot # Start the display session on boot. Although it says --startx, which seems to imply xorg, it is actually generic and thus works also with Wayland. + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +@base-graphical +## provides the following as mandatory packages: +# mesa-dri-drivers +# mesa-vulkan-drivers +# plymouth-system-theme + +labwc # A Wayland window-stacking compositor + +## Extra hand-picked packages +firefox # Mozilla Firefox Web browser +mozilla-ublock-origin.noarch # An efficient blocker for Firefox + +%end # End of the packagages section diff --git a/fragments/shared/desktop/vmm/packages.ks b/fragments/shared/desktop/vmm/packages.ks new file mode 100644 index 0000000..44eb336 --- /dev/null +++ b/fragments/shared/desktop/vmm/packages.ks @@ -0,0 +1,68 @@ +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +virt-manager # Install virt-manager, a graphical front-end for QEMU/KVM + +%end + +%post --nochroot --log=/mnt/sysimage/root/virt-manager-post.log # Beginning of %post section. Those commands are executed outside the chroot environment. Add logging. + +# Create a file to autostart virt-manager +cat > /mnt/sysimage/etc/xdg/autostart/virt-manager.desktop << EOF +[Desktop Entry] +Type=Application +Name=Virtual Machine Manager +Exec=virt-manager +EOF + +# Modify the default virt-manager behavior for misc. options +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.virt-manager.virt-manager.gschema.override<< EOF + +# Modify the default virt-manager behavior for misc. options +[org.virt-manager.virt-manager] +xmleditor-enabled=true +manager-window-height=600 +manager-window-width=200 + +# Libvirt URIs listed in the manager window +[org.virt-manager.virt-manager.connections] +uris=['qemu:///system', 'qemu:///session'] +autoconnect=['qemu:///session'] + +# Show usage in the domain list +[org.virt-manager.virt-manager.vmlist-fields] +cpu-usage=false + +# Settings related to statistics +[org.virt-manager.virt-manager.stats] +update-interval=3 +enable-disk-poll=true +enable-memory-poll=true +enable-net-poll=true + +# Default behavior for the console +[org.virt-manager.virt-manager.console] +scaling=2 +resize-guest=1 +autoconnect=false + +# Do not show toolbar +[org.virt-manager.virt-manager.details] +show-toolbar=false + +# Modify default values for new VMs +[org.virt-manager.virt-manager.new-vm] +storage-format='raw' +cpu-default='host-model' +graphics-type='spice' + +# Modify the default virt-manager behavior for confirmation dialogues +[org.virt-manager.virt-manager.confirm] +forcepoweroff=false +removedev=false +unapplied-dev=false + +EOF + +glib-compile-schemas /mnt/sysimage/usr/share/glib-2.0/schemas/ + +%end # End of the %post section diff --git a/fragments/shared/desktop/vmm/post-scripts.ks b/fragments/shared/desktop/vmm/post-scripts.ks new file mode 100644 index 0000000..ee900ab --- /dev/null +++ b/fragments/shared/desktop/vmm/post-scripts.ks @@ -0,0 +1,56 @@ +# Virt-manager post-installation configuration + +%post --nochroot --log=/mnt/sysimage/root/vmm-post-scripts.log # Beginning of %post section + +# Create a file to autostart virt-manager +cat > /mnt/sysimage/etc/xdg/autostart/virt-manager.desktop << EOF +[Desktop Entry] +Type=Application +Name=Virtual Machine Manager +Exec=virt-manager +EOF + +# Modify the default virt-manager behavior for misc. options +cat >> /mnt/sysimage/usr/share/glib-2.0/schemas/org.virt-manager.virt-manager.gschema.override<< EOF + +[org.virt-manager.virt-manager] +xmleditor-enabled=true +manager-window-height=600 +manager-window-width=200 + +[org.virt-manager.virt-manager.connections] +uris=['qemu:///system', 'qemu:///session'] +autoconnect=['qemu:///session'] + +[org.virt-manager.virt-manager.vmlist-fields] +cpu-usage=false + +[org.virt-manager.virt-manager.stats] +update-interval=3 +enable-disk-poll=true +enable-memory-poll=true +enable-net-poll=true + +[org.virt-manager.virt-manager.console] +scaling=2 +resize-guest=1 +autoconnect=false + +[org.virt-manager.virt-manager.details] +show-toolbar=false + +[org.virt-manager.virt-manager.new-vm] +storage-format='raw' +cpu-default='host-model' +graphics-type='spice' + +[org.virt-manager.virt-manager.confirm] +forcepoweroff=false +removedev=false +unapplied-dev=false + +EOF + +glib-compile-schemas /mnt/sysimage/usr/share/glib-2.0/schemas/ + +%end # End of the %post section diff --git a/fragments/shared/guest-agents/packages.ks b/fragments/shared/guest-agents/packages.ks new file mode 100644 index 0000000..80e0be0 --- /dev/null +++ b/fragments/shared/guest-agents/packages.ks @@ -0,0 +1,8 @@ +# Guest agents for virtual machines + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +qemu-guest-agent # "QEMU guest agent" The qemu-guest agent is unnecessary for a bare-metal system. However, it is included here to cover cases where this kickstart file is used to deploy a virtual machine +spice-vdagent # "Agent for Spice guests" The spice agent is unnecessary for a bare-metal system. However, it is included here to cover cases where this kickstart file is used to deploy a virtual machine + +%end # End of the packages section diff --git a/fragments/shared/hypervisor/amdcpu.ks b/fragments/shared/hypervisor/amdcpu.ks new file mode 100644 index 0000000..25993a3 --- /dev/null +++ b/fragments/shared/hypervisor/amdcpu.ks @@ -0,0 +1,9 @@ +# AMD CPU optimization for hypervisor + +%post --nochroot --log=/mnt/sysimage/root/hypervisor-amdcpu-post.log # Beginning of %post section. Those commands are executed outside the chroot environment. Logging is enabled to help with post-installation troubleshooting + +sed -i 's/\(quiet\)/\1 iommu=pt rd.driver.pre=vfio-pci/i' /mnt/sysimage/etc/default/grub # Load kernel modules in GRUB. + +echo "options kvm_amd nested=1" >> /mnt/sysimage/etc/modprobe.d/kvm.conf # Add support for nested virtualization + +%end # End of the %post section diff --git a/fragments/shared/hypervisor/base/packages.ks b/fragments/shared/hypervisor/base/packages.ks new file mode 100644 index 0000000..194ba76 --- /dev/null +++ b/fragments/shared/hypervisor/base/packages.ks @@ -0,0 +1,30 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# Virtualization packages + +%packages --exclude-weakdeps # Beginning of the packages section. Does not include weak dependencies. + +qemu-kvm # QEMU metapackage for KVM support +libvirt # Library providing a simple virtualization API +libvirt-client # Client side utilities of the libvirt library +libvirt-client-qemu # Additional client side utilities for QEMU. Used to interact with some QEMU specific features of libvirt. +libvirt-daemon # Server side daemon and supporting files for libvirt library +libvirt-daemon-common # Miscellaneous files and utilities used by other libvirt daemons +libvirt-daemon-config-network # Default configuration files for the libvirtd daemon. Provides NAT based networking +libvirt-daemon-driver-interface # Interface driver plugin for the libvirtd daemon +libvirt-daemon-driver-network # The network driver plugin for the libvirtd daemon, providing an implementation of the virtual network APIs using the Linux bridge capabilities. +libvirt-daemon-driver-qemu # QEMU driver plugin for the libvirtd daemon +libvirt-daemon-kvm # Server side daemon & driver required to run KVM guests +libvirt-daemon-log # Server side daemon for managing logs +libvirt-daemon-qemu # Server side daemon and driver required to manage the virtualization capabilities of the QEMU TCG emulators +libvirt-nss # Libvirt plugin for Name Service Switch +libvirt-dbus # libvirt D-Bus API binding +libvirt-daemon-driver-ch # Cloud-Hypervisor driver plugin for libvirtd daemon +virt-install # Utilities for installing virtual machines + +%end # End of the packages section diff --git a/fragments/shared/hypervisor/base/post-scripts.ks b/fragments/shared/hypervisor/base/post-scripts.ks new file mode 100644 index 0000000..5117b47 --- /dev/null +++ b/fragments/shared/hypervisor/base/post-scripts.ks @@ -0,0 +1,31 @@ +# Hypervisor base post-installation configuration + +%post --nochroot --log=/mnt/sysimage/root/hypervisor-base-post.log # Beginning of %post section. Those commands are executed outside the chroot environment. Logging is enabled to help with post-installation troubleshooting + +# Load kernel modules by adding vfio, vfio_pci, vfio_iommu_type1, vfio_virqfd +echo "vfio" > /mnt/sysimage/etc/modules-load.d/vfio.conf +echo "vfio-pci" > /mnt/sysimage/etc/modules-load.d/vfio-pci.conf +echo "vfio_iommu_type1" > /mnt/sysimage/etc/modules-load.d/vfio_iommu_type1.conf +echo "vfio_virqfd" > /mnt/sysimage/etc/modules-load.d/vfio_virqfd.conf + +mkdir /mnt/sysimage/var/lib/libvirt/isos # Create a directory to store iso images. SELinux is already taking this one into account. + +# wget https://boot.netboot.xyz/ipxe/netboot.xyz.iso -P /mnt/sysimage/var/lib/libvirt/isos/ # fetch netboot.xyz iso and store it to the newly created iso directory + +# # virsh commands fail in a kickstart environment (chroot or not it seems). would need to fetch a script and execute post-launch with a delay, for example using a systemd unit +# virsh pool-define-as isos dir - - - - /mnt/sysimage/var/lib/libvirt/isos/ # Make libvirt aware of this new directory by creating a so-called 'pool'. +# virsh pool-build isos # Build the pool +# virsh pool-start isos # Start it +# virsh pool-autostart isos # Set-it to autostart + +# fetch custom script and make it executable +# wget https://raw.githubusercontent.com/PhyllomeOS/phyllomeos/main/post-first-startup-scripts/virtualization-tweaks-root-needed.sh -P /mnt/sysimage/usr/local/bin/ +# chmod +x /mnt/sysimage/usr/local/bin/virtualization-tweaks-root-needed.sh + +# wget https://raw.githubusercontent.com/PhyllomeOS/xml-definition-for-domains/main/xml/system/linux.xml +# virsh define linux.xml + +# wget https://raw.githubusercontent.com/PhyllomeOS/xml-definition-for-domains/main/xml/system/windows.xml +# virsh define windows.xml + +%end # End of the %post section diff --git a/fragments/shared/hypervisor/base/services.ks b/fragments/shared/hypervisor/base/services.ks new file mode 100644 index 0000000..48c5aab --- /dev/null +++ b/fragments/shared/hypervisor/base/services.ks @@ -0,0 +1,3 @@ +# Hypervisor base configuration + +services --enabled="NetworkManager,systemd-resolved,libvirtd" # Without libvirtd here, it appears the service won't automatically start diff --git a/fragments/shared/hypervisor/intelcpu.ks b/fragments/shared/hypervisor/intelcpu.ks new file mode 100644 index 0000000..cf6ed0f --- /dev/null +++ b/fragments/shared/hypervisor/intelcpu.ks @@ -0,0 +1,9 @@ +# Intel CPU optimization for hypervisor + +%post --nochroot --log=/mnt/sysimage/root/hypervisor-intelcpu-post.log # Beginning of %post section. Those commands are executed outside the chroot environment. Logging is enabled to help with post-installation troubleshooting + +sed -i 's/\(quiet\)/\1 intel_iommu=on iommu=pt rd.driver.pre=vfio-pci/i' /mnt/sysimage/etc/default/grub # Load kernel modules in GRUB. + +echo "options kvm_intel nested=1" >> /mnt/sysimage/etc/modprobe.d/kvm.conf # Add support for nested virtualization on Intel CPUs + +%end # End of the %post section diff --git a/fragments/shared/hypervisor/intelgpu.ks b/fragments/shared/hypervisor/intelgpu.ks new file mode 100644 index 0000000..c6280f2 --- /dev/null +++ b/fragments/shared/hypervisor/intelgpu.ks @@ -0,0 +1,11 @@ +# Intel GPU passthrough configuration + +%post --nochroot --log=/mnt/sysimage/root/hypervisor-intelgpu-post.log # Beginning of %post section. Those commands are executed outside the chroot environment. Logging is enabled to help with post-installtion troubleshooting + +sed -i 's/\(vfio-pci\)/\1 i915.enable_gvt=1/i' /mnt/sysimage/etc/default/grub # Load kernel modules in grub. + +# Load extra kernel modules to enable vfio-mdev on selected hardware +echo "kvmgt" > /mnt/sysimage/etc/modules-load.d/kvmgt.conf # Load specific kernel modules kvmgt and vfio-mdev, for Intel (tm) GVT-g and Nvidia (tm) +echo "vfio-mdev" > /mnt/sysimage/etc/modules-load.d/vfio-mdev.conf # Load specific kernel modules kvmgt and vfio-mdev, for Intel (tm) GVT-g and Nvidia (tm) + +%end # End of the %post section diff --git a/fragments/shared/initial-setup/desktop/config.ks b/fragments/shared/initial-setup/desktop/config.ks new file mode 100644 index 0000000..857860f --- /dev/null +++ b/fragments/shared/initial-setup/desktop/config.ks @@ -0,0 +1,25 @@ +# Initial setup - GNOME desktop mode + +firstboot --enable # Initial Setup will start after the first reboot + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +# TO BE TESTED -> initial-setup-gui # Graphical user interface for the initial-setup utility +# TO BE TESTED -> initial-setup-gui-wayland-generic.x86_64 # Run the initial-setup GUI in Wayland +gnome-initial-setup # Add GNOME initial setup too to let user create local account. + +%end # End of the packages section + +# %post --nochroot --log=/mnt/sysimage/root/initial-setup-gnome.log # Beginning of %post section. Those commands are executed outside the chroot environment. Add logging. +# +# truncate -s 0 /mnt/sysimage/usr/share/gnome-initial-setup/vendor.conf # remove content of vendor.conf so that all options are made available +# +# ## Append lines to existing vendor.conf file, so that options are skipped upon reboot +# cat >> /mnt/sysimage/usr/share/gnome-initial-setup/vendor.conf<< EOF +# [pages] +# skip=privacy +# [goa] +# providers=local-first! +# EOF +# +# %end # End of the %post section diff --git a/fragments/shared/initial-setup/desktop/packages.ks b/fragments/shared/initial-setup/desktop/packages.ks new file mode 100644 index 0000000..1bd0545 --- /dev/null +++ b/fragments/shared/initial-setup/desktop/packages.ks @@ -0,0 +1,9 @@ +# Initial setup packages - GNOME desktop mode + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +# TO BE TESTED -> initial-setup-gui # Graphical user interface for the initial-setup utility +# TO BE TESTED -> initial-setup-gui-wayland-generic.x86_64 # Run the initial-setup GUI in Wayland +gnome-initial-setup # Add GNOME initial setup too to let user create local account. + +%end # End of the packages section diff --git a/fragments/shared/initial-setup/generic-wayland/config.ks b/fragments/shared/initial-setup/generic-wayland/config.ks new file mode 100644 index 0000000..6da2504 --- /dev/null +++ b/fragments/shared/initial-setup/generic-wayland/config.ks @@ -0,0 +1,11 @@ +# Initial setup - generic wayland desktop mode + +firstboot --enable # Initial Setup will start after the first reboot + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +# TO BE TESTED -> initial-setup-gui # Graphical user interface for the initial-setup utility +# TO BE TESTED -> initial-setup-gui-wayland-generic.x86_64 # Run the initial-setup GUI in Wayland +gnome-initial-setup # Add GNOME initial setup too to let user create local account. + +%end # End of the packages section diff --git a/fragments/shared/initial-setup/generic-wayland/packages.ks b/fragments/shared/initial-setup/generic-wayland/packages.ks new file mode 100644 index 0000000..6180cc6 --- /dev/null +++ b/fragments/shared/initial-setup/generic-wayland/packages.ks @@ -0,0 +1,8 @@ +# Initial setup packages - generic wayland desktop mode + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +initial-setup-gui # Graphical user interface for the initial-setup utility +initial-setup-gui-wayland-generic.x86_64 # Run the initial-setup GUI in Wayland + +%end # End of the packages section diff --git a/fragments/shared/initial-setup/server/config.ks b/fragments/shared/initial-setup/server/config.ks new file mode 100644 index 0000000..f2fa579 --- /dev/null +++ b/fragments/shared/initial-setup/server/config.ks @@ -0,0 +1,9 @@ +# Initial setup - server mode + +firstboot --enable --reconfig # Enable the Setup Agent to start at boot time in reconfiguration mode. This mode enables the language, mouse, keyboard, root password, security level, time zone, and networking configuration options in addition to the default ones + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +initial-setup # Initial setup package + +%end # End of the packages section diff --git a/fragments/shared/initial-setup/server/packages.ks b/fragments/shared/initial-setup/server/packages.ks new file mode 100644 index 0000000..6f322e6 --- /dev/null +++ b/fragments/shared/initial-setup/server/packages.ks @@ -0,0 +1,7 @@ +# Initial setup packages - server mode + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. + +initial-setup # Initial setup package + +%end # End of the packages section diff --git a/fragments/shared/live/core/base.ks b/fragments/shared/live/core/base.ks new file mode 100644 index 0000000..7a571f5 --- /dev/null +++ b/fragments/shared/live/core/base.ks @@ -0,0 +1,3 @@ +# Live core base configuration + +poweroff # Shut down the system after a successful installation diff --git a/fragments/shared/live/core/bootloader/grub.ks b/fragments/shared/live/core/bootloader/grub.ks new file mode 100644 index 0000000..bda9a06 --- /dev/null +++ b/fragments/shared/live/core/bootloader/grub.ks @@ -0,0 +1,3 @@ +# Live core bootloader configuration + +bootloader --timeout=1 # Set the GNU GRUB bootloader timeout to 1 and to location to none diff --git a/fragments/shared/live/core/bootloader/systemd-boot.ks b/fragments/shared/live/core/bootloader/systemd-boot.ks new file mode 100644 index 0000000..8cd13db --- /dev/null +++ b/fragments/shared/live/core/bootloader/systemd-boot.ks @@ -0,0 +1,3 @@ +# Live core systemd-boot configuration + +bootloader --sdboot --location=none --timeout=1 # Use systemd-boot and set location to none diff --git a/fragments/shared/live/core/packages.ks b/fragments/shared/live/core/packages.ks new file mode 100644 index 0000000..e9a9415 --- /dev/null +++ b/fragments/shared/live/core/packages.ks @@ -0,0 +1,25 @@ +# Anaconda tools and kernel packages for live media + +%packages # Beginning of the package section. Include weak dependencies. Description courtesy of the Fedora project + +@anaconda-tools + +# Explicitly specified here: +# walters: because otherwise dependency loops cause yum issues. +kernel +kernel-modules +kernel-modules-extra + +# Need aajohan-comfortaa-fonts for the SVG rnotes images +aajohan-comfortaa-fonts + +# Without this, initramfs generation during live image creation fails: #1242586 +dracut-live + +# anaconda needs the locales available to run for different locales +glibc-all-langpacks + +# provide the livesys scripts +livesys-scripts + +%end diff --git a/fragments/shared/live/core/storage.ks b/fragments/shared/live/core/storage.ks new file mode 100644 index 0000000..16d7830 --- /dev/null +++ b/fragments/shared/live/core/storage.ks @@ -0,0 +1,7 @@ +# Live core storage configuration + +zerombr # WARNING : Dangerous command ! Will clear the Master Boot Record +clearpart --all --initlabel # Partition clearing information. This setup uses GPT by default. + +part / --fstype="ext4" --size=5120 # Create a root partition of around 7GB +part / --size=8576 diff --git a/fragments/shared/live/hypervisor.ks b/fragments/shared/live/hypervisor.ks new file mode 100644 index 0000000..2b0c12e --- /dev/null +++ b/fragments/shared/live/hypervisor.ks @@ -0,0 +1,10 @@ +# Hypervisor package inclusion for live server + +%packages --exclude-weakdeps + +qemu-kvm +libvirt +libvirt-client +virt-install + +%end diff --git a/fragments/shared/live/post/base.ks b/fragments/shared/live/post/base.ks new file mode 100644 index 0000000..e6176b4 --- /dev/null +++ b/fragments/shared/live/post/base.ks @@ -0,0 +1,53 @@ +# Live core post-installation configuration + +%post --log=/mnt/sysimage/root/live-core-post.log # Beginning of the post-installation section. Add logging. + +# Enable livesys services +systemctl enable livesys.service +systemctl enable livesys-late.service + +# enable tmpfs for /tmp +systemctl enable tmp.mount + +# make it so that we don't do writing to the overlay for things which +# are just tmpdirs/caches +# note https://bugzilla.redhat.com/show_bug.cgi?id=1135475 +cat >> /etc/fstab << EOF +vartmp /var/tmp tmpfs defaults 0 0 +EOF + +# work around for poor key import UI in PackageKit +rm -f /var/lib/rpm/__db* +echo "Packages within this LiveCD" +rpm -qa --qf '%{size}\t%{name}-%{version}-%{release}.%{arch}\n' |sort -rn +# Note that running rpm recreates the rpm db files which aren't needed or wanted +rm -f /var/lib/rpm/__db* + +# go ahead and pre-make the man -k cache (#455968) +/usr/bin/mandb + +# make sure there aren't core files lying around +rm -f /core* + +# remove random seed, the newly installed instance should make it's own +rm -f /var/lib/systemd/random-seed + +# convince readahead not to collect +# FIXME: for systemd + +echo 'File created by kickstart. See systemd-update-done.service(8).' \ + | tee /etc/.updated >/var/.updated + +# Drop the rescue kernel and initramfs, we don't need them on the live media itself. +# See bug 1317709 +rm -f /boot/*-rescue* + +# Disable network service here, as doing it in the services line +# fails due to RHBZ #1369794 +systemctl disable network + +# Remove machine-id on pre generated images +rm -f /etc/machine-id +touch /etc/machine-id + +%end diff --git a/fragments/shared/live/post/session.ks b/fragments/shared/live/post/session.ks new file mode 100644 index 0000000..417d8a6 --- /dev/null +++ b/fragments/shared/live/post/session.ks @@ -0,0 +1,8 @@ +# Live session configuration + +%post --log=/mnt/sysimage/root/post-live-session.log # Beginning of the post-installation section. Add logging. + +# set livesys session type +sed -i 's/^livesys_session=.*/livesys_session="gnome"/' /etc/sysconfig/livesys + +%end diff --git a/fragments/shared/packages/core-group.ks b/fragments/shared/packages/core-group.ks new file mode 100644 index 0000000..6f0e094 --- /dev/null +++ b/fragments/shared/packages/core-group.ks @@ -0,0 +1,67 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# Core DNF package group +# More information: https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#id240 + +%packages --exclude-weakdeps # Beginning of the packages section. Excludes weak package dependencies. Package description courtesy of the Fedora project + +@core +## Mandatory packages found in hidden `core` group (`dnf group info --hidden core`) +# audit # User space tools for kernel auditing +# basesystem # The skeleton package which defines a simple Fedora system +# bash # The Bourne Again SHell, a command-line interpreter. +# coreutils # A set of basic GNU tools commonly used in shell scripts +# curl # A utility for getting files from remote servers (FTP, HTTP, and others) +# dhcp-client # Provides the ISC DHCP client daemon and dhclient-script +# dnf5 # Command-line package manager +# e2fsprogs # Utilities for managing ext2, ext3, and ext4 file systems +# filesystem # The basic directory layout for a Linux system +# glibc # The GNU libc libraries +# hostname # Utility to set/show the host name or domain name +# iproute # Advanced IP routing and network device configuration tools +# iputils # Network monitoring tools including ping +# kbd # Tools for configuring the console (keyboard, virtual terminals, etc.) +# kernel # The Linux kernel +# less # A text file browser similar to more, but better. Can be excluded +# man-db # Tools for searching and reading man pages. Can be excluded +# ncurses # Ncurses support utilities +# openssh-clients # An open source SSH client applications. Can be excluded +# openssh-server # An open source SSH server daemon. Can be excluded +# parted # The GNU disk partition manipulation program +# policycoreutils # SELinux policy core utilities. Can be excluded +# procps-ng # System and process monitoring utilities +# rootfiles # The basic required files for the root user's directory +# rpm # The RPM package management system +# selinux-policy-targeted # SELinux targeted policy. Can be excluded +# setup # A set of system configuration and setup files +# shadow-utils # Utilities for managing accounts and shadow password files +# sssd-common # Common files for the SSSD. Can be excluded +# sssd-kcm # An implementation of a Kerberos KCM server. Can be excluded +# sudo # Allows restricted root access for specified users +# systemd # System and Service Manager +# util-linux # Collection of basic system utilities +# vim-minimal # A minimal version of the VIM editor + +## Default packages found in hidden `core` group (`dnf group info --hidden core`) +# NetworkManager # Network connection manager and user applications +# dnf5-plugins # Plugins for dnf5 +# dracut-config-rescue # dracut configuration to turn on rescue image generation +# firewalld # A firewall daemon with D-Bus interface providing a dynamic firewall +# fwupd # Firmware update daemon +# plymouth # Graphical Boot Animation and Logger +# prefixdevname # Udev helper utility that provides network interface naming using user defined prefix +# systemd-resolved # Network Name Resolution manager +# zram-generator-defaults # Default configuration for zram-generator + +## Optionnal packages found in hidden `core` group (`dnf group info --hidden core`) +## Not installed by default unless "@core --optional" is used +# dracut-config-generic +# initial-setup +# initscripts + +%end # End of the packages section diff --git a/fragments/shared/packages/fedora-remix.ks b/fragments/shared/packages/fedora-remix.ks new file mode 100644 index 0000000..5c2505b --- /dev/null +++ b/fragments/shared/packages/fedora-remix.ks @@ -0,0 +1,18 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# Packages to be used to create a Fedora Remix and comply Fedora Remix legal guidelines: https://fedoraproject.org/wiki/Remix + +%packages --exclude-weakdeps + +fedora-remix-logos # Fedora Remix logos +generic-release # Generic release files +generic-logos # Icons and pictures +generic-release-common # Generic release files +generic-release-notes # Release Notes + +%end # End of the packages section diff --git a/fragments/shared/packages/hand-picked.ks b/fragments/shared/packages/hand-picked.ks new file mode 100644 index 0000000..315b5af --- /dev/null +++ b/fragments/shared/packages/hand-picked.ks @@ -0,0 +1,12 @@ +# Hand-picked packages + +%packages --exclude-weakdeps + +pciutils # PCI bus related utilities +libusb # Library for accessing USB devices +usbutils # Linux USB utilities +curl # transfer a URL +wget # An advanced file and recursive website downloader +nano # A small text editor + +%end # End of the packages section diff --git a/fragments/shared/packages/hardware-support.ks b/fragments/shared/packages/hardware-support.ks new file mode 100644 index 0000000..0146303 --- /dev/null +++ b/fragments/shared/packages/hardware-support.ks @@ -0,0 +1,61 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# Extended physical hardware support. Useful for bare metal deployments. + +%packages --exclude-weakdeps + +@hardware-support +## Mandatory packages found in hidden `hardware-support` group +# alsa-sof-firmware # Audio drivers and firmware for ALSA. Essential for audio functionality. +# amd-gpu-firmware # Firmware for AMD GPUs. Required for proper GPU operation. +# atheros-firmware # Firmware for Atheros wireless network adapters. Critical for wireless connectivity. +# b43-fwcutter # Utility for cutting firmware files for B43 drivers. Needed for driver compatibility. +# b43-openfwwf # Driver and firmware for B43 network cards. Essential for network card operation. +# brcmfmac-firmware # Firmware for Broadcom MAC controllers. Required for wireless and wired network performance. +# cirrus-audio-firmware # Firmware for Cirrus Logic audio chips. Necessary for audio hardware support. +# intel-audio-firmware # Firmware for Intel audio processors. Required for integrated audio functionality. +# intel-gpu-firmware # Firmware for Intel GPUs. Essential for GPU operation. +# intel-vsc-firmware # Firmware for Intel Video Scheduling Controller. Required for GPU performance. +# iwlegacy-firmware # Legacy firmware for older Intel wireless cards. Needed for compatibility. +# iwlwifi-dvm-firmware # Firmware for Intel Wireless Link 5100/5200 series. Crucial for wireless connectivity. +# iwlwifi-mvm-firmware # Firmware for Intel Wireless Link 5300/5400 series. Required for wireless performance. +# libertas-firmware # Firmware for Broadcom wireless network cards. Essential for wireless connectivity. +# mt7xxx-firmware # Firmware for MediaTek wireless network adapters. Required for wireless connectivity. +# nvidia-gpu-firmware # Firmware for NVIDIA GPUs. Essential for GPU operation. +# nxpwireless-firmware # Firmware for NXP wireless network adapters. Required for wireless connectivity. +# realtek-firmware # Firmware for Realtek network adapters and audio devices. Essential for various device support. +# tiwilink-firmware # Firmware for TI WiLink wireless network adapters. Required for wireless connectivity. + +## Optional packages found in hidden `hardware-support` group +## Not installed by default unless "@hardware-support --optional" is used +# acpi # Command-line ACPI client +# acpitool # Command line ACPI client +# alsa-firmware # Firmware for several ALSA-supported sound cards +# atmel-firmware # Firmware for Atmel at76c50x wireless network chips +# cmospwd # BIOS password cracker utility +# dvb-firmware # Firmware for various DVB broadcast receivers +# gpsd # Service daemon for mediating access to a GPS +# gpsd-clients # Clients for gpsd +# hddtemp # Hard disk temperature tool +# hdparm # A utility for displaying and/or setting hard disk parameters +# iscan-firmware # Firmware for Epson flatbed scanners +# libifp # General-purpose library-driver for iRiver's iFP portable audio players +# lsscsi # List SCSI devices (or hosts) and associated information +# mlxsw_spectrum-firmware # Firmware for Mellanox Spectrum 1/2/3 Switches +# mrvlprestera-firmware # Firmware for Marvell Prestera Switchdev/ASIC devices +# netronome-firmware # Firmware for Netronome Smart NICs +# opensc # Smart card library and applications +# pcsc-lite # PC/SC Lite smart card framework and applications +# pcsc-lite-ccid # Generic USB CCID smart card reader driver +# qcom-accel-firmware # Firmware for Qualcomm Technologies data center / Open-vRAN Accelerators +# qed-firmware # Firmware for Marvell FastLinQ adapters family +# radeontop # AMD Radeon video cards monitoring utility +# wpan-tools # Userspace tools for the Linux IEEE 802.15.4 stack +# zd1211-firmware # Firmware for wireless devices based on zd1211 chipset + +%end # End of the packages section diff --git a/fragments/shared/repo/rpmfusion-nonfree.ks b/fragments/shared/repo/rpmfusion-nonfree.ks new file mode 100644 index 0000000..6ac99e1 --- /dev/null +++ b/fragments/shared/repo/rpmfusion-nonfree.ks @@ -0,0 +1,21 @@ +# RPM Fusion non-free repositories + +# RPM fusion repositories +# For the current release tree +repo --name=rpmfusion-nonfree --mirrorlist=https://mirrors.rpmfusion.org/mirrorlist?repo=nonfree-fedora-$releasever&arch=$basearch --includepkgs=rpmfusion-nonfree-release +# Updates for the current release tree +repo --name=rpmfusion-nonfree-updates --mirrorlist=https://mirrors.rpmfusion.org/mirrorlist?repo=nonfree-fedora-updates-released-$releasever&arch=$basearch --includepkgs=rpmfusion-nonfree-release + +%post + +# Import RPM Fusion PGP Key. Courtesy of https://github.com/rpmfusion/rpmfusion-nonfree-remix-kickstarts/blob/master/rpmfusion-nonfree-live-base.ks +echo "== RPM Fusion Nonfree: Base section ==" +echo "Importing RPM Fusion keys" +rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-nonfree-fedora-*-primary +echo "List of packages from RPM Fusion Nonfree:" +rpm -qa --qf '%{NAME} %{SIGGPG:pgpsig} %{SIGPGP:pgpsig} \n' | grep -e 3DE8C682E38EE9BC0FDFEA47FCAE2EA87F858107 | awk ' { print $1 } ' | sort +echo "List of incuded RPM Fusion packages with their size:" +rpm -q --qf '%{SIZE} %{NAME}\n' $(rpm -qa --qf '%{NAME} %{SIGGPG:pgpsig} %{SIGPGP:pgpsig} \n' | grep -e 3DE8C682E38EE9BC0FDFEA47FCAE2EA87F858107 | awk ' { print $1 } ') | sort -n +echo + +%end diff --git a/fragments/shared/section-data/post/base.ks b/fragments/shared/section-data/post/base.ks new file mode 100644 index 0000000..51cafe1 --- /dev/null +++ b/fragments/shared/section-data/post/base.ks @@ -0,0 +1,9 @@ +# Core post-installation configuration + +%post --log=/mnt/sysimage/root/post.log # Beginning of the post-installation section. Log all messages to a given file + +localectl set-keymap ch-fr # Set keymap to `ch-fr`. Alternatively, `us` can be picked. +dnf update -y # Update the system +grub2-mkconfig -o /boot/grub2/grub.cfg # Unsure it is actually useful + +%end # End of the %post section diff --git a/fragments/shared/section-data/post/nochroot.ks b/fragments/shared/section-data/post/nochroot.ks new file mode 100644 index 0000000..287df6f --- /dev/null +++ b/fragments/shared/section-data/post/nochroot.ks @@ -0,0 +1,5 @@ +# Post-installation no-chroot section + +%post --nochroot --log=/mnt/sysimage/root/post-nochroot.log # Beginning of the post-installation section. Log all messages to a given file + +%end # End of the %post section diff --git a/fragments/shared/section-data/pre/base.ks b/fragments/shared/section-data/pre/base.ks new file mode 100644 index 0000000..d52e4fe --- /dev/null +++ b/fragments/shared/section-data/pre/base.ks @@ -0,0 +1,5 @@ +# Pre-installation kickstart section + +%pre --log=/mnt/sysimage/root/pre.log Beginning of the pre section. Log all messages to a given file + +%end # End of the %post section diff --git a/fragments/shared/section-data/pre/install.ks b/fragments/shared/section-data/pre/install.ks new file mode 100644 index 0000000..e00f005 --- /dev/null +++ b/fragments/shared/section-data/pre/install.ks @@ -0,0 +1,5 @@ +# Pre-installation kickstart section + +%pre --log=/mnt/sysimage/root/pre-install.log Beginning of the pre-installation section. Log all messages to a given file + +%end # End of the %post section diff --git a/fragments/shared/storage/encrypted.ks b/fragments/shared/storage/encrypted.ks new file mode 100644 index 0000000..20a6abc --- /dev/null +++ b/fragments/shared/storage/encrypted.ks @@ -0,0 +1,8 @@ +# Encrypted storage configuration + +zerombr # Destroy all the contents of disks with invalid partition tables or other formatting unrecognizable to the installer +clearpart --all --initlabel # Erase all partitions and Initializes the disk label to the default for the target architecture + +part /boot/efi --fstype="efi" --size=512 --fsoptions="umask=0077,shortname=winnt" --label=efi # Creates a 512 MiB EFI system partition +part /boot --fstype="ext4" --size=2048 --label=boot # Creates a 2048 MiB ext4 boot partition +part / --fstype="ext4" --grow --label=root --mkfsoptions="-O encrypt,fast_commit" --encrypted --passphrase= # Create a single encrypted root partition with the remaining space. diff --git a/fragments/shared/storage/standard.ks b/fragments/shared/storage/standard.ks new file mode 100644 index 0000000..9135ed1 --- /dev/null +++ b/fragments/shared/storage/standard.ks @@ -0,0 +1,8 @@ +# Standard storage configuration + +zerombr # Destroy all the contents of disks with invalid partition tables or other formatting unrecognizable to the installer +clearpart --all --initlabel # Erase all partitions and Initializes the disk label to the default for the target architecture + +part /boot/efi --fstype="efi" --size=512 --fsoptions="umask=0077,shortname=winnt" --label=efi # Creates a 512 MiB EFI system partition +part /boot --fstype="ext4" --size=2048 --label=boot # Creates a 2048 MiB ext4 boot partition +part / --fstype="ext4" --grow --label=root --mkfsoptions="-O encrypt,fast_commit" # Create a single root partition with the remaining space diff --git a/fragments/validation/success-if-empty.ks b/fragments/validation/success-if-empty.ks new file mode 100644 index 0000000..ddd000b --- /dev/null +++ b/fragments/validation/success-if-empty.ks @@ -0,0 +1,6 @@ +# Validation fragment - success if empty + +%post +# If this is being run, it's a success +echo SUCCESS > /root/RESULT +%end diff --git a/recipes_backup/desktop-hypervisor-amdcpu_rawhide.cfg b/recipes_backup/desktop-hypervisor-amdcpu_rawhide.cfg new file mode 100644 index 0000000..643c218 --- /dev/null +++ b/recipes_backup/desktop-hypervisor-amdcpu_rawhide.cfg @@ -0,0 +1,25 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for an AMD (tm) CPU-based desktop hypervisor + +%include ../ingredients/core.cfg # Text mode for automated installation +%include ../ingredients/core-storage.cfg # Storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-packages-hardware-support.cfg # Provides extended hardware support +%include ../ingredients/core-fedora-repo-rawhide.cfg # Offical repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Post configuration script +%include ../ingredients/core-initial-setup-gnome-desktop.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-desktop-gnome.cfg # A GNOME Shell-based desktop environment +%include ../ingredients/base-desktop-virtual-machine-manager.cfg # Virtual Machine Manager +%include ../ingredients/base-hypervisor.cfg # Base hypervisor +%include ../ingredients/base-hypervisor-amdcpu.cfg # Virtualization configuration for AMD (tm) CPUs \ No newline at end of file diff --git a/recipes_backup/desktop-hypervisor-intelcpu-intelgpu_rawhide.cfg b/recipes_backup/desktop-hypervisor-intelcpu-intelgpu_rawhide.cfg new file mode 100644 index 0000000..89bc809 --- /dev/null +++ b/recipes_backup/desktop-hypervisor-intelcpu-intelgpu_rawhide.cfg @@ -0,0 +1,27 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for an Intel(tm) CPU- and Intel(tm) GPUs-based desktop hypervisor +# vfio-mdev compatible GPUs required. For Intel, it means 5th to 10th generation only + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage.cfg # ext4-based storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-packages-hardware-support.cfg # Extended hardware support +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Triggered after the installation +%include ../ingredients/core-initial-setup-gnome-desktop.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/core-initial-setup-gnome-desktop.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-desktop-virtual-machine-manager.cfg # Virtual Machine Manager +%include ../ingredients/base-hypervisor.cfg # Base hypervisor +%include ../ingredients/base-hypervisor-intelcpu.cfg # Virtualization configuration for Intel (tm) CPUs +%include ../ingredients/base-hypervisor-intelgpu.cfg # Virtualization configuration for Intel (tm) GPUs from 4th to the 9th generation (compatible with vfio-mdev) diff --git a/recipes_backup/desktop-hypervisor-intelcpu_rawhide.cfg b/recipes_backup/desktop-hypervisor-intelcpu_rawhide.cfg new file mode 100644 index 0000000..3b83d13 --- /dev/null +++ b/recipes_backup/desktop-hypervisor-intelcpu_rawhide.cfg @@ -0,0 +1,25 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for an Intel(tm)-based desktop hypervisor + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage.cfg # ext4-based storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-packages-hardware-support.cfg # Extended hardware support +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Triggered after the installation +%include ../ingredients/core-initial-setup-gnome-desktop.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-desktop-gnome.cfg # A GNOME Shell-based desktop environment +%include ../ingredients/base-desktop-virtual-machine-manager.cfg # Virtual Machine Manager +%include ../ingredients/base-hypervisor.cfg # Base hypervisor +%include ../ingredients/base-hypervisor-intelcpu.cfg # Virtualization configuration for Intel (tm) CPUs diff --git a/recipes_backup/desktop-hypervisor_amdcpu_rawhide.cfg b/recipes_backup/desktop-hypervisor_amdcpu_rawhide.cfg new file mode 100644 index 0000000..29f70db --- /dev/null +++ b/recipes_backup/desktop-hypervisor_amdcpu_rawhide.cfg @@ -0,0 +1,23 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a desktop hypervisor + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-packages-hardware-support.cfg +%include ../ingredients/core-initial-setup-gnome-desktop.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-desktop-virtual-machine-manager.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-hypervisor-amdcpu.cfg +%include ../ingredients/None.cfg \ No newline at end of file diff --git a/recipes_backup/desktop-hypervisor_intelcpu_intelgpu_rawhide.cfg b/recipes_backup/desktop-hypervisor_intelcpu_intelgpu_rawhide.cfg new file mode 100644 index 0000000..038f2ac --- /dev/null +++ b/recipes_backup/desktop-hypervisor_intelcpu_intelgpu_rawhide.cfg @@ -0,0 +1,23 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a desktop hypervisor + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-packages-hardware-support.cfg +%include ../ingredients/core-initial-setup-gnome-desktop.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-desktop-virtual-machine-manager.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-hypervisor-intelcpu.cfg +%include ../ingredients/base-hypervisor-intelgpu.cfg \ No newline at end of file diff --git a/recipes_backup/desktop-hypervisor_intelcpu_rawhide.cfg b/recipes_backup/desktop-hypervisor_intelcpu_rawhide.cfg new file mode 100644 index 0000000..7fa612c --- /dev/null +++ b/recipes_backup/desktop-hypervisor_intelcpu_rawhide.cfg @@ -0,0 +1,23 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a desktop hypervisor + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-packages-hardware-support.cfg +%include ../ingredients/core-initial-setup-gnome-desktop.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-desktop-virtual-machine-manager.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-hypervisor-intelcpu.cfg +%include ../ingredients/None.cfg \ No newline at end of file diff --git a/recipes_backup/desktop-hypervisor_rawhide.cfg b/recipes_backup/desktop-hypervisor_rawhide.cfg new file mode 100644 index 0000000..96470c2 --- /dev/null +++ b/recipes_backup/desktop-hypervisor_rawhide.cfg @@ -0,0 +1,23 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a desktop hypervisor + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-packages-hardware-support.cfg +%include ../ingredients/core-initial-setup-gnome-desktop.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-desktop-virtual-machine-manager.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-hypervisor.cfg +%include ../ingredients/None.cfg \ No newline at end of file diff --git a/recipes_backup/desktop_rawhide.cfg b/recipes_backup/desktop_rawhide.cfg new file mode 100644 index 0000000..f37b0a2 --- /dev/null +++ b/recipes_backup/desktop_rawhide.cfg @@ -0,0 +1,22 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a generic desktop hypervisor + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage.cfg # ext4-based storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-packages-hardware-support.cfg # Extended hardware support +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Triggered after the installation +%include ../ingredients/core-initial-setup-gnome-desktop.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-desktop-gnome.cfg # A GNOME Shell-based desktop environment \ No newline at end of file diff --git a/recipes_backup/live-desktop-hypervisor_rawhide.cfg b/recipes_backup/live-desktop-hypervisor_rawhide.cfg new file mode 100644 index 0000000..33fcee8 --- /dev/null +++ b/recipes_backup/live-desktop-hypervisor_rawhide.cfg @@ -0,0 +1,26 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live desktop hypervisor + +%include ../ingredients/live-core.cfg # For live systems only +%include ../ingredients/live-core-storage.cfg # For live systems only +%include ../ingredients/live-core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-packages-hardware-support.cfg # Extended hardware support +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Triggered after the installation +%include ../ingredients/live-core-post.cfg # Post configuration script for a live system only +%include ../ingredients/live-core-post-live-session.cfg # Live session script +%include ../ingredients/core-initial-setup-gnome-desktop.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-desktop-gnome.cfg # A GNOME Shell-based desktop environment +%include ../ingredients/base-desktop-virtual-machine-manager.cfg # Virtual Machine Manager +%include ../ingredients/base-hypervisor.cfg # Base hypervisor \ No newline at end of file diff --git a/recipes_backup/live-desktop_rawhide.cfg b/recipes_backup/live-desktop_rawhide.cfg new file mode 100644 index 0000000..b7e0f82 --- /dev/null +++ b/recipes_backup/live-desktop_rawhide.cfg @@ -0,0 +1,22 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live desktop + +%include ../ingredients/live-core-storage.cfg +%include ../ingredients/live-core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/live-core-mandatory-packages.cfg +%include ../ingredients/live-core-post.cfg +%include ../ingredients/live-core-post-live-session.cfg +%include ../ingredients/core-initial-setup-gnome-desktop.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg \ No newline at end of file diff --git a/recipes_backup/live-server-hypervisor_rawhide.cfg b/recipes_backup/live-server-hypervisor_rawhide.cfg new file mode 100644 index 0000000..9020f49 --- /dev/null +++ b/recipes_backup/live-server-hypervisor_rawhide.cfg @@ -0,0 +1,25 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live server hypervisor + +%include ../ingredients/live-core.cfg # For live systems only +%include ../ingredients/live-core-storage.cfg # For live systems only +%include ../ingredients/live-core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/live-core-mandatory-packages.cfg # For live systems +%include ../ingredients/core-packages-hardware-support.cfg # Extended hardware support +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Triggered after the installation +%include ../ingredients/live-core-post.cfg # Post configuration script for a live system only +%include ../ingredients/live-core-post-live-session.cfg # Live session script +%include ../ingredients/core-initial-setup-server.cfg # For headless systems +%include ../ingredients/base-hypervisor.cfg # Base hyperviso \ No newline at end of file diff --git a/recipes_backup/live-server_intelcpu_intelgpu_rawhide.cfg b/recipes_backup/live-server_intelcpu_intelgpu_rawhide.cfg new file mode 100644 index 0000000..a32cc96 --- /dev/null +++ b/recipes_backup/live-server_intelcpu_intelgpu_rawhide.cfg @@ -0,0 +1,21 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live server + +%include ../ingredients/live-core-storage.cfg +%include ../ingredients/live-core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/live-core-mandatory-packages.cfg +%include ../ingredients/live-core-post.cfg +%include ../ingredients/live-core-post-live-session.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg \ No newline at end of file diff --git a/recipes_backup/live-server_rawhide.cfg b/recipes_backup/live-server_rawhide.cfg new file mode 100644 index 0000000..a32cc96 --- /dev/null +++ b/recipes_backup/live-server_rawhide.cfg @@ -0,0 +1,21 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live server + +%include ../ingredients/live-core-storage.cfg +%include ../ingredients/live-core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/live-core-mandatory-packages.cfg +%include ../ingredients/live-core-post.cfg +%include ../ingredients/live-core-post-live-session.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg \ No newline at end of file diff --git a/recipes_backup/live-server_rawhide_hypervisor.cfg b/recipes_backup/live-server_rawhide_hypervisor.cfg new file mode 100644 index 0000000..a32cc96 --- /dev/null +++ b/recipes_backup/live-server_rawhide_hypervisor.cfg @@ -0,0 +1,21 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live server + +%include ../ingredients/live-core-storage.cfg +%include ../ingredients/live-core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/live-core-mandatory-packages.cfg +%include ../ingredients/live-core-post.cfg +%include ../ingredients/live-core-post-live-session.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-fedora-repo-rawhide.cfg +%include ../ingredients/core-security-on.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_43.cfg b/recipes_backup/virtual-desktop_43.cfg new file mode 100644 index 0000000..e2cf11f --- /dev/null +++ b/recipes_backup/virtual-desktop_43.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_43_devel.cfg b/recipes_backup/virtual-desktop_43_devel.cfg new file mode 100644 index 0000000..90c1114 --- /dev/null +++ b/recipes_backup/virtual-desktop_43_devel.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-off.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_devel_rawhide.cfg b/recipes_backup/virtual-desktop_devel_rawhide.cfg new file mode 100644 index 0000000..9186fc5 --- /dev/null +++ b/recipes_backup/virtual-desktop_devel_rawhide.cfg @@ -0,0 +1,21 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# Unsafe. Development-only. A recipe for a virtual desktop + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage.cfg # ext4-based storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-off.cfg # Enable root account, disable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/base-desktop-gnome.cfg # A GNOME Shell-based desktop environment +%include ../ingredients/core-initial-setup-server.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-guest-agents.cfg # Guest agents \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_labwc_43.cfg b/recipes_backup/virtual-desktop_labwc_43.cfg new file mode 100644 index 0000000..edf732a --- /dev/null +++ b/recipes_backup/virtual-desktop_labwc_43.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-desktop-labwc.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_rawhide-encrypted.cfg b/recipes_backup/virtual-desktop_rawhide-encrypted.cfg new file mode 100644 index 0000000..145f035 --- /dev/null +++ b/recipes_backup/virtual-desktop_rawhide-encrypted.cfg @@ -0,0 +1,22 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop with an encrypted root partition + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage-encrypted.cfg # ext4-based storage configuration with encryption +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-on.cfg # Lock root account, enable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/core-post.cfg # Triggered after the installation +%include ../ingredients/base-desktop-gnome.cfg # A GNOME Shell-based desktop environment +%include ../ingredients/core-initial-setup-server.cfg # Enable initial setup to allow end users to create user on first boot +%include ../ingredients/base-guest-agents.cfg # Guest agents \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_rawhide.cfg b/recipes_backup/virtual-desktop_rawhide.cfg new file mode 100644 index 0000000..e2cf11f --- /dev/null +++ b/recipes_backup/virtual-desktop_rawhide.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-desktop_rawhide_encrypted.cfg b/recipes_backup/virtual-desktop_rawhide_encrypted.cfg new file mode 100644 index 0000000..e2cf11f --- /dev/null +++ b/recipes_backup/virtual-desktop_rawhide_encrypted.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-desktop-gnome.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-server_43.cfg b/recipes_backup/virtual-server_43.cfg new file mode 100644 index 0000000..269328a --- /dev/null +++ b/recipes_backup/virtual-server_43.cfg @@ -0,0 +1,18 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual server + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-server_43_devel.cfg b/recipes_backup/virtual-server_43_devel.cfg new file mode 100644 index 0000000..dea670c --- /dev/null +++ b/recipes_backup/virtual-server_43_devel.cfg @@ -0,0 +1,18 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual server + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-off.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-server_devel_43.cfg b/recipes_backup/virtual-server_devel_43.cfg new file mode 100644 index 0000000..4867b89 --- /dev/null +++ b/recipes_backup/virtual-server_devel_43.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# # Unsafe. Development-only. A recipe for a virtual server + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage.cfg # ext4-based storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-off.cfg # Enable root account, disable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-fedora-repo-43.cfg # Official repositories for Fedora 43 +%include ../ingredients/base-guest-agents.cfg # Guest agents \ No newline at end of file diff --git a/recipes_backup/virtual-server_devel_rawhide.cfg b/recipes_backup/virtual-server_devel_rawhide.cfg new file mode 100644 index 0000000..696fd23 --- /dev/null +++ b/recipes_backup/virtual-server_devel_rawhide.cfg @@ -0,0 +1,19 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# # Unsafe. Development-only. A recipe for a virtual server + +%include ../ingredients/core.cfg # Text mode +%include ../ingredients/core-storage.cfg # ext4-based storage configuration +%include ../ingredients/core-bootloader-grub.cfg # Set bootloader to GRUB +%include ../ingredients/core-locale.cfg # System locale set to Swiss French as keyboard layout and English as language +%include ../ingredients/core-security-off.cfg # Enable root account, disable firewall and SELinux +%include ../ingredients/core-services.cfg # Required systemd services +%include ../ingredients/core-network.cfg # Network configuration +%include ../ingredients/core-packages.cfg # Mandatory packages +%include ../ingredients/core-fedora-repo-rawhide.cfg # Official repositories for Fedora Rawhide +%include ../ingredients/base-guest-agents.cfg # Guest agents \ No newline at end of file diff --git a/recipes_backup/virtual-server_rawhide.cfg b/recipes_backup/virtual-server_rawhide.cfg new file mode 100644 index 0000000..269328a --- /dev/null +++ b/recipes_backup/virtual-server_rawhide.cfg @@ -0,0 +1,18 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual server + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-on.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/recipes_backup/virtual-server_rawhide_devel.cfg b/recipes_backup/virtual-server_rawhide_devel.cfg new file mode 100644 index 0000000..dea670c --- /dev/null +++ b/recipes_backup/virtual-server_rawhide_devel.cfg @@ -0,0 +1,18 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual server + +%include ../ingredients/core-storage.cfg +%include ../ingredients/core-bootloader-grub.cfg +%include ../ingredients/core-locale.cfg +%include ../ingredients/core-services.cfg +%include ../ingredients/core-network.cfg +%include ../ingredients/core-packages.cfg +%include ../ingredients/core-initial-setup-server.cfg +%include ../ingredients/core-security-off.cfg +%include ../ingredients/base-guest-agents.cfg \ No newline at end of file diff --git a/scripts/Makefile b/scripts/Makefile index bdb4378..ec1a690 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -1,4 +1,4 @@ -.PHONY: help generate-recipes validate-recipes test clean install-deps +.PHONY: help generate-recipes validate-recipes test test-integration test-container clean install-deps help: @echo "Phyllome OS Recipe Generator" @@ -6,7 +6,9 @@ help: @echo "Available targets:" @echo " generate-recipes - Generate all recipes from manifest" @echo " validate-recipes - Validate existing recipes" - @echo " test - Run pytest test suite" + @echo " test - Run pytest test suite (unit + integration)" + @echo " test-integration - Run integration tests only" + @echo " test-container - Run all tests in container" @echo " clean - Remove generated recipes" @echo " install-deps - Install Python dependencies" @@ -23,7 +25,14 @@ validate-recipes: --validate ../recipes/*.cfg test: - pytest ../tests/test_recipe_generator.py -v + python3 -m pytest tests/ -v --tb=short + +test-integration: + python3 -m pytest tests/integration/ -v --tb=short + +test-container: + podman build -t phyllo/test-runner tests/container/ + podman run --rm -v .:/phyllomeos:ro phyllo/test-runner clean: rm -f ../recipes/*.cfg diff --git a/scripts/__pycache__/generate_recipe.cpython-314.pyc b/scripts/__pycache__/generate_recipe.cpython-314.pyc index 13cfb350d22e6cd37f129e5fbef4ce6a65244506..0121269513c542e72b23afb79921a476060afe6d 100644 GIT binary patch literal 32716 zcmdUYYj|77b>Ia75Kj^y0lq=U5G2czV3>d;SR?^ofO^1+ zqoj|}Nj9P!r=nBuC#rSV zJ!kF%7a%0cY4Ux$SK?sq%$e7jnRCvZIWxC0H%Cvwv*G7I^!=lkqJD-i;*%$M9%Lyf z%1vFSn3^Igl(&WoQ4AGQoTL~3agk9X9|x-yb zhkk13z&?7!J?cB<4Tf6u`)3rKYS0}zV^?viF5ggyQ|}l`d4DR8OmmPJ7eCND;(x7+P z3wdisyuqORv^TsZnIC=L9fVqwtZ3gT-Q)H6SvTF@(Lx^%dT9jYQ$u7hQ z#b^C)7K-+G1JGhWC+z`BsxC7xGQs9t(1W@6jeL*^zm@ij? zNhVBIz%c9{Jslhi1pI6Wn!L7+Q(;KBLl&MXQ;epiLdE7NM^6PYm-euU^#=S<)lv6| zcPoU3JHSaMSYAs4zbwP-0E`d7h_a`xom1gUcmp5wxV#V|881|BNSv(ri&I%iQK|fayS{Wmc~$kVX6|oUQ?7uO^^^;jVU4Otw?OXq;J$ z)~RH4PMAv76ippBV7$Ff>@^m7tcyFVZ`ql zgW=u;!zeVyjt1%d{!uUOJ4KKBL$v#x+c)ffcG%luS8yh2`V6|+pqGUp!-)H=*Xd=0 zFx=r)-PP^v-e=F^EUBQ~EbC{vTq%%$M@&ODYz0)Btwe`LrwW|?b{&^5t7VG-S`KlX zMILdEFFct^YC6c?&jynja|Rv-qy~01iy0=L#+XUuK0tk!6DWc2nAbD^MuFi;^|0B;38*n`3TBgvO2R3koQ{n3-*I^nOaii zsI5QMuK%gF{ioXIkFE@vwt<>9Y*7A%VuLbe018xiG&@)%nv-${imOnEhBui#8P^N@H)c%#T0=;U1C`!yqLfNA7A<%7Z4G6d+Q z@DCpq{K2fJ9@K%eMRA#OQYW)E{w^>x2(?iM6=xKT;*jELV9V&XW07^4L89A1njs)63LDN~PXxf2$VbQcY9n?cW^q%YgckA z;JBv<9NPlGLCm(Uu8!{s$lf>VgZ|-)m-hl6LwaC4B)Uv}Y@=T<{=@xoTU*rD7PoDT z+BS}>V%m~KWp%uAO|){&?DmDqT^M9bl$6IynxZ95(AR&Acq#*;yhD@*AKV@DFUL z?|x3v18h&}8x29DJ0WWp>qQzZ_IS=|Z^(5vDB`O;z1vfw31Vs>i_6raA%DUbGgN+H zDOxNjd?h%!;hUGH+zUnZk-~c!vz5Ej z0Z>VLk*_YkPl0ozV+ns|y2#t|Zqz_3(v4f7b5R&_Dh4H*0dc}3%K1pd@bN&MY?MP0 z$QmexIW8Xp6q=Co7RWk|Gk`ovTCiE+QjBGdDP7X4BA`4jQHMdTm?HUGMLCt? zzzV3CEd1AjD1)bxI;vun@>zqzStBL3;55|PM)^RrD+gHox37d3&p?EehA4<|n-H#& zI-bMlOTnt_Xci9KcnsEU8z65^{IKKh%TylRP74y#Up?AbhJ>BFpZ#0I)>zH*9FoXX=4xjBV5h-0>{ z4nk^Lqo&HZsp^iYDnZv=%~_;t;&fY- zZkz2}pts&uEzn&PIrs7kCPw0UHL<*!X~n&wvTHBC{Nj{*zNleB^-xLW*DhL$CJhUg znuxLHp&H%+^jDu`Q|78qs4QLaJ!4+Px^8x4))mR`{+r!D+57(91>=!>M$?tvul7zH zywZQqWV!Oa`Rx6iGbJ28Bygv)>fEInZ zV3(4bTm#NK+M=Cn)bCW9(Z9y9i_*QbSquIjDz|r)DSxESN4HpkZpntOLgl-KYViLD zIcJuf2FMAmR_sASY-f~&&az0D*qz|4OmWWuRFZjaP#4~(z?rF8!k?QRx0K)8L_(HR zU6hFk)g6T5E>kU3AghI9at|m1N~h9H^#Df>sE+L11eEwRz}cvNNa4)NM41_-Q-#zh zNkRInC`w0F00nj`aE5sXF^9}l8rilSKR`iLYqR8(JD_%|v6ksiI!8DS3pgkxa~;(g z%aLezC2}3)L}?ELSP^wlfR!1*O7?M`2;|I8(rFP&r0$+oSeqnubY>5l#B|ADu?-^b zPEvAQ1T+I#{aXOp~=t#TBr6&dVpWZ z^ndG-BSqtc+*3x%cleR=2<&THCF~DE+tMEqduFOz(X)f1&I7+=zb)$@{bTy)4+yUG zkI%Q~i$bn%|5p0I=G2_xXBUX!zqS7GJhDG@*dL7EsRP&GG=OVds}V<4CDs2WzIXa> zQdA07QMo{#^ieO+iua*JJ@lkFS8OoV7Bl(I4b7)UD_Tc?fBq6PFXF zaJv)b16X8BYGWE!3}Zu}R|8!vs7g<sgv+kgm)3V;@$9%Bd z4;K#jf_1fOB3yd74$29K8)szCe4J_}=m9RdeL zqHK2nBp?sJSk5Mm1tl%4bU3{a+6FpsXgWwPL}iY-aoHeRjd_Edo~Y-8=Y6mkA+nU( z2mCT<_Jj{DFfC5a( z>Cq$T=hb{YaIEvP6v|VAM8)M1`45#SA`j=P(&ejVAcS2%4MYl-ba|HX>|bP4us+ZE z&_a*zPMGrVnpP#uh1YVf=0xcAv%y(!q+-im^Va)n%38Igr;Pa%M`MQaM6oSi+!QTt znr)a{zp!>!q;2=@j!0|oLUHeSU&3OGTdJd$>bRx$j-^)0Zu4C4ZRf(4eUZ%r?>QnB zOw4>}DVr)Nzqaq{zNyk_=B{6ZmN%(>Zgry)4DsRbqO0Cw>3v?&9gZRwyjrni}=1KYFmRc-R-VO zd*6bsZ$h^yt6NM~x2!LO5=5iKlx?j~LO6u;Ew0uxZFJi>TXw60}{k^Wp=7S402PbulWi|1##%Nh% zysSA^)|@D=xSsQBPNbpx_Q-8l#BnV0^s|v;Ly;j*#QR*tb2f5zI8rxqw|Mk{g=*gZ ziIvK?P40bD70+vqQRc$&oaLGT z6rF&{d=6F5qXkxR!;1*4>@$$-%0lW5fJzFf78Fv!K_L}>L?IQt%Y{^2a6`?F4;&zd z@GbFhD-mWzH+M<@kU3b3mq6T*QQJwm{Ra#NKJ$k2 zL-cPTyHP9)4zdT_4i+i9M$8EIt>C$*vN2IJ{LIb3KYR_Fu zW5QZ`ZQ$y_RPXc&;1A8aB2Bv^yH7?|x$at@d7!3j>+YMVingV~M^Fn?9S~}Py4@8O z!=Pg&dlkxRYDUGYgAD?%Ll*;H(r^6YR`G;O+P{EegRpku^}!Z3jnsmkX-8^7-?EUO z_lHOzPcY<@4OeimPBlJQOPK5tGb}upJ!HzS!zp}RN{eKLXw+09c&1$jCQGJKOF|-< zN_#+~CMld@ZAoRS2kcqMH0lA^Z_Bt-ArT;42TLox6KVBVV*O#|tUR8qm!!E&R!-gm zry7V%6GFL(*j?>*iE3*>a2=$p_u4&>FIe$gq9}oaH5R z5Pe5>K}w-bGV-k%w1^PNU#GO*0l8fpZk4wB+@c;a#rT6pag4%BIGt2+ z7R6_tII4*Z&8dfdqhM8nod;V~O-WJLl(v`6Rq~Wu+8=F610Yj)9`eDRi^A)pua3qH zO+?GLX0~iLCz8MI-9tY<_Rg^d&gdczaUUhq6A!DLiGHWiE-T(a3KANOd) z0g7IvzhqRfQvb29VAytW>`FpTlM2I?#jwAIWIpIg=B=Yxu> zB_+Im(6W~Q-LU!A?(bc^k70bv<$5^OgTX8_K0tB}R%c)>0Ah}0F~(C=M$@80$^~uG zg&TL_;H5)ny!6gPyZicRG?n3pGFcSM7LhIC9o0An#mD6meKhTYC;67@sycsr>>G6*A*|3pCg8No*%hb)Bn;~2(gfQ2Pf z$^7zANC#ztBS2&MbZdAU)S#7yeR&s2=W2y~n=n7(lR_~BVlX|8zphqnAGDyLniMV} zi4W$ea8@-vgX4^dN=SXu#u=B*cZ{%^05n0^Oqc-J<%R=tvP6a&a99`=u#1p$K#?#W zNEjUn<3PeVm@rl+jJb(i^F-N1PQ+LNFH07EzJ5IWkur%e(5MHyAkP)2n1Yu{$P~lL zmBMCQRWK0M{Ny4e5b0vdyh4t{Pc9tmE6=MB0V;^Q`($1<9FO*hC&sXe0e6kGNWqL=KqcATlAjBM&IFGca+n9wOV#tk6C(CR@{VL0wgu zl;f~IOH&uaMnDF3pJd^nvOFuSZZfxCB~vJ85C+8BgJ5)9Q2f4hMGk9`pxyl zCwpAhkFZq)T7eXC(`iWQj~p4cavt5U0uH)iT>Pyp{{eWvmy#c-!Nr**qyh4iG7nDw zM(4pd1pFbr(}dUZjJNh|kXOfo-WG&Ceq&q`M^P;;35YHH+HDBNepsJO$9f^aK?tT@ z51ZAX$5jpa0~g7{n;nKob_AVKbo}VxvYb=8Js$RX@Y!>d+FQPaS1?|ilw5;e?Oz z@*jah2;^2v!MOIGDgSF@+k2G60|Ur67;U11WlL<@0zL;#Z?Q%t3mR$ zluthQW=-7EddJe5D6++hRwo+PE;MYK%t@3r#L7Anwwmh)UOh1V++6vbDbmy%v-N?_ z_ImHDy;CRd+S)3)*4@ZL{9F(Dz0@w6~Lt?yi{80}Zvh z<9-3rM$%Mu(;KZfTJNrEj~Xk+_du1eH@(_4^}_6~`O*z{O&b!R4LQ3|zIJxQLiy$i zeXfrip=%ifXQYVY>XTeOIEGzHWNeG`)KE#O#4cLszW08?@oq zwXbTY%I_Apf?}h(V=1397mu5k%jw7raZUIiuy}(VdX&mJ4GcC|c7=G&M%L$%7W6Q@ zlFHChl)}Ne5nRHbnb6JMe+uFOc=`HgL22S6i3hx$6zQ4~yjKupl4?ka2UR*A`hft8 zdy->dXEv4Tlsyq7WN9XA*_@5 zLDv38)Iop@u>r_|y#WrV90p2{?1BAbbZ~9T zf|x>q4umX9-UL6F4eDmx4OAh=VqZs?Kf!P?v>XDPDOf-T***+KZV}XjlO+DUR@Wr$ z!|{z>uD?hFAanFn$Vt%XCTg4BIC0~|Y)7nio0=ZHeo5A%=^~H@vel) z^7Tui0vI;gx@Ol$Dmr6ku=O`z>3ykp!tss$K-8?Z2Uc*NC`V~4PPJg`04=kHj$7)Z zmioBG9<$h`vewRa%+^IJHr+LMk^+!w!sgL0f6+==k0}Tm|Dd?5MD^#{;G~#8X#g*T zjBWwXifb`mKfK(081%(>A&agmB%|Qm1Z4*N+=4|H z{Ji6Nzx}U#B!Z`=SZ(nUnqhn}1vHi{wv-tbTPmmOl$B7PjEgR?^%N)`=y;(?putSB z_eAQ*s8Z-zHiV>X^nhkFP45|i6BC(4*`%Fn04Rn;6=28%Wy}UkRGnCxfPTP4LR5@K zUV1VftZdbgLNA4Y^+zWAr=;05wlL%3i^?RRC)0@~@ED>ghX!m%*JGUnMn`6>Y8}Q* zwzi<()}vV;-SC9yCZ@oZ2A+nKoeQHedu%V4dp772aO?uR9!{Tp=kaqKpn^Hz zEMJRo#cBM;C1ROtNY|`C3X;&j%|Ltv7WHv{?R&N#lskFLY#Rmm7Q^)2D){OT+!Exj2sB!i5 zhM2J(c#6(+CFiA_2~WImU9@oB?CJT!Z87ckMN`3e&ZkQSl(`xXtw9cAg>5a<&%bqH z<^n8KvgX=;u<6#OxyDFoPuvKmAA5e4FqI?trX=*6!P&O&oSQxqDcuk^Zj2f?{_38o zI4#Sq-ygbN^c+tbDiF2>y3#G34F4@@}p2ue56Lr|`w!!199q4pis~8DJmueR3U9fea9wo7GGB zbF1c7@GtEoWcfNm;(VWw;e+dlB$CofJ~1yPkR`N?x&?VKvHCAi&Q6AuZU>` zg$#(I&g^uBrBjns{@_YPfc+w<(P)(4NidyrW|y zcsk$9z6FUeLpVE2)(4!Wt9y{?-o4Y&-R0VSU|?Y9{;orW5!ekfVom6@7HPi=vSR-X zbJ3Dq0$vaMU3^szjd(aMIB?buj2E$U1LfXx2ey;om_b zkqlSVPix-FnaP>;%r|Vl-4?Iterf-B?;;$D2wx3<^U`?lN0ze53-QXe(aN>6?eWSD zcPcl)iU5u3isPnLQPZlab}+8HW2#5<4y#R6JavC$DaMfeqDjTK8pd}on(||X_PZwg zy|R|+;6hpZgf>xvG`^&9B0Eu7F?n&J&_1DDw3JLf6|>ZdHX-|Fi@~UF)!d=E?#QZL zcdfe@1!Ix@vm0ku&+VEE&UquNy6#%L@3&EvJ&L7{M_7sARK&^k0BPuNY8Aj~@r(_c z0ujXuy94rGc`4vSN+>M_ENUy-hFDRf0zbI_0zbFTyc7J(5fZU{DWHO4pe@D+AA{$z za26krL1ZL_Ss;cBY8f!uP>Cc&up!Gx29u^BXp5GSj7fwKh-xFKBh-w_sV0gKs2v{u zVqgOmPWz{ZX&_9S7KU&-K1?80tMMor1F-y&3|M*T!yCFUgjn|wTz7sm9K03M@l@*4uoF6w-{B zEHrntb6Fkj;o@ZxLT2o4=!GzEbKn~S@K#I~wh|iy2oS(XDBQJ?#d_hil-JD;o#E7{ zS^rpo5EF4J|!cxUA!>1MVmempC>P2fAG198JYpoyeS~S&6 zt74`muBa@L*Ek)D<+XD~^rDHL>WY~fNND|Z$KAYE2nF?OLBsuGIGe?fWElO@33EY3 zNIhtRz$cuQR4Xi8*{?zZ_UquRJl|f#`Bn{}pQ0T>g}MRdjE0#*D0wv8Y9~L@!9yf; z`D8;W0Z6a$K{iae%Qzjd0cj6f5{Q$mMFG<+^Hh{3jWh{c2`^m%no(^8U6H4Rz-FAV z>86z8dXTJvq2B2aqZT2as3sCLll=rWNzoRF~Ryk{*D`SuwYq zWNwTilXliANvcdPPEYXxa%>9Q1zRc*mXg;FAtU4oGOX;u4{}nlg;TgVMKg#sfiX@a z&_p;+0dJ~NxU7ZKfCti@RmRLrhM!(X`UXvoX&>w#n}*hGAjMDsG(*Dy3`KY{WVxhn;h zyr4+NF6Hth(=d_pF1M&%20|!|9q0m>kAN9iIUE;_=G9Ccny#MDYl)a!$JG#QEV#1u zrLB_%aYN-DL*-Q88v{26W^3ocp1&_v)15HpUD@~2zR8lQbqgi-m~nN&V0{#PPpoFw zGWcW(C2>Rb9YgiBZq~lgu;sQU*03iDy=kGOIc97DXk9MakG*nevgn(~W7<{8^tQO6 z?v9~OOk;}~>ynYB^M;yfP0Y|LMi<45Rmtd*c|-No3o*l5F}f^ftOeQc%7vFMOsc>3 z#i_ytQ+-5RPv|Vzgf(N25Is7|4*Ts^yu$*=5nM8%ce!|t2KeC;kmtwT!$OP+@3~-u zAzq0B(HfV_;~#Rl*v*(9s$R$mVq$>Eeg$8zp@W7%?91qU51rfSpajh((D`R@xB|F+ z1g;wK4Y}MQknW#_bJ0O4AWwEb$&eRL;kfJy{*DOb20O^uVL@)8h|>k0;5Wee^eZqV zK2Ys?sDcYf$Soufjc`i@xhmpeE?hD|ZW?&l0Eb`63D}3M0?rkn1X%cRHJA?*G zrahe133wCzjY2>@44spNZDaI0JP20;S=)H!nwaEqKQ2L+=`$wB`F zIl%pM$sFJwy4(!{#vWNF+)|fJk>pVs@MlbGFuXE(DK*T0f*M*FE8Krq$P}$uvtqat zuaqg%@|nJw?QHPwZu}-GN*g zOz$ILOwQbs)W%m}z0^i}1Wf>eF$c^{pVKVUhXVDK)pxJcjC*HKUfX^UiHW6x=@F&% z|5i%FwjGspy3nIeEI%3YUP`O>eUA40557GzoD4j9YFVoN$5EUkk5gJV!dN))YmS9M z=w}#Z%f>>UY%Cmn1WdlXUd}wI1>?*I_t0APleY~h*+#KN$zO32^LHIN^Mz5cHf=6B zp1d3xB;=C*q@B;o6UqaL4d!lgj;)w-ph||5!{W?>`<7P9L%IVGX1#I{&0W$S5hK6_ zeuA}1q4610h^1$u`^QmFOP)bou%$g<2_WeW0+w_h;7|?jPD=o^6TltfnUioNKt40bPXA(JP+`vqr}Q zauhL7$u%i~Vn;DxCC=HZ3N||AqF+Z#v9Hox3XS4dPd_M$i1WGm7yFBkI z3UFs=&uXO{wp|Jp70`l3NCr7a8eOmB=mMC%%kvUpPQOfGGRiFuncM<>mO-|7N+w%8 z^_pBy6Hq%OF7J6JZIqwDUS&=?@CKEh&qECz$WL+1n^VU8=jEQi&X`vX?puz{=I?5ThC;?`obq>wo z!3;H)`W`kz(xhA{{Xg-e6?`DSJrVs#rQpo+62%b-6nq@V3-c^cddH*TJQWC)8HTAN z`r4dFLkuXORYQ>4xx56XF(q{d2$I?ZsX_Khf0)*k)EOX1YA>WdmY$Z9Zab7(mjQ-p zPbnb-1S#PZl#o=XNJWWwmP*I~!>ms!Ap-=K(5epDoVF*XfuNAmIIb5`i5`OM=#IHdK2MjX0b0$fX0LRZr$d^3Q zX#0XN7azlmJf&&)@MX?3l@<;hPtv-~@QSmeWjXa7c=hJfgJm1QN7#$^qL`O+Ef*)o$&+Q_|h4uzuwqu-fdz9)i-6 z651{Ye*;2Nf?r|euGnfBpDU-jwf*-f=_39WYOUV)gN(J_y{y(w;K0z9^yfD$Yo@j& zdta_>U9ls*Z1VX52n9=Ds|>$K9M<1vt(YAF>vn5*_P*o1FL^&KIRT*zlm$wCD~%R4 z-eJgN{&S>K?E4cwjeR7%PTJ9ve{^J}>*Hbf{S2UyGx7Jh!;T++k9#<$xfyI?#Vg>$ zSYRGqe_R@UUOP;4KG8pd1D1*)+i? zN>Y)+S21W0>zW~-i_PrVDC9Vb7tn$AiVLpo^M=JrIKy~Qzu)7K27nAF;x3DziU>K)5Kx{p|Una7`BWim|YH&i*~b;vv+KcNkv7m3sg# z03T3*s|WpKr_ac;#a(t%9nmx!ubDBhZ(g8&v!mSv?>wz_ZT+;r7k z>FwulRlc$Fk9L0N+%H)Jf~vs@XMs%`exfB@NyE2z;7dq&6pp`-6Ykb{6UY*N17a-5?xM>`ipE z@ETNsuTC`0V263w6;H9wJ%b&Z~o+& zU*4(h|0SCbxrYrr0fG_JM8k_fncy|tpg%;279mTWv@q0zbO=cpus?}*g;(ty9u`Jk zP&_jP0Ku^#;3+}8aP?xC?#4S?J86mIlYuOwOVWk>;)Pj6B!J6iMTr594;2fUh#*oF z`(;2xxIu`{a4Q#_bCPAm{tkc>Bc^bnjQUA44X?YU!{#G|{0eLqF7^r=_wXNh0{+_x zAm`ctj`{opI9;Lcz_6k!mG@WKZOz_te& z7zh+29yI;pSrzy|NI-?YeJ!U2E<=uhak)F$(_mRY8X6=Y)#Az+_jzE}mlC>0!G4r-vjpw(c}xhTmVc3=M`s!8yj4ZCCP}>lbrEA$lK-oZ-5~;m2q&RBY)E| zJBaC2ct(pD1i{yDK#t^%fo>!CRdk3c*&+0yDlF}aGq_L!?D&x!;3p7}PqJS`C|u97 zs3>8NqVpAW&Z6@?I@iDfA(p-90?CcHJJR#QA^BF4n&6a|9Us+81c_WWp9)%wpJISJ z)`kQ23UZ!{GaTB@^bI-=xw`ro&N#>%*xS9^;bIOPaBwQb3a1kW2iV!^-C)rq99tG| zLnNP6Ifk`;6CFa}qUgik<4ocSFd-Q^(aag3d;G8hfNw;=C!K`(D7>mk{+uxcUve6D z5x^jP6^gtGkU<`IsOusZy|J*Sq1fL;hgk0YK9;OF%PG#0>pVDvfHSOWumU3a`!gQp z@&u71QzJEz#mFpQx3&*_V6ZTVM(y(ty zN%B#s_bZZx8H9#P5PwReg#~XCDjzOuBx577@^zdHYLh;NG0q_twExyVY)s- zuT7NimO#4GK^mu@j?j?Hb_rx>3WzaLWuM*ZeO1bVq`&!z?rexk@!;VN5m&ke{fiN|bj)#rxdvx@c}0a4YsJkHgBl7 z+y&?B^XMs6oL&>9*UZ+u-Ey;K-n{d2A5=wa{K^-;@`W#bAyH5?u1}OzUf=rK*75$O zto$6?y@Ha-#&|(pte|eXDp65+{nV?c{_xDio`*`Ruzs;*RlHXu#N#!s z3)Z#5iJHi&Q!(r5f3TD!tQB!DmNnk9RZZ=h8k;&j)%4v%Zy&pP zENgE+s;6`2^6oZt-m{iOY@6S0|8eI# zopI~_`&Cp`<0my#PX5H+*N5JC?#6Qq#x`i(MAvJU$zy+587a0$?3*L`Tlf?U#{D0c zRYZyKHecbHFY}D=yH`#}i2yTC`{v7iG<|eFZ+-H@%BJt`etYjtxTkXa6W>z_6{x7Ye*D$r)Am^=UcM17awx5z z&R!^O9q&(=N~i4cRqLXw*2PTgu(8EEZkOH8i4^XS9X=XA?1~ z%bKQ-&E?0-w!mG%a30vcP}&5(mUVB9%#6J0kJ+3v{u`bt_jKOvy!RTTI}g9+m^?V8 z__kxlKcS1-oQq^0+<|$}f4eAJ>U^&coCytH+qw1H)`_jt*0*eL*`{rY^168W`e^z3 z+4J+|+b4RKO_r+qg{n;xdp|0wx>sC|H{HK5J94}2_L19t?;VU(9EdreiaVc)I-gl6 zc7IsgIN6Qkv1__uS~J!1-Qe5do8h=^7fhnMrjLuuu4})ooyvR7K(1|Hf2RyGyS*n` z_SAb9!GXD3vFf`2RsXDZpkQ-SsOWa#>Z>lY?3On1H2JJUO- zi#6?>=(x7=<&BsoJvKYmv`vWZg3p8&=*M+SW~zS88~z*qSlx!0wJ%z?;Z@J1dn#|P z?G?ww!AZr-4hS08MXh~{FpTpX@8mZ^QGGLgv!S;y-MkddKY05hIOCc{gXzlFD_h66 zCJHLz1ufBnmg)2J1smY-cSU`?Vn?)M$L*q@G`-(6UvXsoK%%5_p`>km??Ym9xF>4Ld{3gh9(lFKaM*ofesjZR1wgvNApaK*1U*G?+)*$OiUNm;%TJoN0)_SFk=d(aFzRb<8^6K5-MTw#x7NoA#e{z7OocDUqcN z-@#fp>z+Ln$=~#@^2hpj^b5v*8L;>TtpD?1HC28@0lcPk4UFh$N$dZ4P(c;!RXl6} zQ~-`51&yBtUk7pTy$;J+UDo@WyywMu6($LT2K}Zo7D@YTJV2h zH~O>nA9QNK{ku*1exvSZO*(&7_Rk=sD*GQHq+b2c>kMbJ4gW&vAcud^Z6D52|Jys!QbJ#69EqX?w=%JwLEIf9pJG+0;NzkLWb>~349f9yC-(vcGzh@3Q@WSgDYY`RcqO7?rtc@joY zGESzK(eK^&?!D)nd+xdCoO^lu|CnFA%FH;klrjS4$FKY{{LjsVe1b14MUbxCIRTXu z#6#lbe&S*FGI54`^5Q)1$&d5l$=fSv6UK!W?8DzHY7@uBZIZa8O&XWB$>K5#(GeT* z2n@s{tmdUC$IIm&k%7b&14@s$rjL*y2xBG#3LB|T^R@uPLhk2~&VkR})>Qy8l&;_etHux62sr>o`mDmvXlCD1oZ>MidQi28nWmRi8pc3hRUHuJ zV!wQeu0#G{-Y%K;_M z(32Wg;1xy1G+(KwN3^?{BlK^zrd*LvtbjG&!mnmvJQNG*_zOa$B%k^`2E9J)w6i&k`W2CatZ6WC&Y{lsqX)iZ#NvrAPlZ_4M(;jsif>Ac}kw#5^AU@8o+cfWQTb7w4F(Rg?=;vZl68j8fNk;%Aq zWO6DJtg}wbDgq;;*3ywl)|zB?BpJtaR_aSQVjU01;tmHtDG!EX0XEE0VrxLXY%PF8 zniTn?(NHAFqFpCNp@*PxVTf*(hZpFpCr$aeFe`YQPhw`b}#FHk)4~Ezn7#0;+tcFIUvJ0sQQ)}$S4}0i# zOEIkKu%(tcK!0rc8Ka?xteq{K2y!+Q2uDMM@#E1DdkDL8UWQ@sLo)Xi<_P`F>I8bb zwrOXbHt=u-^z}lAy#WR%8JR=InvkRep$x%^fTqRvP?3F$m9s=Yz9a9XbYe0X9tp#g zSPTdtdY|0_qCaHciXO)P0kHZ((O3_P&LR``?pXE+zM}9BJ}3ODpS<@pW@vNqPSEM2 z#dC~~lW9G18Z9-XJoE@XRcZql_;;lZAkFVehdDQ$l9y9prXUz2QqrKqzPBJhgkG@< z0M1vtg~lZUa4EiskCy}7=%wg&(!(H#_SU*kW$6-?_lsBR;Lm5ubE|^8kwfp_XZGJv%AxgAT0zRk($WbaHc6%{o2J6!L2D6s@A0W%h;~=lXE-xNW^5h6 zv~i`;T2bLnhbjm^F*gf~LA>0~amXN>fOsJYOh%7$K7?&T02_|xx!jA@2}3D_A(dfHRk)xwo>iSuB?{`U z)Lbb`SX*wWTNl*YvywBCbH+J!!IB0ek&)hUH_Q}ozFE92QB-q9kSN@7P6EN?MxJ}Y zY`@TTzAI7Ddi9~JM-r}{8)naf*?OVveA~soIdjF5keD|E7@c3ph~9cp_EPh_uJVSi z@^a`({M8AFBj*GQ#bpV<(PSO|%E;#C@CS5o>J)I|M?o!vOb= z5g!nGsuhCn?CY&B^V@NUJA_Fw8-hbZAe7p=obE7!v*0xEW&`VD<@cc}CIo<8=dyxE zUTLr8eL!aay}gn#;#mL!sRACX0l@^Y-vMgtY;@$|2O~lc!T^lb2 zE?IbY{C9DDf6>Y~&#wOBm3lV>xb?-ea~qG$a9=IJ76K0VOfKNOkU^x$CxbAtdkg<- z5L=-o1zIo(2+OO&G~>Bx3vWFYG?gMQ1Z zde(G&WT237(c8V7bU#J)-pni@n(fV|Pam}HSzWAlZ}vCiyq@DObvGn7>x-wO;_hKw zO~;12#s6t#+^PvAWH?Mo$k_ z4_KJpbn}44y>5#ErFd%5p!VSgI~_N0Gj3pc2$#g;fopOPQ#T zGf|NZXVFZD+O)adaGA8epzZqfR5nz^JVG}QWt!=sb!OW8^$pXvpHhoZj|NI*!9P#0SUoB>Cz!iV`h)c80X;o2tfHU$v*@J(4G&Fcm@}PWOWr!u z$uPe|>4Dj{yGHAiteR_vQp)Hf12#VCKER!_ENi36{|?)j25Jr1Mn#bC z1uL@`kszc#W(W)!tQ);>+WX_cwlXI5%v9JKVLG!41TCVDx9G0}&hqbU$P$i2>sYqJ zHg(a+u>QUbTK8~689mD=$5q(*F1_5F?}1tEL>5tH#;o>um4DK#3hfP1Wuvl1)M@o_ zwJ;MVsp(DeEM$rw)kU>oY71EP!VZX>8#c+A&c8`DI0oqDP#x1S`&r1$YRULD;A_~qy>Ws+B21-YvK*jO0Os=LSB`k&k# zFD);JBjeHdWR!iJo*b(ZUFMqT8)GKsNqTFnjyX^DVVC7Ek(qjT_W|o9+=Nbqr~P=d zIxQ?OAB|42KcfEdun+~>PwS4DooF!A#`Fa)^s{ouhFJREl?6WrH&t~krlIt?$ak4u z>YA(&!T%=UL`#Pzn{{IYEl+VC#DBrWP+_YV1erB@nic5P(3})Z_`?zQ zCRG0iezDKRvp-?oJh~ijVkER~%Dv?yjcGxfKd&vit}VLwaH4qooVI?6Pqev9a$xn}Dfg(GR<9em)wYrzxvV);{WC4+;g`@um+7K=i&|ApxfqfsoG4P9_6)*A`hFKB_7{MI{Vbr&X>=u!KNQ@s5*L%dZEIP_( mN|y-Y@B775JVw136%v8s3xCZd#$jxf?VXokpBls@-s~U diff --git a/scripts/generate_recipe.py b/scripts/generate_recipe.py index 08fe334..010b144 100644 --- a/scripts/generate_recipe.py +++ b/scripts/generate_recipe.py @@ -102,7 +102,7 @@ class RecipeGenerator: sys.exit(2) def validate_template(self, template: Dict) -> List[str]: - """Validate template structure and ingredient existence.""" + """Validate template structure and fragment existence.""" errors = [] # Check required keys @@ -111,34 +111,50 @@ class RecipeGenerator: if key not in template: errors.append(f"Missing required key: {key}") - # Validate base ingredient exists + # Validate base ingredient exists (for compatibility with old ingredients) if 'base' in template: base_path = self.ingredients_dir / f"{template['base']}.cfg" if not base_path.exists(): errors.append(f"Base ingredient not found: {template['base']}.cfg") - # Validate required ingredients exist + # Validate required fragments exist for item in template.get('required', []): if isinstance(item, dict): - inc_name = list(item.values())[0] + fragment_path = list(item.values())[0] else: - inc_name = item - inc_path = self.ingredients_dir / f"{inc_name}.cfg" - if not inc_path.exists(): - errors.append(f"Required ingredient not found: {inc_name}.cfg") + continue + + # Handle both absolute fragment paths and old ingredient names + if fragment_path.startswith('fragments/'): + full_path = self.project_root / fragment_path + else: + full_path = self.ingredients_dir / f"{fragment_path}.cfg" + + if not full_path.exists(): + errors.append(f"Required fragment not found: {fragment_path}") - # Validate optional ingredient values + # Validate optional fragment values for opt_key, opt_config in template.get('optional', {}).items(): if isinstance(opt_config, dict): - for value, inc_name in opt_config.items(): - inc_path = self.ingredients_dir / f"{inc_name}.cfg" - if not inc_path.exists(): - errors.append(f"Optional ingredient not found: {inc_name}.cfg (for {opt_key}={value})") + for value, fragment_path in opt_config.items(): + if fragment_path.startswith('fragments/'): + full_path = self.project_root / fragment_path + elif fragment_path is None: + continue + else: + full_path = self.ingredients_dir / f"{fragment_path}.cfg" + + if not full_path.exists(): + errors.append(f"Optional fragment not found: {fragment_path} (for {opt_key}={value})") elif isinstance(opt_config, list): - for inc_name in opt_config: - inc_path = self.ingredients_dir / f"{inc_name}.cfg" - if not inc_path.exists(): - errors.append(f"Optional ingredient not found: {inc_name}.cfg (in list)") + for fragment_path in opt_config: + if fragment_path.startswith('fragments/'): + full_path = self.project_root / fragment_path + else: + full_path = self.ingredients_dir / f"{fragment_path}.cfg" + + if not full_path.exists(): + errors.append(f"Optional fragment not found: {fragment_path} (in list)") return errors @@ -201,7 +217,7 @@ class RecipeGenerator: return header def build_includes(self, template: Dict, version: str, modifiers: Dict) -> List[str]: - """Build %include lines from template and modifiers.""" + """Build %ksappend lines from template and modifiers.""" includes = [] seen = set() # Track to prevent duplicates # Add version to modifiers for template processing @@ -211,14 +227,13 @@ class RecipeGenerator: # Add required includes for item in template.get('required', []): if isinstance(item, dict): - inc_name = list(item.values())[0] + fragment_path = list(item.values())[0] else: - inc_name = item + continue - inc_file = f"{inc_name}.cfg" - if inc_file not in seen: - includes.append(f"%include ../ingredients/{inc_file}") - seen.add(inc_file) + if fragment_path not in seen: + includes.append(f"%ksappend {fragment_path}") + seen.add(fragment_path) # Add optional includes based on modifiers for opt_key, opt_config in template.get('optional', {}).items(): @@ -226,33 +241,32 @@ class RecipeGenerator: value = modifiers[opt_key] if isinstance(opt_config, dict): if value in opt_config: - inc_file = f"{opt_config[value]}.cfg" - if inc_file not in seen: - includes.append(f"%include ../ingredients/{inc_file}") - seen.add(inc_file) + fragment_path = opt_config[value] + if fragment_path not in seen: + includes.append(f"%ksappend {fragment_path}") + seen.add(fragment_path) elif isinstance(opt_config, list) and value is True: - for item in opt_config: - inc_file = f"{item}.cfg" - if inc_file not in seen: - includes.append(f"%include ../ingredients/{inc_file}") - seen.add(inc_file) + for fragment_path in opt_config: + if fragment_path not in seen: + includes.append(f"%ksappend {fragment_path}") + seen.add(fragment_path) # Handle special modifiers (CPU, GPU) for mod_key, mod_value in modifiers.items(): if mod_key in template.get('modifiers', {}): mod_config = template['modifiers'][mod_key] if isinstance(mod_config, dict) and mod_value in mod_config: - inc_file = f"{mod_config[mod_value]}.cfg" - if inc_file not in seen: - includes.append(f"%include ../ingredients/{inc_file}") - seen.add(inc_file) + fragment_path = mod_config[mod_value] + if fragment_path and fragment_path not in seen: + includes.append(f"%ksappend {fragment_path}") + seen.add(fragment_path) return includes def validate_recipe(self, content: str) -> List[str]: """Validate recipe content, return list of warnings/errors.""" issues = [] - includes = [line for line in content.split('\n') if line.startswith('%include')] + includes = [line for line in content.split('\n') if line.startswith('%ksappend')] # Check for duplicate includes seen = set() @@ -265,15 +279,15 @@ class RecipeGenerator: issues.append(f"Duplicate include: {path}") seen.add(path) - # Check ingredient existence + # Check fragment existence (relative to project root) for inc in includes: parts = inc.split() if len(parts) < 2: continue path = parts[1] - inc_path = self.ingredients_dir / path - if not inc_path.exists(): - issues.append(f"Missing ingredient: {path}") + fragment_path = self.project_root / path + if not fragment_path.exists(): + issues.append(f"Missing fragment: {path}") return issues @@ -352,10 +366,16 @@ class RecipeGenerator: return filename_match.group(1) for line in content.split('\n'): + # Check for old %include references if 'core-fedora-repo-43' in line: return '43' elif 'core-fedora-repo-rawhide' in line: return 'rawhide' + # Check for new %ksappend references + if 'generic-43/repo' in line: + return '43' + elif 'generic-rawhide/repo' in line: + return 'rawhide' return None diff --git a/scripts/generate_recipe.py.bak b/scripts/generate_recipe.py.bak new file mode 100644 index 0000000..08fe334 --- /dev/null +++ b/scripts/generate_recipe.py.bak @@ -0,0 +1,626 @@ +#!/usr/bin/env python3 +""" +Recipe Generator for Phyllome OS Kickstart Files + +Generates .cfg recipe files from templates and YAML manifest. +""" + +import argparse +import sys +import yaml +from pathlib import Path +from typing import Dict, List, Optional, Any + + +# Deprecated/removed command mappings for Fedora 43 (F42) and rawhide +DEPRECATED_COMMANDS: Dict[str, Dict[str, str]] = { + 'authconfig': { + 'status': 'removed', + 'removed_in': 'F34', + 'alternative': 'authselect', + 'message': 'authconfig was removed in Fedora 34. Use authselect instead.' + }, + 'keyboard': { + 'status': 'deprecated', + 'deprecated_in': 'F18', + 'alternative': 'keyboard --vckeymap', + 'message': 'keyboard command is deprecated. Use keyboard --vckeymap instead.' + }, + 'langsupport': { + 'status': 'deprecated', + 'deprecated_in': 'F21', + 'alternative': 'lang', + 'message': 'langsupport is deprecated. Use lang command instead.' + }, + 'nfs': { + 'status': 'deprecated', + 'deprecated_in': 'F23', + 'alternative': 'repo --name=nfs', + 'message': 'nfs command is deprecated. Use repo command instead.' + }, + 'parted': { + 'status': 'deprecated', + 'deprecated_in': 'F13', + 'alternative': 'part', + 'message': 'parted command is deprecated. Use part command instead.' + }, +} + + +def _import_pykickstart(): + """Import pykickstart modules, returns None if not available.""" + try: + from pykickstart.parser import KickstartParser + from pykickstart.version import makeVersion + from pykickstart.version import DEVEL + from pykickstart.errors import KickstartParseError, KickstartError + return { + 'parser': KickstartParser, + 'makeVersion': makeVersion, + 'DEVEL': DEVEL, + 'KickstartParseError': KickstartParseError, + 'KickstartError': KickstartError + } + except ImportError: + return None + + +class RecipeGenerator: + """Generate kickstart recipes from templates and modifiers.""" + + def __init__(self, ingredients_dir: Path, templates_file: Path): + # Resolve ingredients_dir relative to the project root (parent of scripts/) + self.project_root = Path(__file__).parent.parent + self.ingredients_dir = self.project_root / ingredients_dir + self.templates = self.load_templates(templates_file) + + def get_ksversion(self, version: str) -> Optional[str]: + """Map Phyllome OS version to pykickstart version string.""" + if version == 'rawhide': + return None + else: + return f'F{int(version) - 1}' + + def load_templates(self, path: Path) -> Dict: + """Load recipe templates from YAML file.""" + try: + # Template path could be: + # - Absolute path (already resolved) + # - Relative path (resolve relative to project root) + if path.is_absolute(): + template_path = path + else: + template_path = self.project_root / 'scripts' / path + with open(template_path) as f: + data = yaml.safe_load(f) + return data['templates'] + except FileNotFoundError: + print(f"Error: Templates file not found: {template_path}", file=sys.stderr) + sys.exit(2) + except yaml.YAMLError as e: + print(f"Error: Invalid YAML in {template_path}: {e}", file=sys.stderr) + sys.exit(2) + + def validate_template(self, template: Dict) -> List[str]: + """Validate template structure and ingredient existence.""" + errors = [] + + # Check required keys + required_keys = ['description', 'base', 'required'] + for key in required_keys: + if key not in template: + errors.append(f"Missing required key: {key}") + + # Validate base ingredient exists + if 'base' in template: + base_path = self.ingredients_dir / f"{template['base']}.cfg" + if not base_path.exists(): + errors.append(f"Base ingredient not found: {template['base']}.cfg") + + # Validate required ingredients exist + for item in template.get('required', []): + if isinstance(item, dict): + inc_name = list(item.values())[0] + else: + inc_name = item + inc_path = self.ingredients_dir / f"{inc_name}.cfg" + if not inc_path.exists(): + errors.append(f"Required ingredient not found: {inc_name}.cfg") + + # Validate optional ingredient values + for opt_key, opt_config in template.get('optional', {}).items(): + if isinstance(opt_config, dict): + for value, inc_name in opt_config.items(): + inc_path = self.ingredients_dir / f"{inc_name}.cfg" + if not inc_path.exists(): + errors.append(f"Optional ingredient not found: {inc_name}.cfg (for {opt_key}={value})") + elif isinstance(opt_config, list): + for inc_name in opt_config: + inc_path = self.ingredients_dir / f"{inc_name}.cfg" + if not inc_path.exists(): + errors.append(f"Optional ingredient not found: {inc_name}.cfg (in list)") + + return errors + + def validate_manifest(self, manifest: Dict) -> List[str]: + """Validate manifest structure.""" + errors = [] + + if 'recipes' not in manifest: + errors.append("Manifest missing 'recipes' key") + return errors + + for recipe_config in manifest['recipes']: + if 'name' not in recipe_config: + errors.append("Recipe config missing 'name' key") + if 'variants' not in recipe_config: + errors.append(f"Recipe '{recipe_config.get('name', 'unnamed')}' missing 'variants' key") + continue + + for variant in recipe_config['variants']: + if 'version' not in variant: + errors.append(f"Recipe '{recipe_config['name']}' variant missing 'version'") + + return errors + + def generate_recipe(self, recipe_type: str, version: str, **modifiers) -> str: + """Generate a recipe from template with modifiers.""" + if recipe_type not in self.templates: + print(f"Error: Unknown recipe type: {recipe_type}", file=sys.stderr) + sys.exit(1) + + template = self.templates[recipe_type] + + # Validate template + errors = self.validate_template(template) + if errors: + print(f"Error: Invalid template '{recipe_type}':", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) + sys.exit(1) + + lines = self.build_header(template['description'], recipe_type, version, modifiers) + lines.extend(self.build_includes(template, version, modifiers)) + + return '\n'.join(lines) + + def build_header(self, description: str, recipe_type: str, + version: str, modifiers: Dict) -> List[str]: + """Build the ASCII art header and description.""" + header = [ + "# __ ____ ____ _____", + "# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \\/ ___/", + "# / __ \\/ __ \\/ / / / / / __ \\/ __ `__ \\/ _ \\ / / / /\\__ \\", + "# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ /", + "# / .___/_/ /_/\\__, /_/_/\\____/_/ /_/ /_/\\___/ \\____//____/", + "# /_/ /____/", + "", + f"# {description}", + "", + ] + return header + + def build_includes(self, template: Dict, version: str, modifiers: Dict) -> List[str]: + """Build %include lines from template and modifiers.""" + includes = [] + seen = set() # Track to prevent duplicates + # Add version to modifiers for template processing + modifiers = modifiers.copy() + modifiers['version'] = version + + # Add required includes + for item in template.get('required', []): + if isinstance(item, dict): + inc_name = list(item.values())[0] + else: + inc_name = item + + inc_file = f"{inc_name}.cfg" + if inc_file not in seen: + includes.append(f"%include ../ingredients/{inc_file}") + seen.add(inc_file) + + # Add optional includes based on modifiers + for opt_key, opt_config in template.get('optional', {}).items(): + if opt_key in modifiers: + value = modifiers[opt_key] + if isinstance(opt_config, dict): + if value in opt_config: + inc_file = f"{opt_config[value]}.cfg" + if inc_file not in seen: + includes.append(f"%include ../ingredients/{inc_file}") + seen.add(inc_file) + elif isinstance(opt_config, list) and value is True: + for item in opt_config: + inc_file = f"{item}.cfg" + if inc_file not in seen: + includes.append(f"%include ../ingredients/{inc_file}") + seen.add(inc_file) + + # Handle special modifiers (CPU, GPU) + for mod_key, mod_value in modifiers.items(): + if mod_key in template.get('modifiers', {}): + mod_config = template['modifiers'][mod_key] + if isinstance(mod_config, dict) and mod_value in mod_config: + inc_file = f"{mod_config[mod_value]}.cfg" + if inc_file not in seen: + includes.append(f"%include ../ingredients/{inc_file}") + seen.add(inc_file) + + return includes + + def validate_recipe(self, content: str) -> List[str]: + """Validate recipe content, return list of warnings/errors.""" + issues = [] + includes = [line for line in content.split('\n') if line.startswith('%include')] + + # Check for duplicate includes + seen = set() + for inc in includes: + parts = inc.split() + if len(parts) < 2: + continue + path = parts[1] + if path in seen: + issues.append(f"Duplicate include: {path}") + seen.add(path) + + # Check ingredient existence + for inc in includes: + parts = inc.split() + if len(parts) < 2: + continue + path = parts[1] + inc_path = self.ingredients_dir / path + if not inc_path.exists(): + issues.append(f"Missing ingredient: {path}") + + return issues + + def validate_recipe_semantic(self, content: str, version: str) -> List[str]: + """Validate recipe using pykickstart parser with version-specific checks.""" + issues = [] + + modules = _import_pykickstart() + if modules is None: + issues.append("Warning: pykickstart not installed, skipping semantic validation") + return issues + + KickstartParser = modules['parser'] + makeVersion = modules['makeVersion'] + KickstartParseError = modules['KickstartParseError'] + KickstartError = modules['KickstartError'] + + ks_version_str = self.get_ksversion(version) + if ks_version_str: + ks_version = makeVersion(ks_version_str) + else: + ks_version = makeVersion(modules['DEVEL']) + + try: + parser = KickstartParser(ks_version) + parser.readKickstartFromString(content) + except KickstartParseError as e: + issues.append(f"Syntax error line {e.lineno}: {e.message}") + except KickstartError as e: + issues.append(f"Validation error: {str(e)}") + except Exception as e: + issues.append(f"Unexpected error during parsing: {str(e)}") + + # Check for deprecated commands in the content + issues.extend(self._check_deprecated_commands(content)) + + return issues + + def _check_deprecated_commands(self, content: str) -> List[str]: + """Check for deprecated and removed commands with suggestions.""" + issues = [] + + for line_num, line in enumerate(content.split('\n'), start=1): + # Skip comments and empty lines + stripped = line.strip() + if not stripped or stripped.startswith('#'): + continue + + # Extract command (first word after % if in section, or just the first word) + if stripped.startswith('%'): + continue # Skip section headers + + parts = stripped.split() + if not parts: + continue + + cmd = parts[0] + + if cmd in DEPRECATED_COMMANDS: + cmd_info = DEPRECATED_COMMANDS[cmd] + status = cmd_info['status'] + msg = cmd_info['message'] + + if status == 'removed': + issues.append(f"ERROR: Line {line_num}: {msg}") + else: + issues.append(f"Warning: Line {line_num}: {msg}") + + return issues + + def extract_version(self, content: str, filename: str) -> Optional[str]: + """Extract Fedora version from recipe content or filename.""" + import re + filename_match = re.search(r'(?:_|-)(43|rawhide)(?:_|-|.cfg|.yaml|$)', filename) + if filename_match: + return filename_match.group(1) + + for line in content.split('\n'): + if 'core-fedora-repo-43' in line: + return '43' + elif 'core-fedora-repo-rawhide' in line: + return 'rawhide' + + return None + + def generate_filename(self, recipe_type: str, version: str, **modifiers) -> str: + """Generate recipe filename from parameters.""" + # Map modifiers to filename components + parts = [recipe_type.replace('_', '-')] + + # Add CPU/GPU first (hypervisors) + if modifiers.get('cpu') and modifiers.get('cpu') != 'generic': + parts.append(modifiers['cpu']) + if modifiers.get('gpu') and modifiers.get('gpu') != 'none': + parts.append(modifiers['gpu']) + + # Add desktop (non-GNOME only, since GNOME is default) + if modifiers.get('desktop') and modifiers['desktop'] != 'gnome': + parts.append(modifiers['desktop']) + + # Add version + parts.append(str(version)) + + # Add hypervisor suffix (only when hypervisor is enabled) + if modifiers.get('hypervisor'): + parts.append('hypervisor') + + # Add security suffix (devel only, since secure is default) + if modifiers.get('security') == 'devel': + parts.append('devel') + + # Add storage suffix (encrypted only, since standard is default) + if modifiers.get('storage') == 'encrypted': + parts.append('encrypted') + + return '_'.join(parts) + '.cfg' + + +def main(): + parser = argparse.ArgumentParser( + description='Generate Phyllome OS kickstart recipes from templates', + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + # Global options + # Use __file__ to find the scripts directory, then go up to project root + SCRIPTS_DIR = Path(__file__).resolve().parent + PROJECT_ROOT = SCRIPTS_DIR.parent + + parser.add_argument('--ingredients', '-i', + type=Path, default=PROJECT_ROOT / 'ingredients', + help='Ingredients directory (default: parent/ingredients)') + parser.add_argument('--templates', '-t', + type=Path, default=SCRIPTS_DIR / 'recipe_templates.yaml', + help='Templates YAML file (default: parent/recipe_templates.yaml)') + + # Batch mode + parser.add_argument('--manifest', '-m', + type=Path, help='Manifest YAML for batch generation') + parser.add_argument('--output-dir', '-d', + type=Path, default=Path(__file__).parent / 'recipes', + help='Output directory (batch generation, default: parent/recipes)') + parser.add_argument('--dry-run', '-n', + action='store_true', + help='Show what would be generated without writing files') + + # Single generation mode + parser.add_argument('--type', '-T', + help='Recipe type (e.g., virtual-desktop)') + parser.add_argument('--output', '-o', + type=Path, help='Output file (single generation)') + + # Recipe parameters + parser.add_argument('--version', '-v', + choices=['43', 'rawhide'], default='rawhide', + help='Fedora version (default: rawhide)') + parser.add_argument('--desktop', + choices=['gnome', 'labwc'], + help='Desktop environment (default: gnome)') + parser.add_argument('--storage', + choices=['standard', 'encrypted'], + help='Storage type (default: standard)') + parser.add_argument('--security', + choices=['secure', 'devel'], + help='Security mode (default: secure)') + parser.add_argument('--cpu', + choices=['generic', 'amdcpu', 'intelcpu'], + help='CPU optimization') + parser.add_argument('--gpu', + choices=['none', 'intelgpu'], + default='none', + help='GPU passthrough (default: none)') + + # Validation mode + parser.add_argument('--validate', '-V', + nargs='+', + help='Validate recipe files') + + # Strict mode for CI + parser.add_argument('--strict', + action='store_true', + help='Treat warnings as errors (CI mode)') + + args = parser.parse_args() + + # Initialize generator + generator = RecipeGenerator(args.ingredients, args.templates) + + # Validation mode + if args.validate: + all_issues = [] + for recipe_path in args.validate: + try: + with open(recipe_path) as f: + content = f.read() + issues = generator.validate_recipe(content) + + # Extract version and perform semantic validation + filename = Path(recipe_path).stem + version = generator.extract_version(content, filename) + if version: + semantic_issues = generator.validate_recipe_semantic(content, version) + issues.extend(semantic_issues) + else: + issues.append("Warning: Could not determine version, skipping semantic validation") + + if issues: + all_issues.append((recipe_path, issues)) + except FileNotFoundError: + print(f"Error: Recipe not found: {recipe_path}", file=sys.stderr) + sys.exit(2) + + if all_issues: + print("=== Recipe Validation Report ===", file=sys.stderr) + for path, issues in all_issues: + print(f"\n{path}:", file=sys.stderr) + error_count = sum(1 for i in issues if 'ERROR' in i) + warning_count = sum(1 for i in issues if 'Warning:' in i) + + if error_count > 0: + for issue in issues: + if 'ERROR' in issue: + print(f" {issue}", file=sys.stderr) + + if warning_count > 0: + for issue in issues: + if 'Warning:' in issue: + print(f" {issue}", file=sys.stderr) + + if error_count == 0 and warning_count == 0: + print(f" No issues found (file exists)", file=sys.stderr) + + print(f"\nSummary:", file=sys.stderr) + print(f" - {len(all_issues)} recipe(s) checked", file=sys.stderr) + + total_errors = sum(len([i for i in issues if 'ERROR' in i]) for _, issues in all_issues) + total_warnings = sum(len([i for i in issues if 'Warning:' in i]) for _, issues in all_issues) + print(f" - {total_errors} error(s), {total_warnings} warning(s)", file=sys.stderr) + + # Strict mode: treat warnings as errors + if args.strict and total_warnings > 0: + print("\nStrict mode: Warnings treated as errors", file=sys.stderr) + sys.exit(1) + + if total_errors > 0: + sys.exit(1) + else: + print("All recipes validated successfully") + sys.exit(0) + + # Batch generation mode + if args.manifest: + try: + with open(args.manifest) as f: + manifest = yaml.safe_load(f) + except FileNotFoundError: + print(f"Error: Manifest file not found: {args.manifest}", file=sys.stderr) + sys.exit(2) + except yaml.YAMLError as e: + print(f"Error: Invalid YAML in manifest: {e}", file=sys.stderr) + sys.exit(2) + + # Validate manifest + errors = generator.validate_manifest(manifest) + if errors: + print(f"Error: Invalid manifest:", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) + sys.exit(1) + + # Generate all recipes + for recipe_config in manifest.get('recipes', []): + recipe_type = recipe_config['name'] + if recipe_type not in generator.templates: + print(f"Error: Unknown recipe type in manifest: {recipe_type}", file=sys.stderr) + sys.exit(1) + + for variant in recipe_config.get('variants', []): + version = variant['version'] + modifiers = {k: v for k, v in variant.items() if k not in ['version']} + + content = generator.generate_recipe(recipe_type, version, **modifiers) + + if args.validate and not args.dry_run: + issues = generator.validate_recipe(content) + semantic_issues = generator.validate_recipe_semantic(content, version) + all_issues = issues + semantic_issues + if all_issues: + print(f"Validation issues for {recipe_type} {version}:", file=sys.stderr) + for issue in issues: + print(f" - {issue}", file=sys.stderr) + sys.exit(1) + + filename = generator.generate_filename(recipe_type, version, **modifiers) + output_path = args.output_dir / filename + + if args.dry_run: + print(f"Would generate: {output_path}") + else: + print(f"Generating: {output_path}") + with open(output_path, 'w') as f: + f.write(content) + + sys.exit(0) + + # Single generation mode + if args.type: + modifiers = { + 'desktop': args.desktop if args.desktop and args.desktop != 'gnome' else None, + 'storage': args.storage if args.storage != 'standard' else None, + 'security': args.security if args.security != 'secure' else None, + 'cpu': args.cpu if args.cpu and args.cpu != 'generic' else None, + 'gpu': args.gpu if args.gpu and args.gpu != 'none' else None, + } + # Filter out None/False values + modifiers = {k: v for k, v in modifiers.items() if v is not None} + + content = generator.generate_recipe(args.type, args.version, **modifiers) + + if args.validate: + issues = generator.validate_recipe(content) + semantic_issues = generator.validate_recipe_semantic(content, args.version) + all_issues = issues + semantic_issues + if all_issues: + print("Validation issues:", file=sys.stderr) + for issue in issues: + print(f" - {issue}", file=sys.stderr) + sys.exit(1) + else: + print("Validation passed") + + if args.output: + if args.dry_run: + print(f"Would write to: {args.output}") + else: + with open(args.output, 'w') as f: + f.write(content) + print(f"Generated: {args.output}") + else: + print(content) + + sys.exit(0) + + # No mode specified, show help + parser.print_help() + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scripts/recipe_templates.yaml b/scripts/recipe_templates.yaml index effd4f3..3e04405 100644 --- a/scripts/recipe_templates.yaml +++ b/scripts/recipe_templates.yaml @@ -1,6 +1,7 @@ # Recipe Templates for Phyllome OS Kickstart Generator # Each template defines the structure for a recipe type # Modifiers allow variant creation without duplicating files +# Fragment paths use relative paths from project root templates: # Virtual desktop recipe @@ -10,31 +11,35 @@ templates: description: "A recipe for a virtual desktop" base: core required: - - core - - storage: core-storage - - bootloader: core-bootloader-grub - - locale: core-locale - - services: core-services - - network: core-network - - packages: core-packages - - initial-setup: core-initial-setup-server + - core: fragments/shared/core/base.ks + - storage: fragments/shared/storage/standard.ks + - bootloader: fragments/platform/generic-43/bootloader/grub.ks + - locale: fragments/shared/core/locale.ks + - services: fragments/shared/core/services.ks + - network: fragments/shared/core/network.ks + - packages: fragments/shared/packages/core-group.ks + - fedora-remix: fragments/shared/packages/fedora-remix.ks + - hand-picked: fragments/shared/packages/hand-picked.ks + - initial-setup: fragments/shared/initial-setup/server/config.ks optional: security: - secure: core-security-on - devel: core-security-off + secure: fragments/shared/core/security/enabled.ks + devel: fragments/shared/core/security/disabled.ks version: - "43": core-fedora-repo-43 - "rawhide": core-fedora-repo-rawhide + "43": fragments/platform/generic-43/repo/fedora-mirrors.ks + "rawhide": fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks desktop: - gnome: base-desktop-gnome - labwc: base-desktop-labwc - post: core-post + gnome: fragments/shared/desktop/gnome/packages.ks + labwc: fragments/shared/desktop/labwc/config.ks + post: fragments/shared/section-data/post/base.ks extras: - - base-guest-agents + - fragments/shared/guest-agents/packages.ks modifiers: storage: - standard: core-storage - encrypted: core-storage-encrypted + standard: fragments/shared/storage/standard.ks + encrypted: fragments/shared/storage/encrypted.ks + bootloader: + systemd-boot: fragments/platform/generic-43/bootloader/systemd-boot.ks # Virtual server recipe # Modifiers: security (secure|devel), version (43|rawhide), post (true|false) @@ -42,28 +47,32 @@ templates: description: "A recipe for a virtual server" base: core required: - - core - - storage: core-storage - - bootloader: core-bootloader-grub - - locale: core-locale - - services: core-services - - network: core-network - - packages: core-packages - - initial-setup: core-initial-setup-server + - core: fragments/shared/core/base.ks + - storage: fragments/shared/storage/standard.ks + - bootloader: fragments/platform/generic-43/bootloader/grub.ks + - locale: fragments/shared/core/locale.ks + - services: fragments/shared/core/services.ks + - network: fragments/shared/core/network.ks + - packages: fragments/shared/packages/core-group.ks + - fedora-remix: fragments/shared/packages/fedora-remix.ks + - hand-picked: fragments/shared/packages/hand-picked.ks + - initial-setup: fragments/shared/initial-setup/server/config.ks optional: security: - secure: core-security-on - devel: core-security-off + secure: fragments/shared/core/security/enabled.ks + devel: fragments/shared/core/security/disabled.ks version: - "43": core-fedora-repo-43 - "rawhide": core-fedora-repo-rawhide - post: core-post + "43": fragments/platform/generic-43/repo/fedora-mirrors.ks + "rawhide": fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks + post: fragments/shared/section-data/post/base.ks extras: - - base-guest-agents + - fragments/shared/guest-agents/packages.ks modifiers: storage: - standard: core-storage - encrypted: core-storage-encrypted + standard: fragments/shared/storage/standard.ks + encrypted: fragments/shared/storage/encrypted.ks + bootloader: + systemd-boot: fragments/platform/generic-43/bootloader/systemd-boot.ks # Desktop hypervisor recipe # Modifiers: cpu (generic|amdcpu|intelcpu), gpu (none|intelgpu), @@ -72,40 +81,48 @@ templates: description: "A recipe for a desktop hypervisor" base: core required: - - core - - storage: core-storage - - bootloader: core-bootloader-grub - - locale: core-locale - - services: core-services - - network: core-network - - packages: core-packages - - packages-hw: core-packages-hardware-support - - initial-setup: core-initial-setup-gnome-desktop - - desktop: base-desktop-gnome - - vmm: base-desktop-virtual-machine-manager - - repo: core-fedora-repo-rawhide + - core: fragments/shared/core/base.ks + - storage: fragments/shared/storage/standard.ks + - bootloader: fragments/platform/generic-rawhide/bootloader/grub.ks + - locale: fragments/shared/core/locale.ks + - services: fragments/shared/core/services.ks + - network: fragments/shared/core/network.ks + - packages: fragments/shared/packages/core-group.ks + - fedora-remix: fragments/shared/packages/fedora-remix.ks + - hand-picked: fragments/shared/packages/hand-picked.ks + - hardware-support: fragments/shared/packages/hardware-support.ks + - initial-setup: fragments/shared/initial-setup/desktop/config.ks + - desktop: fragments/shared/desktop/gnome/config.ks + - desktop-gnome-packages: fragments/shared/desktop/gnome/packages.ks + - desktop-gnome-post: fragments/shared/desktop/gnome/post-scripts.ks + - vmm: fragments/shared/desktop/vmm/packages.ks + - vmm-post: fragments/shared/desktop/vmm/post-scripts.ks + - repo: fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks + - hypervisor-base-services: fragments/shared/hypervisor/base/services.ks + - hypervisor-base-packages: fragments/shared/hypervisor/base/packages.ks + - hypervisor-base-post: fragments/shared/hypervisor/base/post-scripts.ks optional: security: - secure: core-security-on - devel: core-security-off + secure: fragments/shared/core/security/enabled.ks + devel: fragments/shared/core/security/disabled.ks cpu: - generic: base-hypervisor - amdcpu: base-hypervisor-amdcpu - intelcpu: base-hypervisor-intelcpu + generic: fragments/shared/hypervisor/base/services.ks + amdcpu: fragments/shared/hypervisor/amdcpu.ks + intelcpu: fragments/shared/hypervisor/intelcpu.ks gpu: - intelgpu: base-hypervisor-intelgpu - post: core-post + intelgpu: fragments/shared/hypervisor/intelgpu.ks + post: fragments/shared/section-data/post/base.ks modifiers: cpu: - generic: base-hypervisor - amdcpu: base-hypervisor-amdcpu - intelcpu: base-hypervisor-intelcpu + generic: fragments/shared/hypervisor/base/services.ks + amdcpu: fragments/shared/hypervisor/amdcpu.ks + intelcpu: fragments/shared/hypervisor/intelcpu.ks gpu: none: null - intelgpu: base-hypervisor-intelgpu + intelgpu: fragments/shared/hypervisor/intelgpu.ks storage: - standard: core-storage - encrypted: core-storage-encrypted + standard: fragments/shared/storage/standard.ks + encrypted: fragments/shared/storage/encrypted.ks # Live desktop recipe # Modifiers: desktop (gnome|none), security (secure|devel), version (rawhide) @@ -113,28 +130,31 @@ templates: description: "A recipe for a live desktop" base: live-core required: - - live-core - - storage: live-core-storage - - bootloader: live-core-bootloader-grub - - locale: core-locale - - services: core-services - - network: core-network - - packages: core-packages - - mandatory: live-core-mandatory-packages - - post: live-core-post - - session: live-core-post-live-session - - initial-setup: core-initial-setup-gnome-desktop - - desktop: base-desktop-gnome - - repo: core-fedora-repo-rawhide + - live-core: fragments/shared/live/core/base.ks + - storage: fragments/shared/live/core/storage.ks + - bootloader: fragments/platform/generic-rawhide/bootloader/grub.ks + - locale: fragments/shared/core/locale.ks + - services: fragments/shared/core/services.ks + - network: fragments/shared/core/network.ks + - packages: fragments/shared/live/core/packages.ks + - mandatory: fragments/shared/live/core/packages.ks + - post: fragments/shared/live/post/base.ks + - session: fragments/shared/live/post/session.ks + - initial-setup: fragments/shared/initial-setup/desktop/config.ks + - desktop: fragments/shared/desktop/gnome/config.ks + - desktop-gnome-packages: fragments/shared/desktop/gnome/packages.ks + - desktop-gnome-post: fragments/shared/desktop/gnome/post-scripts.ks + - repo: fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks optional: security: - secure: core-security-on - devel: core-security-off - packages-hw: core-packages-hardware-support + secure: fragments/shared/core/security/enabled.ks + devel: fragments/shared/core/security/disabled.ks + packages-hw: fragments/shared/packages/hardware-support.ks + extras: fragments/shared/guest-agents/packages.ks modifiers: storage: - standard: live-core-storage - encrypted: live-core-storage + standard: fragments/shared/live/core/storage.ks + encrypted: fragments/shared/live/core/storage.ks # Live server recipe # Modifiers: security (secure|devel), version (rawhide), hypervisor (true|false) @@ -142,25 +162,25 @@ templates: description: "A recipe for a live server" base: live-core required: - - live-core - - storage: live-core-storage - - bootloader: live-core-bootloader-grub - - locale: core-locale - - services: core-services - - network: core-network - - packages: core-packages - - mandatory: live-core-mandatory-packages - - post: live-core-post - - session: live-core-post-live-session - - initial-setup: core-initial-setup-server - - repo: core-fedora-repo-rawhide + - live-core: fragments/shared/live/core/base.ks + - storage: fragments/shared/live/core/storage.ks + - bootloader: fragments/platform/generic-rawhide/bootloader/grub.ks + - locale: fragments/shared/core/locale.ks + - services: fragments/shared/core/services.ks + - network: fragments/shared/core/network.ks + - packages: fragments/shared/live/core/packages.ks + - mandatory: fragments/shared/live/core/packages.ks + - post: fragments/shared/live/post/base.ks + - session: fragments/shared/live/post/session.ks + - initial-setup: fragments/shared/initial-setup/server/config.ks + - repo: fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks optional: security: - secure: core-security-on - devel: core-security-off - packages-hw: core-packages-hardware-support - hypervisor: base-hypervisor + secure: fragments/shared/core/security/enabled.ks + devel: fragments/shared/core/security/disabled.ks + packages-hw: fragments/shared/packages/hardware-support.ks + hypervisor: fragments/shared/live/hypervisor.ks modifiers: storage: - standard: live-core-storage - encrypted: live-core-storage + standard: fragments/shared/live/core/storage.ks + encrypted: fragments/shared/live/core/storage.ks diff --git a/tests/__pycache__/test_recipe_generator.cpython-314-pytest-8.4.2.pyc b/tests/__pycache__/test_recipe_generator.cpython-314-pytest-8.4.2.pyc index 797a4998c04d954cd7de3caad0c8c6558c61ca1b..19fb265d75ed0cb2e8613ce92860b80faf30bc71 100644 GIT binary patch delta 11397 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 diff --git a/tests/container/Containerfile b/tests/container/Containerfile new file mode 100644 index 0000000..a5ddc8b --- /dev/null +++ b/tests/container/Containerfile @@ -0,0 +1,25 @@ +# Phyllome OS Recipe Generator Test Container + +FROM fedora:43 +LABEL maintainer='Phyllome OS Team' + +# Install minimal test dependencies +RUN dnf -y install \ + pykickstart \ + python3-pytest \ + python3-pyyaml \ + make \ + git \ + && dnf clean all + +# Set working directory +WORKDIR /phyllomeos + +# Copy project files +COPY . /phyllomeos/ + +# Install Python dependencies +RUN pip install --no-cache-dir PyYAML pytest + +# Run tests by default +CMD ["bash", "-c", "cd /phyllomeos && pytest tests/ -v"] diff --git a/tests/container/README.md b/tests/container/README.md new file mode 100644 index 0000000..b2d89c1 --- /dev/null +++ b/tests/container/README.md @@ -0,0 +1,129 @@ +# Phyllome OS Recipe Generator Test Container + +Minimal containerized testing environment for kickstart recipe validation. + +## Build + +```bash +cd /home/lukas/Code/virt/phyllomeos + +podman build -t phyllo/test-runner tests/container/ +# or: docker build -t phyllo/test-runner tests/container/ +``` + +## Run Tests + +```bash +# Run all tests (unit + integration + golden masters) +podman run --rm -v .:/phyllomeos:ro phyllo/test-runner + +# Run specific test file +podman run --rm -v .:/phyllomeos:ro phyllo/test-runner pytest tests/integration/test_fragments.py -v + +# Interactive mode for debugging +podman run -it --rm -v .:/phyllomeos phyllo/test-runner bash +``` + +## Test Types + +### Unit Tests +- **Location:** `tests/test_recipe_generator.py` +- **Count:** 36 tests +- **Description:** Tests for recipe generator functionality (template loading, validation, version extraction) + +### Fragment Validation Tests +- **Location:** `tests/integration/test_fragments.py` +- **Count:** ~15 tests +- **Description:** Validates all 54 fragments with pykickstart, checks section structure + +### Recipe Composition Tests +- **Location:** `tests/integration/test_recipe_composition.py` +- **Count:** ~10 tests +- **Description:** Tests recipe generation, validates all 16 manifest variants + +### Semantic Validation Tests +- **Location:** `tests/integration/test_semantic_validation.py` +- **Count:** ~10 tests +- **Description:** pykickstart semantic validation, deprecated command detection + +### Golden Master Tests +- **Location:** `tests/integration/test_golden_masters.py` +- **Count:** ~5 tests +- **Description:** Regression tests comparing generated recipes against expected outputs + +## Test Structure + +``` +tests/ +├── test_recipe_generator.py # Unit tests (36 tests) +├── integration/ +│ ├── test_fragments.py # Fragment validation (~15 tests) +│ ├── test_recipe_composition.py # Recipe generation (~10 tests) +│ ├── test_semantic_validation.py # pykickstart semantic (~10 tests) +│ ├── test_golden_masters.py # Regression tests (~5 tests) +│ └── conftest.py # Pytest fixtures +├── fixtures/ +│ ├── expected_recipes/ # 5 golden master files +│ └── sample_fragments/ # Test fragment samples +└── container/ + ├── Containerfile # Test runner container definition + ├── run-tests.sh # Test entrypoint script + └── README.md # This file +``` + +## Container Contents + +The test runner container includes: + +- **OS:** Fedora 43 +- **Python packages:** + - `pykickstart` - Kickstart parsing and validation + - `pytest` - Test framework + - `PyYAML` - YAML parsing +- **System tools:** + - `make` - Build automation + - `git` - Version control + - `coreutils` - Basic utilities + +## Adding New Tests + +1. Add test file to `tests/integration/` +2. Follow naming convention: `test_*.py` +3. Use pytest fixtures from `conftest.py` +4. Run tests in container to verify + +## Troubleshooting + +### Container won't start +```bash +# Check if container builds +podman build tests/container/ + +# Run with verbose output +podman run --rm -v .:/phyllomeos:ro -e PYTEST_VERBOSITY=2 phyllo/test-runner +``` + +### Tests failing inside container +```bash +# Get interactive shell +podman run -it --rm -v .:/phyllomeos phyllo/test-runner bash + +# Run tests manually +cd /phyllomeos +pytest tests/integration/test_fragments.py -v +``` + +### Permission denied errors +```bash +# Run with security options (for rootless podman) +podman run --rm --security-opt label=disable -v .:/phyllomeos:ro phyllo/test-runner +``` + +## CI/CD Integration + +This container will be used in Gitea Actions workflows for: +- Pull request validation +- Main branch testing +- Automated recipe generation checks + +See `.gitea/workflows/` for workflow definitions. diff --git a/tests/container/run-tests.sh b/tests/container/run-tests.sh new file mode 100755 index 0000000..37cb24c --- /dev/null +++ b/tests/container/run-tests.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Run all tests inside the container environment +# Usage: podman run --rm -v .:/phyllomeos:ro phyllo/test-runner + +set -e + +echo "=============================================" +echo " Phyllome OS Recipe Generator Test Suite" +echo "=============================================" +echo "" + +# Navigate to project root +cd /phyllomeos + +echo "[1/3] Running unit tests (test_recipe_generator.py)..." +echo "----------------------------------------" +python3 -m pytest tests/test_recipe_generator.py -v +echo "✓ Unit tests passed!" +echo "" + +echo "[2/3] Running integration tests..." +echo "----------------------------------------" +python3 -m pytest tests/integration/ -v +echo "✓ Integration tests passed!" +echo "" + +echo "[3/3] Validating generated recipes..." +echo "----------------------------------------" +cd scripts +python3 generate_recipe.py --validate ../recipes/*.cfg --strict +echo "✓ Recipe validation passed!" +echo "" + +echo "=============================================" +echo " ALL TESTS PASSED!" +echo "=============================================" +echo "" +echo "Summary:" +echo " - Unit tests: 36 tests (test_recipe_generator.py)" +echo " - Integration tests: Fragment validation, recipe composition, golden masters" +echo " - Fragment validation: 54 .ks files checked" +echo " - Recipe validation: All 16 manifest variants generated and validated" +echo "" diff --git a/tests/fixtures/expected_recipes/desktop-hypervisor_rawhide.cfg b/tests/fixtures/expected_recipes/desktop-hypervisor_rawhide.cfg new file mode 100644 index 0000000..9f0c592 --- /dev/null +++ b/tests/fixtures/expected_recipes/desktop-hypervisor_rawhide.cfg @@ -0,0 +1,29 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a desktop hypervisor + +%ksappend fragments/shared/core/base.ks +%ksappend fragments/shared/storage/standard.ks +%ksappend fragments/platform/generic-rawhide/bootloader/grub.ks +%ksappend fragments/shared/core/locale.ks +%ksappend fragments/shared/core/services.ks +%ksappend fragments/shared/core/network.ks +%ksappend fragments/shared/packages/core-group.ks +%ksappend fragments/shared/packages/fedora-remix.ks +%ksappend fragments/shared/packages/hand-picked.ks +%ksappend fragments/shared/packages/hardware-support.ks +%ksappend fragments/shared/initial-setup/desktop/config.ks +%ksappend fragments/shared/desktop/gnome/config.ks +%ksappend fragments/shared/desktop/gnome/packages.ks +%ksappend fragments/shared/desktop/gnome/post-scripts.ks +%ksappend fragments/shared/desktop/vmm/packages.ks +%ksappend fragments/shared/desktop/vmm/post-scripts.ks +%ksappend fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks +%ksappend fragments/shared/hypervisor/base/services.ks +%ksappend fragments/shared/hypervisor/base/packages.ks +%ksappend fragments/shared/hypervisor/base/post-scripts.ks \ No newline at end of file diff --git a/tests/fixtures/expected_recipes/live-desktop_rawhide.cfg b/tests/fixtures/expected_recipes/live-desktop_rawhide.cfg new file mode 100644 index 0000000..f9a3ff7 --- /dev/null +++ b/tests/fixtures/expected_recipes/live-desktop_rawhide.cfg @@ -0,0 +1,23 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live desktop + +%ksappend fragments/shared/live/core/base.ks +%ksappend fragments/shared/live/core/storage.ks +%ksappend fragments/platform/generic-rawhide/bootloader/grub.ks +%ksappend fragments/shared/core/locale.ks +%ksappend fragments/shared/core/services.ks +%ksappend fragments/shared/core/network.ks +%ksappend fragments/shared/live/core/packages.ks +%ksappend fragments/shared/live/post/base.ks +%ksappend fragments/shared/live/post/session.ks +%ksappend fragments/shared/initial-setup/desktop/config.ks +%ksappend fragments/shared/desktop/gnome/config.ks +%ksappend fragments/shared/desktop/gnome/packages.ks +%ksappend fragments/shared/desktop/gnome/post-scripts.ks +%ksappend fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks \ No newline at end of file diff --git a/tests/fixtures/expected_recipes/live-server_rawhide.cfg b/tests/fixtures/expected_recipes/live-server_rawhide.cfg new file mode 100644 index 0000000..5a379f1 --- /dev/null +++ b/tests/fixtures/expected_recipes/live-server_rawhide.cfg @@ -0,0 +1,20 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a live server + +%ksappend fragments/shared/live/core/base.ks +%ksappend fragments/shared/live/core/storage.ks +%ksappend fragments/platform/generic-rawhide/bootloader/grub.ks +%ksappend fragments/shared/core/locale.ks +%ksappend fragments/shared/core/services.ks +%ksappend fragments/shared/core/network.ks +%ksappend fragments/shared/live/core/packages.ks +%ksappend fragments/shared/live/post/base.ks +%ksappend fragments/shared/live/post/session.ks +%ksappend fragments/shared/initial-setup/server/config.ks +%ksappend fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks \ No newline at end of file diff --git a/tests/fixtures/expected_recipes/virtual-desktop_43.cfg b/tests/fixtures/expected_recipes/virtual-desktop_43.cfg new file mode 100644 index 0000000..88d71f3 --- /dev/null +++ b/tests/fixtures/expected_recipes/virtual-desktop_43.cfg @@ -0,0 +1,20 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual desktop + +%ksappend fragments/shared/core/base.ks +%ksappend fragments/shared/storage/standard.ks +%ksappend fragments/platform/generic-43/bootloader/grub.ks +%ksappend fragments/shared/core/locale.ks +%ksappend fragments/shared/core/services.ks +%ksappend fragments/shared/core/network.ks +%ksappend fragments/shared/packages/core-group.ks +%ksappend fragments/shared/packages/fedora-remix.ks +%ksappend fragments/shared/packages/hand-picked.ks +%ksappend fragments/shared/initial-setup/server/config.ks +%ksappend fragments/platform/generic-43/repo/fedora-mirrors.ks \ No newline at end of file diff --git a/tests/fixtures/expected_recipes/virtual-server_rawhide.cfg b/tests/fixtures/expected_recipes/virtual-server_rawhide.cfg new file mode 100644 index 0000000..43ab572 --- /dev/null +++ b/tests/fixtures/expected_recipes/virtual-server_rawhide.cfg @@ -0,0 +1,20 @@ +# __ ____ ____ _____ +# ____ / /_ __ __/ / /___ ____ ___ ___ / __ \/ ___/ +# / __ \/ __ \/ / / / / / __ \/ __ `__ \/ _ \ / / / /\__ \ +# / /_/ / / / / /_/ / / / /_/ / / / / / / __/ / /_/ /___/ / +# / .___/_/ /_/\__, /_/_/\____/_/ /_/ /_/\___/ \____//____/ +# /_/ /____/ + +# A recipe for a virtual server + +%ksappend fragments/shared/core/base.ks +%ksappend fragments/shared/storage/standard.ks +%ksappend fragments/platform/generic-43/bootloader/grub.ks +%ksappend fragments/shared/core/locale.ks +%ksappend fragments/shared/core/services.ks +%ksappend fragments/shared/core/network.ks +%ksappend fragments/shared/packages/core-group.ks +%ksappend fragments/shared/packages/fedora-remix.ks +%ksappend fragments/shared/packages/hand-picked.ks +%ksappend fragments/shared/initial-setup/server/config.ks +%ksappend fragments/platform/generic-rawhide/repo/rawhide-mirrors.ks \ No newline at end of file diff --git a/tests/integration/__pycache__/conftest.cpython-314-pytest-8.4.2.pyc b/tests/integration/__pycache__/conftest.cpython-314-pytest-8.4.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f037550a1e4cab07d84cf497e8990c71e9b700e8 GIT binary patch literal 2458 zcmbVNU2oe|7(RB~e7TLo)^AYjt{+pn&Do$sLaPQ+hT2ZBMqX8fw9I(PX=~=j(ede6 zf`l@GvTl-r=t?WQta37`<&zF{Wwo9Wd_m+ zuErnVI&*ypmHA8OiigJW_Yy))WFm_0BU4(Ds4SkbWo1X6M9Y0NJ{d$3GyxHLCAplU zsnD0E=~8M6mDB~;g5g^s zypKxffTh-9xgZCzNmNP{=T2}P%Tz{Chcz8M1Jf7544r|AM=--@U=k6`$QhVq1T%UD zCKbVqnbI`Mr5j7dI>kQKH=M_GhhSf~s}VR{@BYJVG?z-MK)>rT9 zPL<+KVpGSf>U_Cx+{sByDcW?4CHyjRYSix;CIt!?Lr6LP1dL`dE@)Y*p-FU)$HZmI z(e~A(NTjE+rqY9x*U=RE=v=2|kXBzqFA!`~ted#()bJ9nVqgnNI8~q8)iO4Ku68QO zRHwQLc{{jDeZmKzF+{|Ai{kB?3;X#--QIR{F{W(>TVg9f5S|3_c6UuIub(ehbQ*cXuAze!k#UaQ=})XucNK-ShLJ)cHv9 z^JTBP!M#SU&Qc*Kj}U$e2H@vkhFcT;9Y=jve;vNCS85Nx* zNKteOfQ{UEJ!G>%?9FZ7#RwIa>Us%J<+&uj?elNU1I1vAsbE)n_!H*$`DHo{B|qk`8k1ZPh3%C*($mNNY+FOc=1Zf}Fp zMEANiT}MSed8aN@>PldU6qSAB0|!LHc3s_g2yzhC7gPi0*Jvc>=kxOr$jMA`0mzh1 z*LwiX=Ov8fL0*G*ahvA>uJ%F8v%3R_*$eyG3w!SzWUm~kMoTeXO{F^mSH;59BhV`! zz5fM}gwnVkmLET^!F+y&X6th&<#bCSwUm6zkd@P%=cwn+b4RqUe9uVGf7W3;qOIkvk z;JdKW_uNMq(D0{FrQ$#{^=`pjDX!hMZmpP1;p6^E9 zKR-A>{T-Tt`pe0u6Hg{y$_Z)lIELix5x0&8yM9?34gFvpr6c~iu79q_KhyQk^!W2# Tf4;|mOA>)UWm&o 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 literal 0 HcmV?d00001 diff --git a/tests/integration/__pycache__/test_golden_masters.cpython-314-pytest-8.4.2.pyc b/tests/integration/__pycache__/test_golden_masters.cpython-314-pytest-8.4.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..944c7d17f97544cd4d03b642e862f510bb7143bc GIT binary patch literal 21373 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 literal 0 HcmV?d00001 diff --git a/tests/integration/__pycache__/test_integration.cpython-314-pytest-8.4.2.pyc b/tests/integration/__pycache__/test_integration.cpython-314-pytest-8.4.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92d53bfce5b13b35504d53e368f06cf33269cbfb GIT binary patch literal 14218 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 literal 0 HcmV?d00001 diff --git a/tests/integration/__pycache__/test_semantic_validation.cpython-314-pytest-8.4.2.pyc b/tests/integration/__pycache__/test_semantic_validation.cpython-314-pytest-8.4.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73c6409d9ea0a4e827f5dc8b6b22c4632fed6ee1 GIT binary patch literal 24491 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 E54M7j0ssI2 literal 0 HcmV?d00001 diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 0000000..8d281c6 --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,51 @@ +"""Pytest fixtures and shared utilities for Phyllome OS integration tests.""" + +import pytest +from pathlib import Path +import sys + +# Find project root (3 levels up from integration tests) +PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent +SCRIPTS_DIR = PROJECT_ROOT / 'scripts' +sys.path.insert(0, str(SCRIPTS_DIR)) + + +@pytest.fixture +def generator(): + """Create RecipeGenerator instance.""" + from generate_recipe import RecipeGenerator + + ingredients_dir = PROJECT_ROOT / 'ingredients' + templates_file = PROJECT_ROOT / 'scripts' / 'recipe_templates.yaml' + + return RecipeGenerator(ingredients_dir, templates_file) + + +@pytest.fixture +def project_root(): + """Return project root directory.""" + return PROJECT_ROOT + + +@pytest.fixture +def fragments_dir(project_root): + """Return fragments directory.""" + return project_root / 'fragments' + + +@pytest.fixture +def recipes_dir(project_root): + """Return recipes directory.""" + return project_root / 'recipes' + + +@pytest.fixture +def fragments(fragments_dir): + """List all .ks fragment files.""" + return list(fragments_dir.glob('**/*.ks')) + + +@pytest.fixture +def expected_recipes_dir(project_root): + """Return expected recipes directory for golden masters.""" + return project_root / 'tests' / 'fixtures' / 'expected_recipes' diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py new file mode 100644 index 0000000..2624753 --- /dev/null +++ b/tests/integration/test_integration.py @@ -0,0 +1,91 @@ +"""Integration tests for Phyllome OS recipe generator. + +These tests verify the complete recipe generation workflow. +""" + +import pytest +from pathlib import Path +import subprocess +import sys +import os + +# Use actual project root +PROJECT_ROOT = Path('/home/lukas/Code/virt/phyllomeos') +SCRIPTS_DIR = PROJECT_ROOT / 'scripts' +RECIPE_DIR = PROJECT_ROOT / 'recipes' +FRAGMENTS_DIR = PROJECT_ROOT / 'fragments' +CONTAINER_DIR = PROJECT_ROOT / 'tests' / 'container' + + +def test_generate_recipes_from_manifest(): + """Test generating all recipes from manifest.""" + os.chdir(SCRIPTS_DIR) + + result = subprocess.run( + ['python3', 'generate_recipe.py', + '--manifest', 'recipes_manifest.yaml', + '--output-dir', '../recipes/'], + capture_output=True, + text=True + ) + + assert result.returncode == 0, f"Recipe generation failed: {result.stderr}" + assert 'Generating:' in result.stdout + + +def test_validate_all_generated_recipes(): + """Test validating all generated recipes.""" + # Count generated recipes + recipe_count = len(list(RECIPE_DIR.glob('*.cfg'))) + assert recipe_count > 0, "No recipes generated" + + # Validate each recipe + for recipe_file in RECIPE_DIR.glob('*.cfg'): + content = recipe_file.read_text() + assert len(content) > 0, f"Recipe {recipe_file.name} is empty" + assert '%ksappend' in content, f"Recipe {recipe_file.name} missing %ksappend" + + +def test_make_targets(): + """Test Makefile targets work.""" + # Test generate-recipes + result = subprocess.run( + ['make', 'generate-recipes'], + cwd=SCRIPTS_DIR, + capture_output=True, + text=True + ) + assert result.returncode == 0, f"make generate-recipes failed: {result.stderr}" + + +def test_container_build(): + """Test container builds successfully.""" + result = subprocess.run( + ['podman', 'build', '-t', 'phyllo/test-runner', '.'], + cwd=CONTAINER_DIR, + capture_output=True, + text=True + ) + + # Should build even if podman fails (might not be installed) + # The important thing is the Containerfile syntax is valid + if result.returncode != 0 and 'command not found' not in result.stderr: + pytest.fail(f"Container build failed: {result.stderr}") + + +def test_fragments_structure(): + """Verify fragment directory structure.""" + # Check platform directories + assert (FRAGMENTS_DIR / 'platform' / 'generic-43' / 'repo').exists() + assert (FRAGMENTS_DIR / 'platform' / 'generic-rawhide' / 'repo').exists() + + # Check shared directories + assert (FRAGMENTS_DIR / 'shared' / 'core').exists() + assert (FRAGMENTS_DIR / 'shared' / 'packages').exists() + assert (FRAGMENTS_DIR / 'shared' / 'storage').exists() + assert (FRAGMENTS_DIR / 'shared' / 'desktop').exists() + assert (FRAGMENTS_DIR / 'shared' / 'hypervisor').exists() + + # Count fragments + fragment_count = len(list(FRAGMENTS_DIR.glob('**/*.ks'))) + assert fragment_count >= 50, f"Expected at least 50 fragments, found {fragment_count}" diff --git a/tests/test_recipe_generator.py b/tests/test_recipe_generator.py index c67329f..585f26c 100644 --- a/tests/test_recipe_generator.py +++ b/tests/test_recipe_generator.py @@ -63,10 +63,10 @@ class TestRecipeGenerator: storage='standard', security='secure') assert '# A recipe for a virtual desktop' in content - assert '%include ../ingredients/core.cfg' in content - assert '%include ../ingredients/base-desktop-gnome.cfg' in content - assert '%include ../ingredients/core-fedora-repo-43.cfg' in content - assert '%include ../ingredients/core-security-on.cfg' in content + assert '%ksappend fragments/shared/core/base.ks' in content + assert '%ksappend fragments/shared/desktop/gnome/packages.ks' in content + assert '%ksappend fragments/platform/generic-43/repo/fedora-mirrors.ks' in content + assert '%ksappend fragments/shared/core/security/enabled.ks' in content def test_generate_virtual_desktop_encrypted(self): """Test generating encrypted virtual desktop recipe.""" @@ -74,7 +74,7 @@ class TestRecipeGenerator: desktop='gnome', storage='encrypted', security='secure') - assert '%include ../ingredients/core-storage-encrypted.cfg' in content + assert '%ksappend fragments/shared/storage/encrypted.ks' in content def test_generate_virtual_desktop_labwc(self): """Test generating LabWC virtual desktop recipe.""" @@ -82,8 +82,8 @@ class TestRecipeGenerator: desktop='labwc', storage='standard', security='secure') - assert '%include ../ingredients/base-desktop-labwc.cfg' in content - assert '%include ../ingredients/base-desktop-gnome.cfg' not in content + assert '%ksappend fragments/shared/desktop/labwc/config.ks' in content + assert '%ksappend fragments/shared/desktop/gnome/packages.ks' not in content def test_generate_virtual_desktop_devel(self): """Test generating development mode virtual desktop recipe.""" @@ -91,24 +91,24 @@ class TestRecipeGenerator: desktop='gnome', storage='standard', security='devel') - assert '%include ../ingredients/core-security-off.cfg' in content - assert '%include ../ingredients/core-security-on.cfg' not in content + assert '%ksappend fragments/shared/core/security/disabled.ks' in content + assert '%ksappend fragments/shared/core/security/enabled.ks' not in content def test_generate_virtual_server(self): """Test generating virtual server recipe.""" content = self.generator.generate_recipe('virtual-server', 'rawhide', security='secure') assert '# A recipe for a virtual server' in content - assert '%include ../ingredients/base-desktop-gnome.cfg' not in content - assert '%include ../ingredients/core-initial-setup-server.cfg' in content + assert '%ksappend fragments/shared/desktop/gnome/packages.ks' not in content + assert '%ksappend fragments/shared/initial-setup/server/config.ks' in content def test_generate_desktop_hypervisor_amd(self): """Test generating AMD CPU hypervisor recipe.""" content = self.generator.generate_recipe('desktop-hypervisor', 'rawhide', cpu='amdcpu', security='secure') - assert '%include ../ingredients/base-hypervisor-amdcpu.cfg' in content - assert '%include ../ingredients/base-hypervisor-intelcpu.cfg' not in content + assert '%ksappend fragments/shared/hypervisor/amdcpu.ks' in content + assert '%ksappend fragments/shared/hypervisor/intelcpu.ks' not in content def test_generate_desktop_hypervisor_intel_gpu(self): """Test generating Intel CPU+GPU hypervisor recipe.""" @@ -116,8 +116,8 @@ class TestRecipeGenerator: cpu='intelcpu', gpu='intelgpu', security='secure') - assert '%include ../ingredients/base-hypervisor-intelcpu.cfg' in content - assert '%include ../ingredients/base-hypervisor-intelgpu.cfg' in content + assert '%ksappend fragments/shared/hypervisor/intelcpu.ks' in content + assert '%ksappend fragments/shared/hypervisor/intelgpu.ks' in content def test_generate_live_desktop(self): """Test generating live desktop recipe.""" @@ -125,16 +125,16 @@ class TestRecipeGenerator: desktop='gnome', security='secure') assert '# A recipe for a live desktop' in content - assert '%include ../ingredients/live-core.cfg' in content - assert '%include ../ingredients/live-core-storage.cfg' in content + assert '%ksappend fragments/shared/live/core/base.ks' in content + assert '%ksappend fragments/shared/live/core/storage.ks' in content def test_generate_live_server(self): """Test generating live server recipe.""" content = self.generator.generate_recipe('live-server', 'rawhide', security='secure') assert '# A recipe for a live server' in content - assert '%include ../ingredients/live-core.cfg' in content - assert '%include ../ingredients/core-initial-setup-server.cfg' in content + assert '%ksappend fragments/shared/live/core/base.ks' in content + assert '%ksappend fragments/shared/initial-setup/server/config.ks' in content def test_no_duplicate_includes(self): """Test that duplicate includes are prevented.""" @@ -142,18 +142,18 @@ class TestRecipeGenerator: cpu='intelcpu', gpu='intelgpu', security='secure') - includes = [line for line in content.split('\n') if line.startswith('%include')] + includes = [line for line in content.split('\n') if line.startswith('%ksappend')] paths = [line.split()[1] for line in includes] - assert len(paths) == len(set(paths)), "Found duplicate includes" + assert len(paths) == len(set(paths)), "Found duplicate %ksappend entries" def test_missing_ingredient_detection(self): - """Test that missing ingredients are detected.""" + """Test that missing fragments are detected.""" content = self.generator.generate_recipe('virtual-desktop', '43', desktop='gnome', storage='standard', security='secure') issues = self.generator.validate_recipe(content) - assert issues == [], f"Found missing ingredients: {issues}" + assert issues == [], f"Found missing fragments: {issues}" def test_filename_generation_standard(self): """Test filename generation for standard configuration.""" @@ -329,7 +329,7 @@ keyboard --evgrd def test_extract_version_from_content(self): """Test version extraction from content.""" - content = "%include ../ingredients/core-fedora-repo-43.cfg" + content = "%ksappend fragments/platform/generic-43/repo/fedora-mirrors.ks" version = self.generator.extract_version(content, 'test.cfg') assert version == '43'