mirror of
https://github.com/ansible/ansible.git
synced 2026-08-02 19:51:59 +02:00
Deconstruct AGENTS.md to context dir (#87162)
* Deconstruct AGENTS.md to context dir * Split guidelines applicable to both humans and agents to top-level `context` dir. * Instruct agents to read all context files for now- piecemeal/just-in-time context ingestion has been spotty and problematic to date. Co-authored-by: Matt Clay <matt@mystile.com> Assisted-by: Claude * update package-data sanity to ignore context * markdown line length sanity * Ignore context dir in change detection * Remove core plugin deprecation instructions * Inconsistent with published docs and docsite behavior- resolve those first. --------- Co-authored-by: Matt Clay <matt@mystile.com>
This commit is contained in:
@@ -9,33 +9,30 @@ This file provides guidance to Claude Code (claude.ai/code) and other compatible
|
||||
**BEFORE starting any PR review or development task:**
|
||||
|
||||
1. **Read this file first** - Don't work from memory or assumptions
|
||||
2. **Use TodoWrite** to create a task list and track progress systematically
|
||||
3. **Follow the numbered steps** in the relevant process sections
|
||||
4. **Reference Quick Reference** for correct commands and patterns
|
||||
2. **Read all [context](context) files** for the project's coding conventions and policies
|
||||
3. **Use TodoWrite** to create a task list and track progress systematically
|
||||
4. **Follow the numbered steps** in the relevant process sections
|
||||
5. **Reference Quick Reference** for correct commands and patterns
|
||||
|
||||
## ⚠️ CRITICAL: Licensing Requirements
|
||||
|
||||
**NEVER suggest, recommend, or approve code that violates these requirements:**
|
||||
**NEVER suggest, recommend, or approve code that violates the project's [licensing requirements](context/licensing.md).**
|
||||
Always verify any new dependencies or suggested libraries are license-compatible.
|
||||
This is non-negotiable -- licensing violations can create serious legal issues for the project.
|
||||
|
||||
- **ansible-core**: All code must be **GPLv3 compatible**
|
||||
- **lib/ansible/module_utils/**: Defaults to **BSD-2-Clause** (more permissive)
|
||||
- **External dependencies**: Only recommend libraries compatible with these licenses
|
||||
- **PR reviews**: Always verify any new dependencies or suggested libraries are license-compatible
|
||||
- **When in doubt**: Ask about licensing compatibility rather than assuming
|
||||
## General Principles
|
||||
|
||||
**This is non-negotiable** - licensing violations can create serious legal issues for the project.
|
||||
When reviewing code, don't flag issues that `ansible-test sanity` already catches.
|
||||
Focus review effort on things automated checks can't verify.
|
||||
|
||||
## Attribution
|
||||
|
||||
Agents should disclose their involvement in contributions.
|
||||
We recommend using an `Assisted-by:` commit trailer identifying the AI tool that assisted.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
Most commonly used commands and patterns:
|
||||
|
||||
```bash
|
||||
# Testing
|
||||
ansible-test sanity -v --docker default # Run all sanity tests
|
||||
ansible-test sanity -v --docker default --test <test> # Run specific sanity test
|
||||
ansible-test units -v --docker default # Run unit tests
|
||||
ansible-test integration -v --docker ubuntu2404 # Run integration tests
|
||||
|
||||
# PR Review and CI
|
||||
gh pr view <number> # Get PR details
|
||||
gh pr view <number> --comments # Check for ansibot CI failures
|
||||
@@ -45,72 +42,12 @@ gh pr diff <number> # See all changes
|
||||
/azp-logs <number> # Download CI logs for PR
|
||||
```
|
||||
|
||||
**Container Selection:**
|
||||
|
||||
- Sanity/Unit tests: `--docker default`
|
||||
- Integration tests: `--docker ubuntu2204`, `--docker ubuntu2404`, etc. (NOT default/base)
|
||||
|
||||
**Critical Reminders:**
|
||||
|
||||
- **Licensing**: See [Licensing Requirements](#️-critical-licensing-requirements) - GPLv3/BSD-2-Clause only
|
||||
- **Licensing**: See [Licensing Requirements](context/licensing.md) - GPLv3/BSD-2-Clause only
|
||||
- **Testing**: See [running-tests](context/running-tests.md) for `ansible-test` commands and container selection
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
Ansible development typically uses an editable install after forking and cloning:
|
||||
|
||||
```bash
|
||||
# After forking and cloning the repository
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
**Note:** ansible-core and all CLIs (including ansible-test) require a POSIX OS. On Windows, use WSL (Windows Subsystem for Linux).
|
||||
|
||||
## Testing and CI
|
||||
|
||||
### Basic Testing Commands
|
||||
|
||||
```bash
|
||||
# Run sanity tests - these are linting/static analysis (pylint, mypy, pep8, etc.)
|
||||
ansible-test sanity -v --docker default
|
||||
|
||||
# List available sanity tests
|
||||
ansible-test sanity --list-tests
|
||||
|
||||
# Run specific sanity tests
|
||||
ansible-test sanity -v --docker default --test pep8 --test pylint
|
||||
|
||||
# Run sanity on specific files (paths relative to repo root)
|
||||
ansible-test sanity -v --docker default lib/ansible/modules/command.py
|
||||
|
||||
# Run unit tests (recommended with Docker)
|
||||
ansible-test units -v --docker default
|
||||
|
||||
# Run specific unit test (paths relative to repo root, targets in test/units/)
|
||||
ansible-test units -v --docker default test/units/modules/test_command.py
|
||||
|
||||
# Run integration tests (choose appropriate container - NOT base/default)
|
||||
ansible-test integration -v --docker ubuntu2404
|
||||
|
||||
# Run specific integration target (directory name in test/integration/targets/)
|
||||
ansible-test integration -v --docker ubuntu2404 setup_remote_tmp_dir
|
||||
|
||||
# Run with coverage
|
||||
ansible-test units -v --docker default --coverage
|
||||
|
||||
# Alternative: use --venv if Docker/Podman unavailable (less reliable for units/integration)
|
||||
ansible-test sanity -v --venv
|
||||
```
|
||||
|
||||
Available Docker containers for testing can be found in `./test/lib/ansible_test/_data/completion/docker.txt`.
|
||||
The `base` and `default` containers are for sanity/unit tests only. For integration tests, use distro-specific
|
||||
containers, depending on the modules being tested.
|
||||
|
||||
**Test isolation options:**
|
||||
|
||||
- `--docker` (supports Docker or Podman) - preferred for reliable, isolated testing
|
||||
- `--venv` - fallback when containers unavailable, but unit tests may be unreliable due to host environment differences
|
||||
|
||||
### Helping Developers with CI Failures
|
||||
## Helping Developers with CI Failures
|
||||
|
||||
When developers submit PRs and encounter CI failures, use these approaches to help diagnose and resolve issues:
|
||||
|
||||
@@ -138,13 +75,7 @@ This shows:
|
||||
- Direct links to Azure DevOps build results
|
||||
- Individual job results (Sanity Test 1/2, Docker tests, Units, etc.)
|
||||
|
||||
**3. Common CI failure patterns:**
|
||||
|
||||
- **Sanity failures**: Usually have specific fixes (trailing whitespace, import errors, etc.)
|
||||
- **Integration test failures**: May require platform-specific containers or test adjustments
|
||||
- **Unit test failures**: Often indicate actual code issues that need debugging
|
||||
|
||||
**4. CI failure analysis workflow:**
|
||||
**3. CI failure analysis workflow:**
|
||||
|
||||
1. Check ansibot comments first for immediate error details
|
||||
2. Use `gh pr checks <number>` to get Azure Pipelines URLs for detailed logs
|
||||
@@ -152,7 +83,7 @@ This shows:
|
||||
4. For sanity test failures, the error messages usually indicate exactly what needs to be fixed
|
||||
5. For test failures, run the same tests locally using `ansible-test` to reproduce and debug
|
||||
|
||||
**5. Downloading Azure Pipelines logs for analysis:**
|
||||
**4. Downloading Azure Pipelines logs for analysis:**
|
||||
|
||||
When CI failures need deeper investigation beyond what's visible in ansibot comments or the web UI, use the `/azp-logs` skill:
|
||||
|
||||
@@ -209,13 +140,8 @@ Use this checklist for EVERY PR review:
|
||||
|
||||
When assisting with PR reviews, verify:
|
||||
|
||||
### Required Components
|
||||
|
||||
- Changelog fragment exists in `changelogs/fragments/`
|
||||
- Appropriate tests are included and cover the changed code
|
||||
- Unit tests should be pytest style, and functional rather than tightly coupled to mocking
|
||||
- Integration tests required for almost all plugin changes (tests the public API)
|
||||
- Tests should exercise the actual changed code, not just add random coverage
|
||||
- Changelog fragment exists (see [changelog requirements](context/documentation-standards.md#changelog-requirements))
|
||||
- Appropriate tests are included (see [test expectations](context/writing-tests.md#test-expectations-for-pull-requests))
|
||||
|
||||
### Review Process
|
||||
|
||||
@@ -223,21 +149,12 @@ Follow these steps in order for thorough PR reviews:
|
||||
|
||||
1. **Get PR details**: Use `gh pr view <number>` to understand the PR scope and description
|
||||
2. **Get PR diff**: Use `gh pr diff <number>` to see all changes
|
||||
3. **Check required components FIRST**:
|
||||
- Verify changelog fragment exists and uses correct section (check `changelogs/config.yaml` for valid sections)
|
||||
- Verify tests exist and specifically cover the changed code paths
|
||||
3. **Check required components FIRST** (changelog and tests -- see links above)
|
||||
4. **Checkout PR branch**: Use `gh pr checkout <number>` to examine code holistically with changes applied
|
||||
5. **Review existing feedback**: Use `gh pr view <number> --comments` to see all comments and previous review feedback
|
||||
6. **Verify all issues addressed**: Ensure all bot failures, reviewer requests, and discussion points are resolved
|
||||
7. **Call out any unresolved review feedback**: Explicitly mention any discussions or requests that remain unaddressed
|
||||
|
||||
### Common Review Issues to Check
|
||||
|
||||
- **Changelog section errors**: Verify changelog uses valid section from `changelogs/config.yaml`. Fragment structure follows sections defined there.
|
||||
- **Test scope**: Ensure tests exercise the actual changed code, not just add random coverage.
|
||||
Integration tests required for almost all plugin changes (tests the public API).
|
||||
Tests should be functional rather than tightly coupled to mocking.
|
||||
|
||||
### Review Task Management
|
||||
|
||||
- Use TodoWrite tool to track review steps for complex PRs
|
||||
@@ -253,107 +170,3 @@ Follow these steps in order for thorough PR reviews:
|
||||
- `gh pr checkout <number>` - Switch to PR branch for holistic examination
|
||||
- `Read` tool - Examine specific changed files in detail
|
||||
- `Grep` tool - Search for related code patterns or test coverage (uses ripgrep/rg)
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Code Style Notes
|
||||
|
||||
- Line limit is 160 characters (not 80)
|
||||
- E402 (module level import not at top) is ignored
|
||||
- In `lib/ansible/modules/`, imports must come after DOCUMENTATION, EXAMPLES, and RETURN definitions
|
||||
- Don't add obvious comments about code
|
||||
- Use native type hints with `from __future__ import annotations` (converts to strings at runtime)
|
||||
- Don't document module parameters in docstrings - migrate to type hints instead
|
||||
- **No trailing whitespace**: Always clean up trailing spaces on lines, especially when editing existing files
|
||||
|
||||
### Python Version Support
|
||||
|
||||
- Controller code: support range defined in `pyproject.toml`
|
||||
- Modules/module_utils: minimum version in `lib/ansible/module_utils/basic.py` (`_PY_MIN`) up to max from `pyproject.toml`
|
||||
- Modules support a wider Python version range than controller code
|
||||
|
||||
### Dependencies and Imports
|
||||
|
||||
- Prefer Python stdlib over external dependencies
|
||||
- Use existing code from within the Ansible project
|
||||
- `lib/ansible/modules/` can only import from `lib/ansible/module_utils/` (modules are packaged for remote execution)
|
||||
- `lib/ansible/module_utils/` cannot import from outside itself
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
### Module and Plugin Documentation
|
||||
|
||||
- Modules and plugins require DOCUMENTATION, EXAMPLES, and RETURN blocks as static YAML string variables
|
||||
- These blocks cannot be dynamically generated - they are parsed via AST/token parsing
|
||||
- Alternative: "sidecar" documentation as `.yml` files with same stem name adjacent to plugin files
|
||||
- All modules should have a `main()` function and `if __name__ == '__main__':` block
|
||||
- Use `version_added` fields in documentation following existing version format patterns
|
||||
|
||||
### Changelog Requirements
|
||||
|
||||
- Changes require entries in `changelogs/fragments/` as YAML files
|
||||
- Create a new fragment file per PR (never reuse existing fragments to avoid merge conflicts)
|
||||
- Fragment structure follows sections defined in `changelogs/config.yaml` under the `sections` key
|
||||
- Naming: `{issue_number}-{short-description}.yml` or `{component}-{description}.yml` if no issue
|
||||
- Format: `- {component} - {description} ({optional URL to GH issue})`
|
||||
- Content supports Sphinx markup (use double backticks for code references)
|
||||
|
||||
## Repository Management
|
||||
|
||||
### Plugin Development
|
||||
|
||||
- New plugins should go into collections, not ansible-core
|
||||
- ansible-core rarely accepts new plugins; core team makes these decisions
|
||||
|
||||
### Branch and Release Management
|
||||
|
||||
- All PRs target the `devel` branch
|
||||
- Use GitHub templates when creating issues/PRs (`.github/ISSUE_TEMPLATE/` and `.github/PULL_REQUEST_TEMPLATE/`)
|
||||
- For issues: fill out the `component` field with project root relative file path
|
||||
- For PRs: adjust the issue type in the template as listed in `.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md`
|
||||
- Validate issues are fixed in `devel` before reporting against stable releases
|
||||
- Bug fixes: backported to latest stable only
|
||||
- Critical bug fixes: backported to latest and previous stable
|
||||
- Security issues: contact security@ansible.com privately, not via GitHub
|
||||
|
||||
### Backwards Compatibility
|
||||
|
||||
- Backwards compatibility is prioritized over most other concerns
|
||||
- Deprecation cycle: 4 releases (deprecation + 2 releases + removal)
|
||||
- Use `Display.deprecated` or `AnsibleModule.deprecate` with version from `lib/ansible/release.py` plus 3
|
||||
- Example: deprecating in 2.19 means removal in 2.22
|
||||
|
||||
## Code Structure Reference
|
||||
|
||||
### Core Structure
|
||||
|
||||
- `lib/ansible/` - Main Ansible library code
|
||||
- `cli/` - Command-line interface implementations (ansible, ansible-playbook, etc.)
|
||||
- `executor/` - Task execution engine and strategies (includes PowerShell support in `powershell/`)
|
||||
- `inventory/` - Inventory management and parsing
|
||||
- `modules/` - Core modules (built-in automation modules)
|
||||
- `module_utils/` - Shared utilities for modules (includes C# in `csharp/` and PowerShell in `powershell/`)
|
||||
- `plugins/` - Plugin framework (filters, tests, lookups, etc.)
|
||||
- `vars/` - Variable management
|
||||
- `config/` - Configuration handling
|
||||
- `collections/` - Ansible Collections framework
|
||||
|
||||
### Key Components
|
||||
|
||||
- **CLI Layer**: Entry points in `lib/ansible/cli/` handle command parsing and dispatch
|
||||
- **Executor**: `lib/ansible/executor/` contains the core execution engine that runs tasks and plays
|
||||
- **Module System**: Modules in `lib/ansible/modules/` are the units of work; they're executed remotely
|
||||
- **Plugin Architecture**: `lib/ansible/plugins/` provides extensibility through filters, tests, lookups, etc.
|
||||
- **Inventory**: `lib/ansible/inventory/` manages host and group definitions
|
||||
- **Collections**: Modern packaging format for distributing Ansible content
|
||||
|
||||
### Testing Infrastructure
|
||||
|
||||
- `test/units/` - Unit tests mirroring the lib structure
|
||||
- `test/integration/` - Integration tests organized by target (named after plugin/functionality being tested)
|
||||
- Some targets have `context/controller` or `context/target` in their `aliases` file when not easily inferable
|
||||
- Only modules run on target hosts; all other plugins execute locally in the ansible process
|
||||
- `test/lib/` - Test utilities and frameworks
|
||||
- `ansible-test` - Unified testing tool for all test types
|
||||
|
||||
For CI failure debugging, see [Helping Developers with CI Failures](#helping-developers-with-ci-failures).
|
||||
|
||||
@@ -70,7 +70,9 @@ For more ways to get in touch, see [Communicating with the Ansible community](ht
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
We document our Coding Guidelines in the [Developer Guide](https://docs.ansible.com/ansible/devel/dev_guide/). We particularly suggest you review:
|
||||
Development context for ansible-core can be found in the [context](context/) directory.
|
||||
|
||||
We also document our Coding Guidelines in the [Developer Guide](https://docs.ansible.com/ansible/devel/dev_guide/). We particularly suggest you review:
|
||||
|
||||
* [Contributing your module to Ansible](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_checklist.html)
|
||||
* [Conventions, tips, and pitfalls](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_best_practices.html)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Context for Humans and Agents
|
||||
|
||||
This directory contains information about developing ansible-core.
|
||||
It should be equally applicable to both humans and agents.
|
||||
|
||||
This is **not** the place for:
|
||||
|
||||
- **Documentation on how to use Ansible** -- see the [ansible-documentation](https://github.com/ansible/ansible-documentation/) repository.
|
||||
- **Learning how to develop content for Ansible** (modules, plugins, collections, roles, etc.) -- see the [Ansible Developer Guide](https://docs.ansible.com/ansible/devel/dev_guide/index.html).
|
||||
- **Agent-specific instructions or guidance** -- see [AGENTS.md](../AGENTS.md).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [contributing](contributing.md) - Contributing changes to ansible-core.
|
||||
- [licensing](licensing.md) - Licensing requirements.
|
||||
- [dev-environment](dev-environment.md) - Development environment setup.
|
||||
- [code-structure](code-structure.md) - Directory layout, key components, import restrictions, and plugin policy.
|
||||
- [coding-style](coding-style.md) - Python versions, dependencies, code formatting, and syntax conventions.
|
||||
- [error-handling](error-handling.md) - Error and exception handling patterns.
|
||||
- [data-tagging](data-tagging.md) - Working with data tags.
|
||||
- [public-api](public-api.md) - Public surface area and internal-by-default conventions.
|
||||
- [documentation-standards](documentation-standards.md) - Module/plugin documentation and changelog requirements.
|
||||
- [running-tests](running-tests.md) - Running tests with ansible-test.
|
||||
- [writing-tests](writing-tests.md) - Test expectations for pull requests.
|
||||
- [deprecation](deprecation.md) - Backward compatibility and deprecation.
|
||||
- [ci](ci.md) - Continuous integration.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Continuous integration
|
||||
|
||||
## Common failure patterns
|
||||
|
||||
- **Sanity failures**: Usually have specific fixes (trailing whitespace, import errors, etc.).
|
||||
- **Integration test failures**: May require platform-specific containers or test adjustments.
|
||||
- **Unit test failures**: Often indicate actual code issues that need debugging.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Core structure
|
||||
|
||||
- `lib/ansible/` - Main Ansible library code.
|
||||
- `cli/` - Command-line interface implementations (ansible, ansible-playbook, etc.).
|
||||
- `executor/` - Task execution engine and strategies (includes PowerShell support in `powershell/`).
|
||||
- `inventory/` - Inventory management and parsing.
|
||||
- `modules/` - Core modules (built-in automation modules).
|
||||
- `module_utils/` - Shared utilities for modules (includes C# in `csharp/` and PowerShell in `powershell/`).
|
||||
- `plugins/` - Plugin framework (filters, tests, lookups, etc.).
|
||||
- `vars/` - Variable management.
|
||||
- `config/` - Configuration handling.
|
||||
- `collections/` - Ansible Collections framework.
|
||||
|
||||
## Key components
|
||||
|
||||
- **CLI Layer**: Entry points in `lib/ansible/cli/` handle command parsing and dispatch.
|
||||
- **Executor**: `lib/ansible/executor/` contains the core execution engine that runs tasks and plays.
|
||||
- **Module System**: Modules in `lib/ansible/modules/` are the units of work; they're executed remotely.
|
||||
- **Plugin Architecture**: `lib/ansible/plugins/` provides extensibility through filters, tests, lookups, etc.
|
||||
- **Inventory**: `lib/ansible/inventory/` manages host and group definitions.
|
||||
- **Collections**: Modern packaging format for distributing Ansible content.
|
||||
|
||||
## Plugin development
|
||||
|
||||
- New plugins should go into collections, not ansible-core.
|
||||
- ansible-core rarely accepts new plugins; core team makes these decisions.
|
||||
|
||||
## Import restrictions
|
||||
|
||||
- `lib/ansible/modules/` can only import from `lib/ansible/module_utils/` (modules are packaged for remote execution).
|
||||
- `lib/ansible/module_utils/` cannot import from outside itself.
|
||||
|
||||
## Resource embedding
|
||||
|
||||
Modules that need to execute code on Python versions outside the normal module_utils range
|
||||
can use `EmbedManager.embed()` from `ansible.module_utils.embed` to bundle standalone scripts into the AnsiballZ payload.
|
||||
Embedded resources go in `lib/ansible/module_utils/_embed/`.
|
||||
|
||||
## Testing infrastructure
|
||||
|
||||
- `test/units/` - Unit tests mirroring the lib structure.
|
||||
- `test/integration/` - Integration tests organized by target (named after plugin/functionality being tested).
|
||||
- Some targets have `context/controller` or `context/target` in their `aliases` file when not easily inferable.
|
||||
- Only modules run on target hosts; all other plugins execute locally in the ansible process.
|
||||
- `test/lib/` - Test utilities and frameworks.
|
||||
- `ansible-test` - Unified testing tool for all test types.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Coding style and syntax
|
||||
|
||||
For new code and updates to existing code, including unit tests.
|
||||
|
||||
In general, prefer newer Python features as they become available.
|
||||
A feature is available when the minimum supported Python version for the code being written supports it,
|
||||
and it doesn't conflict with other supported versions.
|
||||
|
||||
## Python version support
|
||||
|
||||
- Controller code: minimum version defined in `pyproject.toml` (`requires-python`).
|
||||
- Modules/module_utils: minimum version in `lib/ansible/module_utils/basic.py` (`_PY_MIN`).
|
||||
- Modules support a wider Python version range than controller code.
|
||||
- Supported Python versions for testing are defined within `ansible-test`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Prefer Python stdlib over external dependencies.
|
||||
- Use existing code from within the Ansible project.
|
||||
|
||||
## Markdown
|
||||
|
||||
Markdown files use [GitHub Flavored Markdown](https://github.github.com/gfm/) (verified by the "pymarkdown" sanity test).
|
||||
|
||||
Use dashes (`-`) for unordered list items, not asterisks.
|
||||
End list items with periods.
|
||||
|
||||
## ASCII characters
|
||||
|
||||
- Use ASCII quotes (`'` and `"`) instead of Unicode smart quotes (verified by the "no-smart-quotes" sanity test).
|
||||
- Use ASCII dashes (`-` or `--`) instead of em dashes.
|
||||
|
||||
## Line length
|
||||
|
||||
The line limit is 160 characters.
|
||||
|
||||
## Trailing whitespace
|
||||
|
||||
Don't leave trailing whitespace on lines.
|
||||
|
||||
## Docstrings
|
||||
|
||||
Explain what the annotated code does, but don't create structured entries for parameters.
|
||||
Don't document parameter types in docstrings -- use type hints instead.
|
||||
|
||||
Anything considered a public API must have a docstring.
|
||||
Internal code should, and it often makes sense for unit tests, too.
|
||||
|
||||
## Line breaks in source text
|
||||
|
||||
Try to stick to one sentence per line in text like docstrings, comments and changelog fragments.
|
||||
|
||||
## Native type annotations
|
||||
|
||||
Use native type hints with `from __future__ import annotations` (verified by the "boilerplate" sanity test).
|
||||
Include type annotations on function/method arguments and return types, unless the annotation becomes too complex (e.g. `TypedDict`).
|
||||
The "mypy" sanity test only performs type checking on annotated functions/methods.
|
||||
|
||||
Prefer PEP 695 type parameter syntax over `TypeVar` and `ParamSpec` declarations.
|
||||
For example, use `def foo[T](x: T) -> T` instead of declaring `T = TypeVar('T')` separately.
|
||||
This does not apply to `module_utils/` code, which must support older Python versions that lack the feature.
|
||||
|
||||
## Format strings
|
||||
|
||||
Use f-strings instead of `%` strings or `str.format`, except for logging where formatting is deferred.
|
||||
|
||||
## Quoting strings
|
||||
|
||||
Use the `!r` format qualifier to quote a value instead of manually quoting.
|
||||
|
||||
Example: `f"A string with a {quoted!r} value."`
|
||||
|
||||
## Code formatting check/fix for internals
|
||||
|
||||
The `black` sanity test runs against all `_internal` packages, using default settings with an increased line length of 160 and no quote conversion.
|
||||
Use `ansible-test` to automatically apply required formatting changes.
|
||||
|
||||
Example: `ansible-test sanity --test black --fix`
|
||||
|
||||
## Import ordering in modules
|
||||
|
||||
The E402 pycodestyle rule (module level import not at top) is ignored.
|
||||
In `lib/ansible/modules/`, imports must come after the `DOCUMENTATION`, `EXAMPLES`, and `RETURN` definitions.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Contributing
|
||||
|
||||
## Keep changes focused
|
||||
|
||||
Changes should be limited to what's necessary to solve the problem at hand.
|
||||
Avoid unrelated reformatting, refactoring, or style adjustments in the same change.
|
||||
|
||||
Unfocused changes make review harder by increasing the diff size and obscuring the important parts.
|
||||
They can also lead to unnecessary back-and-forth around style and preferences.
|
||||
|
||||
## Branch and release management
|
||||
|
||||
- All PRs target the `devel` branch.
|
||||
- Use GitHub templates when creating issues/PRs (`.github/ISSUE_TEMPLATE/` and `.github/PULL_REQUEST_TEMPLATE/`).
|
||||
- For issues: fill out the `component` field with project root relative file path.
|
||||
- For PRs: adjust the issue type in the template as listed in `.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md`.
|
||||
- Validate issues are fixed in `devel` before reporting against stable releases.
|
||||
- Bug fixes: backported to latest stable only.
|
||||
- Critical bug fixes: backported to latest and previous stable.
|
||||
- Security issues: contact security@ansible.com privately, not via GitHub.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Data tagging
|
||||
|
||||
## How not to break things
|
||||
|
||||
Avoid unnecessary mutation of values, such as calling `str.strip` or `to_text`, etc. as these will drop tags, losing things like origin and trust for templating.
|
||||
|
||||
When mutation of a value is necessary, carefully consider which tags, if any, need to be propagated to the resulting value.
|
||||
|
||||
## When tags must be removed
|
||||
|
||||
Some APIs don't understand tagged types and can't automatically treat them as their untagged equivalents.
|
||||
This includes C-implemented Python APIs that perform exact type checks, serialization libraries that reject derived types,
|
||||
and any interface where the consumer expects or requires plain types.
|
||||
In these cases, use `transform_to_native_types()` from `ansible.utils.vars` to convert tagged values to native types before passing them to the API.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Backward compatibility and deprecation
|
||||
|
||||
Backward compatibility is prioritized over most other concerns.
|
||||
|
||||
## Deprecation cycle
|
||||
|
||||
- Deprecation cycle: 4 releases (deprecation + 2 releases + removal).
|
||||
- Removal version: current version in `lib/ansible/release.py` plus 3.
|
||||
- Example: deprecating in 2.19 means removal in 2.22.
|
||||
|
||||
## Deprecating code
|
||||
|
||||
Use `Display.deprecated` or `AnsibleModule.deprecate` with the removal version.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Development environment setup
|
||||
|
||||
ansible-core and all CLIs (including ansible-test) require a POSIX OS.
|
||||
On Windows, use WSL (Windows Subsystem for Linux).
|
||||
|
||||
## Editable install
|
||||
|
||||
Ansible development typically uses an editable installation after forking and cloning:
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Hacking script
|
||||
|
||||
An alternative to an editable installation is to source the `hacking/env-setup` script:
|
||||
|
||||
```bash
|
||||
source hacking/env-setup
|
||||
```
|
||||
|
||||
## Running tests without installation
|
||||
|
||||
If you only need to run tests, you can invoke `ansible-test` directly without installing or sourcing the hacking script:
|
||||
|
||||
```bash
|
||||
bin/ansible-test sanity -v --docker default
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
# Documentation standards
|
||||
|
||||
## Module and plugin documentation
|
||||
|
||||
- Modules and plugins require `DOCUMENTATION`, `EXAMPLES`, and `RETURN` blocks as static YAML string variables.
|
||||
- These blocks cannot be dynamically generated -- they are parsed via AST/token parsing.
|
||||
- Alternative: "sidecar" documentation as `.yml` files with same stem name adjacent to plugin files.
|
||||
- All modules should have a `main()` function and `if __name__ == '__main__':` block.
|
||||
- Use `version_added` fields in documentation following existing version format patterns.
|
||||
- Filter, test, and lookup plugins should declare positional arguments using the `positional` key in `DOCUMENTATION`.
|
||||
|
||||
## Markup in plugin documentation blocks
|
||||
|
||||
Plugin `DOCUMENTATION`, `EXAMPLES`, and `RETURN` blocks use Ansible-specific markup, not reStructuredText or Markdown.
|
||||
|
||||
### Semantic markup
|
||||
|
||||
- `O(name)` -- option name, optionally with a value: `O(state=present)`.
|
||||
- `V(value)` -- option value mentioned alone: `V(present)`.
|
||||
- `RV(name)` -- return value name, optionally with a value: `RV(changed=true)`.
|
||||
- `E(name)` -- environment variable: `E(ANSIBLE_CONFIG)`.
|
||||
|
||||
Suboptions use dot-separated paths: `O(foo.bar)`.
|
||||
Backslash escaping is supported inside these macros.
|
||||
|
||||
### Formatting
|
||||
|
||||
- `C()` -- monospace/code text. Example: `This works like the unix command C(grep).`.
|
||||
- `B()` -- bold text.
|
||||
- `I()` -- italic text.
|
||||
|
||||
`C()`, `B()`, and `I()` do not support escaping, so they cannot contain `)`.
|
||||
|
||||
Use `V()` instead of `C()` when the value may contain `)`.
|
||||
|
||||
### Linking
|
||||
|
||||
- `M(ansible.builtin.copy)` -- link to a module (FQCN required).
|
||||
- `P(ansible.builtin.file#lookup)` -- link to a plugin (FQCN and type required).
|
||||
- `L(title,https://example.com)` -- link with custom title.
|
||||
- `U(https://example.com)` -- bare URL.
|
||||
- `R(title,rst_anchor)` -- cross-reference to an RST anchor.
|
||||
|
||||
`O()` and `RV()` can also link to options/return values in other plugins using the syntax
|
||||
`O(ansible.builtin.copy#module:src)`.
|
||||
|
||||
## Changelog requirements
|
||||
|
||||
- Changes require entries in `changelogs/fragments/` as YAML files.
|
||||
- Create a new fragment file per PR (never reuse existing fragments to avoid merge conflicts).
|
||||
- Fragment structure follows sections defined in `changelogs/config.yaml` under the `sections` key.
|
||||
- Verify changelog fragments use a valid section from `changelogs/config.yaml`.
|
||||
- Naming: `{issue_number}-{short-description}.yml` or `{component}-{description}.yml` if no issue.
|
||||
- Format: `- {component} - {description} ({optional URL to GH issue})`.
|
||||
- Content supports Sphinx markup (use double backticks for code references).
|
||||
@@ -0,0 +1,72 @@
|
||||
# Error handling
|
||||
|
||||
## Tracebacks
|
||||
|
||||
Do not manually generate tracebacks for errors or warnings.
|
||||
Standardized traceback capture is included for errors, warnings and deprecation warnings for both controller and module code (Python).
|
||||
See the `DISPLAY_TRACEBACK` config option for details on how to enable traceback capture and display.
|
||||
|
||||
## In modules
|
||||
|
||||
In most cases, just raise an exception.
|
||||
The AnsiballZ wrapper now provides a general exception handler for Python modules, making use of `fail_json` unnecessary, unless the module result needs to be customized.
|
||||
Calls to the various Python-module-side `warn` and `deprecate` methods/functions will also capture and marshal tracebacks to the controller when enabled.
|
||||
When using `fail_json` to customize a module failure result, passing the `exception` argument to provide the currently active exception is unnecessary.
|
||||
|
||||
## Deferred exceptions in modules
|
||||
|
||||
When deferring exceptions in modules using `try/except` and `fail_json`, pass the captured `Exception` instance to `fail_json` using the `exception` argument.
|
||||
The error handling infrastructure will handle collection of error details and traceback formatting.
|
||||
|
||||
## Exception context
|
||||
|
||||
Raising exceptions while another exception is active will cause the active exception to become the `__context__` for the newly raised exception.
|
||||
This is usually not the intended behavior, and should be reserved for unexpected errors while handling the original exception.
|
||||
In most cases a `raise from` is desired, either with `None` to suppress the original exception if it's not helpful,
|
||||
or with the captured exception to set it as the `__cause__` for the newly raised exception.
|
||||
|
||||
Suppression example: `raise Exception("something") from None`
|
||||
|
||||
Cause example: `raise Exception("something") from ex`
|
||||
|
||||
## Raising new exceptions
|
||||
|
||||
Don't catch exceptions just to re-raise them, unless there's additional information that can be added in the newly raised exception.
|
||||
In most cases, particularly for plugin/module failures, contextual information is automatically added,
|
||||
making granular `try/except/raise` within the plugin or module unnecessary.
|
||||
|
||||
## Error messages
|
||||
|
||||
Do not repeat previous exception messages when constructing new exceptions.
|
||||
|
||||
Anti-pattern: `raise Exception("it broke: {ex}") from ex`
|
||||
|
||||
The built-in error chain handling mechanisms in Ansible will include the messages from cause/context exceptions automatically.
|
||||
|
||||
In general, error messages passed to exceptions should be a fairly terse description of what happened,
|
||||
and not contain extra diagnostic, contextual, or prescriptive correction advice (see `obj` and `help_text` below for more on that).
|
||||
|
||||
## When and how to use AnsibleError
|
||||
|
||||
The `AnsibleError` exception type provides support for improved error reporting.
|
||||
However, if no arguments other than a message are given, there's usually no benefit over using built-in exception types.
|
||||
So what are those other arguments?
|
||||
|
||||
- `obj` - Usually a variable responsible for the error being raised -- not an `Exception` instance.
|
||||
If this value is `Origin` tagged, then the error message shown to the user will be able to provide context showing what content triggered the error.
|
||||
- `help_text` - Instructions and additional detail that helps the user understand how to resolve the error.
|
||||
By putting that information here, it allows the `message` to be shorter and focus on the problem.
|
||||
This information will be shown *after* the contextual error details provided by `obj`, if any.
|
||||
|
||||
## Display warnings and errors
|
||||
|
||||
The existing `warning` and `deprecated` methods on the `Display` object now support passing optional `help_text` and `obj` arguments,
|
||||
matching those on `AnsibleError` for prescriptive guidance and source context from `Origin`-tagged values.
|
||||
Additionally, the new `error_as_warning` method accepts an exception object and an optional contextual message directly,
|
||||
allowing for a caught exception to be converted to a warning automatically
|
||||
while still preserving the exception detail, traceback, and source object context (where applicable).
|
||||
|
||||
## Jinja plugin errors
|
||||
|
||||
In Jinja plugins, the `AnsibleFilterError` and `AnsibleLookupError` exception types are no longer needed.
|
||||
Instead, use whatever exception type is appropriate for the error condition.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Licensing requirements
|
||||
|
||||
- **ansible-core**: All code must be **GPLv3 compatible**.
|
||||
- **lib/ansible/module_utils/**: Defaults to **BSD-2-Clause** (more permissive).
|
||||
- **External dependencies**: Only use libraries compatible with these licenses.
|
||||
- **When in doubt**: Ask about licensing compatibility rather than assuming.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Public surface area
|
||||
|
||||
Starting with ansible-core 2.19 we're trying to be more intentional about what features are part of our public API
|
||||
and other public surface areas (e.g. CLI, configuration options, module arguments, ansible-core provided Jinja globals, etc.).
|
||||
|
||||
## Imports in public Python modules
|
||||
|
||||
To make public Python modules more self-documenting:
|
||||
|
||||
- Imports in any file considered public API must be sunder-prefixed (e.g. `_module_name`),
|
||||
to avoid confusion about imported objects being part of the public module API.
|
||||
- Prefer module-level imports (e.g. `from ansible._internal import _amodule`) with dotted usage (e.g. `foo: _amodule.thing`).
|
||||
This solves many circular import issues and reduces the need for sunder-prefix aliasing on internal imports in public API.
|
||||
Hot code paths can use locals or aliased objects, but sparingly and only where it really matters.
|
||||
|
||||
## Internal by default
|
||||
|
||||
New feature implementations in Python code should always be:
|
||||
|
||||
- Added to a module beneath the `ansible._internal` or `ansible.module_utils._internal` package.
|
||||
- In a sunder-prefixed module (easier public module imports without aliasing).
|
||||
|
||||
Only public types and functions should be added to new modules outside `_internal` packages.
|
||||
Small sunder-prefixed utility types and functions are okay in public modules,
|
||||
but in general, non-public API surface area for most new implementations should live completely under an `_internal` package.
|
||||
|
||||
Internal-only types/functions/methods beneath an `_internal` package do not generally require sunder-prefixing on their names.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Running tests
|
||||
|
||||
All testing uses `ansible-test`, which supports sanity tests (linting/static analysis), unit tests, and integration tests.
|
||||
|
||||
## Sanity tests
|
||||
|
||||
Sanity tests don't require `--docker`.
|
||||
Some tests may be skipped if their dependencies aren't installed locally (e.g. additional Python versions, shellcheck, PowerShell),
|
||||
but this rarely matters when testing your own changes.
|
||||
|
||||
Run sanity tests against all files in your change set, not just the ones you're actively editing.
|
||||
|
||||
```bash
|
||||
# Run all sanity tests
|
||||
ansible-test sanity -v
|
||||
|
||||
# List available sanity tests
|
||||
ansible-test sanity --list-tests
|
||||
|
||||
# Run specific sanity tests
|
||||
ansible-test sanity -v --test pep8 --test pylint
|
||||
|
||||
# Run sanity on specific files (paths relative to repo root)
|
||||
ansible-test sanity -v lib/ansible/modules/command.py
|
||||
|
||||
# Run all sanity tests in a container (for full coverage)
|
||||
ansible-test sanity -v --docker
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `--docker` without an argument defaults to the `default` container.
|
||||
> Avoid placing a non-container argument immediately after `--docker`, as it will be interpreted as an image name.
|
||||
|
||||
## Unit tests
|
||||
|
||||
```bash
|
||||
# Run all unit tests
|
||||
ansible-test units -v --docker
|
||||
|
||||
# Run a specific unit test (paths relative to repo root, targets in test/units/)
|
||||
ansible-test units -v --docker test/units/modules/test_command.py
|
||||
|
||||
# Run with coverage
|
||||
ansible-test units -v --docker --coverage
|
||||
```
|
||||
|
||||
## Integration tests
|
||||
|
||||
```bash
|
||||
# Run all integration tests
|
||||
ansible-test integration -v --docker ubuntu
|
||||
|
||||
# Run a specific integration target (directory name in test/integration/targets/)
|
||||
ansible-test integration -v --docker ubuntu ping
|
||||
```
|
||||
|
||||
## Container selection
|
||||
|
||||
- Sanity/Unit tests: `--docker` (defaults to the `default` container).
|
||||
- Integration tests: `--docker ubuntu`, `--docker fedora`, etc. (NOT default/base).
|
||||
|
||||
The `base` and `default` containers are for sanity/unit tests only.
|
||||
For integration tests, use distro-specific containers, depending on the modules being tested.
|
||||
|
||||
Available containers and their supported Python versions are listed in the `--help` output for each test command.
|
||||
|
||||
## Test isolation options
|
||||
|
||||
- `--docker` (supports Docker or Podman) -- preferred for reliable, isolated testing.
|
||||
- `--venv` -- fallback when containers are unavailable, but unit tests may be unreliable due to host environment differences.
|
||||
@@ -0,0 +1,8 @@
|
||||
# Writing tests
|
||||
|
||||
## Test expectations for pull requests
|
||||
|
||||
- Appropriate tests are required and should cover the changed code.
|
||||
- Unit tests should be pytest style, and functional rather than tightly coupled to mocking.
|
||||
- Integration tests are required for almost all plugin changes (tests the public API).
|
||||
- Tests should exercise the actual changed code, not just add random coverage.
|
||||
@@ -710,6 +710,9 @@ class PathMapper:
|
||||
if path.startswith('changelogs/'):
|
||||
return minimal
|
||||
|
||||
if path.startswith('context/'):
|
||||
return minimal
|
||||
|
||||
if path.startswith('hacking/'):
|
||||
return minimal
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ def collect_sdist_files(complete_file_list: list[str]) -> list[str]:
|
||||
'changelogs/fragments/*',
|
||||
'AGENTS.md',
|
||||
'CLAUDE.md',
|
||||
'context/*',
|
||||
'hacking/*',
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user