libre-devops-helpers 0.4.1__tar.gz → 0.5.0__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 (271) hide show
  1. libre_devops_helpers-0.5.0/.github/copilot-instructions.md +14 -0
  2. libre_devops_helpers-0.5.0/AGENTS.md +118 -0
  3. libre_devops_helpers-0.5.0/AI.md +116 -0
  4. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/CHANGELOG.md +36 -0
  5. libre_devops_helpers-0.5.0/CLAUDE.md +1 -0
  6. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/CONTRIBUTING.md +4 -0
  7. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/PKG-INFO +16 -2
  8. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/README.md +15 -1
  9. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/README.md +1 -0
  10. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/authentication.md +2 -2
  11. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/configuration.md +8 -2
  12. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/containers.md +7 -4
  13. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/development.md +4 -2
  14. libre_devops_helpers-0.5.0/docs/network.md +106 -0
  15. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/justfile +5 -0
  16. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/pyproject.toml +1 -1
  17. libre_devops_helpers-0.5.0/scripts/ai_instructions.py +46 -0
  18. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/__init__.py +1 -1
  19. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/app.py +3 -0
  20. libre_devops_helpers-0.5.0/src/libre_devops_helpers/cli/commands/network.py +220 -0
  21. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/runtime.py +16 -8
  22. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/config.py +24 -5
  23. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/http.py +9 -2
  24. libre_devops_helpers-0.5.0/src/libre_devops_helpers/core/network.py +454 -0
  25. libre_devops_helpers-0.5.0/src/libre_devops_helpers/core/trust.py +275 -0
  26. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/process.py +11 -0
  27. libre_devops_helpers-0.5.0/tests/cli/commands/test_network.py +105 -0
  28. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_runtime.py +24 -1
  29. libre_devops_helpers-0.5.0/tests/conftest.py +47 -0
  30. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_config.py +25 -0
  31. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_http.py +28 -0
  32. libre_devops_helpers-0.5.0/tests/core/test_network.py +236 -0
  33. libre_devops_helpers-0.5.0/tests/core/test_trust.py +136 -0
  34. libre_devops_helpers-0.5.0/tests/fakes/certificates.py +24 -0
  35. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/http.py +3 -0
  36. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_process.py +16 -0
  37. libre_devops_helpers-0.5.0/tests/project/test_ai_instructions.py +35 -0
  38. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_docs.py +10 -2
  39. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/uv.lock +1 -1
  40. libre_devops_helpers-0.4.1/tests/conftest.py +0 -22
  41. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.dockerignore +0 -0
  42. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  43. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  44. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  45. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/dependabot.yml +0 -0
  46. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/pull_request_template.md +0 -0
  47. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/badges.yml +0 -0
  48. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/ci.yml +0 -0
  49. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/codeql.yml +0 -0
  50. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/container.yml +0 -0
  51. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/dependency-review.yml +0 -0
  52. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/release.yml +0 -0
  53. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.gitignore +0 -0
  54. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.gitleaks.toml +0 -0
  55. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/Containerfile +0 -0
  56. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/LICENSE +0 -0
  57. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/SECURITY.md +0 -0
  58. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/brand.toml +0 -0
  59. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/container/azure-cli/pyproject.toml +0 -0
  60. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/container/azure-cli/uv.lock +0 -0
  61. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/azure.md +0 -0
  62. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/defender.md +0 -0
  63. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/devices.md +0 -0
  64. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/entra.md +0 -0
  65. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/graph.md +0 -0
  66. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/library.md +0 -0
  67. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/logic-apps.md +0 -0
  68. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/permissions.md +0 -0
  69. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/pim.md +0 -0
  70. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/rebranding.md +0 -0
  71. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/servicenow.md +0 -0
  72. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/scripts/image_tags.py +0 -0
  73. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/scripts/rebrand.py +0 -0
  74. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/__main__.py +0 -0
  75. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/__init__.py +0 -0
  76. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/__init__.py +0 -0
  77. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/automation.py +0 -0
  78. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/az.py +0 -0
  79. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/azure.py +0 -0
  80. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/config.py +0 -0
  81. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/devices.py +0 -0
  82. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/entra.py +0 -0
  83. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/graph.py +0 -0
  84. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/incidents.py +0 -0
  85. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/intune.py +0 -0
  86. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/keyvault.py +0 -0
  87. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/logicapp.py +0 -0
  88. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/logs.py +0 -0
  89. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/pim.py +0 -0
  90. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/pretty.py +0 -0
  91. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/profiles.py +0 -0
  92. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/snow.py +0 -0
  93. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/token.py +0 -0
  94. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/welcome.py +0 -0
  95. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/xdr.py +0 -0
  96. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/exits.py +0 -0
  97. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/options.py +0 -0
  98. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/render.py +0 -0
  99. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/servicenow_runtime.py +0 -0
  100. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/__init__.py +0 -0
  101. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/auth.py +0 -0
  102. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/brand.py +0 -0
  103. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/browser.py +0 -0
  104. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/dpapi.py +0 -0
  105. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/errors.py +0 -0
  106. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/inputs.py +0 -0
  107. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/log.py +0 -0
  108. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/poll.py +0 -0
  109. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/process.py +0 -0
  110. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/sheets.py +0 -0
  111. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/tables.py +0 -0
  112. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/timewindow.py +0 -0
  113. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/token_store.py +0 -0
  114. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/util.py +0 -0
  115. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/yaml_text.py +0 -0
  116. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/__init__.py +0 -0
  117. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/__init__.py +0 -0
  118. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/azure_cli.py +0 -0
  119. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/delegated.py +0 -0
  120. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/entra.py +0 -0
  121. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/factory.py +0 -0
  122. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/lapse.py +0 -0
  123. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/managed_identity.py +0 -0
  124. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/automation/__init__.py +0 -0
  125. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/automation/client.py +0 -0
  126. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/automation/models.py +0 -0
  127. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azcli/__init__.py +0 -0
  128. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azcli/client.py +0 -0
  129. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azcli/context.py +0 -0
  130. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azure/__init__.py +0 -0
  131. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azure/client.py +0 -0
  132. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azure/models.py +0 -0
  133. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/clouds.py +0 -0
  134. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/config.py +0 -0
  135. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/__init__.py +0 -0
  136. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/antivirus.py +0 -0
  137. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/check.py +0 -0
  138. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/inspect.py +0 -0
  139. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/models.py +0 -0
  140. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/__init__.py +0 -0
  141. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/client.py +0 -0
  142. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/models.py +0 -0
  143. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/permissions.py +0 -0
  144. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/graph/__init__.py +0 -0
  145. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/graph/client.py +0 -0
  146. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/__init__.py +0 -0
  147. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/client.py +0 -0
  148. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/models.py +0 -0
  149. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/permissions.py +0 -0
  150. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/__init__.py +0 -0
  151. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/client.py +0 -0
  152. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/models.py +0 -0
  153. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/permissions.py +0 -0
  154. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/keyvault/__init__.py +0 -0
  155. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/keyvault/client.py +0 -0
  156. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/loganalytics/__init__.py +0 -0
  157. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/loganalytics/client.py +0 -0
  158. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/__init__.py +0 -0
  159. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/checks.py +0 -0
  160. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/client.py +0 -0
  161. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/document.py +0 -0
  162. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/__init__.py +0 -0
  163. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/azure.py +0 -0
  164. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/entra.py +0 -0
  165. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/models.py +0 -0
  166. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/permissions.py +0 -0
  167. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/rules.py +0 -0
  168. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/resources.py +0 -0
  169. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/tokens.py +0 -0
  170. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/__init__.py +0 -0
  171. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/client.py +0 -0
  172. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/models.py +0 -0
  173. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/permissions.py +0 -0
  174. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/py.typed +0 -0
  175. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/__init__.py +0 -0
  176. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/auth.py +0 -0
  177. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/config.py +0 -0
  178. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/instance/__init__.py +0 -0
  179. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/instance/client.py +0 -0
  180. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/instance/models.py +0 -0
  181. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/roles.py +0 -0
  182. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/tables.py +0 -0
  183. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_automation.py +0 -0
  184. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_az.py +0 -0
  185. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_azure.py +0 -0
  186. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_config.py +0 -0
  187. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_devices.py +0 -0
  188. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_entra.py +0 -0
  189. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_graph.py +0 -0
  190. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_incidents.py +0 -0
  191. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_intune.py +0 -0
  192. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_keyvault.py +0 -0
  193. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_logicapp.py +0 -0
  194. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_logs.py +0 -0
  195. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_pim.py +0 -0
  196. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_pretty.py +0 -0
  197. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_profiles.py +0 -0
  198. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_snow.py +0 -0
  199. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_token.py +0 -0
  200. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_welcome.py +0 -0
  201. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_xdr.py +0 -0
  202. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/conftest.py +0 -0
  203. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_app.py +0 -0
  204. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_options.py +0 -0
  205. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_render.py +0 -0
  206. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_servicenow_runtime.py +0 -0
  207. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_auth.py +0 -0
  208. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_browser.py +0 -0
  209. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_dpapi.py +0 -0
  210. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_inputs.py +0 -0
  211. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_log.py +0 -0
  212. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_poll.py +0 -0
  213. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_process.py +0 -0
  214. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_sheets.py +0 -0
  215. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_timewindow.py +0 -0
  216. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_token_store.py +0 -0
  217. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_util.py +0 -0
  218. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_yaml_text.py +0 -0
  219. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/__init__.py +0 -0
  220. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/automation.py +0 -0
  221. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/azcli.py +0 -0
  222. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/clock.py +0 -0
  223. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/devices.py +0 -0
  224. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/ids.py +0 -0
  225. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/incidents.py +0 -0
  226. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/logicapps.py +0 -0
  227. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/pim.py +0 -0
  228. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/process.py +0 -0
  229. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/servicenow.py +0 -0
  230. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/tenant.py +0 -0
  231. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/tokens.py +0 -0
  232. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/workbooks.py +0 -0
  233. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_azure_cli.py +0 -0
  234. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_delegated.py +0 -0
  235. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_entra.py +0 -0
  236. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_factory.py +0 -0
  237. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_lapse.py +0 -0
  238. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_managed_identity.py +0 -0
  239. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/automation/test_client.py +0 -0
  240. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/azcli/test_client.py +0 -0
  241. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/azcli/test_context.py +0 -0
  242. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/azure/test_client.py +0 -0
  243. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/devices/test_antivirus.py +0 -0
  244. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/devices/test_check.py +0 -0
  245. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/devices/test_inspect.py +0 -0
  246. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/entra/test_client.py +0 -0
  247. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/graph/test_client.py +0 -0
  248. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/incidents/test_client.py +0 -0
  249. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/intune/test_client.py +0 -0
  250. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/keyvault/test_client.py +0 -0
  251. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/loganalytics/test_client.py +0 -0
  252. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/logicapps/test_checks.py +0 -0
  253. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/logicapps/test_client.py +0 -0
  254. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/logicapps/test_document.py +0 -0
  255. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/pim/test_azure.py +0 -0
  256. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/pim/test_entra.py +0 -0
  257. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/pim/test_rules.py +0 -0
  258. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_clouds.py +0 -0
  259. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_config.py +0 -0
  260. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_tokens.py +0 -0
  261. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/xdr/test_client.py +0 -0
  262. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_containerfile.py +0 -0
  263. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_image_tags.py +0 -0
  264. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_layout.py +0 -0
  265. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_package_surface.py +0 -0
  266. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_rebrand.py +0 -0
  267. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/instance/test_client.py +0 -0
  268. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_auth.py +0 -0
  269. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_config.py +0 -0
  270. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_roles.py +0 -0
  271. {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_tables.py +0 -0
@@ -0,0 +1,14 @@
1
+ # Instructions for GitHub Copilot
2
+
3
+ This repository's instructions for AI coding assistants are in [`AI.md`](../AI.md) at the
4
+ root (and, word for word, in `AGENTS.md`, which the Copilot coding agent also reads). Read
5
+ `AI.md` before suggesting or making any change here.
6
+
7
+ The rules that matter most:
8
+
9
+ - Every command reads; nothing changes a tenant or an instance unless asked.
10
+ - Runtime dependencies are `requests` and `typer` only; the rest is the standard library.
11
+ - Tests never touch the network: use the fakes in `tests/fakes`. Run `just check`.
12
+ - Never hard-code `ldo` or `LDO_`: take names from `core/brand.py`.
13
+ - UK English, and no em or en dashes anywhere.
14
+ - No AI attribution in commits, and nothing is committed until the person asks.
@@ -0,0 +1,118 @@
1
+ <!-- Generated from AI.md by 'just ai'. Edit AI.md, not this file. -->
2
+
3
+ # Instructions for AI coding assistants
4
+
5
+ This file is for Claude Code, GitHub Copilot, OpenAI Codex and any other assistant working
6
+ in this repository. It is the one to edit: `AGENTS.md` is generated from it (`just ai`),
7
+ `CLAUDE.md` imports it, and `.github/copilot-instructions.md` points here.
8
+
9
+ ## What this is
10
+
11
+ `ldo` (distribution `libre-devops-helpers`, import `libre_devops_helpers`): a fast, read-only
12
+ CLI and library for Microsoft (Entra ID, Defender XDR, Intune, Azure, Graph, PIM, Logic Apps,
13
+ Automation) and ServiceNow. Its users are people signing in as themselves, usually through
14
+ the Azure CLI; automation is second. Everything reads, apart from `ldo az use`, which switches
15
+ the Azure CLI's account. Never add a command that changes a tenant or an instance without
16
+ being asked.
17
+
18
+ ## Working here
19
+
20
+ - Run everything through `just` (or `uv run`): `just check` before calling anything done,
21
+ `just ci` for all of CI's checks, `just fmt` to format, `just run <args>` to try the CLI.
22
+ - Python 3.11 or later: nothing newer than 3.11 in the code (`just test-311` proves it).
23
+ - ruff, line length 100. Match the surrounding code's naming, comment density and idiom.
24
+ - Runtime dependencies are `requests` and `typer` only. Everything else is the standard
25
+ library. Do not add a runtime dependency without asking; test-only ones go in the `dev`
26
+ group.
27
+
28
+ ## Layout and layering
29
+
30
+ ```text
31
+ src/libre_devops_helpers/
32
+ core/ vendor-neutral: errors, config, brand, HTTP client, token store,
33
+ polling, inputs (CSV and Excel), logging, YAML writer
34
+ microsoft/ the shared Microsoft layer: clouds, credentials, tokens, profiles
35
+ microsoft/<feature>/ one package per API: entra, xdr, graph, azure, pim, automation ...
36
+ microsoft/devices/ the one composite, using entra, xdr and intune
37
+ servicenow/ the ServiceNow vendor, same shape
38
+ cli/ the ldo command: parses, calls a client, renders. No logic here.
39
+ ```
40
+
41
+ `tests/project/test_package_surface.py` enforces the layering: `core` imports nothing of
42
+ ours, a feature imports only `core` and its vendor layer, `cli` sits on top. Cross-cutting
43
+ code (auth, HTTP, input parsing) goes in `core` or the vendor layer, never in a feature. A new
44
+ feature package needs its entry in that test, a `REQUIREMENTS` tuple, and a test folder.
45
+
46
+ ## Tests
47
+
48
+ - Tests mirror `src` (`tests/core`, `tests/microsoft/<feature>`, `tests/cli/commands`), one
49
+ test module per module, and `tests/project/test_layout.py` checks it.
50
+ - Nothing touches the network, a real `az` or a real clock. HTTP goes through
51
+ `tests/fakes/http.py` (`routes`, `fake_session`), `az` through `fakes/azcli.py`, time
52
+ through `fakes/clock.py`. Shared fakes live in `tests/fakes`, one module per concern.
53
+ - Coverage is gated at 93% (`fail_under` in `pyproject.toml`). Keep new code covered.
54
+ - CI runs with `GITHUB_ACTIONS` set, which makes Typer colour and wrap errors: compare error
55
+ text with `usage_error(result)`, never a raw substring of `result.output`.
56
+ - Windows caps an environment variable at 32,767 characters and pytest puts each test id in
57
+ one: give big parametrised values short `ids`.
58
+ - `tests/project/test_rebrand.py` renames a copy of the repository and runs its whole suite.
59
+
60
+ ## Names and branding
61
+
62
+ The project can be renamed (`just rebrand`). Never hard-code `ldo`, `LDO_` or the package
63
+ name in code: use `core/brand.py` (`brand.COMMAND`, `brand.env_var("X")`,
64
+ `brand.command("config init")`, `brand.docs("page")` for a link to a docs page).
65
+
66
+ ## Writing code
67
+
68
+ - Library code raises `LdoError` subclasses (`InputError`, `NotFoundError`,
69
+ `AmbiguousError`, `ApiError`, ...) with a `hint` saying what to do, and never exits. Only
70
+ the CLI turns errors into messages and exit codes (0 fine, 1 error, 2 usage, 3 needs
71
+ attention, 130 interrupted).
72
+ - Data goes to stdout; notes, warnings and progress to stderr, through `cli/render.py`
73
+ (`note`, `warn`, `error`, `emit`). In the `json` and `otlp` log formats those become log
74
+ records, so never write to stderr directly.
75
+ - Every data command takes `-o table|json|csv` and `-p PROFILE`; lists of names take
76
+ arguments, `-` for stdin and `-f FILE` with `--column` and `--sheet`.
77
+ - All HTTP goes through `core.http.ApiClient`, which applies `core/network.py` (the proxy
78
+ and `no_proxy`) and `core/trust.py` (the public roots, the OS store and `ca_bundle`), and
79
+ `az` runs with the same through `network.subprocess_env()`. Never call `requests` directly.
80
+ - Anything a person types that goes into a URL path or a query is validated first (names,
81
+ ids, KQL values). A token or secret is never printed (only with an explicit `--raw`), never
82
+ logged, and never accepted on the command line.
83
+ - Help text is rendered as markdown: no `<`, no `*`, and paragraphs reflow. A test checks.
84
+ - Do not hide findings from a scanner or linter by renaming things; fix the code, or say it
85
+ is a false positive and why.
86
+
87
+ ## Docs
88
+
89
+ - `README.md` is a short front page: the command table, install, quickstart and links.
90
+ Detail goes in `docs/`, one page per area. `CHANGELOG.md` gets an `Unreleased` entry for
91
+ every change a user would notice.
92
+ - `tests/project/test_docs.py` runs every `ldo` example in the README, `docs/` and this file
93
+ with `--help`, checks every `just` example is a recipe, and follows every link. Keep
94
+ examples real.
95
+ - UK English (colour, organisation, licence as a noun). Never use em or en dashes, in any
96
+ file: use commas, colons, brackets or a plain hyphen.
97
+
98
+ ## Commits and releases
99
+
100
+ - Do not commit, push, tag or release until the person asks. Use their own git identity.
101
+ - No AI attribution in commits or pull requests: no `Co-Authored-By` or "Generated with"
102
+ lines.
103
+ - A release is `just release` after the version is set in `pyproject.toml` and
104
+ `src/libre_devops_helpers/__init__.py` and `CHANGELOG.md` has its section; see
105
+ `docs/development.md`. Never move or reuse a released tag, and remember a PyPI version can
106
+ never be replaced.
107
+
108
+ ## Pitfalls already met
109
+
110
+ - `.gitignore` rules match anywhere unless anchored with `/`; ruff and hatchling honour
111
+ `.gitignore`, so an ignored folder is silently left out of linting and the wheel.
112
+ - The arm64 image is built under emulation: keep heavy work (such as compiling bytecode) in
113
+ a `FROM --platform=$BUILDPLATFORM` stage.
114
+ - The Azure CLI pins some dependencies exactly. A fix it holds back is forced in
115
+ `container/azure-cli/pyproject.toml` (see the comment there), then signed in with.
116
+ - Microsoft APIs return stale duplicates (devices share names), paginate with `nextLink`
117
+ or `@odata.nextLink`, and answer 403 for a missing scope and a suspended tenant alike: say
118
+ which in the hint.
@@ -0,0 +1,116 @@
1
+ # Instructions for AI coding assistants
2
+
3
+ This file is for Claude Code, GitHub Copilot, OpenAI Codex and any other assistant working
4
+ in this repository. It is the one to edit: `AGENTS.md` is generated from it (`just ai`),
5
+ `CLAUDE.md` imports it, and `.github/copilot-instructions.md` points here.
6
+
7
+ ## What this is
8
+
9
+ `ldo` (distribution `libre-devops-helpers`, import `libre_devops_helpers`): a fast, read-only
10
+ CLI and library for Microsoft (Entra ID, Defender XDR, Intune, Azure, Graph, PIM, Logic Apps,
11
+ Automation) and ServiceNow. Its users are people signing in as themselves, usually through
12
+ the Azure CLI; automation is second. Everything reads, apart from `ldo az use`, which switches
13
+ the Azure CLI's account. Never add a command that changes a tenant or an instance without
14
+ being asked.
15
+
16
+ ## Working here
17
+
18
+ - Run everything through `just` (or `uv run`): `just check` before calling anything done,
19
+ `just ci` for all of CI's checks, `just fmt` to format, `just run <args>` to try the CLI.
20
+ - Python 3.11 or later: nothing newer than 3.11 in the code (`just test-311` proves it).
21
+ - ruff, line length 100. Match the surrounding code's naming, comment density and idiom.
22
+ - Runtime dependencies are `requests` and `typer` only. Everything else is the standard
23
+ library. Do not add a runtime dependency without asking; test-only ones go in the `dev`
24
+ group.
25
+
26
+ ## Layout and layering
27
+
28
+ ```text
29
+ src/libre_devops_helpers/
30
+ core/ vendor-neutral: errors, config, brand, HTTP client, token store,
31
+ polling, inputs (CSV and Excel), logging, YAML writer
32
+ microsoft/ the shared Microsoft layer: clouds, credentials, tokens, profiles
33
+ microsoft/<feature>/ one package per API: entra, xdr, graph, azure, pim, automation ...
34
+ microsoft/devices/ the one composite, using entra, xdr and intune
35
+ servicenow/ the ServiceNow vendor, same shape
36
+ cli/ the ldo command: parses, calls a client, renders. No logic here.
37
+ ```
38
+
39
+ `tests/project/test_package_surface.py` enforces the layering: `core` imports nothing of
40
+ ours, a feature imports only `core` and its vendor layer, `cli` sits on top. Cross-cutting
41
+ code (auth, HTTP, input parsing) goes in `core` or the vendor layer, never in a feature. A new
42
+ feature package needs its entry in that test, a `REQUIREMENTS` tuple, and a test folder.
43
+
44
+ ## Tests
45
+
46
+ - Tests mirror `src` (`tests/core`, `tests/microsoft/<feature>`, `tests/cli/commands`), one
47
+ test module per module, and `tests/project/test_layout.py` checks it.
48
+ - Nothing touches the network, a real `az` or a real clock. HTTP goes through
49
+ `tests/fakes/http.py` (`routes`, `fake_session`), `az` through `fakes/azcli.py`, time
50
+ through `fakes/clock.py`. Shared fakes live in `tests/fakes`, one module per concern.
51
+ - Coverage is gated at 93% (`fail_under` in `pyproject.toml`). Keep new code covered.
52
+ - CI runs with `GITHUB_ACTIONS` set, which makes Typer colour and wrap errors: compare error
53
+ text with `usage_error(result)`, never a raw substring of `result.output`.
54
+ - Windows caps an environment variable at 32,767 characters and pytest puts each test id in
55
+ one: give big parametrised values short `ids`.
56
+ - `tests/project/test_rebrand.py` renames a copy of the repository and runs its whole suite.
57
+
58
+ ## Names and branding
59
+
60
+ The project can be renamed (`just rebrand`). Never hard-code `ldo`, `LDO_` or the package
61
+ name in code: use `core/brand.py` (`brand.COMMAND`, `brand.env_var("X")`,
62
+ `brand.command("config init")`, `brand.docs("page")` for a link to a docs page).
63
+
64
+ ## Writing code
65
+
66
+ - Library code raises `LdoError` subclasses (`InputError`, `NotFoundError`,
67
+ `AmbiguousError`, `ApiError`, ...) with a `hint` saying what to do, and never exits. Only
68
+ the CLI turns errors into messages and exit codes (0 fine, 1 error, 2 usage, 3 needs
69
+ attention, 130 interrupted).
70
+ - Data goes to stdout; notes, warnings and progress to stderr, through `cli/render.py`
71
+ (`note`, `warn`, `error`, `emit`). In the `json` and `otlp` log formats those become log
72
+ records, so never write to stderr directly.
73
+ - Every data command takes `-o table|json|csv` and `-p PROFILE`; lists of names take
74
+ arguments, `-` for stdin and `-f FILE` with `--column` and `--sheet`.
75
+ - All HTTP goes through `core.http.ApiClient`, which applies `core/network.py` (the proxy
76
+ and `no_proxy`) and `core/trust.py` (the public roots, the OS store and `ca_bundle`), and
77
+ `az` runs with the same through `network.subprocess_env()`. Never call `requests` directly.
78
+ - Anything a person types that goes into a URL path or a query is validated first (names,
79
+ ids, KQL values). A token or secret is never printed (only with an explicit `--raw`), never
80
+ logged, and never accepted on the command line.
81
+ - Help text is rendered as markdown: no `<`, no `*`, and paragraphs reflow. A test checks.
82
+ - Do not hide findings from a scanner or linter by renaming things; fix the code, or say it
83
+ is a false positive and why.
84
+
85
+ ## Docs
86
+
87
+ - `README.md` is a short front page: the command table, install, quickstart and links.
88
+ Detail goes in `docs/`, one page per area. `CHANGELOG.md` gets an `Unreleased` entry for
89
+ every change a user would notice.
90
+ - `tests/project/test_docs.py` runs every `ldo` example in the README, `docs/` and this file
91
+ with `--help`, checks every `just` example is a recipe, and follows every link. Keep
92
+ examples real.
93
+ - UK English (colour, organisation, licence as a noun). Never use em or en dashes, in any
94
+ file: use commas, colons, brackets or a plain hyphen.
95
+
96
+ ## Commits and releases
97
+
98
+ - Do not commit, push, tag or release until the person asks. Use their own git identity.
99
+ - No AI attribution in commits or pull requests: no `Co-Authored-By` or "Generated with"
100
+ lines.
101
+ - A release is `just release` after the version is set in `pyproject.toml` and
102
+ `src/libre_devops_helpers/__init__.py` and `CHANGELOG.md` has its section; see
103
+ `docs/development.md`. Never move or reuse a released tag, and remember a PyPI version can
104
+ never be replaced.
105
+
106
+ ## Pitfalls already met
107
+
108
+ - `.gitignore` rules match anywhere unless anchored with `/`; ruff and hatchling honour
109
+ `.gitignore`, so an ignored folder is silently left out of linting and the wheel.
110
+ - The arm64 image is built under emulation: keep heavy work (such as compiling bytecode) in
111
+ a `FROM --platform=$BUILDPLATFORM` stage.
112
+ - The Azure CLI pins some dependencies exactly. A fix it holds back is forced in
113
+ `container/azure-cli/pyproject.toml` (see the comment there), then signed in with.
114
+ - Microsoft APIs return stale duplicates (devices share names), paginate with `nextLink`
115
+ or `@odata.nextLink`, and answer 403 for a missing scope and a suspended tenant alike: say
116
+ which in the hint.
@@ -3,6 +3,42 @@
3
3
  All notable changes to libre-devops-helpers are recorded here. The project follows
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## 0.5.0
7
+
8
+ ### Added
9
+
10
+ - Corporate proxy support. Every HTTPS call, and every `az` `ldo` runs, follow one set of
11
+ rules: loopback and the metadata endpoint always direct, then `no_proxy` and `NO_PROXY`,
12
+ then `LDO_PROXY_ADDRESS`, the config file's `proxy`, `HTTPS_PROXY` / `HTTP_PROXY` /
13
+ `ALL_PROXY`, and the operating system's setting. An address without a scheme means
14
+ `http://`, as for cntlm and Px, which sign in to an NTLM or Kerberos proxy for you.
15
+ - The operating system's certificate store is trusted by default, with the public roots
16
+ and the config file's `ca_bundle`, in one bundle the Azure CLI is handed as
17
+ `REQUESTS_CA_BUNDLE` too, so both work behind a TLS-inspecting proxy whose root IT has
18
+ installed. `LDO_CA_BUNDLE`, `REQUESTS_CA_BUNDLE` or `CURL_CA_BUNDLE` names a bundle to use
19
+ exactly instead. No new dependency.
20
+ - `ldo network test`: asks Entra ID, Graph, Azure Resource Manager, Defender and each
21
+ ServiceNow instance (and any `--url`) for an unsigned answer, and says which proxy each
22
+ call used, which certificates are trusted, and what to try when one fails: a proxy's
23
+ NTLM sign-in (cntlm or Px), a proxy that is not running, a TLS-inspecting proxy's
24
+ certificate (with its issuer named), or a local proxy listening on 3128 or 3129.
25
+ - `AI.md`: instructions for AI coding assistants, read by Claude Code (through `CLAUDE.md`),
26
+ GitHub Copilot (`.github/copilot-instructions.md`, and `AGENTS.md` for its coding agent)
27
+ and Codex (`AGENTS.md`). `AGENTS.md` is written from it by `just ai`, and a test fails
28
+ when it falls behind.
29
+
30
+ ### Changed
31
+
32
+ - The README installs from PyPI (`uv tool install`, `pipx`, `uv pip` or `pip`), with a
33
+ PyPI badge; installing a tag from GitHub is still shown.
34
+
35
+ ### Fixed
36
+
37
+ - `ca_bundle` replaced the public roots rather than adding to them, so a host a proxy does
38
+ not inspect (sign-in often is not) failed to verify, and it never reached the Azure CLI.
39
+ It now adds to the public roots and the OS store, for both.
40
+
41
+
6
42
  ## 0.4.1
7
43
 
8
44
  ### Added
@@ -0,0 +1 @@
1
+ @AI.md
@@ -5,6 +5,10 @@ command or improving the documentation.
5
5
 
6
6
  ## Workflow
7
7
 
8
+ Working with an AI coding assistant? Its instructions are in [AI.md](AI.md), which Claude
9
+ Code, GitHub Copilot and Codex all read (through `CLAUDE.md`, `.github/copilot-instructions.md`
10
+ and `AGENTS.md`). Edit `AI.md` and run `just ai`; a test fails when `AGENTS.md` lags behind.
11
+
8
12
  1. Fork the repository and branch from `main`.
9
13
  2. Run `just sync` (or `uv sync`) to create the environment.
10
14
  3. Make your change, with tests.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: libre-devops-helpers
3
- Version: 0.4.1
3
+ Version: 0.5.0
4
4
  Summary: Libre DevOps Helpers: a fast, read-only CLI and importable helpers for Entra ID, Defender XDR, Intune, Azure, Graph, PIM, Logic Apps and ServiceNow, also shipped as a container image.
5
5
  Project-URL: Homepage, https://github.com/libre-devops/python-helpers
6
6
  Project-URL: Documentation, https://github.com/libre-devops/python-helpers/tree/main/docs
@@ -49,6 +49,7 @@ Description-Content-Type: text/markdown
49
49
  [![Container](https://github.com/libre-devops/python-helpers/actions/workflows/container.yml/badge.svg)](https://github.com/libre-devops/python-helpers/actions/workflows/container.yml)
50
50
 
51
51
  [![Release](https://img.shields.io/github/v/release/libre-devops/python-helpers?label=release&color=1793D1)](https://github.com/libre-devops/python-helpers/releases)
52
+ [![PyPI](https://img.shields.io/pypi/v/libre-devops-helpers?label=pypi&logo=pypi&logoColor=white&color=3775A9)](https://pypi.org/project/libre-devops-helpers/)
52
53
  [![Container images](https://img.shields.io/badge/ghcr.io-python--helpers-2496ED?logo=docker&logoColor=white)](https://github.com/libre-devops/python-helpers/pkgs/container/python-helpers)
53
54
  [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-3776AB?logo=python&logoColor=white)](https://github.com/libre-devops/python-helpers/blob/main/pyproject.toml)
54
55
  [![Licence: MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](https://github.com/libre-devops/python-helpers/blob/main/LICENSE)
@@ -83,6 +84,7 @@ PowerShell module, and importable as a library too.
83
84
  | `ldo logicapp` | offline checks, export and validation for Consumption Logic Apps and Sentinel playbooks | [logic apps](https://github.com/libre-devops/python-helpers/blob/main/docs/logic-apps.md) |
84
85
  | `ldo snow` | ServiceNow: sign in, whoami, the instance, applications, a token | [servicenow](https://github.com/libre-devops/python-helpers/blob/main/docs/servicenow.md) |
85
86
  | `ldo az` | switch the Azure CLI between profiles | [signing in](https://github.com/libre-devops/python-helpers/blob/main/docs/authentication.md) |
87
+ | `ldo network test` | test the way out through a corporate proxy: the proxy, the certificates, each service | [network](https://github.com/libre-devops/python-helpers/blob/main/docs/network.md) |
86
88
  | `ldo json` | pretty-print any JSON (`az rest ... \| ldo json`) in colour, or as YAML | [configuration](https://github.com/libre-devops/python-helpers/blob/main/docs/configuration.md#json-yaml-and-logs) |
87
89
  | `ldo profiles`, `ldo config` | your profiles, and the config file | [configuration](https://github.com/libre-devops/python-helpers/blob/main/docs/configuration.md) |
88
90
 
@@ -93,10 +95,20 @@ arguments, stdin, a text file, or a column of a CSV or Excel workbook.
93
95
 
94
96
  ## Install
95
97
 
98
+ From [PyPI](https://pypi.org/project/libre-devops-helpers/):
99
+
96
100
  ```bash
97
- uv tool install git+https://github.com/libre-devops/python-helpers@v0.4.1
101
+ uv tool install libre-devops-helpers # the ldo command, in an environment of its own
102
+ pipx install libre-devops-helpers # the same, with pipx
103
+ uv pip install libre-devops-helpers # into the current environment, to use it as a library
104
+ pip install libre-devops-helpers # the same, with pip
105
+ uv tool upgrade libre-devops-helpers # later, to the newest release
98
106
  ```
99
107
 
108
+ Add the `keychain` extra (`"libre-devops-helpers[keychain]"`) to keep sign-ins in the macOS
109
+ Keychain or the Linux Secret Service. A tagged release installs straight from GitHub too:
110
+ `uv tool install git+https://github.com/libre-devops/python-helpers@v0.5.0`.
111
+
100
112
  Or run the container image, which has the Azure CLI inside:
101
113
  `podman run --rm -it ghcr.io/libre-devops/python-helpers:latest --help`
102
114
  (see [Container images](https://github.com/libre-devops/python-helpers/blob/main/docs/containers.md)).
@@ -135,10 +147,12 @@ for those. [Permissions](https://github.com/libre-devops/python-helpers/blob/mai
135
147
  ## Documentation
136
148
 
137
149
  - [Configuration](https://github.com/libre-devops/python-helpers/blob/main/docs/configuration.md): profiles, common options, environment variables, exit codes
150
+ - [Proxies and certificates](https://github.com/libre-devops/python-helpers/blob/main/docs/network.md): corporate proxies, cntlm, TLS inspection
138
151
  - [Signing in](https://github.com/libre-devops/python-helpers/blob/main/docs/authentication.md) and [Permissions](https://github.com/libre-devops/python-helpers/blob/main/docs/permissions.md)
139
152
  - [Container images](https://github.com/libre-devops/python-helpers/blob/main/docs/containers.md)
140
153
  - [Using it as a library](https://github.com/libre-devops/python-helpers/blob/main/docs/library.md) and [Rebranding](https://github.com/libre-devops/python-helpers/blob/main/docs/rebranding.md) for your organisation
141
154
  - [Development](https://github.com/libre-devops/python-helpers/blob/main/docs/development.md): `just` recipes, tests, CI and releasing
155
+ - [AI.md](https://github.com/libre-devops/python-helpers/blob/main/AI.md): the instructions for AI coding assistants (Claude Code, Copilot, Codex)
142
156
 
143
157
  Contributions are welcome: see [CONTRIBUTING.md](https://github.com/libre-devops/python-helpers/blob/main/CONTRIBUTING.md), and
144
158
  [SECURITY.md](https://github.com/libre-devops/python-helpers/blob/main/SECURITY.md) to report a vulnerability. Licensed under [MIT](https://github.com/libre-devops/python-helpers/blob/main/LICENSE).
@@ -17,6 +17,7 @@
17
17
  [![Container](https://github.com/libre-devops/python-helpers/actions/workflows/container.yml/badge.svg)](https://github.com/libre-devops/python-helpers/actions/workflows/container.yml)
18
18
 
19
19
  [![Release](https://img.shields.io/github/v/release/libre-devops/python-helpers?label=release&color=1793D1)](https://github.com/libre-devops/python-helpers/releases)
20
+ [![PyPI](https://img.shields.io/pypi/v/libre-devops-helpers?label=pypi&logo=pypi&logoColor=white&color=3775A9)](https://pypi.org/project/libre-devops-helpers/)
20
21
  [![Container images](https://img.shields.io/badge/ghcr.io-python--helpers-2496ED?logo=docker&logoColor=white)](https://github.com/libre-devops/python-helpers/pkgs/container/python-helpers)
21
22
  [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-3776AB?logo=python&logoColor=white)](pyproject.toml)
22
23
  [![Licence: MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](LICENSE)
@@ -51,6 +52,7 @@ PowerShell module, and importable as a library too.
51
52
  | `ldo logicapp` | offline checks, export and validation for Consumption Logic Apps and Sentinel playbooks | [logic apps](docs/logic-apps.md) |
52
53
  | `ldo snow` | ServiceNow: sign in, whoami, the instance, applications, a token | [servicenow](docs/servicenow.md) |
53
54
  | `ldo az` | switch the Azure CLI between profiles | [signing in](docs/authentication.md) |
55
+ | `ldo network test` | test the way out through a corporate proxy: the proxy, the certificates, each service | [network](docs/network.md) |
54
56
  | `ldo json` | pretty-print any JSON (`az rest ... \| ldo json`) in colour, or as YAML | [configuration](docs/configuration.md#json-yaml-and-logs) |
55
57
  | `ldo profiles`, `ldo config` | your profiles, and the config file | [configuration](docs/configuration.md) |
56
58
 
@@ -61,10 +63,20 @@ arguments, stdin, a text file, or a column of a CSV or Excel workbook.
61
63
 
62
64
  ## Install
63
65
 
66
+ From [PyPI](https://pypi.org/project/libre-devops-helpers/):
67
+
64
68
  ```bash
65
- uv tool install git+https://github.com/libre-devops/python-helpers@v0.4.1
69
+ uv tool install libre-devops-helpers # the ldo command, in an environment of its own
70
+ pipx install libre-devops-helpers # the same, with pipx
71
+ uv pip install libre-devops-helpers # into the current environment, to use it as a library
72
+ pip install libre-devops-helpers # the same, with pip
73
+ uv tool upgrade libre-devops-helpers # later, to the newest release
66
74
  ```
67
75
 
76
+ Add the `keychain` extra (`"libre-devops-helpers[keychain]"`) to keep sign-ins in the macOS
77
+ Keychain or the Linux Secret Service. A tagged release installs straight from GitHub too:
78
+ `uv tool install git+https://github.com/libre-devops/python-helpers@v0.5.0`.
79
+
68
80
  Or run the container image, which has the Azure CLI inside:
69
81
  `podman run --rm -it ghcr.io/libre-devops/python-helpers:latest --help`
70
82
  (see [Container images](docs/containers.md)).
@@ -103,10 +115,12 @@ for those. [Permissions](docs/permissions.md) lists what each command needs.
103
115
  ## Documentation
104
116
 
105
117
  - [Configuration](docs/configuration.md): profiles, common options, environment variables, exit codes
118
+ - [Proxies and certificates](docs/network.md): corporate proxies, cntlm, TLS inspection
106
119
  - [Signing in](docs/authentication.md) and [Permissions](docs/permissions.md)
107
120
  - [Container images](docs/containers.md)
108
121
  - [Using it as a library](docs/library.md) and [Rebranding](docs/rebranding.md) for your organisation
109
122
  - [Development](docs/development.md): `just` recipes, tests, CI and releasing
123
+ - [AI.md](AI.md): the instructions for AI coding assistants (Claude Code, Copilot, Codex)
110
124
 
111
125
  Contributions are welcome: see [CONTRIBUTING.md](CONTRIBUTING.md), and
112
126
  [SECURITY.md](SECURITY.md) to report a vulnerability. Licensed under [MIT](LICENSE).
@@ -9,6 +9,7 @@
9
9
  | [Configuration](configuration.md) | the config file, profiles, options every command takes, environment variables, exit codes |
10
10
  | [Signing in](authentication.md) | the ways to sign in, automation, when a sign-in lapses, keeping a sign-in, your own app registration |
11
11
  | [Permissions](permissions.md) | what each command needs, and what the Azure CLI's token covers |
12
+ | [Proxies and certificates](network.md) | corporate proxies, cntlm and Px, TLS inspection, `network test` |
12
13
  | [Container images](containers.md) | the images, their tags, running and building them |
13
14
 
14
15
  ## Commands
@@ -41,7 +41,7 @@ permissions:
41
41
  id-token: write
42
42
  contents: read
43
43
  steps:
44
- - run: uv tool install git+https://github.com/libre-devops/python-helpers@v0.4.1
44
+ - run: uv tool install libre-devops-helpers==0.5.0
45
45
  - run: ldo --config .github/ldo.toml entra app-credentials -p ci --expiring 30d
46
46
  ```
47
47
 
@@ -87,7 +87,7 @@ other accounts can read is refused, as ssh refuses a readable key. `keychain` on
87
87
  Linux needs the `keychain` extra:
88
88
 
89
89
  ```bash
90
- uv tool install "libre-devops-helpers[keychain] @ git+https://github.com/libre-devops/python-helpers@v0.4.1"
90
+ uv tool install "libre-devops-helpers[keychain]"
91
91
  ```
92
92
 
93
93
  ## Your own app registration
@@ -15,7 +15,8 @@ or `LDO_CONFIG` points. It is created readable only by you, has a section per ve
15
15
  never holds a secret. Unknown keys are rejected, so a typo fails loudly.
16
16
 
17
17
  ```toml
18
- # ca_bundle = "~/certs/proxy-ca.pem" # a CA for every HTTPS call, behind a TLS-inspecting proxy
18
+ # proxy = "127.0.0.1:3128" # behind a corporate proxy, e.g. cntlm (see network.md)
19
+ # ca_bundle = "~/certs/proxy-ca.pem" # a TLS-inspecting proxy's root, when not in the OS store
19
20
 
20
21
  [microsoft]
21
22
  default_profile = "prod-tenant"
@@ -49,7 +50,9 @@ A profile is a tenant, optionally pinned to a subscription, with one way of gett
49
50
  | `workspace_id` | The Log Analytics workspace for `logs query` without `--workspace`. |
50
51
  | `mde_url` | A regional Defender for Endpoint endpoint, e.g. `https://api-eu.securitycenter.microsoft.com`. |
51
52
 
52
- ServiceNow profiles are described in [ServiceNow](servicenow.md#configuration).
53
+ ServiceNow profiles are described in [ServiceNow](servicenow.md#configuration). The
54
+ top-level `proxy`, `no_proxy` and `ca_bundle` apply to every call, and to the Azure CLI; see
55
+ [Proxies and certificates](network.md).
53
56
 
54
57
  ## Options every command takes
55
58
 
@@ -165,6 +168,9 @@ service:
165
168
  | `LDO_SERVICE_NAME`, `LDO_TRACE_ID` and the rest | What OTLP logs say about themselves; see [OpenTelemetry logs](#opentelemetry-logs). |
166
169
  | `LDO_REAUTH` | When the Azure CLI's sign-in lapses on a terminal: unset asks to sign in again in a browser, `device-code` asks and uses a device code, `off` never asks. |
167
170
  | `LDO_TOKEN_CACHE` | Another file for kept sign-ins, e.g. a container volume. |
171
+ | `LDO_PROXY_ADDRESS` | The proxy for `ldo` (and the Azure CLI it runs), winning over `proxy` and `HTTPS_PROXY`, e.g. `127.0.0.1:3129` for cntlm. |
172
+ | `HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`, `NO_PROXY` | As every tool reads them. See [Proxies and certificates](network.md). |
173
+ | `LDO_CA_BUNDLE`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE` | A CA bundle to use exactly as it is, in place of the public roots with the OS store. |
168
174
  | `LDO_NO_BANNER`, `NO_COLOR` | No banner; no colour. The banner only ever shows on a terminal. |
169
175
  | `AZURE_CLIENT_SECRET`, `AZURE_FEDERATED_TOKEN_FILE` | For `client-secret` and `workload-identity` profiles, as the Azure SDKs use them. |
170
176
  | `SNOW_INSTANCE_URL`, `SNOW_CLIENT_ID`, `SNOW_CLIENT_SECRET`, `SNOW_INSTANCE_USERNAME`, `SNOW_INSTANCE_PASSWORD` | ServiceNow, with or without a config file. |
@@ -15,12 +15,12 @@ locked in `container/azure-cli`, and no pip, so `az extension add` does not work
15
15
 
16
16
  | Tag (`-slim` for slim) | Moves? | Meaning |
17
17
  | --- | --- | --- |
18
- | `0.4.1` | to patched rebuilds | that release |
19
- | `0.4` | yes | the newest 0.4.x |
18
+ | `0.5.0` | to patched rebuilds | that release |
19
+ | `0.5` | yes | the newest 0.5.x |
20
20
  | `latest`, `slim` | yes | the newest release |
21
- | `0.4.1-20260928.57` | never | one build: its date and workflow run |
21
+ | `0.5.0-20260928.57` | never | one build: its date and workflow run |
22
22
 
23
- Pin a stamped tag or a digest for a build that never changes, or `0.4` to take patches as
23
+ Pin a stamped tag or a digest for a build that never changes, or `0.5` to take patches as
24
24
  they land. Each image carries a build provenance attestation and an SBOM:
25
25
 
26
26
  ```bash
@@ -47,6 +47,9 @@ ldo devices check -f plan.xlsx --column FQDN
47
47
  With docker, use `--user "$(id -u):$(id -g)"` in place of `--userns`. The `z` option relabels
48
48
  mounts for SELinux and does nothing elsewhere.
49
49
 
50
+ Behind a corporate proxy, pass it in with `-e LDO_PROXY_ADDRESS=...` (a cntlm on the host is
51
+ at `host.containers.internal`); see [Proxies and certificates](network.md#in-a-container).
52
+
50
53
  `slim` has no Azure CLI. Use it with a `device-code` profile (and a volume at
51
54
  `/home/ldo/.local/state/ldo` to keep the sign-in), or for automation with a
52
55
  `workload-identity` profile and the job's federated token passed through.
@@ -15,6 +15,7 @@ just run devices check web01 # run ldo from the working tree
15
15
  just install # put the working tree's ldo on PATH
16
16
  just lock # relock the tool and the image's Azure CLI
17
17
  just secrets # the secret scan, uncommitted changes included (needs gitleaks)
18
+ just ai # write AGENTS.md from AI.md, after editing AI.md
18
19
  ```
19
20
 
20
21
  Run `just` for the full list. See [CONTRIBUTING.md](../CONTRIBUTING.md) before opening a pull
@@ -44,8 +45,9 @@ tab; the full scan is kept with each run.
44
45
  ## Releasing
45
46
 
46
47
  1. Set the version in `pyproject.toml` and `src/libre_devops_helpers/__init__.py` (a test
47
- keeps them equal), run `just lock`, and turn `## Unreleased` in `CHANGELOG.md` into
48
- `## <version>`.
48
+ keeps them equal), run `just lock`, turn `## Unreleased` in `CHANGELOG.md` into
49
+ `## <version>`, and move the pinned install lines in the docs to it (a test names any
50
+ left behind).
49
51
  2. Merge to `main` and let CI pass.
50
52
  3. `just release`: it checks the tree is clean and in step with `origin/main`, that the
51
53
  changelog has the version and the tag is new, then tags and pushes.
@@ -0,0 +1,106 @@
1
+ # Proxies and certificates
2
+
3
+ [Back to the docs](README.md)
4
+
5
+ Behind a corporate proxy, `ldo` works with the usual settings, and every HTTPS call it makes
6
+ goes the same way as the Azure CLI it runs, so both behave alike.
7
+
8
+ ```bash
9
+ ldo network test # the proxy, the certificates, each service
10
+ ldo network test --url https://intranet.corp.example/health
11
+ LDO_PROXY_ADDRESS=127.0.0.1:3129 ldo network test # try a proxy before setting it anywhere
12
+ ```
13
+
14
+ `network test` asks Entra ID, Graph, Azure Resource Manager and Defender (in the profile's
15
+ cloud), and each ServiceNow instance configured, for something they answer without a
16
+ sign-in, and expects a 2xx (Defender, which has nothing to show without a token, answers
17
+ 401). It shows the proxy each call went through, which certificates are trusted, and when a
18
+ call fails, what to try. It exits 3 when any fails.
19
+
20
+ ## The proxy
21
+
22
+ Each call takes the first of these that applies:
23
+
24
+ | | Setting | For |
25
+ | --- | --- | --- |
26
+ | 1 | this machine, and `169.254.169.254` | never a proxy: sign-in redirects to localhost, managed identity |
27
+ | 2 | `no_proxy` in the config file, and `NO_PROXY` | hosts that go direct, e.g. `.corp.example,10.0.0.0/8` |
28
+ | 3 | `LDO_PROXY_ADDRESS` | `ldo` alone, without changing `HTTPS_PROXY` for everything else |
29
+ | 4 | `proxy` in the config file | the same, kept in the file |
30
+ | 5 | `HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY` | the usual variables, which most tools read |
31
+ | 6 | the operating system's proxy setting | Windows and macOS |
32
+
33
+ An address without a scheme, such as `127.0.0.1:3128`, means `http://`. Whichever proxy
34
+ applies, the Azure CLI gets it too, as `HTTPS_PROXY` and `NO_PROXY`.
35
+
36
+ ```toml
37
+ proxy = "127.0.0.1:3128"
38
+ no_proxy = ".corp.example, 10.0.0.0/8"
39
+ ```
40
+
41
+ ### A proxy that wants NTLM or Kerberos
42
+
43
+ Corporate proxies often want a sign-in of their own, which a 407 from `network test` shows.
44
+ Neither `ldo` nor the Azure CLI speaks NTLM, so run a small local proxy that does, and point
45
+ `ldo` at it:
46
+
47
+ - [cntlm](https://cntlm.sourceforge.net/): set `Username`, `Domain` and `Proxy` in
48
+ `cntlm.conf`, then `cntlm -H` for the password hashes and `cntlm -I -M
49
+ https://graph.microsoft.com` to test them. It listens on `127.0.0.1:3128` unless its
50
+ `Listen` line says otherwise.
51
+ - [Px](https://github.com/genotrance/px): the same on Windows, signing in as you with no
52
+ password stored.
53
+
54
+ ```bash
55
+ export LDO_PROXY_ADDRESS=127.0.0.1:3129 # wherever cntlm or Px listens
56
+ ldo network test
57
+ ```
58
+
59
+ When nothing is set and a call cannot get out, `network test` looks for something listening
60
+ on 3128 or 3129 and suggests it. A 407 from a local proxy means it could not sign in
61
+ upstream: check its credentials.
62
+
63
+ Automatic proxy scripts (PAC and WPAD files) are not read: name the proxy instead.
64
+
65
+ ## Certificates
66
+
67
+ A TLS-inspecting proxy re-signs every site with its own certificate. By default `ldo` trusts
68
+ three sets together, so it works where IT has installed that certificate on the machine:
69
+
70
+ - the public roots,
71
+ - the operating system's store: the Windows certificate store, the macOS system keychains,
72
+ or the Linux system bundle,
73
+ - and any the config file's `ca_bundle` adds.
74
+
75
+ They are combined into one file in your cache folder (`~/.cache/ldo`, or
76
+ `%LOCALAPPDATA%\ldo\cache`), which the Azure CLI is handed as `REQUESTS_CA_BUNDLE`, so both
77
+ trust the same. The OS store only ever adds trust: public certificates still verify.
78
+
79
+ ```toml
80
+ ca_bundle = "~/certs/corp-root.pem" # the proxy's root, when it is not in the OS store
81
+ ```
82
+
83
+ To use one bundle exactly as it is instead, nothing added, name it with `LDO_CA_BUNDLE`
84
+ (or the standard `REQUESTS_CA_BUNDLE` or `CURL_CA_BUNDLE`):
85
+
86
+ ```bash
87
+ export LDO_CA_BUNDLE=/etc/corp/ca-bundle.pem
88
+ ```
89
+
90
+ When a certificate does not verify, `network test` names who issued it: a proxy's own name
91
+ (Zscaler, Netskope, a company CA) shows it is inspecting traffic.
92
+
93
+ ## In a container
94
+
95
+ The container's `127.0.0.1` is the container itself, so reach a cntlm on the host through
96
+ the host's name, and make cntlm listen where the container can reach it (`Listen 0.0.0.0:3129`,
97
+ with `Allow` set to the container network):
98
+
99
+ ```bash
100
+ podman run --rm -it -e LDO_PROXY_ADDRESS=host.containers.internal:3129 \
101
+ ghcr.io/libre-devops/python-helpers:latest network test
102
+ ```
103
+
104
+ With docker, use `host.docker.internal` (on Linux, add `--add-host
105
+ host.docker.internal:host-gateway`). The image's own store is Debian's: mount a corporate
106
+ root and set `LDO_CA_BUNDLE`, or `ca_bundle` in a mounted config, when the proxy inspects TLS.
@@ -24,6 +24,11 @@ lock:
24
24
  uv lock
25
25
  uv lock --project container/azure-cli
26
26
 
27
+ # Write AGENTS.md from AI.md, the instructions every AI coding assistant reads
28
+ [group('setup')]
29
+ ai:
30
+ uv run --no-sync python scripts/ai_instructions.py
31
+
27
32
  # Run ldo from the working tree, e.g. just run devices check web01
28
33
  [group('run')]
29
34
  run *args: