dsoxlab 0.2.2__tar.gz → 0.2.4__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.
Files changed (326) hide show
  1. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/workflows/appliance.yml +48 -6
  2. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/CHANGELOG.fr.md +89 -4
  3. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/CHANGELOG.md +84 -3
  4. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/PKG-INFO +44 -2
  5. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/README.fr.md +45 -2
  6. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/README.md +43 -1
  7. dsoxlab-0.2.4/docs/appliance.fr.md +288 -0
  8. dsoxlab-0.2.4/docs/appliance.md +277 -0
  9. dsoxlab-0.2.4/docs/assets/appliance-connexion.png +0 -0
  10. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/catalog-author.fr.md +63 -1
  11. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/catalog-author.md +62 -1
  12. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/commands.fr.md +11 -2
  13. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/commands.md +10 -2
  14. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/contract-v1.fr.md +2 -1
  15. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/contract-v1.md +2 -1
  16. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/files.fr.md +5 -3
  17. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/files.md +4 -3
  18. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/learner.fr.md +53 -8
  19. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/learner.md +48 -7
  20. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/trainer.fr.md +16 -3
  21. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/trainer.md +16 -3
  22. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/README.md +6 -2
  23. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/pyproject.toml +1 -1
  24. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/uv.lock +1 -1
  25. dsoxlab-0.2.2/docs/appliance.fr.md +0 -169
  26. dsoxlab-0.2.2/docs/appliance.md +0 -155
  27. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.bestpractices.json +0 -0
  28. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/CODEOWNERS +0 -0
  29. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/FUNDING.yml +0 -0
  30. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  31. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  32. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  33. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  34. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/dependabot.yml +0 -0
  35. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/workflows/ci.yml +0 -0
  36. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/workflows/plumber.yml +0 -0
  37. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/workflows/release.yml +0 -0
  38. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.github/workflows/scorecard.yml +0 -0
  39. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.gitignore +0 -0
  40. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.plumber.yaml +0 -0
  41. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.poutine.yml +0 -0
  42. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/.pre-commit-config.yaml +0 -0
  43. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/CODE_OF_CONDUCT.fr.md +0 -0
  44. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/CODE_OF_CONDUCT.md +0 -0
  45. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/CONTRIBUTING.fr.md +0 -0
  46. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/CONTRIBUTING.md +0 -0
  47. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/LICENSE +0 -0
  48. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/NOTICE +0 -0
  49. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/RELEASING.fr.md +0 -0
  50. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/RELEASING.md +0 -0
  51. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/SECURITY.fr.md +0 -0
  52. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/SECURITY.md +0 -0
  53. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/README.fr.md +0 -0
  54. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/README.md +0 -0
  55. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/appliance-bureau.png +0 -0
  56. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/appliance-lab-vm.png +0 -0
  57. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/dsoxlab-icon-dark.svg +0 -0
  58. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/dsoxlab-icon-mono.svg +0 -0
  59. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/dsoxlab-icon.svg +0 -0
  60. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/dsoxlab-lockup-dark.svg +0 -0
  61. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/dsoxlab-lockup-light.svg +0 -0
  62. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/dsoxlab-lockup-mono.svg +0 -0
  63. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-icon-1024.png +0 -0
  64. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-icon-256.png +0 -0
  65. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-icon-512.png +0 -0
  66. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-icon-dark-1024.png +0 -0
  67. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-icon-dark-512.png +0 -0
  68. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-lockup-dark-1024.png +0 -0
  69. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-lockup-dark-2048.png +0 -0
  70. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-lockup-light-1024.png +0 -0
  71. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-lockup-light-2048.png +0 -0
  72. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-social-preview-dark.png +0 -0
  73. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/assets/brand/png/dsoxlab-social-preview.png +0 -0
  74. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/brand.fr.md +0 -0
  75. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/brand.md +0 -0
  76. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/demo.gif +0 -0
  77. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/demo.tape +0 -0
  78. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/exit-codes.fr.md +0 -0
  79. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/exit-codes.md +0 -0
  80. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/infra-only.fr.md +0 -0
  81. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/infra-only.md +0 -0
  82. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/machine-output.fr.md +0 -0
  83. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/docs/machine-output.md +0 -0
  84. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/champ-inconnu.json +0 -0
  85. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/complet.json +0 -0
  86. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/position-negative.json +0 -0
  87. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/position-non-numerique.json +0 -0
  88. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/racine-chaine.json +0 -0
  89. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/racine-liste.json +0 -0
  90. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/racine-nulle.json +0 -0
  91. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/section-liste.json +0 -0
  92. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/tronque.json +0 -0
  93. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/context_json/vide.json +0 -0
  94. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/capstone-exam-score.yaml +0 -0
  95. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/schema-version-courante.yaml +0 -0
  96. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/schema-version-future.yaml +0 -0
  97. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/schema-version-mauvais-type.yaml +0 -0
  98. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/shell-fixtures-nested.yaml +0 -0
  99. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/shell-minimal.yaml +0 -0
  100. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/shell-services-init.yaml +0 -0
  101. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/shell-services.yaml +0 -0
  102. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/vm-multi-target.yaml +0 -0
  103. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/lab_yaml/vm-snapshot-requis.yaml +0 -0
  104. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/meta_yml/full-infra.yml +0 -0
  105. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/meta_yml/schema-version-courante.yml +0 -0
  106. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/meta_yml/schema-version-future.yml +0 -0
  107. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/avec-bastion.json +0 -0
  108. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/bastion-sans-ip.json +0 -0
  109. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/hosts-liste.json +0 -0
  110. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/ip-numerique.json +0 -0
  111. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/outputs-aplatis.json +0 -0
  112. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/outputs-encapsules.json +0 -0
  113. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/sans-hosts.json +0 -0
  114. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/value-non-objet.json +0 -0
  115. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/corpus/terraform_outputs/vide.json +0 -0
  116. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/dict/yaml_contract.dict +0 -0
  117. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/fuzz_context_json.py +0 -0
  118. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/fuzz_lab_yaml.py +0 -0
  119. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/fuzz_meta_yaml.py +0 -0
  120. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/fuzz/fuzz_terraform_outputs.py +0 -0
  121. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/dsoxlab-appliance.pkr.hcl +0 -0
  122. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/faire-ova.sh +0 -0
  123. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/http/preseed.cfg +0 -0
  124. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/scripts/10-base.sh +0 -0
  125. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/scripts/20-outils.sh +0 -0
  126. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/scripts/30-premier-demarrage.sh +0 -0
  127. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/scripts/40-reseau-portable.sh +0 -0
  128. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/packer/scripts/90-nettoyage.sh +0 -0
  129. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/schemas/lab.schema.json +0 -0
  130. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/schemas/meta.schema.json +0 -0
  131. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/scripts/check-release.py +0 -0
  132. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/scripts/generer-doc.py +0 -0
  133. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/scripts/generer-png-marque.py +0 -0
  134. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/scripts/parcours-distribution.py +0 -0
  135. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/__init__.py +0 -0
  136. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/__init__.py +0 -0
  137. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/_amorcage.py +0 -0
  138. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/_barres.py +0 -0
  139. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/_commun.py +0 -0
  140. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/_socle.py +0 -0
  141. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/_validation.py +0 -0
  142. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/auteur.py +0 -0
  143. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/catalogues.py +0 -0
  144. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/contexte.py +0 -0
  145. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/demarrage.py +0 -0
  146. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/destruction.py +0 -0
  147. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/diagnostic.py +0 -0
  148. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/etat.py +0 -0
  149. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/indices.py +0 -0
  150. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/infrastructure.py +0 -0
  151. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/instructeur.py +0 -0
  152. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/parcours.py +0 -0
  153. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/cli/progression.py +0 -0
  154. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/config.py +0 -0
  155. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/discovery/__init__.py +0 -0
  156. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/discovery/repo.py +0 -0
  157. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/discovery/scanner.py +0 -0
  158. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/exit_codes.py +0 -0
  159. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/i18n/__init__.py +0 -0
  160. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/i18n/strings/__init__.py +0 -0
  161. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/i18n/strings/en.py +0 -0
  162. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/i18n/strings/fr.py +0 -0
  163. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/__init__.py +0 -0
  164. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/ansible.py +0 -0
  165. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/credentials.py +0 -0
  166. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/inventory.py +0 -0
  167. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/libvirt.py +0 -0
  168. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/pont.py +0 -0
  169. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/snapshot/__init__.py +0 -0
  170. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/snapshot/kvm.py +0 -0
  171. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/infra/terraform.py +0 -0
  172. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/interrupt.py +0 -0
  173. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/locking.py +0 -0
  174. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/logging_setup.py +0 -0
  175. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/__init__.py +0 -0
  176. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/_contract.py +0 -0
  177. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/course.py +0 -0
  178. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/hint.py +0 -0
  179. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/lab.py +0 -0
  180. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/repo.py +0 -0
  181. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/runtime.py +0 -0
  182. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/models/schema_version.py +0 -0
  183. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/py.typed +0 -0
  184. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/reporting/__init__.py +0 -0
  185. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/reporting/console.py +0 -0
  186. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/reporting/machine.py +0 -0
  187. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/runtimes/__init__.py +0 -0
  188. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/runtimes/base.py +0 -0
  189. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/runtimes/manager.py +0 -0
  190. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/runtimes/services.py +0 -0
  191. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/runtimes/shell.py +0 -0
  192. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/runtimes/vm.py +0 -0
  193. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/__init__.py +0 -0
  194. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/catalog.py +0 -0
  195. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/demo.py +0 -0
  196. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/doctor.py +0 -0
  197. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/guide_service.py +0 -0
  198. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/host_diagnosis.py +0 -0
  199. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/issue_service.py +0 -0
  200. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/lab_service.py +0 -0
  201. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/lab_state.py +0 -0
  202. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/progress_service.py +0 -0
  203. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/scaffold.py +0 -0
  204. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/support.py +0 -0
  205. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/services/update_check.py +0 -0
  206. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/sessions/__init__.py +0 -0
  207. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/sessions/store.py +0 -0
  208. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/__init__.py +0 -0
  209. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/catalogues.yml +0 -0
  210. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/cloud-init/README.md +0 -0
  211. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/cloud-init/almalinux.yaml.tmpl +0 -0
  212. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/cloud-init/debian.yaml.tmpl +0 -0
  213. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/cloud-init/ubuntu.yaml.tmpl +0 -0
  214. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/README.fr.md +0 -0
  215. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/README.md +0 -0
  216. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/README.fr.md +0 -0
  217. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/README.md +0 -0
  218. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/hints.yaml +0 -0
  219. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/tests/conftest.py +0 -0
  220. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/challenge/tests/test_functional.py +0 -0
  221. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/lab.fr.yaml +0 -0
  222. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/lab.yaml +0 -0
  223. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/scenario.fr.md +0 -0
  224. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/labs/demo/premiers-pas/scenario.md +0 -0
  225. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/meta.fr.yml +0 -0
  226. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/demo/meta.yml +0 -0
  227. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/README.md +0 -0
  228. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/incus/main.tf +0 -0
  229. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/incus/outputs.tf +0 -0
  230. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/incus/variables.tf +0 -0
  231. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/incus/versions.tf +0 -0
  232. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/kvm/main.tf +0 -0
  233. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/kvm/outputs.tf +0 -0
  234. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/kvm/variables.tf +0 -0
  235. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/kvm/versions.tf +0 -0
  236. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/outscale/main.tf +0 -0
  237. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/outscale/outputs.tf +0 -0
  238. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/outscale/variables.tf +0 -0
  239. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/templates/terraform/outscale/versions.tf +0 -0
  240. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/utils/__init__.py +0 -0
  241. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/utils/fichiers.py +0 -0
  242. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/utils/shell.py +0 -0
  243. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/validators/__init__.py +0 -0
  244. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/validators/content.py +0 -0
  245. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/validators/contract.py +0 -0
  246. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/validators/metadata.py +0 -0
  247. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/src/dsoxlab/validators/structure.py +0 -0
  248. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/__init__.py +0 -0
  249. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/conftest.py +0 -0
  250. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_ansible_availability.py +0 -0
  251. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_catalog.py +0 -0
  252. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_check_release.py +0 -0
  253. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_ci_valide_les_templates.py +0 -0
  254. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_cles_i18n_existantes.py +0 -0
  255. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_cli_smoke.py +0 -0
  256. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_cloud_init_degrade.py +0 -0
  257. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_cloud_init_incus_agent.py +0 -0
  258. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_cloud_init_templates.py +0 -0
  259. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_codes_de_sortie.py +0 -0
  260. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_commande_absente.py +0 -0
  261. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_completion.py +0 -0
  262. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_conteneurs_engendres.py +0 -0
  263. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_contrat_honore.py +0 -0
  264. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_convention_de_nommage.py +0 -0
  265. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_demo.py +0 -0
  266. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_description_alignee.py +0 -0
  267. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doc_auteur_couvre_les_controles.py +0 -0
  268. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doctor.py +0 -0
  269. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doctor_explique.py +0 -0
  270. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doctor_fix.py +0 -0
  271. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doctor_installation.py +0 -0
  272. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doctor_prerequis_vm.py +0 -0
  273. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_doctor_strict.py +0 -0
  274. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_documentation_synchrone.py +0 -0
  275. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_etat_libvirt.py +0 -0
  276. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_executable_absent.py +0 -0
  277. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_fixtures_declarees.py +0 -0
  278. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_guide_service.py +0 -0
  279. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_host_ready_timeout.py +0 -0
  280. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_i18n_coverage.py +0 -0
  281. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_i18n_validators.py +0 -0
  282. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_incus_pool.py +0 -0
  283. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_install_et_contexte.py +0 -0
  284. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_instructor_bootstrap.py +0 -0
  285. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_interruption.py +0 -0
  286. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_issue_service.py +0 -0
  287. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_journal_en_anglais.py +0 -0
  288. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_journalisation.py +0 -0
  289. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_json_output.py +0 -0
  290. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_json_schemas.py +0 -0
  291. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_kvm_disques_apparmor.py +0 -0
  292. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_lab_state.py +0 -0
  293. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_moteur_injoignable.py +0 -0
  294. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_nom_de_pont.py +0 -0
  295. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_note_sans_mesure.py +0 -0
  296. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_outputs_terraform.py +0 -0
  297. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_pager.py +0 -0
  298. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_parcours_distribution.py +0 -0
  299. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_progress_service.py +0 -0
  300. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_provision_ciblee.py +0 -0
  301. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_provision_et_json_services.py +0 -0
  302. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_repertoire_de_travail_annonce.py +0 -0
  303. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_scaffold.py +0 -0
  304. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_schema_version.py +0 -0
  305. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_schemas_sur_documents.py +0 -0
  306. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_services.py +0 -0
  307. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_session_spec.py +0 -0
  308. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_shell_fixtures.py +0 -0
  309. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_snapshot_kvm.py +0 -0
  310. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_sondes_des_tests_docker.py +0 -0
  311. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_sondes_virsh.py +0 -0
  312. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_stack_infra.py +0 -0
  313. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_start_sequence.py +0 -0
  314. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_support.py +0 -0
  315. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_support_en_anglais.py +0 -0
  316. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_update_check.py +0 -0
  317. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_validator_voit_tout.py +0 -0
  318. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_validators_content.py +0 -0
  319. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_verrou_depot.py +0 -0
  320. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_versions_supportees.py +0 -0
  321. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests/test_yaml_contract.py +0 -0
  322. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests_e2e/conftest.py +0 -0
  323. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests_e2e/pytest.ini +0 -0
  324. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests_e2e/test_boite_noire.py +0 -0
  325. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests_e2e/test_installation.py +0 -0
  326. {dsoxlab-0.2.2 → dsoxlab-0.2.4}/tests_e2e/test_parcours.py +0 -0
@@ -9,16 +9,23 @@
9
9
  # 2. un build d'image de vingt minutes ne doit ni retarder une publication PyPI,
10
10
  # ni pouvoir la faire échouer.
11
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.
12
+ # Il se déclenche sur **toute** version publiée, correctifs compris. La règle
13
+ # précédente — les seuls tags mineurs — économisait de la bande passante et
14
+ # coûtait plus cher que ça : elle laissait la doc annoncer une appliance que la
15
+ # dernière release ne portait pas, et obligeait le lecteur à chercher laquelle
16
+ # des releases précédentes en avait une. « La dernière release porte l'image »
17
+ # est la seule phrase qu'on n'a pas à nuancer.
18
+ #
19
+ # Ce que ça ne coûte pas : de l'espace qui s'accumule. Seuls les deux derniers
20
+ # jeux d'images sont conservés (voir l'étape de rétention), et l'image
21
+ # n'épinglant aucune version de dsoxlab, une ancienne n'offre rien d'autre que
22
+ # son poids.
16
23
 
17
24
  name: Appliance
18
25
 
19
26
  on:
20
27
  push:
21
- tags: ["v[0-9]+.[0-9]+.0"]
28
+ tags: ["v[0-9]+.[0-9]+.[0-9]+"]
22
29
  workflow_dispatch:
23
30
  inputs:
24
31
  tag:
@@ -97,6 +104,24 @@ jobs:
97
104
  echo "sum=$sum" >> "$GITHUB_OUTPUT"
98
105
  echo "ISO : $name"
99
106
 
107
+ - name: Prepare the build directory on /mnt
108
+ # `/mnt` has the space (~70 GB against ~14 on `/`), but it belongs to
109
+ # root: the runner cannot create anything there. The first real run of
110
+ # this workflow died on exactly that, four hundredths of a second in —
111
+ # a defect no local build could show, since the directory exists and is
112
+ # writable on a developer's machine.
113
+ #
114
+ # `out/` is deliberately NOT created: Packer refuses to start when its
115
+ # output directory already exists ("It must not exist"), which is how
116
+ # it guarantees it never writes over a previous build. Creating it here
117
+ # traded one failure for another, one step later — measured on the
118
+ # second real run.
119
+ run: |
120
+ set -euo pipefail
121
+ sudo mkdir -p "${BUILD_DIR}/tmp"
122
+ sudo chown -R "$(id -u):$(id -g)" "${BUILD_DIR}"
123
+ df -h "${BUILD_DIR}"
124
+
100
125
  - name: packer init && validate
101
126
  working-directory: packer
102
127
  env:
@@ -104,7 +129,6 @@ jobs:
104
129
  ISO_SUM: ${{ steps.iso.outputs.sum }}
105
130
  run: |
106
131
  set -euo pipefail
107
- mkdir -p "${BUILD_DIR}/out" "${BUILD_DIR}/tmp"
108
132
  packer init .
109
133
  packer validate \
110
134
  -var "image_version=${GITHUB_REF_NAME#v}" \
@@ -206,6 +230,24 @@ jobs:
206
230
  REPO: ${{ github.repository }}
207
231
  run: |
208
232
  set -euo pipefail
233
+
234
+ # Les deux workflows partent du même tag : celui-ci construit pendant
235
+ # vingt minutes, `release.yml` publie en deux. La Release existe donc
236
+ # en pratique bien avant qu'on arrive ici — mais « en pratique » n'est
237
+ # pas une garantie, et un upload sur une Release absente échouerait
238
+ # sur un message qui ne dirait pas pourquoi. On attend, et on le dit.
239
+ restants=30 # 30 × 20 s = dix minutes
240
+ until gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; do
241
+ restants=$((restants - 1))
242
+ if [ "$restants" -le 0 ]; then
243
+ echo "::error::La Release $TAG n'existe toujours pas après dix minutes."
244
+ echo "::error::Regardez release.yml : s'il a échoué, les images n'ont rien où s'attacher."
245
+ exit 1
246
+ fi
247
+ echo "En attente de la Release $TAG…"
248
+ sleep 20
249
+ done
250
+
209
251
  gh release upload "$TAG" --repo "$REPO" --clobber \
210
252
  "${BUILD_DIR}"/out/*.ova \
211
253
  "${BUILD_DIR}"/out/*.qcow2 \
@@ -9,6 +9,90 @@ et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
9
9
 
10
10
  ## [Non publié]
11
11
 
12
+ ## [0.2.4] - 2026-09-26
13
+
14
+ ### Corrigé
15
+
16
+ - **Le build de l'appliance a avancé d'une étape, et s'est arrêté sur la
17
+ précédente.** `/mnt` étant enfin accessible en écriture, c'est Packer qui a
18
+ refusé de démarrer : *Output directory '/mnt/appliance/out' already exists.
19
+ It must not exist.* Ce refus est une qualité — c'est ainsi que Packer
20
+ garantit qu'il n'écrase jamais un build précédent — et l'étape de
21
+ préparation créait précisément ce répertoire. Elle ne crée plus que le
22
+ parent et le répertoire temporaire, et laisse `out/` à Packer. Deux échecs
23
+ de suite au même endroit disent quelque chose qu'il vaut la peine de
24
+ retenir : un workflow qui n'a jamais tourné est un workflow qui ne marche
25
+ pas, et le seul moyen de le savoir est de le jouer.
26
+
27
+ ## [0.2.3] - 2026-09-26
28
+
29
+ ### Corrigé
30
+
31
+ - **Le workflow de l'appliance n'avait jamais tourné, et il est mort au bout de
32
+ quatre centièmes de seconde.** `mkdir: cannot create directory
33
+ '/mnt/appliance': Permission denied` — sur un runner GitHub, `/mnt`
34
+ appartient à root, et c'est là qu'est la place (environ 70 Go contre 14 sur
35
+ `/`). Aucun build local ne pouvait le montrer : sur une machine de
36
+ développement, ce répertoire existe et s'écrit. Une étape dédiée le crée
37
+ désormais en `sudo` et le remet au runner.
38
+
39
+ - **L'image est publiée à chaque release, et plus seulement aux mineures.**
40
+ L'ancienne règle économisait de la bande passante et coûtait plus qu'elle
41
+ n'économisait : la documentation annonçait une appliance que la dernière
42
+ release ne portait pas, et le lecteur devait deviner laquelle des
43
+ précédentes l'avait. « La dernière release porte l'image » est la seule
44
+ phrase qui n'appelle aucune nuance, et rien ne s'accumule puisque l'étape de
45
+ rétention garde les deux derniers jeux. Les deux workflows partant désormais
46
+ du même tag, l'upload attend que la Release existe au lieu de le supposer —
47
+ et dit quel workflow regarder si elle n'arrive jamais.
48
+
49
+ ### Modifié
50
+
51
+ - **La documentation est un parcours, plus une pile de pages.** Relue de bout
52
+ en bout contre le code, elle portait des ruptures qu'aucune page seule ne
53
+ pouvait révéler :
54
+
55
+ `learner.fr.md` promettait `run` juste après le clonage d'un catalogue, alors
56
+ que **66 des 86 labs du catalogue Linux** sont des labs `vm` : ce `run` sort
57
+ en 2, le `provision` qu'il suggère sort en 1 faute de clé SSH, et ni
58
+ `instructor bootstrap`, ni `start`, ni le mot Terraform n'apparaissaient sur
59
+ la page. Elle nomme maintenant ce qu'un lab `vm` demande à une machine, et
60
+ met `start` en premier — la commande qui joue tout l'ordre en annonçant
61
+ chaque étape avec la commande qui la rejoue seule.
62
+
63
+ `catalog-author.fr.md` listait les fichiers d'un lab sans dire ce qu'on y
64
+ met. Le barème que `validate-structure` impose (`### … (20 pts)` face à un
65
+ en-tête `N tâches, M points`, et un point par test), le format de
66
+ `hints.yaml`, et l'emplacement de `conftest.py` — pytest s'exécute depuis la
67
+ racine du catalogue — n'étaient documentés nulle part.
68
+
69
+ Le README n'atteignait ni l'index, ni `exit-codes.fr.md`,
70
+ `machine-output.fr.md` et `infra-only.fr.md` : trois pages de référence
71
+ inaccessibles depuis la porte d'entrée.
72
+
73
+ La clé SSH était décrite comme quelque chose que le catalogue livre. Il ne la
74
+ livre pas : les catalogues publiés ignorent tout le répertoire `ssh/`, et
75
+ `provision` refuse de démarrer sans la moitié privée. Elle se génère **par
76
+ clone**, avec `instructor bootstrap`, apprenants compris.
77
+
78
+ `contract-v1.fr.md` se contredisait à neuf lignes d'intervalle sur le
79
+ caractère obligatoire de `repo.category`.
80
+
81
+ - **`docs/appliance.fr.md` est un pas-à-pas**, de l'installation de VirtualBox
82
+ au premier lab, avec la vérification d'empreinte, la mémoire à donner à la
83
+ machine selon celle de l'hôte, ce que montre le premier démarrage en trois
84
+ temps, et un tableau de dépannage. Les deux README ouvrent sur les deux
85
+ façons d'entrer, chacune avec son parcours en quatre étapes.
86
+
87
+ ### Ajouté
88
+
89
+ - **Rendre à César** : l'appliance est l'idée de [@cedric-ribier], et elle
90
+ existe parce qu'il en avait déjà construit une de bout en bout, documentée
91
+ dans [#91](https://github.com/stephrobert/dsoxlab/issues/91). Celle qui est
92
+ livrée ici s'en inspire directement.
93
+
94
+ [@cedric-ribier]: https://github.com/cedric-ribier
95
+
12
96
  ## [0.2.2] - 2026-09-26
13
97
 
14
98
  ### Ajouté
@@ -28,9 +112,10 @@ et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
28
112
 
29
113
  L'image **n'épingle rien**. Le premier démarrage installe le dernier dsoxlab, et
30
114
  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.
115
+ en direct et jamais supposée. Elle est reconstruite à **chaque version
116
+ publiée**, correctifs compris : la dernière release porte l'image, et c'est la
117
+ seule phrase qui n'appelle aucune nuance. Rien ne s'accumule pour autant —
118
+ seuls les deux derniers jeux sont conservés.
34
119
 
35
120
  Mesuré, non estimé : **461 Mio** pour le qcow2 et **446 Mio** pour l'OVA,
36
121
  construits en 4 min 50 s, contre un budget que le workflow impose à 800 Mio. Une
@@ -41,7 +126,7 @@ et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
41
126
  bonne sous Linux, plutôt que de les vendre à égalité.
42
127
 
43
128
  - **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
129
+ Environ 900 Mo par version, pour toujours, pour des images qui
45
130
  n'épinglent aucune version de dsoxlab et installent la dernière au premier
46
131
  démarrage : une ancienne n'offre aucune reproductibilité, seulement son
47
132
  poids. Deux plutôt qu'une, pour qu'une image cassée ait un recours. Les
@@ -9,6 +9,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.2.4] - 2026-09-26
13
+
14
+ ### Fixed
15
+
16
+ - **The appliance build got one step further, and stopped on the step before.**
17
+ With `/mnt` finally writable, Packer refused to start: *Output directory
18
+ '/mnt/appliance/out' already exists. It must not exist.* That refusal is a
19
+ feature — it is how Packer guarantees it never writes over a previous
20
+ build — and the preparation step was creating that very directory. It now
21
+ creates only the parent and the temporary directory, and leaves `out/` to
22
+ Packer. Two failures in a row at the same place say something worth keeping:
23
+ a workflow that has never run is a workflow that does not work, and the only
24
+ way to know is to run it.
25
+
26
+ ## [0.2.3] - 2026-09-26
27
+
28
+ ### Fixed
29
+
30
+ - **The appliance workflow had never run, and died four hundredths of a second
31
+ in.** `mkdir: cannot create directory '/mnt/appliance': Permission denied` —
32
+ on a GitHub runner `/mnt` belongs to root, and that is where the disk space
33
+ is (~70 GB against ~14 on `/`). No local build could show it, since the
34
+ directory exists and is writable on a developer's machine. A dedicated step
35
+ now creates it with `sudo` and hands it to the runner.
36
+
37
+ - **The image is published on every release, not only on minor ones.** The old
38
+ rule saved bandwidth and cost more than it saved: the documentation announced
39
+ an appliance the latest release did not carry, and the reader had to work out
40
+ which earlier release did. "The latest release carries the image" is the only
41
+ sentence that needs no caveat, and nothing accumulates since the retention
42
+ step keeps the last two sets. The two workflows now start from the same tag,
43
+ so the upload waits for the Release to exist instead of assuming it — and
44
+ says which workflow to look at if it never does.
45
+
46
+ ### Changed
47
+
48
+ - **The documentation is a path now, not a pile of pages.** Read end to end
49
+ against the code, it had breaks that no single page could reveal:
50
+
51
+ `learner.md` promised `run` right after cloning a catalog, while **66 of the
52
+ Linux catalog's 86 labs** are `vm` labs: that `run` exits 2, the `provision`
53
+ it suggests exits 1 for want of an SSH key, and neither `instructor
54
+ bootstrap`, nor `start`, nor the word Terraform appeared anywhere on the
55
+ page. It now names what a `vm` lab asks of a machine, and puts `start` first
56
+ — the command that plays the whole order and announces each step with the
57
+ command that replays it alone.
58
+
59
+ `catalog-author.md` listed a lab's files without saying what goes in them.
60
+ The scale that `validate-structure` enforces (`### … (20 pts)` against a
61
+ `N tasks, M points` header, and one point per test), the format of
62
+ `hints.yaml`, and where `conftest.py` lives — pytest runs from the catalog
63
+ root — were documented nowhere.
64
+
65
+ The README reached neither the index nor `exit-codes.md`,
66
+ `machine-output.md` and `infra-only.md`: three reference pages unreachable
67
+ from the front door.
68
+
69
+ The SSH key was described as something a catalog ships. It is not: published
70
+ catalogs ignore the whole `ssh/` directory, and `provision` refuses to start
71
+ without the private half. It is generated **per clone**, by `instructor
72
+ bootstrap`, learners included.
73
+
74
+ `contract-v1.md` contradicted itself nine lines apart on whether
75
+ `repo.category` is required.
76
+
77
+ - **`docs/appliance.md` is a step-by-step**, from installing VirtualBox to the
78
+ first lab, with the digest check, the memory to give the machine depending on
79
+ the host's, what the first boot looks like in three acts, and a
80
+ troubleshooting table. Both READMEs open on the two ways in, each with its
81
+ own four-step path.
82
+
83
+ ### Added
84
+
85
+ - **Credit where it is due**: the appliance is [@cedric-ribier]'s idea, and it
86
+ exists because he had already built one end to end and documented it in
87
+ [#91](https://github.com/stephrobert/dsoxlab/issues/91). The one shipped here
88
+ is directly inspired by his.
89
+
90
+ [@cedric-ribier]: https://github.com/cedric-ribier
91
+
12
92
  ## [0.2.2] - 2026-09-26
13
93
 
14
94
  ### Added
@@ -27,8 +107,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
27
107
 
28
108
  The image **pins nothing**. The first boot installs the latest dsoxlab, and the
29
109
  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.
110
+ assumed. It is rebuilt on **every published version**, patches included: the
111
+ latest release carries the image, which is the only sentence that needs no
112
+ caveat. Nothing accumulates — only the last two sets are kept.
32
113
 
33
114
  Measured, not estimated: **461 MiB** for the qcow2 and **446 MiB** for the OVA,
34
115
  built in 4 min 50 s, against a budget the workflow enforces at 800 MiB. A
@@ -39,7 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
39
120
  selling them as equals.
40
121
 
41
122
  - **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
123
+ per version, forever, for images that pin no dsoxlab version and
43
124
  install the latest at first boot: an old one offers no reproducibility, only
44
125
  weight. Two rather than one, so a broken image has a fallback. The Releases,
45
126
  their changelog and the Python distributions are untouched.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dsoxlab
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Turn declarative exercises into reproducible, runnable and verifiable lab environments
5
5
  Project-URL: Homepage, https://blog.stephane-robert.info/
6
6
  Project-URL: Documentation, https://github.com/stephrobert/dsoxlab/blob/main/docs/README.md
@@ -118,9 +118,42 @@ and no Docker: it runs anywhere dsoxlab runs.
118
118
 
119
119
  ---
120
120
 
121
+ ## Or: the appliance, in four steps
122
+
123
+ For Windows and macOS, where `uv tool install` is not an option. No knowledge
124
+ of virtualization required.
125
+
126
+ 1. **Install VirtualBox** from <https://www.virtualbox.org/wiki/Downloads>. The
127
+ *Extension Pack* it offers is not needed.
128
+ 2. **Download the `.ova`** from the latest
129
+ [release](https://github.com/stephrobert/dsoxlab/releases) that carries one,
130
+ and check its digest against the `SHA256SUMS` published next to it.
131
+ 3. **Import and start**: in VirtualBox, *File → Import Appliance*, pick the
132
+ `.ova`, *Finish*, then *Start*. The first boot installs dsoxlab, the
133
+ hypervisors and the desktop, then **reboots on its own**: count five to
134
+ fifteen minutes, with nothing to type.
135
+ 4. **Log in** with `student` / `dsoxlab` — the machine requires you to change
136
+ that password, which is public — then, in the desktop's terminal:
137
+
138
+ ```bash
139
+ dsoxlab demo && cd ~/.local/share/dsoxlab/demo
140
+ dsoxlab course premiers-pas
141
+ ```
142
+
143
+ `vm` labs need one more step, **nested virtualization**, which is enabled on
144
+ your computer and not inside the machine: *Settings → System → Processor →
145
+ Enable Nested VT-x/AMD-V*, appliance powered off. `dsoxlab doctor` says so when
146
+ it is missing.
147
+
148
+ Every step in detail, the actual requirements and a troubleshooting table are
149
+ on **[the appliance page](docs/appliance.md)**.
150
+
151
+ ---
152
+
121
153
  ## Documentation
122
154
 
123
- Three readers, three doors. Every page names its audience in its first lines.
155
+ Every page names its audience in its first lines, and
156
+ [the index](docs/README.md) lists them all.
124
157
 
125
158
  | I want to… | Read |
126
159
  | --- | --- |
@@ -128,6 +161,8 @@ Three readers, three doors. Every page names its audience in its first lines.
128
161
  | Play labs on Windows or macOS, without installing anything | **[The appliance](docs/appliance.md)**, a ready-to-play VM |
129
162
  | Write my own catalog of labs | **[For the catalog author](docs/catalog-author.md)**, then [the v1 contract](docs/contract-v1.md) field by field |
130
163
  | Run the machines the labs need | **[For the trainer](docs/trainer.md)** |
164
+ | Get throwaway VMs, with no exercise to write | [Infrastructure without labs](docs/infra-only.md) |
165
+ | Call dsoxlab from a script or a CI, and branch on the result | [Exit codes](docs/exit-codes.md), then [the machine output](docs/machine-output.md) for `--json` |
131
166
  | Know where dsoxlab writes on my disk | [Where dsoxlab writes](docs/files.md) |
132
167
  | See every command | [Command reference](docs/commands.md), generated from the CLI |
133
168
 
@@ -217,6 +252,13 @@ persistent, and four releases come from his reports:
217
252
  ready-to-use appliance, discussed in
218
253
  [#91](https://github.com/stephrobert/dsoxlab/issues/91).
219
254
 
255
+ **And the appliance itself is his idea.** He did not ask for it: he had already
256
+ **built one end to end**, then documented it in
257
+ [#91](https://github.com/stephrobert/dsoxlab/issues/91). The one dsoxlab ships
258
+ today is directly inspired by his — the idea and the approach both. It is the
259
+ contribution a changelog shows least and the product owes most: without it,
260
+ there would be nothing to offer anyone working on Windows or macOS.
261
+
220
262
  **[@Gedd18](https://github.com/Gedd18)** found that the lab catalog's
221
263
  `conftest.py` failed to load whenever no infrastructure provider was resolved,
222
264
  which silently blocked **every `shell` lab** — the very first ones a learner
@@ -80,10 +80,43 @@ conteneur, ni Docker : il tourne partout où dsoxlab tourne.
80
80
 
81
81
  ---
82
82
 
83
+ ## Ou : l'appliance, en quatre étapes
84
+
85
+ Pour Windows et macOS, où `uv tool install` n'est pas une option. Aucune
86
+ connaissance de la virtualisation n'est nécessaire.
87
+
88
+ 1. **Installez VirtualBox** depuis
89
+ <https://www.virtualbox.org/wiki/Downloads>. L'*Extension Pack* proposé
90
+ n'est pas nécessaire.
91
+ 2. **Téléchargez le fichier `.ova`** de la dernière
92
+ [release](https://github.com/stephrobert/dsoxlab/releases) qui en porte un,
93
+ et vérifiez son empreinte avec le `SHA256SUMS` publié à côté.
94
+ 3. **Importez et démarrez** : dans VirtualBox, *Fichier → Importer un appareil
95
+ virtuel*, choisissez le `.ova`, *Terminer*, puis *Démarrer*. Le premier
96
+ démarrage installe dsoxlab, les hyperviseurs et le bureau, puis **redémarre
97
+ tout seul** : comptez cinq à quinze minutes, sans rien taper.
98
+ 4. **Connectez-vous** avec `student` / `dsoxlab` — la machine exige de changer
99
+ ce mot de passe, qui est public — puis, dans le terminal du bureau :
100
+
101
+ ```bash
102
+ dsoxlab demo && cd ~/.local/share/dsoxlab/demo
103
+ dsoxlab course premiers-pas
104
+ ```
105
+
106
+ Les labs `vm` demandent une étape de plus, la **virtualisation imbriquée**, qui
107
+ s'active sur votre ordinateur et non dans la machine : *Configuration → Système
108
+ → Processeur → Activer VT-x/AMD-V imbriqué*, appliance éteinte. `dsoxlab
109
+ doctor` le dit s'il manque.
110
+
111
+ Le détail de chaque étape, les prérequis chiffrés et un tableau de dépannage
112
+ sont dans **[la page de l'appliance](docs/appliance.fr.md)**.
113
+
114
+ ---
115
+
83
116
  ## Documentation
84
117
 
85
- Trois lecteurs, trois portes. Chaque page nomme son public dès ses premières
86
- lignes.
118
+ Chaque page nomme son public dès ses premières lignes, et
119
+ [l'index](docs/README.fr.md) les répertorie toutes.
87
120
 
88
121
  | Je veux… | Lire |
89
122
  | --- | --- |
@@ -91,6 +124,8 @@ lignes.
91
124
  | Jouer des labs sous Windows ou macOS, sans rien installer | **[L'appliance](docs/appliance.fr.md)**, une VM prête à jouer |
92
125
  | Écrire mon propre catalogue de labs | **[Pour l'auteur de catalogue](docs/catalog-author.fr.md)**, puis [le contrat v1](docs/contract-v1.fr.md) champ par champ |
93
126
  | Monter les machines dont les labs ont besoin | **[Pour le formateur](docs/trainer.fr.md)** |
127
+ | Obtenir des VM jetables, sans aucun exercice à écrire | [L'infrastructure sans labs](docs/infra-only.fr.md) |
128
+ | Appeler dsoxlab depuis un script ou une CI, et décider sur le résultat | [Les codes de sortie](docs/exit-codes.fr.md), puis [la sortie machine](docs/machine-output.fr.md) pour `--json` |
94
129
  | Savoir où dsoxlab écrit sur mon disque | [Où dsoxlab écrit](docs/files.fr.md) |
95
130
  | Voir toutes les commandes | [Référence des commandes](docs/commands.fr.md), produite par la CLI |
96
131
 
@@ -186,6 +221,14 @@ et quatre versions viennent de ses remontées :
186
221
  une image prête à l'emploi, discuté dans
187
222
  [#91](https://github.com/stephrobert/dsoxlab/issues/91).
188
223
 
224
+ **Et l'appliance elle-même est son idée.** Il ne l'a pas demandée : il l'avait
225
+ déjà **construite de bout en bout**, puis documentée dans
226
+ [#91](https://github.com/stephrobert/dsoxlab/issues/91). Celle que dsoxlab
227
+ publie aujourd'hui s'inspire directement de la sienne — l'idée comme la
228
+ démarche. C'est la contribution la moins visible dans un journal des
229
+ modifications et la plus structurante pour le produit : sans elle, il n'y
230
+ aurait rien à proposer à qui travaille sous Windows ou macOS.
231
+
189
232
  **[@Gedd18](https://github.com/Gedd18)** a trouvé que le `conftest.py` du
190
233
  catalogue de labs ne se chargeait plus dès qu'aucun provider d'infrastructure
191
234
  n'était résolu, ce qui bloquait en silence **tous les labs `shell`**, c'est-à-dire
@@ -81,9 +81,42 @@ and no Docker: it runs anywhere dsoxlab runs.
81
81
 
82
82
  ---
83
83
 
84
+ ## Or: the appliance, in four steps
85
+
86
+ For Windows and macOS, where `uv tool install` is not an option. No knowledge
87
+ of virtualization required.
88
+
89
+ 1. **Install VirtualBox** from <https://www.virtualbox.org/wiki/Downloads>. The
90
+ *Extension Pack* it offers is not needed.
91
+ 2. **Download the `.ova`** from the latest
92
+ [release](https://github.com/stephrobert/dsoxlab/releases) that carries one,
93
+ and check its digest against the `SHA256SUMS` published next to it.
94
+ 3. **Import and start**: in VirtualBox, *File → Import Appliance*, pick the
95
+ `.ova`, *Finish*, then *Start*. The first boot installs dsoxlab, the
96
+ hypervisors and the desktop, then **reboots on its own**: count five to
97
+ fifteen minutes, with nothing to type.
98
+ 4. **Log in** with `student` / `dsoxlab` — the machine requires you to change
99
+ that password, which is public — then, in the desktop's terminal:
100
+
101
+ ```bash
102
+ dsoxlab demo && cd ~/.local/share/dsoxlab/demo
103
+ dsoxlab course premiers-pas
104
+ ```
105
+
106
+ `vm` labs need one more step, **nested virtualization**, which is enabled on
107
+ your computer and not inside the machine: *Settings → System → Processor →
108
+ Enable Nested VT-x/AMD-V*, appliance powered off. `dsoxlab doctor` says so when
109
+ it is missing.
110
+
111
+ Every step in detail, the actual requirements and a troubleshooting table are
112
+ on **[the appliance page](docs/appliance.md)**.
113
+
114
+ ---
115
+
84
116
  ## Documentation
85
117
 
86
- Three readers, three doors. Every page names its audience in its first lines.
118
+ Every page names its audience in its first lines, and
119
+ [the index](docs/README.md) lists them all.
87
120
 
88
121
  | I want to… | Read |
89
122
  | --- | --- |
@@ -91,6 +124,8 @@ Three readers, three doors. Every page names its audience in its first lines.
91
124
  | Play labs on Windows or macOS, without installing anything | **[The appliance](docs/appliance.md)**, a ready-to-play VM |
92
125
  | Write my own catalog of labs | **[For the catalog author](docs/catalog-author.md)**, then [the v1 contract](docs/contract-v1.md) field by field |
93
126
  | Run the machines the labs need | **[For the trainer](docs/trainer.md)** |
127
+ | Get throwaway VMs, with no exercise to write | [Infrastructure without labs](docs/infra-only.md) |
128
+ | Call dsoxlab from a script or a CI, and branch on the result | [Exit codes](docs/exit-codes.md), then [the machine output](docs/machine-output.md) for `--json` |
94
129
  | Know where dsoxlab writes on my disk | [Where dsoxlab writes](docs/files.md) |
95
130
  | See every command | [Command reference](docs/commands.md), generated from the CLI |
96
131
 
@@ -180,6 +215,13 @@ persistent, and four releases come from his reports:
180
215
  ready-to-use appliance, discussed in
181
216
  [#91](https://github.com/stephrobert/dsoxlab/issues/91).
182
217
 
218
+ **And the appliance itself is his idea.** He did not ask for it: he had already
219
+ **built one end to end**, then documented it in
220
+ [#91](https://github.com/stephrobert/dsoxlab/issues/91). The one dsoxlab ships
221
+ today is directly inspired by his — the idea and the approach both. It is the
222
+ contribution a changelog shows least and the product owes most: without it,
223
+ there would be nothing to offer anyone working on Windows or macOS.
224
+
183
225
  **[@Gedd18](https://github.com/Gedd18)** found that the lab catalog's
184
226
  `conftest.py` failed to load whenever no infrastructure provider was resolved,
185
227
  which silently blocked **every `shell` lab** — the very first ones a learner