dsoxlab 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsoxlab-0.2.2/.github/workflows/appliance.yml +261 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/CHANGELOG.fr.md +164 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/CHANGELOG.md +149 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/PKG-INFO +64 -1
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/README.fr.md +65 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/README.md +63 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/RELEASING.fr.md +24 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/README.fr.md +4 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/README.md +4 -0
- dsoxlab-0.2.2/docs/appliance.fr.md +169 -0
- dsoxlab-0.2.2/docs/appliance.md +155 -0
- dsoxlab-0.2.2/docs/assets/appliance-bureau.png +0 -0
- dsoxlab-0.2.2/docs/assets/appliance-lab-vm.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/machine-output.md +1 -1
- dsoxlab-0.2.2/packer/README.md +105 -0
- dsoxlab-0.2.2/packer/dsoxlab-appliance.pkr.hcl +188 -0
- dsoxlab-0.2.2/packer/faire-ova.sh +148 -0
- dsoxlab-0.2.2/packer/http/preseed.cfg +80 -0
- dsoxlab-0.2.2/packer/scripts/10-base.sh +87 -0
- dsoxlab-0.2.2/packer/scripts/20-outils.sh +33 -0
- dsoxlab-0.2.2/packer/scripts/30-premier-demarrage.sh +278 -0
- dsoxlab-0.2.2/packer/scripts/40-reseau-portable.sh +81 -0
- dsoxlab-0.2.2/packer/scripts/90-nettoyage.sh +81 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/pyproject.toml +1 -1
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/demarrage.py +43 -7
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/i18n/strings/en.py +5 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/i18n/strings/fr.py +6 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/doctor.py +8 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doctor_prerequis_vm.py +19 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_start_sequence.py +30 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/uv.lock +1 -1
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.bestpractices.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/CODEOWNERS +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/FUNDING.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/dependabot.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/workflows/ci.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/workflows/plumber.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/workflows/release.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.github/workflows/scorecard.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.gitignore +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.plumber.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.poutine.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/.pre-commit-config.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/CODE_OF_CONDUCT.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/CODE_OF_CONDUCT.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/CONTRIBUTING.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/CONTRIBUTING.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/LICENSE +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/NOTICE +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/RELEASING.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/SECURITY.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/SECURITY.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/dsoxlab-icon-dark.svg +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/dsoxlab-icon-mono.svg +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/dsoxlab-icon.svg +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/dsoxlab-lockup-dark.svg +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/dsoxlab-lockup-light.svg +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/dsoxlab-lockup-mono.svg +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-icon-1024.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-icon-256.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-icon-512.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-icon-dark-1024.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-icon-dark-512.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-lockup-dark-1024.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-lockup-dark-2048.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-lockup-light-1024.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-lockup-light-2048.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-social-preview-dark.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/assets/brand/png/dsoxlab-social-preview.png +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/brand.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/brand.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/catalog-author.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/catalog-author.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/commands.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/commands.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/contract-v1.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/contract-v1.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/demo.gif +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/demo.tape +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/exit-codes.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/exit-codes.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/files.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/files.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/infra-only.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/infra-only.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/learner.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/learner.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/machine-output.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/trainer.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/docs/trainer.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/champ-inconnu.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/complet.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/position-negative.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/position-non-numerique.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/racine-chaine.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/racine-liste.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/racine-nulle.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/section-liste.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/tronque.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/context_json/vide.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/capstone-exam-score.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/schema-version-courante.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/schema-version-future.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/schema-version-mauvais-type.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/shell-fixtures-nested.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/shell-minimal.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/shell-services-init.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/shell-services.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/vm-multi-target.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/lab_yaml/vm-snapshot-requis.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/meta_yml/full-infra.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/meta_yml/schema-version-courante.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/meta_yml/schema-version-future.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/avec-bastion.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/bastion-sans-ip.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/hosts-liste.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/ip-numerique.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/outputs-aplatis.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/outputs-encapsules.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/sans-hosts.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/value-non-objet.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/corpus/terraform_outputs/vide.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/dict/yaml_contract.dict +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/fuzz_context_json.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/fuzz_lab_yaml.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/fuzz_meta_yaml.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/fuzz/fuzz_terraform_outputs.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/schemas/lab.schema.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/schemas/meta.schema.json +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/scripts/check-release.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/scripts/generer-doc.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/scripts/generer-png-marque.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/scripts/parcours-distribution.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/_amorcage.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/_barres.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/_commun.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/_socle.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/_validation.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/auteur.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/catalogues.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/contexte.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/destruction.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/diagnostic.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/etat.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/indices.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/infrastructure.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/instructeur.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/parcours.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/cli/progression.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/config.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/discovery/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/discovery/repo.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/discovery/scanner.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/exit_codes.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/i18n/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/i18n/strings/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/ansible.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/credentials.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/inventory.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/libvirt.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/pont.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/snapshot/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/snapshot/kvm.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/infra/terraform.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/interrupt.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/locking.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/logging_setup.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/_contract.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/course.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/hint.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/lab.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/repo.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/runtime.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/models/schema_version.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/py.typed +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/reporting/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/reporting/console.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/reporting/machine.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/runtimes/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/runtimes/base.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/runtimes/manager.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/runtimes/services.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/runtimes/shell.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/runtimes/vm.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/catalog.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/demo.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/guide_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/host_diagnosis.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/issue_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/lab_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/lab_state.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/progress_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/scaffold.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/support.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/services/update_check.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/sessions/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/sessions/store.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/catalogues.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/cloud-init/README.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/cloud-init/almalinux.yaml.tmpl +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/cloud-init/debian.yaml.tmpl +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/cloud-init/ubuntu.yaml.tmpl +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/README.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/README.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/README.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/README.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/hints.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/tests/conftest.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/tests/test_functional.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/lab.fr.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/lab.yaml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/scenario.fr.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/scenario.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/meta.fr.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/demo/meta.yml +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/README.md +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/incus/main.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/incus/outputs.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/incus/variables.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/incus/versions.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/kvm/main.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/kvm/outputs.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/kvm/variables.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/kvm/versions.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/outscale/main.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/outscale/outputs.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/outscale/variables.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/templates/terraform/outscale/versions.tf +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/utils/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/utils/fichiers.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/utils/shell.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/validators/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/validators/content.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/validators/contract.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/validators/metadata.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/src/dsoxlab/validators/structure.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/__init__.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/conftest.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_ansible_availability.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_catalog.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_check_release.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_ci_valide_les_templates.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_cles_i18n_existantes.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_cli_smoke.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_cloud_init_degrade.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_cloud_init_incus_agent.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_cloud_init_templates.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_codes_de_sortie.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_commande_absente.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_completion.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_conteneurs_engendres.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_contrat_honore.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_convention_de_nommage.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_demo.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_description_alignee.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doc_auteur_couvre_les_controles.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doctor.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doctor_explique.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doctor_fix.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doctor_installation.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_doctor_strict.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_documentation_synchrone.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_etat_libvirt.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_executable_absent.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_fixtures_declarees.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_guide_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_host_ready_timeout.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_i18n_coverage.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_i18n_validators.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_incus_pool.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_install_et_contexte.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_instructor_bootstrap.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_interruption.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_issue_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_journal_en_anglais.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_journalisation.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_json_output.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_json_schemas.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_kvm_disques_apparmor.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_lab_state.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_moteur_injoignable.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_nom_de_pont.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_note_sans_mesure.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_outputs_terraform.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_pager.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_parcours_distribution.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_progress_service.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_provision_ciblee.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_provision_et_json_services.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_repertoire_de_travail_annonce.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_scaffold.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_schema_version.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_schemas_sur_documents.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_services.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_session_spec.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_shell_fixtures.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_snapshot_kvm.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_sondes_des_tests_docker.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_sondes_virsh.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_stack_infra.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_support.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_support_en_anglais.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_update_check.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_validator_voit_tout.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_validators_content.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_verrou_depot.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_versions_supportees.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests/test_yaml_contract.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests_e2e/conftest.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests_e2e/pytest.ini +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests_e2e/test_boite_noire.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests_e2e/test_installation.py +0 -0
- {dsoxlab-0.2.0 → dsoxlab-0.2.2}/tests_e2e/test_parcours.py +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Construit l'appliance dsoxlab et l'attache à une Release.
|
|
2
|
+
#
|
|
3
|
+
# Workflow SÉPARÉ de `release.yml`, pour deux raisons :
|
|
4
|
+
#
|
|
5
|
+
# 1. `workflow_dispatch` permet de l'éprouver sans poser de tag. Greffé à la
|
|
6
|
+
# release, ce job n'aurait tourné qu'au prochain tag mineur, c'est-à-dire
|
|
7
|
+
# jamais avant d'en avoir besoin : un workflow qu'on n'a jamais joué est un
|
|
8
|
+
# workflow qui ne marche pas ;
|
|
9
|
+
# 2. un build d'image de vingt minutes ne doit ni retarder une publication PyPI,
|
|
10
|
+
# ni pouvoir la faire échouer.
|
|
11
|
+
#
|
|
12
|
+
# Sur les tags, il ne se déclenche que sur les **mineurs** (`v0.3.0`, `v0.4.0`) :
|
|
13
|
+
# l'image n'épingle pas la version de dsoxlab, elle installe la dernière au
|
|
14
|
+
# premier démarrage, donc republier un gigaoctet à chaque correctif coûterait
|
|
15
|
+
# cher pour ne rien changer.
|
|
16
|
+
|
|
17
|
+
name: Appliance
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
push:
|
|
21
|
+
tags: ["v[0-9]+.[0-9]+.0"]
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
inputs:
|
|
24
|
+
tag:
|
|
25
|
+
description: "Release existante à laquelle attacher les images (vide = ne rien attacher)"
|
|
26
|
+
required: false
|
|
27
|
+
type: string
|
|
28
|
+
|
|
29
|
+
permissions: {}
|
|
30
|
+
|
|
31
|
+
concurrency:
|
|
32
|
+
group: appliance-${{ github.ref }}
|
|
33
|
+
cancel-in-progress: false
|
|
34
|
+
|
|
35
|
+
jobs:
|
|
36
|
+
appliance:
|
|
37
|
+
name: Build the appliance image
|
|
38
|
+
# AFTER the Release exists, and never blocking it. A 20-minute image build
|
|
39
|
+
# must not delay a package that is already on PyPI, and its failure must not
|
|
40
|
+
# leave a version half-published.
|
|
41
|
+
runs-on: ubuntu-24.04
|
|
42
|
+
timeout-minutes: 120
|
|
43
|
+
permissions:
|
|
44
|
+
contents: write # attach the assets to the existing Release
|
|
45
|
+
id-token: write # provenance attestation
|
|
46
|
+
attestations: write
|
|
47
|
+
env:
|
|
48
|
+
# `/` has ~14 GB free on a runner, `/mnt` has ~70. Building anywhere else
|
|
49
|
+
# dies on disk space, twenty minutes in.
|
|
50
|
+
BUILD_DIR: /mnt/appliance
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- name: Harden the runner
|
|
54
|
+
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
55
|
+
with:
|
|
56
|
+
egress-policy: audit
|
|
57
|
+
|
|
58
|
+
- name: Checkout
|
|
59
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
60
|
+
with:
|
|
61
|
+
persist-credentials: false
|
|
62
|
+
|
|
63
|
+
- name: KVM must really be available
|
|
64
|
+
# The one fact this whole job rests on: GitHub-hosted Linux runners expose
|
|
65
|
+
# /dev/kvm. Checked FIRST — without acceleration the build would exceed the
|
|
66
|
+
# job limit, and the failure would arrive an hour later.
|
|
67
|
+
run: |
|
|
68
|
+
set -euo pipefail
|
|
69
|
+
test -e /dev/kvm || { echo "::error::no /dev/kvm on this runner"; exit 1; }
|
|
70
|
+
sudo apt-get update -qq
|
|
71
|
+
sudo apt-get install -y -qq qemu-system-x86 qemu-utils cpu-checker
|
|
72
|
+
kvm-ok
|
|
73
|
+
df -h / /mnt | tail -2
|
|
74
|
+
|
|
75
|
+
- name: Install Packer
|
|
76
|
+
run: |
|
|
77
|
+
set -euo pipefail
|
|
78
|
+
wget -qO- https://apt.releases.hashicorp.com/gpg \
|
|
79
|
+
| sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
|
80
|
+
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
|
|
81
|
+
https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
|
|
82
|
+
| sudo tee /etc/apt/sources.list.d/hashicorp.list >/dev/null
|
|
83
|
+
sudo apt-get update -qq && sudo apt-get install -y -qq packer
|
|
84
|
+
packer version
|
|
85
|
+
|
|
86
|
+
- name: Resolve the Debian netinst ISO and its checksum
|
|
87
|
+
id: iso
|
|
88
|
+
# The checksum is read from Debian's signed SHA256SUMS rather than pinned
|
|
89
|
+
# here: a hash copied by hand is a hash that rots.
|
|
90
|
+
run: |
|
|
91
|
+
set -euo pipefail
|
|
92
|
+
base="https://cdimage.debian.org/debian-cd/current/amd64/iso-cd"
|
|
93
|
+
name="$(curl -fsSL "$base/SHA256SUMS" | grep -oE 'debian-[0-9.]+-amd64-netinst\.iso' | head -1)"
|
|
94
|
+
sum="$(curl -fsSL "$base/SHA256SUMS" | grep " $name\$" | cut -d' ' -f1)"
|
|
95
|
+
test -n "$name" && test -n "$sum"
|
|
96
|
+
echo "url=$base/$name" >> "$GITHUB_OUTPUT"
|
|
97
|
+
echo "sum=$sum" >> "$GITHUB_OUTPUT"
|
|
98
|
+
echo "ISO : $name"
|
|
99
|
+
|
|
100
|
+
- name: packer init && validate
|
|
101
|
+
working-directory: packer
|
|
102
|
+
env:
|
|
103
|
+
ISO_URL: ${{ steps.iso.outputs.url }}
|
|
104
|
+
ISO_SUM: ${{ steps.iso.outputs.sum }}
|
|
105
|
+
run: |
|
|
106
|
+
set -euo pipefail
|
|
107
|
+
mkdir -p "${BUILD_DIR}/out" "${BUILD_DIR}/tmp"
|
|
108
|
+
packer init .
|
|
109
|
+
packer validate \
|
|
110
|
+
-var "image_version=${GITHUB_REF_NAME#v}" \
|
|
111
|
+
-var "iso_url=${ISO_URL}" \
|
|
112
|
+
-var "iso_checksum=sha256:${ISO_SUM}" \
|
|
113
|
+
-var "output_directory=${BUILD_DIR}/out" .
|
|
114
|
+
|
|
115
|
+
- name: packer build
|
|
116
|
+
working-directory: packer
|
|
117
|
+
env:
|
|
118
|
+
PACKER_TMP_DIR: /mnt/appliance/tmp
|
|
119
|
+
ISO_URL: ${{ steps.iso.outputs.url }}
|
|
120
|
+
ISO_SUM: ${{ steps.iso.outputs.sum }}
|
|
121
|
+
run: |
|
|
122
|
+
set -euo pipefail
|
|
123
|
+
packer build \
|
|
124
|
+
-var "image_version=${GITHUB_REF_NAME#v}" \
|
|
125
|
+
-var "iso_url=${ISO_URL}" \
|
|
126
|
+
-var "iso_checksum=sha256:${ISO_SUM}" \
|
|
127
|
+
-var "output_directory=${BUILD_DIR}/out" .
|
|
128
|
+
ls -lh "${BUILD_DIR}/out"
|
|
129
|
+
|
|
130
|
+
- name: Derive the OVA from the qcow2
|
|
131
|
+
# No VirtualBox, no ovftool (proprietary). An OVA is a tar of three files,
|
|
132
|
+
# in THIS order: the .ovf, the .mf, then the disk — VirtualBox refuses the
|
|
133
|
+
# archive if the .ovf is not its first member.
|
|
134
|
+
#
|
|
135
|
+
# Verified locally before this workflow existed: the resulting OVA imports
|
|
136
|
+
# into VirtualBox 7.0 ("Successfully imported the appliance"), and the disk
|
|
137
|
+
# comes back byte-identical after the conversion.
|
|
138
|
+
run: bash packer/faire-ova.sh "${GITHUB_REF_NAME#v}" "${BUILD_DIR}/out"
|
|
139
|
+
|
|
140
|
+
- name: The OVF must validate against the DMTF schema
|
|
141
|
+
# VirtualBox imports almost anything; VMware checks. Since the same OVA
|
|
142
|
+
# is offered for both, the only way to keep that promise without a
|
|
143
|
+
# Broadcom licence is to hold the file to the specification it claims to
|
|
144
|
+
# follow. `xmllint` resolves the imported CIM schemas itself.
|
|
145
|
+
#
|
|
146
|
+
# Blocking on purpose: an OVF that no longer validates is exactly the
|
|
147
|
+
# kind of drift nothing else here would catch, and it would only show up
|
|
148
|
+
# on a user's machine.
|
|
149
|
+
run: |
|
|
150
|
+
set -euo pipefail
|
|
151
|
+
sudo apt-get install -y --no-install-recommends libxml2-utils
|
|
152
|
+
curl -fsS -o /tmp/dsp8023.xsd \
|
|
153
|
+
https://schemas.dmtf.org/ovf/envelope/1/dsp8023_1.1.0.xsd
|
|
154
|
+
ovf=$(mktemp -d)
|
|
155
|
+
tar -xf "${BUILD_DIR}"/out/*.ova -C "$ovf" --wildcards '*.ovf'
|
|
156
|
+
xmllint --noout --schema /tmp/dsp8023.xsd "$ovf"/*.ovf
|
|
157
|
+
|
|
158
|
+
- name: Every asset must stay within its budget
|
|
159
|
+
env:
|
|
160
|
+
# A BUDGET, not the 2 GB Release limit. Measured reference: 533 MiB for
|
|
161
|
+
# the qcow2 and 511 for the OVA, with 20 GiB of declared disk. A check
|
|
162
|
+
# set at the hard limit would silently wave through a 1.1 GB image the
|
|
163
|
+
# day the discard setting regresses — which is exactly how this recipe
|
|
164
|
+
# was found to be carrying 512 MiB of rubbish.
|
|
165
|
+
BUDGET_MIB: 800
|
|
166
|
+
run: |
|
|
167
|
+
set -euo pipefail
|
|
168
|
+
# `qemu-img info` and `check` give the real shape of the image: virtual
|
|
169
|
+
# size, allocated size, and the share of compressed clusters. Printed
|
|
170
|
+
# rather than guessed, so a regression is visible in the log.
|
|
171
|
+
for f in "${BUILD_DIR}"/out/*.qcow2; do
|
|
172
|
+
[ -f "$f" ] || continue
|
|
173
|
+
qemu-img info "$f"
|
|
174
|
+
qemu-img check "$f" || true
|
|
175
|
+
done
|
|
176
|
+
|
|
177
|
+
depasse=0
|
|
178
|
+
for f in "${BUILD_DIR}"/out/*.ova "${BUILD_DIR}"/out/*.qcow2; do
|
|
179
|
+
[ -f "$f" ] || continue
|
|
180
|
+
mib=$(( $(stat -c%s "$f") / 1024 / 1024 ))
|
|
181
|
+
echo "$(basename "$f") : ${mib} MiB (budget ${BUDGET_MIB})"
|
|
182
|
+
if [ "$mib" -gt "$BUDGET_MIB" ]; then
|
|
183
|
+
echo "::error::$(basename "$f") is ${mib} MiB, over the ${BUDGET_MIB} MiB budget."
|
|
184
|
+
echo "::error::Check the trim first: the log of 90-nettoyage.sh shows what fstrim returned."
|
|
185
|
+
depasse=1
|
|
186
|
+
fi
|
|
187
|
+
done
|
|
188
|
+
test "$depasse" -eq 0
|
|
189
|
+
|
|
190
|
+
( cd "${BUILD_DIR}/out" && sha256sum ./*.ova ./*.qcow2 > SHA256SUMS && cat SHA256SUMS )
|
|
191
|
+
|
|
192
|
+
- name: Attest build provenance
|
|
193
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
194
|
+
with:
|
|
195
|
+
subject-path: |
|
|
196
|
+
/mnt/appliance/out/*.ova
|
|
197
|
+
/mnt/appliance/out/*.qcow2
|
|
198
|
+
|
|
199
|
+
- name: Attach the images to the Release
|
|
200
|
+
# Sur un essai manuel sans tag, il n'y a rien à quoi les attacher : le
|
|
201
|
+
# build seul a prouvé ce qu'on voulait prouver.
|
|
202
|
+
if: github.event_name == 'push' || inputs.tag != ''
|
|
203
|
+
env:
|
|
204
|
+
GH_TOKEN: ${{ github.token }}
|
|
205
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
206
|
+
REPO: ${{ github.repository }}
|
|
207
|
+
run: |
|
|
208
|
+
set -euo pipefail
|
|
209
|
+
gh release upload "$TAG" --repo "$REPO" --clobber \
|
|
210
|
+
"${BUILD_DIR}"/out/*.ova \
|
|
211
|
+
"${BUILD_DIR}"/out/*.qcow2 \
|
|
212
|
+
"${BUILD_DIR}/out/SHA256SUMS"
|
|
213
|
+
|
|
214
|
+
- name: Keep only the last images, not all of them
|
|
215
|
+
# Roughly 900 MB per minor version, forever, for images that are worth
|
|
216
|
+
# nothing once the next one exists: the image pins NO dsoxlab version,
|
|
217
|
+
# it installs the latest at first boot. An old one therefore offers no
|
|
218
|
+
# reproducibility — only the weight.
|
|
219
|
+
#
|
|
220
|
+
# Two are kept, not one: if the newest turns out to be broken, the
|
|
221
|
+
# previous one is the fallback while it gets fixed. Below that there is
|
|
222
|
+
# no safety net; above, we are archiving for nobody.
|
|
223
|
+
#
|
|
224
|
+
# Only the images are removed. The Releases themselves, their changelog
|
|
225
|
+
# and the Python distributions stay: they are small, and pip and uv
|
|
226
|
+
# resolve versions through them.
|
|
227
|
+
if: github.event_name == 'push' || inputs.tag != ''
|
|
228
|
+
env:
|
|
229
|
+
GH_TOKEN: ${{ github.token }}
|
|
230
|
+
REPO: ${{ github.repository }}
|
|
231
|
+
GARDEES: 2
|
|
232
|
+
run: |
|
|
233
|
+
set -euo pipefail
|
|
234
|
+
mapfile -t tags < <(
|
|
235
|
+
gh release list --repo "$REPO" --limit 100 \
|
|
236
|
+
--json tagName,createdAt \
|
|
237
|
+
--jq 'sort_by(.createdAt) | reverse | .[].tagName'
|
|
238
|
+
)
|
|
239
|
+
porteuses=0
|
|
240
|
+
for tag in "${tags[@]}"; do
|
|
241
|
+
images=$(gh release view "$tag" --repo "$REPO" --json assets \
|
|
242
|
+
--jq '.assets[].name
|
|
243
|
+
| select(endswith(".ova") or endswith(".qcow2"))')
|
|
244
|
+
[ -n "$images" ] || continue
|
|
245
|
+
porteuses=$((porteuses + 1))
|
|
246
|
+
if [ "$porteuses" -le "$GARDEES" ]; then
|
|
247
|
+
echo "$tag : images conservées (${porteuses}/${GARDEES})"
|
|
248
|
+
continue
|
|
249
|
+
fi
|
|
250
|
+
echo "$tag : retrait des images"
|
|
251
|
+
while IFS= read -r nom; do
|
|
252
|
+
[ -n "$nom" ] || continue
|
|
253
|
+
gh release delete-asset "$tag" "$nom" --repo "$REPO" --yes
|
|
254
|
+
echo " supprimé : $nom"
|
|
255
|
+
done < <(
|
|
256
|
+
gh release view "$tag" --repo "$REPO" --json assets \
|
|
257
|
+
--jq '.assets[].name
|
|
258
|
+
| select(endswith(".ova") or endswith(".qcow2")
|
|
259
|
+
or . == "SHA256SUMS")'
|
|
260
|
+
)
|
|
261
|
+
done
|
|
@@ -9,6 +9,170 @@ et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
|
|
|
9
9
|
|
|
10
10
|
## [Non publié]
|
|
11
11
|
|
|
12
|
+
## [0.2.2] - 2026-09-26
|
|
13
|
+
|
|
14
|
+
### Ajouté
|
|
15
|
+
|
|
16
|
+
- **L'appliance : une VM prête à jouer, construite par la CI** (issue #91,
|
|
17
|
+
déplacée au jalon 0.3.0 et désormais entamée). Pour Windows et macOS, où
|
|
18
|
+
`uv tool install dsoxlab` n'est pas une option. Sur Linux elle reste la mauvaise
|
|
19
|
+
réponse, et le README le dit : télécharger un demi-gigaoctet pour éviter une
|
|
20
|
+
commande n'a aucun sens.
|
|
21
|
+
|
|
22
|
+
`packer/` porte la recette (builder `qemu`, installation Debian 13 automatisée,
|
|
23
|
+
quatre scripts de provisioning) et `.github/workflows/appliance.yml` la construit
|
|
24
|
+
**sur un runner GitHub-hosted**, sans machine à nous : les runners Linux exposent
|
|
25
|
+
`/dev/kvm`, donc QEMU y est accéléré. Ce choix retire du même coup `ovftool`
|
|
26
|
+
(propriétaire) et VirtualBox de la chaîne : l'OVA est dérivée du qcow2 avec
|
|
27
|
+
`qemu-img` et `tar` seulement.
|
|
28
|
+
|
|
29
|
+
L'image **n'épingle rien**. Le premier démarrage installe le dernier dsoxlab, et
|
|
30
|
+
les hyperviseurs seulement si l'hôte expose la virtualisation imbriquée, vérifiée
|
|
31
|
+
en direct et jamais supposée. D'où sa reconstruction sur les **tags mineurs
|
|
32
|
+
seulement** : republier un demi-gigaoctet à chaque correctif coûterait cher pour
|
|
33
|
+
ne rien changer.
|
|
34
|
+
|
|
35
|
+
Mesuré, non estimé : **461 Mio** pour le qcow2 et **446 Mio** pour l'OVA,
|
|
36
|
+
construits en 4 min 50 s, contre un budget que le workflow impose à 800 Mio. Une
|
|
37
|
+
appliance comparable construite avec `virtualbox-iso` pèse 979 Mio.
|
|
38
|
+
|
|
39
|
+
- **Le README présente désormais les deux façons d'entrer**, côte à côte :
|
|
40
|
+
installer l'outil, ou télécharger l'appliance. Il dit aussi laquelle est la
|
|
41
|
+
bonne sous Linux, plutôt que de les vendre à égalité.
|
|
42
|
+
|
|
43
|
+
- **Seuls les deux derniers jeux d'images sont conservés sur les Releases.**
|
|
44
|
+
Environ 900 Mo par version mineure, pour toujours, pour des images qui
|
|
45
|
+
n'épinglent aucune version de dsoxlab et installent la dernière au premier
|
|
46
|
+
démarrage : une ancienne n'offre aucune reproductibilité, seulement son
|
|
47
|
+
poids. Deux plutôt qu'une, pour qu'une image cassée ait un recours. Les
|
|
48
|
+
Releases, leur changelog et les distributions Python ne sont pas touchés.
|
|
49
|
+
|
|
50
|
+
- **L'OVA est proposée pour VMware autant que pour VirtualBox**, et le workflow
|
|
51
|
+
tient maintenant cette promesse : l'OVF est validé contre le schéma DMTF à
|
|
52
|
+
chaque build. VirtualBox importe à peu près n'importe quoi, VMware contrôle —
|
|
53
|
+
et sans licence Broadcom, la conformité à la spécification est la seule façon
|
|
54
|
+
honnête de tenir l'affirmation. La page de l'appliance dit franchement ce qui
|
|
55
|
+
a été éprouvé et ce qui ne l'a pas été, et qu'**Apple Silicon est hors de
|
|
56
|
+
portée** : les deux images sont x86-64, les runners arm64 de GitHub n'exposent
|
|
57
|
+
aucun `/dev/kvm` pour en construire une autre, et la virtualisation imbriquée
|
|
58
|
+
sur ces Mac n'existe qu'à partir du M3.
|
|
59
|
+
|
|
60
|
+
### Corrigé
|
|
61
|
+
|
|
62
|
+
- **Trois défauts de cette recette, chacun attrapé par une mesure et non par une
|
|
63
|
+
relecture.** Ils valent d'être consignés parce qu'ils sont du genre qui se livre
|
|
64
|
+
en silence :
|
|
65
|
+
|
|
66
|
+
`fstrim` **rend un succès sans rien libérer** quand le disque de build est
|
|
67
|
+
attaché en `discard=ignore`, qui est le défaut de Packer. L'invité annonçait
|
|
68
|
+
« 520.4 MiB trimmed » pendant que le qcow2 gardait tout : 1110 Mio d'artefact
|
|
69
|
+
contre 597 pour le témoin. Le disque est donc attaché en `unmap`, avec
|
|
70
|
+
`detect_zeroes=unmap` pour que la mise à zéro ne coûte rien, et le repli
|
|
71
|
+
`|| dd` qui ne se déclenchait jamais a disparu, avec les 17 Gio qu'il écrivait
|
|
72
|
+
quand il se déclenchait.
|
|
73
|
+
|
|
74
|
+
`vm_name` **n'avait pas d'extension**, et Packer n'en ajoute aucune : les globs
|
|
75
|
+
`*.qcow2` du contrôle de budget, des empreintes et de l'envoi n'auraient jamais
|
|
76
|
+
vu le fichier. Le premier build local, qui a produit `dsoxlab-appliance-dev`,
|
|
77
|
+
l'a prouvé.
|
|
78
|
+
|
|
79
|
+
Le contrôle de taille testait la **limite dure de 2 Go**, donc il aurait laissé
|
|
80
|
+
passer une image de 1,1 Gio le jour où le trim se recasse. C'est désormais un
|
|
81
|
+
**budget de 800 Mio**, avec `qemu-img info` et `check` affichés, et une erreur
|
|
82
|
+
qui dit où regarder.
|
|
83
|
+
|
|
84
|
+
- **`lsb_release` n'existe plus** dès que la tâche `standard` quitte le preseed, or
|
|
85
|
+
`20-outils.sh` s'en servait pour le dépôt HashiCorp. Lu dans `/etc/os-release` à
|
|
86
|
+
la place : sans cela le build aurait échoué net.
|
|
87
|
+
|
|
88
|
+
- **Dix défauts de plus, trouvés en jouant l'appliance plutôt qu'en relisant la
|
|
89
|
+
recette.** Chacun échappait à tous les contrôles précédents, et le premier à lui
|
|
90
|
+
seul rendait l'image inutilisable pour ceux à qui elle est destinée :
|
|
91
|
+
|
|
92
|
+
**Le réseau ne survivait pas au changement d'hyperviseur.** L'installateur
|
|
93
|
+
Debian fige le nom de l'interface qu'il a vue — `enp0s2` sous le QEMU qui
|
|
94
|
+
construit l'image — et ce nom dérive de la position PCI de la carte. Sous
|
|
95
|
+
VirtualBox, la carte s'appelle `enp0s17` : la configuration ne s'appliquait donc
|
|
96
|
+
à rien. L'appliance importée démarrait avec `enp0s17 DOWN`, un `resolv.conf`
|
|
97
|
+
sans le moindre `nameserver`, et un premier démarrage qui échouait sur
|
|
98
|
+
« Temporary failure in name resolution ». Rien n'était installé : ni dsoxlab, ni
|
|
99
|
+
les hyperviseurs, ni le bureau. Le réseau passe désormais par
|
|
100
|
+
`systemd-networkd`, dont le `[Match]` **décrit** la carte (`en*`, `eth*`) au
|
|
101
|
+
lieu de la nommer.
|
|
102
|
+
|
|
103
|
+
**Le premier démarrage se marquait fait même quand tout avait échoué**, et la
|
|
104
|
+
machine n'avait plus aucun moyen de se rattraper. Il dit maintenant ce qui a
|
|
105
|
+
échoué, ne pose pas de marqueur, et recommence au démarrage suivant. Il attend
|
|
106
|
+
aussi qu'un nom se résolve vraiment avant de commencer :
|
|
107
|
+
`network-online.target` était atteinte sans qu'aucun bail DHCP soit pris, d'où
|
|
108
|
+
les huit secondes du défaut ci-dessus.
|
|
109
|
+
|
|
110
|
+
**Root héritait du `HOME` de student** pendant la construction (`sudo -E`),
|
|
111
|
+
si bien que `terraform version` et `ansible-playbook --version` créaient un
|
|
112
|
+
`~/.ansible` et un `~/.terraform.d` appartenant à root. Ansible refusait alors
|
|
113
|
+
de démarrer pour l'apprenant, et dsoxlab rendait un `rc=5, Stats: {}` que rien
|
|
114
|
+
ne reliait à la cause. Corrigé par `sudo -H`, avec une ceinture `chown`.
|
|
115
|
+
|
|
116
|
+
**`ovmf` et `qemu-utils` manquaient** : sur Debian, tous deux ne sont que des
|
|
117
|
+
*recommandations* de `qemu-kvm`, donc `--no-install-recommends` les écartait.
|
|
118
|
+
Sans le premier, libvirt n'expose aucun firmware EFI et tout `provision`
|
|
119
|
+
s'arrête net ; sans le second, il ne sait pas créer de volume qcow2. Trouvés en
|
|
120
|
+
provisionnant pour de vrai depuis l'appliance — et le premier a été nommé en
|
|
121
|
+
2,3 secondes par le garde-fou de l'issue #234.
|
|
122
|
+
|
|
123
|
+
**Aucun pool libvirt `default`, aucune appartenance de groupe** : une Debian
|
|
124
|
+
fraîche n'en définit ni l'un ni l'autre, donc `doctor --strict` sortait en 9 et
|
|
125
|
+
`student` ne pouvait pas ouvrir `/dev/kvm`. Les deux sont posés au premier
|
|
126
|
+
démarrage, ce qui est aussi la raison de son redémarrage final.
|
|
127
|
+
|
|
128
|
+
**Le bureau n'avait pas de serveur X.** Même motif qu'`ovmf` et `qemu-utils`,
|
|
129
|
+
pour la troisième fois : `xserver-xorg` est une *recommandation* de `xfce4` et
|
|
130
|
+
de `lightdm`, jamais une dépendance, donc `--no-install-recommends` l'écartait.
|
|
131
|
+
`/usr/bin/Xorg` n'existait pas, `lightdm` était « failed », et la machine
|
|
132
|
+
arrivait sur une console malgré `graphical.target` — sans que rien ne relie ce
|
|
133
|
+
symptôme à un paquet manquant. Le serveur et ses pilotes vidéo (`vmware` pour
|
|
134
|
+
le contrôleur VMSVGA que présentent VirtualBox et VMware, `vesa` et `fbdev` en
|
|
135
|
+
secours) sont désormais nommés explicitement.
|
|
136
|
+
|
|
137
|
+
**`~/.ssh/config` n'incluait pas ce que dsoxlab y écrit.** `provision` dépose un
|
|
138
|
+
`~/.ssh/config.d/<catalogue>.conf` et avertit, à chaque exécution, que rien ne
|
|
139
|
+
le lit. L'`Include` est maintenant posé en tête du fichier, là où OpenSSH
|
|
140
|
+
l'exige.
|
|
141
|
+
|
|
142
|
+
**`start` déclarait « déjà provisionnée » une infrastructure inutilisable.**
|
|
143
|
+
Il tranchait sur le seul state, donc une adresse suffisait. L'enchaînement qui
|
|
144
|
+
suivait était absurde : `provision` sortait en 8 sur « les hôtes n'ont pas
|
|
145
|
+
répondu : l'infrastructure existe, mais elle n'est pas utilisable en l'état »,
|
|
146
|
+
et le `start` relancé juste après — donc relancé *parce que* rien ne répondait
|
|
147
|
+
— annonçait « déjà provisionné, rien à reconstruire », avant d'échouer plus
|
|
148
|
+
loin sur un `UNREACHABLE` brut d'Ansible que rien ne reliait à la cause. Il
|
|
149
|
+
sonde désormais aussi le port 22 : une adresse n'est pas une machine. Rejouer
|
|
150
|
+
`provision` ne coûte rien, puisqu'il reprend sans rien recréer, et il attend.
|
|
151
|
+
|
|
152
|
+
**La console était noyée sous des erreurs `AF_VSOCK`**, une par rechargement de
|
|
153
|
+
systemd, soit une dizaine de lignes rouges en cinq secondes pendant
|
|
154
|
+
l'installation des paquets. Rien n'était cassé, mais rien ne le disait non
|
|
155
|
+
plus. `systemd-ssh-generator` est maintenant neutralisé par un lien vers
|
|
156
|
+
`/dev/null`, la façon documentée de le faire.
|
|
157
|
+
|
|
158
|
+
## [0.2.1] - 2026-09-25
|
|
159
|
+
|
|
160
|
+
### Ajouté
|
|
161
|
+
|
|
162
|
+
- **Une section `Remerciements` dans les deux README.** Trois personnes
|
|
163
|
+
extérieures au projet ont remonté des défauts avec le diagnostic, et souvent le
|
|
164
|
+
correctif. La section nomme **ce que chaque retour a changé** plutôt que de
|
|
165
|
+
lister des pseudonymes : quatre versions viennent de
|
|
166
|
+
[@cedric-ribier](https://github.com/cedric-ribier) (tailles de disque nominales,
|
|
167
|
+
libvirt 8, l'agent Incus sur AlmaLinux avec l'attente réglable, et la
|
|
168
|
+
virtualisation imbriquée nommée), le `conftest.py` qui bloquait en silence tous
|
|
169
|
+
les labs `shell` vient de [@Gedd18](https://github.com/Gedd18), et deux labs dont
|
|
170
|
+
l'état de départ était inutilisable de
|
|
171
|
+
[@VictorVare](https://github.com/VictorVare).
|
|
172
|
+
|
|
173
|
+
Un crédit qui dit ce qu'il a corrigé vaut mieux qu'une liste, et il apprend au
|
|
174
|
+
lecteur suivant quel genre de rapport est utile.
|
|
175
|
+
|
|
12
176
|
## [0.2.0] - 2026-09-25
|
|
13
177
|
|
|
14
178
|
**Passage au produit.** Cette version n'ajoute aucun code : elle marque le
|
|
@@ -9,6 +9,155 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.2.2] - 2026-09-26
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **The appliance: a ready-to-play VM, built by the CI** (issue #91, moved to the
|
|
17
|
+
0.3.0 milestone and now started). For Windows and macOS, where `uv tool install
|
|
18
|
+
dsoxlab` is not an option. On Linux it stays the wrong answer, and the README
|
|
19
|
+
says so: downloading half a gigabyte to avoid one command makes no sense.
|
|
20
|
+
|
|
21
|
+
`packer/` holds the recipe — `qemu` builder, automated Debian 13 install, four
|
|
22
|
+
provisioning scripts — and `.github/workflows/appliance.yml` builds it **on a
|
|
23
|
+
GitHub-hosted runner**, with no self-hosted machine: Linux runners expose
|
|
24
|
+
`/dev/kvm`, so QEMU is accelerated. That choice also removes `ovftool`
|
|
25
|
+
(proprietary) and VirtualBox from the chain: the OVA is derived from the qcow2
|
|
26
|
+
with `qemu-img` and `tar` alone.
|
|
27
|
+
|
|
28
|
+
The image **pins nothing**. The first boot installs the latest dsoxlab, and the
|
|
29
|
+
hypervisors only if the host exposes nested virtualization — checked live, never
|
|
30
|
+
assumed. Which is why it is rebuilt on **minor tags only**: republishing half a
|
|
31
|
+
gigabyte for every patch would cost a lot and change nothing.
|
|
32
|
+
|
|
33
|
+
Measured, not estimated: **461 MiB** for the qcow2 and **446 MiB** for the OVA,
|
|
34
|
+
built in 4 min 50 s, against a budget the workflow enforces at 800 MiB. A
|
|
35
|
+
comparable appliance built with `virtualbox-iso` weighs 979 MiB.
|
|
36
|
+
|
|
37
|
+
- **The README now presents both ways in**, side by side: install the tool, or
|
|
38
|
+
download the appliance. It also says which one is right on Linux, rather than
|
|
39
|
+
selling them as equals.
|
|
40
|
+
|
|
41
|
+
- **Only the last two sets of images are kept on the Releases.** Roughly 900 MB
|
|
42
|
+
per minor version, forever, for images that pin no dsoxlab version and
|
|
43
|
+
install the latest at first boot: an old one offers no reproducibility, only
|
|
44
|
+
weight. Two rather than one, so a broken image has a fallback. The Releases,
|
|
45
|
+
their changelog and the Python distributions are untouched.
|
|
46
|
+
|
|
47
|
+
- **The OVA is offered for VMware as well as VirtualBox**, and the workflow now
|
|
48
|
+
holds it to that promise: the OVF is validated against the DMTF schema at
|
|
49
|
+
every build. VirtualBox imports almost anything, VMware checks — and without
|
|
50
|
+
a Broadcom licence, conformity to the specification is the only honest way to
|
|
51
|
+
keep the claim. The appliance page says plainly what was tested and what was
|
|
52
|
+
not, and that **Apple Silicon is out of reach**: both images are x86-64,
|
|
53
|
+
GitHub's arm64 runners expose no `/dev/kvm` to build another, and nested
|
|
54
|
+
virtualization on those Macs only exists from the M3.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **Three defects in that recipe, each caught by a measurement rather than a
|
|
59
|
+
reading.** They are worth recording because they are the kind that ship
|
|
60
|
+
silently:
|
|
61
|
+
|
|
62
|
+
`fstrim` **returns success without freeing anything** when the build disk is
|
|
63
|
+
attached with `discard=ignore`, which is Packer's default. The guest announced
|
|
64
|
+
"520.4 MiB trimmed" while the qcow2 kept every byte: 1110 MiB of artifact
|
|
65
|
+
against 597 for the control. So the disk is now attached `unmap`, with
|
|
66
|
+
`detect_zeroes=unmap` so that zeroing costs nothing — and the never-triggered
|
|
67
|
+
`|| dd` fallback is gone, along with the 17 GiB it wrote when it did fire.
|
|
68
|
+
|
|
69
|
+
`vm_name` **had no extension**, and Packer adds none: the `*.qcow2` globs of the
|
|
70
|
+
budget check, the checksums and the upload would never have seen the file. The
|
|
71
|
+
first local build produced `dsoxlab-appliance-dev`, which proved it.
|
|
72
|
+
|
|
73
|
+
The size check tested the **2 GB hard limit**, so it would have waved through a
|
|
74
|
+
1.1 GB image the day the trim regressed. It is now a **budget of 800 MiB**, with
|
|
75
|
+
`qemu-img info` and `check` printed, and an error that says where to look.
|
|
76
|
+
|
|
77
|
+
- **`lsb_release` no longer exists** once the `standard` task is dropped from the
|
|
78
|
+
preseed, and `20-outils.sh` used it for the HashiCorp repository. Read from
|
|
79
|
+
`/etc/os-release` instead — without this the build would have failed outright.
|
|
80
|
+
|
|
81
|
+
- **Ten more defects, found by playing the appliance rather than reading the
|
|
82
|
+
recipe.** Each was invisible to every check that came before, and the first one
|
|
83
|
+
alone made the image useless to the very people it is built for:
|
|
84
|
+
|
|
85
|
+
**The network did not survive a change of hypervisor.** Debian's installer
|
|
86
|
+
freezes the interface name it saw at install time — `enp0s2` under the QEMU that
|
|
87
|
+
builds the image — and that name is derived from the card's PCI position. Under
|
|
88
|
+
VirtualBox the card is `enp0s17`, so the configuration applied to nothing: the
|
|
89
|
+
imported appliance came up with `enp0s17 DOWN`, a `resolv.conf` without a single
|
|
90
|
+
`nameserver`, and a first boot that failed on "Temporary failure in name
|
|
91
|
+
resolution". Nothing was installed — no dsoxlab, no hypervisors, no desktop. The
|
|
92
|
+
image now configures the network with `systemd-networkd`, whose `[Match]`
|
|
93
|
+
**describes** the card (`en*`, `eth*`) instead of naming it.
|
|
94
|
+
|
|
95
|
+
**The first boot marked itself done even when everything had failed**, so the
|
|
96
|
+
machine had no way left to catch up. It now reports what failed, keeps no
|
|
97
|
+
marker, and starts over at the next boot. It also waits for a name to actually
|
|
98
|
+
resolve before it begins: `network-online.target` was reached with no DHCP lease
|
|
99
|
+
taken, which is how the failure above got its eight-second timeout.
|
|
100
|
+
|
|
101
|
+
**Root inherited the student's `HOME`** during the build (`sudo -E`), so
|
|
102
|
+
`terraform version` and `ansible-playbook --version` created a root-owned
|
|
103
|
+
`~/.ansible` and `~/.terraform.d`. Ansible then refused to start for the learner
|
|
104
|
+
and dsoxlab reported `rc=5, Stats: {}` — a symptom nothing connected to the
|
|
105
|
+
cause. Fixed with `sudo -H`, plus a `chown` belt.
|
|
106
|
+
|
|
107
|
+
**`ovmf` and `qemu-utils` were missing**: both are mere *recommendations* of
|
|
108
|
+
`qemu-kvm` on Debian, so `--no-install-recommends` dropped them. Without the
|
|
109
|
+
first, libvirt exposes no EFI firmware and every `provision` stops dead; without
|
|
110
|
+
the second, it cannot create a qcow2 volume. Found by provisioning for real from
|
|
111
|
+
inside the appliance — and the first was named in 2.3 seconds by the guard added
|
|
112
|
+
for issue #234.
|
|
113
|
+
|
|
114
|
+
**No libvirt `default` pool and no group membership**: a fresh Debian defines
|
|
115
|
+
neither, so `doctor --strict` exited 9 and `student` could not open `/dev/kvm`.
|
|
116
|
+
Both are now set up at first boot, which is also why it reboots at the end.
|
|
117
|
+
|
|
118
|
+
**The desktop had no X server.** Same pattern as `ovmf` and `qemu-utils`, for
|
|
119
|
+
the third time: `xserver-xorg` is a *recommendation* of `xfce4` and `lightdm`,
|
|
120
|
+
never a dependency, so `--no-install-recommends` dropped it. `/usr/bin/Xorg`
|
|
121
|
+
did not exist, `lightdm` was `failed`, and the machine came up on a console
|
|
122
|
+
despite `graphical.target` — with nothing tying that to a missing package. The
|
|
123
|
+
server and its video drivers (`vmware` for the VMSVGA controller VirtualBox and
|
|
124
|
+
VMware present, `vesa` and `fbdev` as fallbacks) are now named explicitly.
|
|
125
|
+
|
|
126
|
+
**`~/.ssh/config` did not include what dsoxlab writes.** `provision` drops a
|
|
127
|
+
`~/.ssh/config.d/<catalog>.conf` and warns, at every run, that nothing reads it.
|
|
128
|
+
The `Include` is now placed at the top of the file, where OpenSSH requires it.
|
|
129
|
+
|
|
130
|
+
**`start` called an unusable infrastructure "already provisioned".** It
|
|
131
|
+
decided from the state alone, so an address was enough. The sequence that
|
|
132
|
+
followed was absurd: `provision` exited 8 on "some hosts did not answer: the
|
|
133
|
+
infrastructure exists, but it is not usable as it stands", and the `start`
|
|
134
|
+
relaunched right after — relaunched *because* nothing answered — announced
|
|
135
|
+
"already provisioned, nothing to build again", then failed further down on a
|
|
136
|
+
raw Ansible `UNREACHABLE` that nothing tied to the cause. It now probes port
|
|
137
|
+
22 as well: an address is not a machine. Replaying `provision` costs nothing,
|
|
138
|
+
since it resumes without recreating anything, and it waits.
|
|
139
|
+
|
|
140
|
+
**The console was flooded with `AF_VSOCK` errors**, one per systemd reload, so
|
|
141
|
+
a dozen red lines in five seconds while packages installed. Nothing was broken,
|
|
142
|
+
but nothing said so either. `systemd-ssh-generator` is now masked, the
|
|
143
|
+
documented way, with a symlink to `/dev/null`.
|
|
144
|
+
|
|
145
|
+
## [0.2.1] - 2026-09-25
|
|
146
|
+
|
|
147
|
+
### Added
|
|
148
|
+
|
|
149
|
+
- **A `Thanks` section in both READMEs.** Three people outside the project have
|
|
150
|
+
reported defects with the diagnosis, and often the fix. The section names **what
|
|
151
|
+
each report changed** rather than listing handles: four releases come from
|
|
152
|
+
[@cedric-ribier](https://github.com/cedric-ribier) (nominal disk sizes, libvirt 8,
|
|
153
|
+
the Incus agent on AlmaLinux with a configurable host wait, and nested
|
|
154
|
+
virtualization named), the `conftest.py` that silently blocked every `shell` lab
|
|
155
|
+
from [@Gedd18](https://github.com/Gedd18), and two labs whose starting state was
|
|
156
|
+
unusable from [@VictorVare](https://github.com/VictorVare).
|
|
157
|
+
|
|
158
|
+
A credit that says what it fixed is worth more than a list, and it tells the next
|
|
159
|
+
reader what kind of report is useful.
|
|
160
|
+
|
|
12
161
|
## [0.2.0] - 2026-09-25
|
|
13
162
|
|
|
14
163
|
**Productization.** This version adds no code: it marks a milestone, and its
|