Imported from gothick/ansible-klegg (
ansible_collections/community/general/AGENTS.md). Install upstream withnpx skills add gothick/ansible-klegg --skill general. Copyright stays with the author.
Rules for community.general
Ansible Collection
- This collection follows Semantic Versioning.
- Being an Ansible collection, its version number is specificied in the
galaxy.yml. - The version set there, for the
mainbranch is the next version to be released that accepts new features. - For this collection, you will want to read the description of the issue: https://github.com/ansible-collections/community.general/issues/11482
- The guidelines for contributors are found in the
CONTRIBUTING.md. - It is very important to maintain backwards compatibility in the changes.
- When something needs to change and break that, a longer process must be taken, involving deprecations and sometimes feature flags to enable the new behaviour.
- Deprecations usually plan for the removal of the deprecated code in two major versions (X+2).0.0 from the current version. Depending on the situation, this target may be pushed for the (X+3).0.0 version.
- If a deprecation is needed, ingest https://github.com/russoz-ansible/ansible-contrib-unofficial/blob/main/deprecations.md for more information on how to implement the deprecations.
- Always refer to modules and plugins by their FQCN.
- When setting
version_addedon a new parameter or plugin, always readgalaxy.ymland use that version string directly — it holds the next version for a feature release.
Licensing and Copyright
This project abides to the REUSE specification from the Free Software Foundation.
They provide a tool, reuse, to to check for compliance; the command nox -e license-check
can be used to perform that check, but only when a marker is added, changed or removed.
Licensing and Copyright Rules
- All new content added to this collection must fall under the GPL-3 license.
- Every file should have a license and copyright markers.
- These markers should be placed as comments by the beginning of the file.
If the file has a "shebang", then it should start in line 2, otherwise, in line 1.
Alternatively, the license should be placed in another file
with the exact same name, plus a suffix
.license, when:- The file format does not allow comments.
- The file is automatically generated by some process.
- The directory
changelog/fragmentsis exempt from this rule, as defined inREUSE.toml. - The format of the marker will typically consist of 3 or more lines. Example:
There might be multiple Copyright lines, for different authors.# Copyright (c) 2018, Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later - Whenever you create a new copyright marker, check the current date and use the current year.
- Do not update dates in existing copyright markers.
Deprecation Rules
Reference guide: https://github.com/russoz-ansible/ansible-contrib-unofficial/blob/main/deprecations.md
Deprecating a parameter with no default value
Use removed_in_version + removed_from_collection in argument_spec, plus a description note in the docs.
Deprecating a parameter that has a default value
Do NOT use removed_in_version — it triggers on every run because the parameter is always resolved (via its default), even when the user never explicitly set it.
Instead:
- Remove
defaultfromargument_spec - Remove
default:from the docs; describe the old default in the description text alongside the deprecation note - In
main(), detectNone(meaning the user didn't set it), apply the old default manually, and call:
Depending on the option, only callmodule.deprecate( "The <param> option will be removed ...", version="X.Y.0", collection_name="community.general", )module.deprecate()if the changing default has an effect on the task.
Removal target versions
- Default: plan removal at
(X+2).0.0from the current major version - May be pushed to
(X+3).0.0depending on the situation
Writing changelog fragments
Changelog Fragments:
- MUST be created for modifications in the productive code (mostly code under
plugins/) - MUST NOT be created for:
- New modules or new plugins.
- Changes touching only documentation, comments, and/or tests.
- MUST be files in
changelogs/fragmentsnamed as<PR number>-<some description, possibly matching the branch name>.yml. - MUST have a dict as the top-level element, and the keys MUST be one of: minor_changes, breaking_changes, deprecated_features, removed_features, bugfixes.
- MUST list, under each top-level element, one entry for each file changed in the PR.
- MUST use the format:
for the entries.- <component spec> - <description> (<URLs>).- The "component spec" is different for different types of plugins, in this order:
- Modules (
plugins/modules): "" (or ) - Module utils (
plugins/module_utils): " module utils" - Plugin utils (
plugins/plugin_utils): " plugin utils" - All other plugins (
plugins/<PLUGIN_TYPE>/): " <PLUGIN_TYPE> plugin" - Plugin filenames starting with
_are not to be added to the changelog fragment
- Modules (
- The description MUST be concise — one sentence stating what changed, from the user's perspective.
Do NOT explain why the change was made, how it was implemented, or what Python constructs were used. Do not provide examples of values.
The audience is end users and operators, not developers; avoid implementation details.
Exception: if the mechanism itself is directly relevant to the user (e.g. a new retry behavior
that affects timing or side effects), a brief mention is acceptable — but only if it adds real value.
No need to provide details that can be found in the issue or in the PR.
That content should be in RST format, so for example symbol names must be enclosed in double back ticks, as in "
symbol". Use American English spelling (e.g. "behavior" not "behaviour", "customize" not "customise"). Description must start with lower-case - do not capitalize it. - The URLs - the URL of the current PR and, if the PR fixes one or more issues, the URLs of those issues as well.
Preferably the issues before the PR. The URLs must be separated with
,. Do not create an issue for the PR after creating the PR.
- The "component spec" is different for different types of plugins, in this order:
- SHOULD NOT mix
bugfixeswith other changes: fixes are backported and no new features should come along - SHOULD NOT mix
deprecated_featureswith other changes
Given the fact that the PR number is required for the file name and for the URLs part of the entry, the fragment must be generated (then commited and pushed) after the PR is first created.
After drafting a changelog fragment, always present it to the user for review and explicit approval before committing or pushing it.
BOTMETA Rules
- Add an entry to the
.github/BOTMETA.ymlfile every time a new file is added within the paths:- docs/docsite/rst/**
- plugins/**
- The entry should be placed within the existing section where it belongs, and within that section, files should be listed on alphabetical order.
- User handles are never removed, unless explicitly requested.
Git usage
- Never mention any user or group with
@in the commit message. - Before commiting changes:
- Ensure you are in the right branch
- If containing code changes, look for unit and integration tests and apply them as possible
- When branching:
- Always ensure you are branching off the
mainbranch - Do not use
fixor any other prefix indicating the type of the branch - If there is an issue associated with the branch, prefix the name with
####-(where #### is the is issue number), e.g. if fixing issue 9999 about the xfconf module, name it like9999-xfconf-something.
- Always ensure you are branching off the
Github Issues Rules
-
Classify issues correctly: features are not bugfixes even if they address a gap or missing behavior. Use the issues template's categories accurately.
- For an issue to be considered a bug, it must refer to a behavior promised in the plugin documentation or something that defeats its purpose. Elective preferences are not bugs. Missing features are not bugs.
-
The "Community.general Version" and "Ansible Version" fields must report the versions used to detect the problem and they should be supported versions of: the collection itself, ansible-core, Python.
-
MUST use one of the templates in
.github/ISSUE_TEMPLATE- Component name: one per line, use the relative path in the repo, one per line
- Community.general version: if bug exists in
mainbranch, simply state so. - Do generate the Code of Conduct checkbox - I am responsible for what you do, my child
-
When the issue is about code:
The issue title must follow:
Case Format Example Module issue <name>: <summary>postgresql_db: fails when password contains special charactersNon-module plugin <name> <type> plugin: <summary>passwordstore lookup plugin: add support for multiline secretsNew plugin request <name>: new moduleor<name>: new <type> pluginproxmox_vm: new moduleMultiple related plugins <common_prefix>*: <summary>proxmox*: multiple modules fail with newer API versionsMultiple unrelated plugins multiple: <summary>multiple: deprecation warnings not shown correctly- Use lowercase for the summary part (after the colon).
- Strip noise words: "Issue with", "Bug:", "[BUG]", "[FR]", "Request:".
- When in doubt about the plugin name, skim the issue body for FQCN references or task examples.
-
When the issue is NOT about code:
An issue title like below is suggested (never enforced) to the user:
Case Format Example Non-plugin (CI, docs, infra) docs:,CI:,testing:,build:,meta:prefixdocs: update contribution guidelines
Github Pull Request Rules
- MUST use the template
.github/pull_request_template.md - MUST create the PR in the upstream repository
- Classify PRs correctly: features are not bugfixes even if they address a gap or missing behavior. Use the PR template's categories accurately.
- Do not comment in the description about the state of the changelog fragment.
- The PR title should be in one of the forms:
<module>: <short-description>e.g.xfconf: adjust return value<plugin> <plugin-type> plugin: <short-description>e.g.pbrun become plugin: refactor function xyz()new module/<plugin-type> plugin: <name> <short-description>e.g.new module: xyz interacts with XyZ serviceornew inventory plugin: abc retrieves hosts from ABC daemon
- PR title should use short names for modules/plugins, e.g.
xfconfinstead ofcommunity.general.xfconf - PR title should use single backticks for terms like commands, variables, functions, etc. E.g. "xfconf: use command
xfconf-query" - PR title may have a prefix indicating it is a work in progress. E.g. "[WIP] xfconf: use command
xfconf-query" - If the PR fixes issues, add one line with
Fixes #<issue-number>for each issue being solved to the PR description - When a fix is speculative or lacks test coverage, use hedged language in the PR description (e.g. "may address" rather than "this fixes").
- Keep PR descriptions concise; do not explain implementation choices or reproduce information already visible in the diff or commit messages.
Tests
Running Tests
- Run sanity tests with:
ansible-test sanity --docker default --python 3.14 <files>(omit files to test everything) - Run unit tests with:
ansible-test units --docker default --python 3.14 tests/unit/plugins/<plugin_type>/<test_file.py>(omit the path to test everything) - Run integration tests with:
ansible-test integration --docker default --python 3.14 <target name>(omit target to test everything, but that is discouraged as it takes a long while)- Some integration tests require a proper OS container (
ubuntu,fedora,alpine) instead of the default container (default). For OS containers, do not specify--python <version>. - Some integration tests must run on a full VM, not in a container (e.g.
snap)
- Some integration tests require a proper OS container (
- PRs are only merged into
mainif they pass the tests - Do not re-run a test suite that already passed in the current session unless new code changes have been made since the last run.
Writing unit tests
- Prefer
pytestidioms to write tests- Prefer plain functions instead of Test classes
- Use pytest fixtures when applicable
- Avoid unittest class-based tests
- Use
mockerto mock patch symbols - Use the common tools from
community.internal_test_toolswhen applicable, instead of reinventing the wheel.- E.g.
ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils.set_module_args
- E.g.
- When testing modules that call CLI commands, prefer
uthelper. Other tests can be mixed with theUTHelpercall. - Try and avoid adding new entries to the
tests/unit/requirements.txtfile, as it is installed every time, for every unit testing, no matter how small or unrelated to the requirements it might be. - Tests should:
- Mock all interaction with external services, APIs, commands
- NEVER mock the entire module - it defeats the purpose of the testing. Instead, run the module
and capture the
SystemExitexception from it. - Assert the "happy-path" for the module execution. There might be multiple ones (changes, no changes, partial changes).
- Assert some exception paths (when things go wrong) are handled as gracefully as possible
- NOT assert Ansible features, e.g. if two parameters are marked in the argument spec as
mutually exclusive, there is no need to write a test to verify that they cannot be used together.
It is a given, and
ansible-corehas plenty of tests for those.
Writing integration tests
- The generic documentation on integration tests is found at: https://docs.ansible.com/projects/ansible/latest/dev_guide/testing_integration.html
- Each directory under
tests/integration/targetsis akin to an Ansible role - The
aliasesfiles contains directives that control how/where the tests are executed.- More info on the aliases is found at: https://docs.ansible.com/projects/ansible/latest/dev_guide/testing/sanity/integration-aliases.html
- Targets named
setup_*are actual supporting roles. Some noteworthy ones:setup_remote_tmp_dirsetup_snapsetup_dockersetup_pkg_mgr
- Tests should:
- Interact with the actual external services, APIs, commands
- This may not be feasible for some third party services requiring authentication, paid services, etc
- Use local alternatives as possible, e.g. docker images (use
setup_docker) - Be implemented in a "black-box" style: we provide inputs and assert the outputs, not interested in the internal details
- Assert idempotency, i.e. run the same command twice and assert the second time bears no change
- Interact with the actual external services, APIs, commands
- For modules with larget sets of functions, break the tests into smaller files and use
include_tasks
Use of AI for contributions
Please note that using AI is accepted but you MUST comply with the Ansible Community Policy for AI-Assisted Contributions!
The main point is being transparent about it. Add a Co-authored: tag in the issues and PR descriptions, as well as in the commit texts.