| |
@@ -0,0 +1,446 @@
|
| |
+ = Ansible Collection Packaging Guidelines
|
| |
+ :last-reviewed: 2022-09-25
|
| |
+ :toc:
|
| |
+
|
| |
+ == Forward
|
| |
+
|
| |
+ Ansible collections are packaged units of Ansible content,
|
| |
+ including modules and other types of plugins.
|
| |
+ Most Ansible Plugins are written in Python or Powershell.
|
| |
+
|
| |
+ Some collections are also included in Ansible Community's `+ansible+`
|
| |
+ collection bundle, which is packaged in Fedora.
|
| |
+ All collections, whether or not they are included in the `+ansible+` package,
|
| |
+ MAY be packaged in Fedora.
|
| |
+
|
| |
+ `+ansible+` depends on `+ansible-core+`, which contains the core engine and
|
| |
+ CLI programs (e.g. `+ansible+`, `+ansible-playbook+`).
|
| |
+ The `+ansible+` package has a different release cycle than individual collections,
|
| |
+ and it may contain older versions of the individual components.
|
| |
+ `+ansible+` installs collections in a different namespace and is parallel
|
| |
+ installable with individual collections.
|
| |
+ The Ansible engine searches for collections in the standalone collections
|
| |
+ directory first.
|
| |
+
|
| |
+ See https://fedoraproject.org/wiki/Changes/Ansible5[Changes/Ansible5]
|
| |
+ for more information about the split between `+ansible+` and `+ansible-core+`.
|
| |
+
|
| |
+ == Naming
|
| |
+
|
| |
+ Collection packages MUST be named `+ansible-collection-NAMESPACE-NAME+`.
|
| |
+ For example, the `+community.general+` collection package is named
|
| |
+ `+ansible-collection-community-general+`.
|
| |
+
|
| |
+
|
| |
+ == Collection Source
|
| |
+
|
| |
+ Collection source code MUST be downloaded from the collection's respective
|
| |
+ Git forge/other SCM repository.
|
| |
+ While the tarballs published to Ansible Galaxy contain all of the
|
| |
+ collection's Python/Powershell source code as well as some development files,
|
| |
+ they do not include the `+galaxy.yml+` build configuration
|
| |
+ and development files (e.g. unit tests) that the author may choose to remove.
|
| |
+ Note that the Ansible Community collection requirements mandate that
|
| |
+ collections tag releases in a public SCM repository.
|
| |
+
|
| |
+ Collection packages SHOULD use `+%{ansible_collection_url NAMESPACE NAME}+`
|
| |
+ as the package's `+URL:+`.
|
| |
+ This points to the collection's homepage on Ansible Galaxy.
|
| |
+
|
| |
+
|
| |
+ == Dependencies
|
| |
+
|
| |
+ === Buildtime
|
| |
+
|
| |
+ Collections MUST have `+BuildRequires: ansible-packaging+`.
|
| |
+ `+ansible-packaging+` provides macros and a dependency generator for packaging
|
| |
+ Ansible Collections.
|
| |
+ It also pulls in `+ansible-core+`,
|
| |
+ so `+BuildRequires: ansible-core+` SHOULD NOT be added manually.
|
| |
+
|
| |
+ === Runtime
|
| |
+
|
| |
+ The dependency generator will generate the appropriate dependency on the Ansible engine.
|
| |
+ This ensures compatibility with Fedora 35
|
| |
+ which contains the classic `+ansible+` 2.9 package (instead of the collections bundle)
|
| |
+ and `+ansible-core+`. Both versions of the Ansible engine support collections,
|
| |
+ but they are not parallel installable.
|
| |
+ Packages MUST NOT manually `+Require+` `+ansible-core+` or `+ansible+`,
|
| |
+ unless they are known to require a specific version,
|
| |
+ in which case the appropriate version constraints should be used.
|
| |
+
|
| |
+ The dependency generator also handles inter-collection dependencies.
|
| |
+
|
| |
+ ==== External dependencies of plugins
|
| |
+
|
| |
+ Ansible collections may contain various plugins that have
|
| |
+ various external dependencies.
|
| |
+ The Ansible dev guide
|
| |
+ https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_best_practices.html#importing-and-using-shared-code[mandates]
|
| |
+ that plugins fail cleanly if these dependencies are not installed.
|
| |
+ Many times, external dependencies are only needed for a small subset of the
|
| |
+ collection which may or may not be widely used.
|
| |
+ Therefore, collection packages SHOULD weakly depend on these external libraries,
|
| |
+ i.e. use Recommends instead of Requires.
|
| |
+
|
| |
+ Module dependencies are only needed on the target node not the controller node.
|
| |
+ Therefore, collection packages SHOULD NOT depend on these dependencies at all,
|
| |
+ weakly or strongly.
|
| |
+ Users are responsible for installing these dependencies on the target host.
|
| |
+ Modules that are intended to be used with `+delegate_to: localhost+` are an
|
| |
+ exception to this rule.
|
| |
+
|
| |
+ The situation is a bit different for controller plugins, such as
|
| |
+ filter plugins, lookup plugins, connection plugins, or inventory plugins.
|
| |
+ Collections MAY add `+Recommends+` for dependencies of controller plugins.
|
| |
+ However, packagers should use discretion when adding any type of dependency
|
| |
+ and only do so when it is required for
|
| |
+ the central functionality of the collection.
|
| |
+ For instance, it makes sense for `+ansible-collection-community-docker+`
|
| |
+ to Recommend `+python3-docker+`,
|
| |
+ but it doesn't make sense for the larger, more general
|
| |
+ ansible-collection-community-general collection to Recommend `+python3-redis+`
|
| |
+ for the `+redis+` lookup plugin.
|
| |
+ This guideline seeks to prevent ballooning collection packages.
|
| |
+ `+ansible-core+` and `+ansible+` follow this same principal.
|
| |
+
|
| |
+
|
| |
+ == Build and Installation
|
| |
+
|
| |
+ To build the collection artifact,
|
| |
+ packages MUST use `+%ansible_collection_build+` in `+%build+`.
|
| |
+ `+%ansible_collection_install+` MUST be used in `+%install+` to install the
|
| |
+ artifact.
|
| |
+
|
| |
+ Packagers SHOULD use `+%files -f %{ansible_collection_filelist}+` to install
|
| |
+ the collection.
|
| |
+ The `+%{ansible_collection_filelist}+` is populated by `+%ansible_collection_install+`.
|
| |
+
|
| |
+ == Unit Tests
|
| |
+
|
| |
+ As per xref:index.adoc#_test_suites[the general Fedora Packaging Guidelines],
|
| |
+ collection packages SHOULD run upstream unit tests in `+%check+` if practical.
|
| |
+ Integration tests are impossible to run in the RPM build environment.
|
| |
+ In order to run unit tests, collections MUST `+BuildRequire+`
|
| |
+ `+ansible-packaging-tests+`, which pulls in the necessary dependencies.
|
| |
+ Some collections have other testing dependencies,
|
| |
+ which are usually specified in `+tests/unit/requirements.txt+`.
|
| |
+ These have to be added manually.
|
| |
+ The `+%ansible_test_unit+` macro MUST be used to run tests.
|
| |
+
|
| |
+ [NOTE]
|
| |
+ .EPEL Compatibility
|
| |
+ ====
|
| |
+ It is currently impossible to run unit tests on EPEL 8 and 9.
|
| |
+
|
| |
+ ansible-core in RHEL 8.6 is built against python38. In c8s and the next RHEL
|
| |
+ 8 minor release, it will be built against python39. The testing dependencies
|
| |
+ are not yet packaged for either Python version in EPEL 8.
|
| |
+
|
| |
+ ansible-test in RHEL 9.0 still needs python3-mock, but this
|
| |
+ requirement has been removed in CentOS 9 Stream.
|
| |
+
|
| |
+ The rest of these guidelines are applicable to EPEL 8 and 9,
|
| |
+ and `+ansible-packaging+` itself is available there.
|
| |
+ ====
|
| |
+
|
| |
+ == Unnecessary Files
|
| |
+
|
| |
+ By default, collections ship with all of the files in the repository root,
|
| |
+ unless they are manually excluded.
|
| |
+ Therefore, many collections contain development files that are unwanted by users.
|
| |
+
|
| |
+ Packagers SHOULD exclude these files, which SHOULD be done by patching the
|
| |
+ collection's `+galaxy.yml+` to add these files to the `+build_ignore+`
|
| |
+ configuration.
|
| |
+ These files SHOULD NOT be removed with `+rm+`.
|
| |
+ See the https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html#collection-galaxy-metadata-structure[Ansible documentation]
|
| |
+ for more information on the `+galaxy.yml+` syntax.
|
| |
+
|
| |
+ Common development files include:
|
| |
+
|
| |
+ * The `+tests+` directory containing unit and integration tests
|
| |
+ * SCM configuration such as `+.gitignore+` and `+.keep+` files
|
| |
+ * The `+.azure-pipelines+` and `+.github+` directories that contain CI configuration
|
| |
+
|
| |
+ These files often have to be removed downstream, as there are some unresolved
|
| |
+ issues with pushing these changes to upstream community collections. These
|
| |
+ issues are irrelevant in the Fedora context.
|
| |
+
|
| |
+ == Shebangs
|
| |
+
|
| |
+ Ansible plugins are not executable. However, many of them have `+#!/usr/bin/python+`
|
| |
+ shebangs for legacy reasons.
|
| |
+ These shebangs MUST be removed for the following reasons:
|
| |
+
|
| |
+ 1. Non-executable files shouldn't have shebangs
|
| |
+ 2. Keeping the shebangs results in an unnecessary dependency
|
| |
+ on `+python-unversioned-command+`.
|
| |
+
|
| |
+ `+%py3_shebang_fix+` MUST NOT be used, as it will break compatibility
|
| |
+ with certain Ansible target nodes.
|
| |
+ It won't fix the non-executable file issue, either.
|
| |
+
|
| |
+ Shebangs can be removed with:
|
| |
+
|
| |
+ [source,bash]
|
| |
+ ----
|
| |
+ find -type f ! -executable -name '*.py' -print -exec sed -i -e '1{\@^#!.*@d}' '{}' +
|
| |
+ ----
|
| |
+
|
| |
+
|
| |
+ == Documentation and License Files
|
| |
+
|
| |
+ License files and documentation for collections are installed to the
|
| |
+ collection's directory in `+/usr/share/ansible+`, by default.
|
| |
+ Packagers MAY choose to either
|
| |
+ mark the license and documentation files in this directory with `+%license+`
|
| |
+ and `+%doc+`
|
| |
+ or to add the correct paths to `+build_ignore+` in `+galaxy.yml+` and
|
| |
+ install them into the standard directories.
|
| |
+ Avoid duplicating these files in both places.
|
| |
+
|
| |
+ Note that some multi-licensed collections store licenses in a `+LICENSES+`
|
| |
+ directory. This whole directory MUST be marked with `+%license+`.
|
| |
+
|
| |
+ Refer to the xref:legal::index.adoc[Legal docs] for the rules about
|
| |
+ allowed licenses and determining the `+License:+` field.
|
| |
+
|
| |
+
|
| |
+ == Example Specfile
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ # Only run tests where the dependencies are available
|
| |
+ %if %{defined fedora}
|
| |
+ %bcond_without tests
|
| |
+ %else
|
| |
+ %bcond_with tests
|
| |
+ %endif
|
| |
+
|
| |
+ Name: ansible-collection-community-rabbitmq
|
| |
+ Version: 1.2.2
|
| |
+ Release: 1%{?dist}
|
| |
+ Summary: RabbitMQ collection for Ansible
|
| |
+
|
| |
+ # plugins/module_utils/_version.py: Python Software Foundation License version 2
|
| |
+ License: GPL-3.0-or-later and PSF-2.0
|
| |
+ URL: %{ansible_collection_url community rabbitmq}
|
| |
+ %global forgeurl https://github.com/ansible-collections/community.rabbitmq
|
| |
+ Source0: %{forgeurl}/archive/%{version}/%{name}-%{version}.tar.gz
|
| |
+ # Patch galaxy.yml to exclude unnecessary files from the built collection.
|
| |
+ # This is a downstream only patch.
|
| |
+ Patch0: build_ignore.patch
|
| |
+
|
| |
+ BuildRequires: ansible-packaging
|
| |
+ %if %{with tests}
|
| |
+ BuildRequires: ansible-packaging-tests
|
| |
+ # Collection specific test dependency
|
| |
+ BuildRequires: glibc-all-langpacks
|
| |
+ %endif
|
| |
+
|
| |
+ BuildArch: noarch
|
| |
+
|
| |
+ %description
|
| |
+ %{summary}.
|
| |
+
|
| |
+
|
| |
+ %prep
|
| |
+ %autosetup -n community.rabbitmq-%{version} -p1
|
| |
+ find -type f ! -executable -name '*.py' -print -exec sed -i -e '1{\@^#!.*@d}' '{}' +
|
| |
+
|
| |
+
|
| |
+ %build
|
| |
+ %ansible_collection_build
|
| |
+
|
| |
+
|
| |
+ %install
|
| |
+ %ansible_collection_install
|
| |
+
|
| |
+
|
| |
+ %if %{with tests}
|
| |
+ %check
|
| |
+ %ansible_test_unit
|
| |
+ %endif
|
| |
+
|
| |
+
|
| |
+ %files -f %{ansible_collection_filelist}
|
| |
+ %license COPYING PSF-license.txt
|
| |
+ %doc README.md CHANGELOG.rst
|
| |
+
|
| |
+ %changelog
|
| |
+ ----
|
| |
+
|
| |
+ build_ignore.patch:
|
| |
+
|
| |
+ [source, patch]
|
| |
+ ----
|
| |
+ diff --git a/galaxy.yml b/galaxy.yml
|
| |
+ index 0b37162..acd029a 100644
|
| |
+ --- a/galaxy.yml
|
| |
+ +++ b/galaxy.yml
|
| |
+ @@ -13,3 +13,13 @@ repository: https://github.com/ansible-collections/community.rabbitmq
|
| |
+ documentation: https://docs.ansible.com/ansible/latest/collections/community/rabbitmq/
|
| |
+ homepage: https://github.com/ansible-collections/community.rabbitmq
|
| |
+ issues: https://github.com/ansible-collections/community.rabbitmq/issues
|
| |
+ +build_ignore:
|
| |
+ + # Remove unnecessary development files from the built package.
|
| |
+ + - tests
|
| |
+ + - .azure-pipelines
|
| |
+ + - .gitignore
|
| |
+ + # Licenses and docs are installed with %%doc and %%license
|
| |
+ + - PSF-license.txt
|
| |
+ + - COPYING
|
| |
+ + - README.md
|
| |
+ + - CHANGELOG.rst
|
| |
+ ----
|
| |
+
|
| |
+
|
| |
+ == Macro Breakdown
|
| |
+
|
| |
+ Here is a short breakdown of exactly what each macro included in
|
| |
+ `+ansible-packaging+` does.
|
| |
+
|
| |
+
|
| |
+ [#ansible_collection_url]
|
| |
+ ===== `+%ansible_collection_url+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ URL: %{ansible_collection_url NAMESPACE NAME}
|
| |
+ ----
|
| |
+
|
| |
+ This macro points to a collection's Ansible Galaxy page.
|
| |
+ It is intended to be used for the `+URL:+` tag in the specfile preamble.
|
| |
+ It takes the collection namespace and collection name as arguments.
|
| |
+
|
| |
+ If no arguments are passed to this macro, it falls back to the values
|
| |
+ of `+%{collection_namespace}+` and `+%{collection_name}+` if they are set in the specfile.
|
| |
+ New packages SHOULD explicitly pass the namespace and name as arguments.
|
| |
+ The fallback may be removed in the future.
|
| |
+ See the link:#legacy_macros[Legacy Macros] section for more information.
|
| |
+
|
| |
+
|
| |
+ [#ansible_collection_build]
|
| |
+
|
| |
+ ===== `+%ansible_collection_build+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %build
|
| |
+ %ansible_collection_build
|
| |
+ ----
|
| |
+
|
| |
+ This macro simply runs `+ansible-galaxy collection build+`.
|
| |
+
|
| |
+
|
| |
+ [#ansible_collection_install]
|
| |
+ ===== `+%ansible_collection_install+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %install
|
| |
+ %ansible_collection_install
|
| |
+ ----
|
| |
+
|
| |
+ This macro pulls out the collection namespace, name, and version from `+galaxy.yml+`
|
| |
+ and then uses it to run `+ansible-galaxy collection install+`.
|
| |
+ After that, it writes out `+%{ansible_collection_filelist}` based on the
|
| |
+ metadata it previously extracted
|
| |
+
|
| |
+
|
| |
+ [#ansible_test_unit]
|
| |
+ ===== `+%ansible_test_unit+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %check
|
| |
+ %ansible_test_unit
|
| |
+ ----
|
| |
+ This macro parses galaxy.yml to determine the collection namespace and name
|
| |
+ that's needed to create the directory structure that ansible-test expects.
|
| |
+ After creating a temporary build directory with the needed structure, the
|
| |
+ script runs ansible-test units with the provided arguments.
|
| |
+
|
| |
+
|
| |
+ [#ansible_collection_filelist]
|
| |
+ ===== `+%{ansible_collection_filelist}+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %files -f %{ansible_collection_filelist}
|
| |
+ %doc ...
|
| |
+ %license ...
|
| |
+ ----
|
| |
+
|
| |
+ This macro points a file list that's written out by `+%ansible_collection_install+`.
|
| |
+ Currently, it only contains a single entry to own the collection's entire
|
| |
+ directory in `+%{ansible_collections_dir}+`
|
| |
+
|
| |
+
|
| |
+ [#ansible_collections_dir]
|
| |
+
|
| |
+ This macro expands to `+%{_datadir}/ansible/collections/ansible_collections+`.
|
| |
+ It is used internally by the other macros.
|
| |
+ Packagers are expected to use `+%ansible_collection_install+` and
|
| |
+ `+%ansible_collection_filelist+` instead of directly referencing this directory.
|
| |
+
|
| |
+ [#legacy_macros]
|
| |
+ === Legacy macros
|
| |
+
|
| |
+ [#collection_namespace]
|
| |
+ ===== `+%{collection_namepsace}+`
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %global collection_namespace NAMESPACE
|
| |
+ ----
|
| |
+
|
| |
+ The ansible-packaging macros previously required
|
| |
+ packagers to manually set `+%collection_namespace+` in specfiles.
|
| |
+ Now, the macros extract the collection namespace from the `galaxy.yml`.
|
| |
+
|
| |
+
|
| |
+ [#collection_name]
|
| |
+ ===== `+%{collection_name}+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %global collection_name NAME
|
| |
+ ----
|
| |
+
|
| |
+ The ansible-packaging macros previously required
|
| |
+ packagers to manually set `+%collection_namespace+` in specfiles.
|
| |
+ Now, the macros extract the collection namespace from the `galaxy.yml`.
|
| |
+
|
| |
+
|
| |
+ [#ansible_collection_files]
|
| |
+ ===== `+%{ansible_collection_files}+`
|
| |
+
|
| |
+ *Usage:*
|
| |
+
|
| |
+ [source,RPMSpec]
|
| |
+ ----
|
| |
+ %files
|
| |
+ %doc ...
|
| |
+ %license ...
|
| |
+ %{ansible_collection_files}
|
| |
+ ----
|
| |
+
|
| |
+ New specfiles should use `+%files -f %{ansible_collection_filelist}+` instead
|
| |
+ of this macro.
|
| |
+ `+%{ansible_collection_files}+` requires setting
|
| |
+ `+%collection_namespace+` and `+%collection_name+`.
|
| |
We've had Ansible collection packaging macros for two years now, so I figured it was time to write up some guidelines. This is marked as a draft, as I'm still soliciting feedback and tying up some loose ends:
Mainly, I'm thinking about removing the need for the
%global collection_*
boilerplate and having the macros read from the metadata, instead. For that to happen,%{ansible_collection_files}
would need to be replaced with an approach that passes a file list to%files -f
so it could be dynamically generated. This is a nice side effect, IMO, because this would align the ansible macros with other ecosystems.To give credit where credit is due, @ignatenkobrain initially wrote these macros.
/cc @kevin @dmsimard @ngompa @ignatenkobrain