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.
- libre_devops_helpers-0.5.0/.github/copilot-instructions.md +14 -0
- libre_devops_helpers-0.5.0/AGENTS.md +118 -0
- libre_devops_helpers-0.5.0/AI.md +116 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/CHANGELOG.md +36 -0
- libre_devops_helpers-0.5.0/CLAUDE.md +1 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/CONTRIBUTING.md +4 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/PKG-INFO +16 -2
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/README.md +15 -1
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/README.md +1 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/authentication.md +2 -2
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/configuration.md +8 -2
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/containers.md +7 -4
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/development.md +4 -2
- libre_devops_helpers-0.5.0/docs/network.md +106 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/justfile +5 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/pyproject.toml +1 -1
- libre_devops_helpers-0.5.0/scripts/ai_instructions.py +46 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/__init__.py +1 -1
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/app.py +3 -0
- libre_devops_helpers-0.5.0/src/libre_devops_helpers/cli/commands/network.py +220 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/runtime.py +16 -8
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/config.py +24 -5
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/http.py +9 -2
- libre_devops_helpers-0.5.0/src/libre_devops_helpers/core/network.py +454 -0
- libre_devops_helpers-0.5.0/src/libre_devops_helpers/core/trust.py +275 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/process.py +11 -0
- libre_devops_helpers-0.5.0/tests/cli/commands/test_network.py +105 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_runtime.py +24 -1
- libre_devops_helpers-0.5.0/tests/conftest.py +47 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_config.py +25 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_http.py +28 -0
- libre_devops_helpers-0.5.0/tests/core/test_network.py +236 -0
- libre_devops_helpers-0.5.0/tests/core/test_trust.py +136 -0
- libre_devops_helpers-0.5.0/tests/fakes/certificates.py +24 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/http.py +3 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_process.py +16 -0
- libre_devops_helpers-0.5.0/tests/project/test_ai_instructions.py +35 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_docs.py +10 -2
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/uv.lock +1 -1
- libre_devops_helpers-0.4.1/tests/conftest.py +0 -22
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.dockerignore +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/dependabot.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/pull_request_template.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/badges.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/ci.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/codeql.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/container.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/dependency-review.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.github/workflows/release.yml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.gitignore +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/.gitleaks.toml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/Containerfile +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/LICENSE +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/SECURITY.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/brand.toml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/container/azure-cli/pyproject.toml +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/container/azure-cli/uv.lock +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/azure.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/defender.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/devices.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/entra.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/graph.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/library.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/logic-apps.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/permissions.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/pim.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/rebranding.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/docs/servicenow.md +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/scripts/image_tags.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/scripts/rebrand.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/__main__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/automation.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/az.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/azure.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/config.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/devices.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/entra.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/graph.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/incidents.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/intune.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/keyvault.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/logicapp.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/logs.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/pim.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/pretty.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/profiles.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/snow.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/token.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/welcome.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/commands/xdr.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/exits.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/options.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/render.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/cli/servicenow_runtime.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/auth.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/brand.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/browser.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/dpapi.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/errors.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/inputs.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/log.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/poll.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/process.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/sheets.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/tables.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/timewindow.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/token_store.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/util.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/core/yaml_text.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/azure_cli.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/delegated.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/entra.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/factory.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/lapse.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/auth/managed_identity.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/automation/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/automation/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/automation/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azcli/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azcli/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azcli/context.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azure/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azure/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/azure/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/clouds.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/config.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/antivirus.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/check.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/inspect.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/devices/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/entra/permissions.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/graph/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/graph/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/incidents/permissions.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/intune/permissions.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/keyvault/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/keyvault/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/loganalytics/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/loganalytics/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/checks.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/logicapps/document.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/azure.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/entra.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/permissions.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/pim/rules.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/resources.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/tokens.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/microsoft/xdr/permissions.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/py.typed +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/auth.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/config.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/instance/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/instance/client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/instance/models.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/roles.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/src/libre_devops_helpers/servicenow/tables.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_automation.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_az.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_azure.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_config.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_devices.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_entra.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_graph.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_incidents.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_intune.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_keyvault.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_logicapp.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_logs.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_pim.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_pretty.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_profiles.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_snow.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_token.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_welcome.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/commands/test_xdr.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/conftest.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_app.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_options.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_render.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/cli/test_servicenow_runtime.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_auth.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_browser.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_dpapi.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_inputs.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_log.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_poll.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_process.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_sheets.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_timewindow.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_token_store.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_util.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/core/test_yaml_text.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/__init__.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/automation.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/azcli.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/clock.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/devices.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/ids.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/incidents.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/logicapps.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/pim.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/process.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/servicenow.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/tenant.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/tokens.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/fakes/workbooks.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_azure_cli.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_delegated.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_entra.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_factory.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_lapse.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/auth/test_managed_identity.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/automation/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/azcli/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/azcli/test_context.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/azure/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/devices/test_antivirus.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/devices/test_check.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/devices/test_inspect.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/entra/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/graph/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/incidents/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/intune/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/keyvault/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/loganalytics/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/logicapps/test_checks.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/logicapps/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/logicapps/test_document.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/pim/test_azure.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/pim/test_entra.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/pim/test_rules.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_clouds.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_config.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/test_tokens.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/microsoft/xdr/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_containerfile.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_image_tags.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_layout.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_package_surface.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/project/test_rebrand.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/instance/test_client.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_auth.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_config.py +0 -0
- {libre_devops_helpers-0.4.1 → libre_devops_helpers-0.5.0}/tests/servicenow/test_roles.py +0 -0
- {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.
|
|
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
|
[](https://github.com/libre-devops/python-helpers/actions/workflows/container.yml)
|
|
50
50
|
|
|
51
51
|
[](https://github.com/libre-devops/python-helpers/releases)
|
|
52
|
+
[](https://pypi.org/project/libre-devops-helpers/)
|
|
52
53
|
[](https://github.com/libre-devops/python-helpers/pkgs/container/python-helpers)
|
|
53
54
|
[](https://github.com/libre-devops/python-helpers/blob/main/pyproject.toml)
|
|
54
55
|
[](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
|
|
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
|
[](https://github.com/libre-devops/python-helpers/actions/workflows/container.yml)
|
|
18
18
|
|
|
19
19
|
[](https://github.com/libre-devops/python-helpers/releases)
|
|
20
|
+
[](https://pypi.org/project/libre-devops-helpers/)
|
|
20
21
|
[](https://github.com/libre-devops/python-helpers/pkgs/container/python-helpers)
|
|
21
22
|
[](pyproject.toml)
|
|
22
23
|
[](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
|
|
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
|
|
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]
|
|
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
|
-
#
|
|
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.
|
|
19
|
-
| `0.
|
|
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.
|
|
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.
|
|
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`,
|
|
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:
|