[APT] Add policy/preference-related facts/operations/options
Is your feature request related to a problem? Please describe
In some cases it is useful to force or ban specific versions of packages from being installed, at the initial installation step, as well as in downstream upgrade steps. This can be managed through apt-cache policy (aka "preferences") features.
These cases range from compatibility with the application being run to excluding buggy/unsafe versions of the software or even completely forbidding some package to be installed.
Those settings are intended to last beyond the initial installation of the package, through the entire lifetime of the target system.
Describe the solution you'd like
In a pyinfra deploy, I'd like to be able to describe these policies with operations and let apt.packages and apt.upgrade naturally respect them.
Main interface
Ideas for the format this could take:
apt.policy(
name="Pin version of python",
packages="python3.8",
pin={
"version": "3.8.5*",
"distrib": "stable",
"origin": "Debian",
},
priority=1001,
)
With careful defaults and maybe a couple constants fora readability, this could be much shorter for simple use cases. For example, the same would be:
apt.policy(
name="Pin version of python",
packages="python3.8",
pin={"version": "3.8.5*"}, # Default both origin and release to being "*" (== unset)
priority=apt.policy.ALWAYS, # This would be the default value, just adding here to highlight constant usage
)
Integration in apt.packages
In addition, this could be combined into the apt.packages operation. The example above would be achieved with:
apt.packages(
name="Install Python 3.8.5",
packages="python3.8=3.8.5*",
policy_pin=apt.policy.ALWAYS,
)
Hey @gchazot! I like this idea a lot, apt.policy would be a great addition to the module.
This could be implemented relatively painlessly in the same way as [yum|dnf].repo where they create an in-memory file for the repo and then use files.put to ensure it exists (https://github.com/Fizzadar/pyinfra/blob/current/pyinfra/operations/util/packaging.py#L236-L256).
So for your Python example it'd just need to create a file like so:
# /etc/apt/preferences.d/X
Package: python3.8
Pin: version=3.8.5*
Pin-Priority: 1001
The hard bit is what filename to use :)
The secondary part could be more complex - would probably have to split the version, and error(?) if there's no version.. More to think about for that part...