Metadata-Version: 2.4
Name: ansible-el-compat
Version: 0.1.5
Summary: Ansible collection carlijoy.compat — backport of ansible.builtin.dnf for EL8 + ansible-core >= 2.17 via /usr/libexec/platform-python
Project-URL: Homepage, https://github.com/CarliJoy/ansible-el-compat
Project-URL: Repository, https://github.com/CarliJoy/ansible-el-compat
Project-URL: Issues, https://github.com/CarliJoy/ansible-el-compat/issues
Project-URL: Changelog, https://github.com/CarliJoy/ansible-el-compat/releases
Author-email: CarliJoy <carlijoy@users.noreply.github.com>
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: almalinux,ansible,backport,compat,dnf,el8,rhel8,rocky
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Requires-Dist: ansible-core>=2.17
Description-Content-Type: text/markdown

# ansible-el-compat

**Ansible collection `carlijoy.compat`** — backport of `ansible.builtin` modules for EL8 + ansible-core >= 2.17.

## The problem

`ansible-core >= 2.17` ships module code that uses `from __future__ import annotations`,
requiring Python 3.10+ on managed hosts. On AlmaLinux / Rocky Linux / RHEL 8,
`python3-dnf` is only available for Python 3.6 and 3.9 — so `ansible.builtin.dnf`
fails with a modern ansible-core.

## The solution

This collection backports `ansible.builtin.dnf` to EL8 by shipping a pre-built
binary module that **is** the original `ansible.builtin.dnf` from ansible-core 2.15
(the last release that fully supported EL8), bundled together with all of its
`ansible.module_utils.*` dependencies into a self-executing zip archive.

The bundle carries a `#!/usr/libexec/platform-python` shebang. Ansible detects
it as a binary module, copies it to the managed host, and runs it directly —
without the ansiballz wrapper — under `/usr/libexec/platform-python`, the EL8
system Python that has `python3-dnf` available. The result is 100% feature
parity with `ansible.builtin.dnf` on EL8 targets, regardless of which Python
interpreter drives ansible-core on the controller.

The bundle is regenerated by `build-script/build_dnf_bundle.py` (uses
`uv run` with ansible-core 2.15 pinned via inline script metadata).

## Installation

### via pip / uv (recommended)

```bash
pip install ansible-el-compat
# or
uv pip install ansible-el-compat
```

No `ansible-galaxy` required — the collection is discovered automatically from
`site-packages/ansible_collections/`.

### via ansible-galaxy

```bash
ansible-galaxy collection install carlijoy.compat
```

## Modules

### `carlijoy.compat.dnf`

Backport of `ansible.builtin.dnf` for EL8. Accepts the same parameters as
`ansible.builtin.dnf` — it is that module, just packaged to run under the
right Python interpreter.

#### Common options

| Option              | Type        | Default   | Description                                    |
|---------------------|-------------|-----------|------------------------------------------------|
| `name`              | str or list | required  | Package name(s), version constraints supported |
| `state`             | str         | `present` | `present`, `absent`, or `latest`               |
| `enablerepo`        | list        | `[]`      | Repos to enable for this transaction only      |
| `disablerepo`       | list        | `[]`      | Repos to disable for this transaction only     |
| `disable_gpg_check` | bool        | `false`   | Pass `--nogpgcheck` to dnf                     |
| `update_cache`      | bool        | `false`   | Run `dnf makecache` before the transaction     |

#### Examples

```yaml
- name: Install a package
  carlijoy.compat.dnf:
    name: htop
    state: present

- name: Install multiple packages
  carlijoy.compat.dnf:
    name:
      - htop
      - curl
    state: present

- name: Remove a package
  carlijoy.compat.dnf:
    name: telnet
    state: absent

- name: Install from a specific repo only
  carlijoy.compat.dnf:
    name: rabbitmq-server <= 4
    disablerepo: '*'
    enablerepo: rabbitmq
    state: present

- name: Upgrade, refreshing cache first
  carlijoy.compat.dnf:
    name: openssl
    state: latest
    update_cache: true
```

#### Migrating from ansible.builtin.dnf

```yaml
# Before
- ansible.builtin.dnf:
    name: httpd
    state: present

# After
- carlijoy.compat.dnf:
    name: httpd
    state: present
```

For persistent repo enable/disable (not per-transaction), continue using
`community.general.dnf_config_manager`.

## Development

```bash
git clone https://github.com/CarliJoy/ansible-el-compat
cd ansible-el-compat
uv sync --group dev
uv run prek install          # install git hooks
uv run prek run --all-files  # lint + type check
uv run pytest tests/ -v      # integration tests (requires Docker)
```

Tests use [testcontainers](https://testcontainers.com/) to spin up a real
AlmaLinux 8 container with SSH and Python 3.12, then run Ansible against it.
Docker must be available on the test host.

### Rebuilding the backported bundle

```bash
uv run build-script/build_dnf_bundle.py
```

This downloads ansible-core 2.15 into an isolated environment (via the inline
`uv` script metadata), extracts `ansible/modules/dnf.py` and all transitive
`ansible/module_utils/` dependencies, and writes the self-executing zip to
`src/ansible_collections/carlijoy/compat/plugins/modules/dnf`.

## Attribution & Bundled Components

This collection bundles the original `ansible.builtin.dnf` module from
**ansible-core 2.15**, written by the Ansible project contributors. The bundle
is not a fork or reimplementation — it is that exact module, unchanged, just
repackaged to run under the EL8 system Python interpreter that has access to
`python3-dnf`. All credit for the actual dnf functionality belongs to the
original authors.

### Why the module is bundled

ansible-core 2.17 introduced `from __future__ import annotations` across its
module code, which requires Python 3.10+ on managed hosts. On EL8 targets,
`python3-dnf` is only available for Python 3.6 and 3.9, so
`ansible.builtin.dnf` started failing. This collection solves that by bundling
the last ansible-core version (2.15) that fully supports EL8 and shipping it as
a binary module with a `#!/usr/libexec/platform-python` shebang that bypasses
the controller's Python choice entirely.

### Links to the original source

- ansible/ansible repository: <https://github.com/ansible/ansible>
- `dnf.py` at v2.15.0: <https://github.com/ansible/ansible/blob/v2.15.0/lib/ansible/modules/dnf.py>
- `yumdnf.py` at v2.15.0: <https://github.com/ansible/ansible/blob/v2.15.0/lib/ansible/module_utils/yumdnf.py>

Thank you to the Ansible project and all Red Hat contributors for the work that
makes this compatibility shim possible. See the `NOTICE` file in this
repository for the complete attribution and license notices for every bundled
file.

## License

GPL-3.0-or-later

The backported module bundle contains code from
[ansible-core](https://github.com/ansible/ansible), which is licensed under
the GNU General Public License v3.0 or later. This collection is therefore
also distributed under GPL-3.0-or-later.
