pyinfra 0.11.dev3__py3-none-any.whl → 3.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. pyinfra/__init__.py +9 -12
  2. pyinfra/__main__.py +4 -0
  3. pyinfra/api/__init__.py +19 -3
  4. pyinfra/api/arguments.py +413 -0
  5. pyinfra/api/arguments_typed.py +79 -0
  6. pyinfra/api/command.py +274 -0
  7. pyinfra/api/config.py +222 -28
  8. pyinfra/api/connect.py +33 -13
  9. pyinfra/api/connectors.py +27 -0
  10. pyinfra/api/deploy.py +65 -66
  11. pyinfra/api/exceptions.py +73 -18
  12. pyinfra/api/facts.py +267 -200
  13. pyinfra/api/host.py +416 -50
  14. pyinfra/api/inventory.py +121 -160
  15. pyinfra/api/metadata.py +69 -0
  16. pyinfra/api/operation.py +432 -262
  17. pyinfra/api/operations.py +273 -260
  18. pyinfra/api/state.py +302 -248
  19. pyinfra/api/util.py +309 -369
  20. pyinfra/connectors/base.py +173 -0
  21. pyinfra/connectors/chroot.py +212 -0
  22. pyinfra/connectors/docker.py +405 -0
  23. pyinfra/connectors/dockerssh.py +297 -0
  24. pyinfra/connectors/local.py +238 -0
  25. pyinfra/connectors/scp/__init__.py +1 -0
  26. pyinfra/connectors/scp/client.py +204 -0
  27. pyinfra/connectors/ssh.py +727 -0
  28. pyinfra/connectors/ssh_util.py +114 -0
  29. pyinfra/connectors/sshuserclient/client.py +309 -0
  30. pyinfra/connectors/sshuserclient/config.py +102 -0
  31. pyinfra/connectors/terraform.py +135 -0
  32. pyinfra/connectors/util.py +417 -0
  33. pyinfra/connectors/vagrant.py +183 -0
  34. pyinfra/context.py +145 -0
  35. pyinfra/facts/__init__.py +7 -6
  36. pyinfra/facts/apk.py +22 -7
  37. pyinfra/facts/apt.py +117 -60
  38. pyinfra/facts/brew.py +100 -15
  39. pyinfra/facts/bsdinit.py +23 -0
  40. pyinfra/facts/cargo.py +37 -0
  41. pyinfra/facts/choco.py +47 -0
  42. pyinfra/facts/crontab.py +195 -0
  43. pyinfra/facts/deb.py +94 -0
  44. pyinfra/facts/dnf.py +48 -0
  45. pyinfra/facts/docker.py +96 -23
  46. pyinfra/facts/efibootmgr.py +113 -0
  47. pyinfra/facts/files.py +629 -58
  48. pyinfra/facts/flatpak.py +77 -0
  49. pyinfra/facts/freebsd.py +70 -0
  50. pyinfra/facts/gem.py +19 -6
  51. pyinfra/facts/git.py +59 -14
  52. pyinfra/facts/gpg.py +150 -0
  53. pyinfra/facts/hardware.py +313 -167
  54. pyinfra/facts/iptables.py +72 -62
  55. pyinfra/facts/launchd.py +44 -0
  56. pyinfra/facts/lxd.py +17 -4
  57. pyinfra/facts/mysql.py +122 -86
  58. pyinfra/facts/npm.py +17 -9
  59. pyinfra/facts/openrc.py +71 -0
  60. pyinfra/facts/opkg.py +246 -0
  61. pyinfra/facts/pacman.py +50 -7
  62. pyinfra/facts/pip.py +24 -7
  63. pyinfra/facts/pipx.py +82 -0
  64. pyinfra/facts/pkg.py +15 -6
  65. pyinfra/facts/pkgin.py +35 -0
  66. pyinfra/facts/podman.py +54 -0
  67. pyinfra/facts/postgres.py +178 -0
  68. pyinfra/facts/postgresql.py +6 -147
  69. pyinfra/facts/rpm.py +105 -0
  70. pyinfra/facts/runit.py +77 -0
  71. pyinfra/facts/selinux.py +161 -0
  72. pyinfra/facts/server.py +762 -285
  73. pyinfra/facts/snap.py +88 -0
  74. pyinfra/facts/systemd.py +139 -0
  75. pyinfra/facts/sysvinit.py +59 -0
  76. pyinfra/facts/upstart.py +35 -0
  77. pyinfra/facts/util/__init__.py +17 -0
  78. pyinfra/facts/util/databases.py +4 -6
  79. pyinfra/facts/util/packaging.py +37 -6
  80. pyinfra/facts/util/units.py +30 -0
  81. pyinfra/facts/util/win_files.py +99 -0
  82. pyinfra/facts/vzctl.py +20 -13
  83. pyinfra/facts/xbps.py +35 -0
  84. pyinfra/facts/yum.py +34 -40
  85. pyinfra/facts/zfs.py +77 -0
  86. pyinfra/facts/zypper.py +42 -0
  87. pyinfra/local.py +45 -83
  88. pyinfra/operations/__init__.py +12 -0
  89. pyinfra/operations/apk.py +99 -0
  90. pyinfra/operations/apt.py +496 -0
  91. pyinfra/operations/brew.py +232 -0
  92. pyinfra/operations/bsdinit.py +59 -0
  93. pyinfra/operations/cargo.py +45 -0
  94. pyinfra/operations/choco.py +61 -0
  95. pyinfra/operations/crontab.py +194 -0
  96. pyinfra/operations/dnf.py +213 -0
  97. pyinfra/operations/docker.py +492 -0
  98. pyinfra/operations/files.py +2014 -0
  99. pyinfra/operations/flatpak.py +95 -0
  100. pyinfra/operations/freebsd/__init__.py +12 -0
  101. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  102. pyinfra/operations/freebsd/pkg.py +219 -0
  103. pyinfra/operations/freebsd/service.py +116 -0
  104. pyinfra/operations/freebsd/sysrc.py +92 -0
  105. pyinfra/operations/gem.py +48 -0
  106. pyinfra/operations/git.py +420 -0
  107. pyinfra/operations/iptables.py +312 -0
  108. pyinfra/operations/launchd.py +45 -0
  109. pyinfra/operations/lxd.py +69 -0
  110. pyinfra/operations/mysql.py +610 -0
  111. pyinfra/operations/npm.py +57 -0
  112. pyinfra/operations/openrc.py +63 -0
  113. pyinfra/operations/opkg.py +89 -0
  114. pyinfra/operations/pacman.py +82 -0
  115. pyinfra/operations/pip.py +206 -0
  116. pyinfra/operations/pipx.py +103 -0
  117. pyinfra/operations/pkg.py +71 -0
  118. pyinfra/operations/pkgin.py +92 -0
  119. pyinfra/operations/postgres.py +437 -0
  120. pyinfra/operations/postgresql.py +30 -0
  121. pyinfra/operations/puppet.py +41 -0
  122. pyinfra/operations/python.py +73 -0
  123. pyinfra/operations/runit.py +184 -0
  124. pyinfra/operations/selinux.py +190 -0
  125. pyinfra/operations/server.py +1100 -0
  126. pyinfra/operations/snap.py +118 -0
  127. pyinfra/operations/ssh.py +217 -0
  128. pyinfra/operations/systemd.py +150 -0
  129. pyinfra/operations/sysvinit.py +142 -0
  130. pyinfra/operations/upstart.py +68 -0
  131. pyinfra/operations/util/__init__.py +12 -0
  132. pyinfra/operations/util/docker.py +407 -0
  133. pyinfra/operations/util/files.py +247 -0
  134. pyinfra/operations/util/packaging.py +338 -0
  135. pyinfra/operations/util/service.py +46 -0
  136. pyinfra/operations/vzctl.py +137 -0
  137. pyinfra/operations/xbps.py +78 -0
  138. pyinfra/operations/yum.py +213 -0
  139. pyinfra/operations/zfs.py +176 -0
  140. pyinfra/operations/zypper.py +193 -0
  141. pyinfra/progress.py +44 -32
  142. pyinfra/py.typed +0 -0
  143. pyinfra/version.py +9 -1
  144. pyinfra-3.6.dist-info/METADATA +142 -0
  145. pyinfra-3.6.dist-info/RECORD +160 -0
  146. {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info}/WHEEL +1 -2
  147. pyinfra-3.6.dist-info/entry_points.txt +12 -0
  148. {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info/licenses}/LICENSE.md +1 -1
  149. pyinfra_cli/__init__.py +1 -0
  150. pyinfra_cli/cli.py +793 -0
  151. pyinfra_cli/commands.py +66 -0
  152. pyinfra_cli/exceptions.py +155 -65
  153. pyinfra_cli/inventory.py +233 -89
  154. pyinfra_cli/log.py +39 -43
  155. pyinfra_cli/main.py +26 -495
  156. pyinfra_cli/prints.py +215 -156
  157. pyinfra_cli/util.py +172 -105
  158. pyinfra_cli/virtualenv.py +25 -20
  159. pyinfra/api/connectors/__init__.py +0 -21
  160. pyinfra/api/connectors/ansible.py +0 -99
  161. pyinfra/api/connectors/docker.py +0 -178
  162. pyinfra/api/connectors/local.py +0 -169
  163. pyinfra/api/connectors/ssh.py +0 -402
  164. pyinfra/api/connectors/sshuserclient/client.py +0 -105
  165. pyinfra/api/connectors/sshuserclient/config.py +0 -90
  166. pyinfra/api/connectors/util.py +0 -63
  167. pyinfra/api/connectors/vagrant.py +0 -155
  168. pyinfra/facts/init.py +0 -176
  169. pyinfra/facts/util/files.py +0 -102
  170. pyinfra/hook.py +0 -41
  171. pyinfra/modules/__init__.py +0 -11
  172. pyinfra/modules/apk.py +0 -64
  173. pyinfra/modules/apt.py +0 -272
  174. pyinfra/modules/brew.py +0 -122
  175. pyinfra/modules/files.py +0 -711
  176. pyinfra/modules/gem.py +0 -30
  177. pyinfra/modules/git.py +0 -115
  178. pyinfra/modules/init.py +0 -344
  179. pyinfra/modules/iptables.py +0 -271
  180. pyinfra/modules/lxd.py +0 -45
  181. pyinfra/modules/mysql.py +0 -347
  182. pyinfra/modules/npm.py +0 -47
  183. pyinfra/modules/pacman.py +0 -60
  184. pyinfra/modules/pip.py +0 -99
  185. pyinfra/modules/pkg.py +0 -43
  186. pyinfra/modules/postgresql.py +0 -245
  187. pyinfra/modules/puppet.py +0 -20
  188. pyinfra/modules/python.py +0 -37
  189. pyinfra/modules/server.py +0 -524
  190. pyinfra/modules/ssh.py +0 -150
  191. pyinfra/modules/util/files.py +0 -52
  192. pyinfra/modules/util/packaging.py +0 -118
  193. pyinfra/modules/vzctl.py +0 -133
  194. pyinfra/modules/yum.py +0 -171
  195. pyinfra/pseudo_modules.py +0 -64
  196. pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
  197. pyinfra-0.11.dev3.dist-info/METADATA +0 -135
  198. pyinfra-0.11.dev3.dist-info/RECORD +0 -95
  199. pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
  200. pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
  201. pyinfra_cli/__main__.py +0 -40
  202. pyinfra_cli/config.py +0 -92
  203. /pyinfra/{modules/util → connectors}/__init__.py +0 -0
  204. /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
@@ -0,0 +1,63 @@
1
+ """
2
+ Manage OpenRC init services.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pyinfra import host
8
+ from pyinfra.api import operation
9
+ from pyinfra.facts.openrc import OpenrcEnabled, OpenrcStatus
10
+
11
+ from .util.service import handle_service_control
12
+
13
+
14
+ @operation()
15
+ def service(
16
+ service: str,
17
+ running=True,
18
+ restarted=False,
19
+ reloaded=False,
20
+ command: str | None = None,
21
+ enabled: bool | None = None,
22
+ runlevel="default",
23
+ ):
24
+ """
25
+ Manage the state of OpenRC services.
26
+
27
+ + service: name of the service to manage
28
+ + running: whether the service should be running
29
+ + restarted: whether the service should be restarted
30
+ + reloaded: whether the service should be reloaded
31
+ + command: custom command to pass like: ``rc-service <service> <command>``
32
+ + enabled: whether this service should be enabled/disabled on boot
33
+ + runlevel: runlevel to manage services for
34
+ """
35
+
36
+ yield from handle_service_control(
37
+ host,
38
+ service,
39
+ host.get_fact(OpenrcStatus, runlevel=runlevel),
40
+ "rc-service {0} {1}",
41
+ running,
42
+ restarted,
43
+ reloaded,
44
+ command,
45
+ )
46
+
47
+ if isinstance(enabled, bool):
48
+ openrc_enabled = host.get_fact(OpenrcEnabled, runlevel=runlevel)
49
+ is_enabled = openrc_enabled.get(service, False)
50
+
51
+ if enabled is True:
52
+ if not is_enabled:
53
+ yield "rc-update add {0} {1}".format(service, runlevel)
54
+ openrc_enabled[service] = True
55
+ else:
56
+ host.noop("service {0} is enabled".format(service))
57
+
58
+ if enabled is False:
59
+ if is_enabled:
60
+ yield "rc-update del {0} {1}".format(service, runlevel)
61
+ openrc_enabled[service] = False
62
+ else:
63
+ host.noop("service {0} is disabled".format(service))
@@ -0,0 +1,89 @@
1
+ """
2
+ Manage packages on OpenWrt using opkg
3
+ + ``update`` - update local copy of package information
4
+ + ``packages`` - install and remove packages
5
+
6
+ see https://openwrt.org/docs/guide-user/additional-software/opkg
7
+ OpenWrt recommends against upgrading all packages thus there is no ``opkg.upgrade`` function
8
+ """
9
+
10
+ from typing import List, Union
11
+
12
+ from pyinfra import host
13
+ from pyinfra.api import StringCommand, operation
14
+ from pyinfra.facts.opkg import OpkgPackages
15
+ from pyinfra.operations.util.packaging import ensure_packages
16
+
17
+ EQUALS = "="
18
+
19
+
20
+ @operation(is_idempotent=False)
21
+ def update():
22
+ """
23
+ Update the local opkg information.
24
+ """
25
+
26
+ yield StringCommand("opkg update")
27
+
28
+
29
+ _update = update
30
+
31
+
32
+ @operation()
33
+ def packages(
34
+ packages: Union[str, List[str]] = "",
35
+ present: bool = True,
36
+ latest: bool = False,
37
+ update: bool = True,
38
+ ):
39
+ """
40
+ Add/remove/update opkg packages.
41
+
42
+ + packages: package or list of packages to that must/must not be present
43
+ + present: whether the package(s) should be installed (default True) or removed
44
+ + latest: whether to attempt to upgrade the specified package(s) (default False)
45
+ + update: run ``opkg update`` before installing packages (default True)
46
+
47
+ Not Supported:
48
+ Opkg does not support version pinning, i.e. ``<pkg>=<version>`` is not allowed
49
+ and will cause an exception.
50
+
51
+ **Examples:**
52
+
53
+ .. code:: python
54
+
55
+ from pyinfra.operations import opkg
56
+ # Ensure packages are installed (will not force package upgrade)
57
+ opkg.packages(['asterisk', 'vim'], name="Install Asterisk and Vim")
58
+
59
+ # Install the latest versions of packages (always check)
60
+ opkg.packages(
61
+ 'vim',
62
+ latest=True,
63
+ name="Ensure we have the latest version of Vim"
64
+ )
65
+ """
66
+ if str(packages) == "" or (
67
+ isinstance(packages, list) and (len(packages) < 1 or all(len(p) < 1 for p in packages))
68
+ ):
69
+ host.noop("empty or invalid package list provided to opkg.packages")
70
+ return
71
+
72
+ pkg_list = packages if isinstance(packages, list) else [packages]
73
+ have_equals = ",".join([pkg.split(EQUALS)[0] for pkg in pkg_list if EQUALS in pkg])
74
+ if len(have_equals) > 0:
75
+ raise ValueError(f"opkg does not support version pinning but found for: '{have_equals}'")
76
+
77
+ if update:
78
+ yield from _update._inner()
79
+
80
+ yield from ensure_packages(
81
+ host,
82
+ pkg_list,
83
+ host.get_fact(OpkgPackages),
84
+ present,
85
+ install_command="opkg install",
86
+ upgrade_command="opkg upgrade",
87
+ uninstall_command="opkg remove",
88
+ latest=latest,
89
+ )
@@ -0,0 +1,82 @@
1
+ """
2
+ Manage pacman packages. (Arch Linux package manager)
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pyinfra import host
8
+ from pyinfra.api import operation
9
+ from pyinfra.facts.pacman import PacmanPackages, PacmanUnpackGroup
10
+
11
+ from .util.packaging import ensure_packages
12
+
13
+
14
+ @operation(is_idempotent=False)
15
+ def upgrade():
16
+ """
17
+ Upgrades all pacman packages.
18
+ """
19
+
20
+ yield "pacman --noconfirm -Su"
21
+
22
+
23
+ _upgrade = upgrade._inner # noqa: E305
24
+
25
+
26
+ @operation(is_idempotent=False)
27
+ def update():
28
+ """
29
+ Updates pacman repositories.
30
+ """
31
+
32
+ yield "pacman -Sy"
33
+
34
+
35
+ _update = update._inner # noqa: E305
36
+
37
+
38
+ @operation()
39
+ def packages(
40
+ packages: str | list[str] | None = None,
41
+ present=True,
42
+ update=False,
43
+ upgrade=False,
44
+ ):
45
+ """
46
+ Add/remove pacman packages.
47
+
48
+ + packages: list of packages to ensure
49
+ + present: whether the packages should be installed
50
+ + update: run ``pacman -Sy`` before installing packages
51
+ + upgrade: run ``pacman -Su`` before installing packages
52
+
53
+ Versions:
54
+ Package versions can be pinned like pacman: ``<pkg>=<version>``.
55
+
56
+ **Example:**
57
+
58
+ .. code:: python
59
+
60
+ from pyinfra.operations import pacman
61
+ pacman.packages(
62
+ name="Install Vim and a plugin",
63
+ packages=["vim-fugitive", "vim"],
64
+ update=True,
65
+ )
66
+ """
67
+
68
+ if update:
69
+ yield from _update()
70
+
71
+ if upgrade:
72
+ yield from _upgrade()
73
+
74
+ yield from ensure_packages(
75
+ host,
76
+ packages,
77
+ host.get_fact(PacmanPackages),
78
+ present,
79
+ install_command="pacman --noconfirm -S",
80
+ uninstall_command="pacman --noconfirm -R",
81
+ expand_package_fact=lambda package: host.get_fact(PacmanUnpackGroup, package=package),
82
+ )
@@ -0,0 +1,206 @@
1
+ """
2
+ Manage pip (python) packages. Compatible globally or inside
3
+ a virtualenv (virtual environment).
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pyinfra import host
9
+ from pyinfra.api import operation
10
+ from pyinfra.facts.files import File
11
+ from pyinfra.facts.pip import PipPackages
12
+
13
+ from . import files
14
+ from .util.packaging import PkgInfo, ensure_packages
15
+
16
+
17
+ @operation()
18
+ def virtualenv(
19
+ path: str,
20
+ python: str | None = None,
21
+ venv=False,
22
+ site_packages=False,
23
+ always_copy=False,
24
+ present=True,
25
+ ):
26
+ """
27
+ Add/remove Python virtualenvs.
28
+
29
+ + python: python interpreter to use
30
+ + venv: use standard venv module instead of virtualenv
31
+ + site_packages: give access to the global site-packages
32
+ + always_copy: always copy files rather than symlinking
33
+ + present: whether the virtualenv should exist
34
+
35
+ **Example:**
36
+
37
+ .. code:: python
38
+
39
+ from pyinfra.operations import pip
40
+ pip.virtualenv(
41
+ name="Create a virtualenv",
42
+ path="/usr/local/bin/venv",
43
+ )
44
+ """
45
+
46
+ # Check for *contents* of a virtualenv, ie don't accept an empty directory
47
+ # as a valid virtualenv but ensure the activate script exists.
48
+ activate_script_path = "{0}/bin/activate".format(path)
49
+
50
+ if present is False:
51
+ if host.get_fact(File, path=activate_script_path):
52
+ yield from files.directory._inner(path, present=False)
53
+ else:
54
+ host.noop("virtualenv {0} does not exist".format(path))
55
+
56
+ if present:
57
+ if not host.get_fact(File, path=activate_script_path):
58
+ # Create missing virtualenv
59
+ command = ["virtualenv"]
60
+
61
+ if venv:
62
+ command = [python or "python", "-m", "venv"]
63
+
64
+ if python and not venv:
65
+ command.append("-p {0}".format(python))
66
+
67
+ if site_packages:
68
+ command.append("--system-site-packages")
69
+
70
+ if always_copy and not venv:
71
+ command.append("--always-copy")
72
+ elif always_copy and venv:
73
+ command.append("--copies")
74
+
75
+ command.append(path)
76
+
77
+ yield " ".join(command)
78
+ else:
79
+ host.noop("virtualenv {0} exists".format(path))
80
+
81
+
82
+ _virtualenv = virtualenv._inner # noqa
83
+
84
+
85
+ @operation()
86
+ def venv(
87
+ path: str,
88
+ python: str | None = None,
89
+ site_packages=False,
90
+ always_copy=False,
91
+ present=True,
92
+ ):
93
+ """
94
+ Add/remove Python virtualenvs.
95
+
96
+ + python: python interpreter to use
97
+ + site_packages: give access to the global site-packages
98
+ + always_copy: always copy files rather than symlinking
99
+ + present: whether the virtualenv should exist
100
+
101
+ **Example:**
102
+
103
+ .. code:: python
104
+
105
+ pip.venv(
106
+ name="Create a virtualenv",
107
+ path="/usr/local/bin/venv",
108
+ )
109
+ """
110
+
111
+ yield from _virtualenv(
112
+ venv=True,
113
+ path=path,
114
+ python=python,
115
+ site_packages=site_packages,
116
+ always_copy=always_copy,
117
+ present=present,
118
+ )
119
+
120
+
121
+ @operation()
122
+ def packages(
123
+ packages: str | list[str] | None = None,
124
+ present=True,
125
+ latest=False,
126
+ requirements: str | None = None,
127
+ pip="pip",
128
+ virtualenv: str | None = None,
129
+ virtualenv_kwargs: dict | None = None,
130
+ extra_install_args: str | None = None,
131
+ ):
132
+ """
133
+ Install/remove/update pip packages.
134
+
135
+ + packages: list of packages to ensure
136
+ + present: whether the packages should be installed
137
+ + latest: whether to upgrade packages without a specified version
138
+ + requirements: location of requirements file to install/uninstall
139
+ + pip: name or path of the pip directory to use
140
+ + virtualenv: root directory of virtualenv to work in
141
+ + virtualenv_kwargs: dictionary of arguments to pass to ``pip.virtualenv``
142
+ + extra_install_args: additional arguments to the pip install command
143
+
144
+ Virtualenv:
145
+ This will be created if it does not exist already. ``virtualenv_kwargs``
146
+ will be passed to ``pip.virtualenv`` which can be used to control how
147
+ the env is created.
148
+
149
+ Versions:
150
+ Package versions can be pinned like pip: ``<pkg>==<version>``.
151
+
152
+ **Example:**
153
+
154
+ .. code:: python
155
+
156
+ pip.packages(
157
+ name="Install pyinfra into a virtualenv",
158
+ packages=["pyinfra"],
159
+ virtualenv="/usr/local/bin/venv",
160
+ )
161
+ """
162
+
163
+ virtualenv_kwargs = virtualenv_kwargs or {}
164
+
165
+ # Ensure any virtualenv
166
+ if virtualenv:
167
+ yield from _virtualenv(virtualenv, **virtualenv_kwargs)
168
+
169
+ # And update pip path
170
+ virtualenv = virtualenv.rstrip("/")
171
+ pip = "{0}/bin/{1}".format(virtualenv, pip)
172
+
173
+ install_command_args = [pip, "install"]
174
+ if extra_install_args:
175
+ install_command_args.append(extra_install_args)
176
+ install_command = " ".join(install_command_args)
177
+
178
+ upgrade_command = "{0} --upgrade".format(install_command)
179
+ uninstall_command = " ".join([pip, "uninstall", "--yes"])
180
+
181
+ # (un)Install requirements
182
+ if requirements is not None:
183
+ if present:
184
+ yield "{0} -r {1}".format(upgrade_command if latest else install_command, requirements)
185
+ else:
186
+ yield "{0} -r {1}".format(uninstall_command, requirements)
187
+
188
+ # Handle passed in packages
189
+ if packages:
190
+ if isinstance(packages, str):
191
+ packages = [packages]
192
+ # PEP-0426 states that Python packages should be compared using lowercase, so lowercase the
193
+ # current packages. PkgInfo.from_pep508 takes care of the package name
194
+ current_packages = host.get_fact(PipPackages, pip=pip)
195
+ current_packages = {pkg.lower(): versions for pkg, versions in current_packages.items()}
196
+
197
+ yield from ensure_packages(
198
+ host,
199
+ list(filter(None, (PkgInfo.from_pep508(package) for package in packages))),
200
+ current_packages,
201
+ present,
202
+ install_command=install_command,
203
+ uninstall_command=uninstall_command,
204
+ upgrade_command=upgrade_command,
205
+ latest=latest,
206
+ )
@@ -0,0 +1,103 @@
1
+ """
2
+ Manage pipx (python) applications.
3
+ """
4
+
5
+ from typing import Optional, Union
6
+
7
+ from pyinfra import host
8
+ from pyinfra.api import operation
9
+ from pyinfra.facts.pipx import PipxEnvironment, PipxPackages
10
+ from pyinfra.facts.server import Path
11
+
12
+ from .util.packaging import PkgInfo, ensure_packages
13
+
14
+
15
+ @operation()
16
+ def packages(
17
+ packages: Optional[Union[str, list[str]]] = None,
18
+ present=True,
19
+ latest=False,
20
+ extra_args: Optional[str] = None,
21
+ ):
22
+ """
23
+ Install/remove/update pipx packages.
24
+
25
+ + packages: list of packages (PEP-508 format) to ensure
26
+ + present: whether the packages should be installed
27
+ + latest: whether to upgrade packages without a specified version
28
+ + extra_args: additional arguments to the pipx command
29
+
30
+ Versions:
31
+ Package versions can be pinned like pip: ``<pkg>==<version>``.
32
+
33
+ **Example:**
34
+
35
+ .. code:: python
36
+
37
+ from pyinfra.operations import pipx
38
+ pipx.packages(
39
+ name="Install ",
40
+ packages=["pyinfra"],
41
+ )
42
+ """
43
+ if packages is None:
44
+ host.noop("no package list provided to pipx.packages")
45
+ return
46
+
47
+ prep_install_command = ["pipx", "install"]
48
+
49
+ if extra_args:
50
+ prep_install_command.append(extra_args)
51
+ install_command = " ".join(prep_install_command)
52
+
53
+ uninstall_command = "pipx uninstall"
54
+ upgrade_command = "pipx upgrade"
55
+
56
+ # PEP-0426 states that Python packages should be compared using lowercase, so lowercase the
57
+ # current packages. PkgInfo.from_pep508 takes care of it for the package names
58
+ current_packages = {
59
+ pkg.lower(): version for pkg, version in host.get_fact(PipxPackages).items()
60
+ }
61
+ if isinstance(packages, str):
62
+ packages = [packages]
63
+
64
+ # pipx support only one package name at a time
65
+ for package in packages:
66
+ if (pkg_info := PkgInfo.from_pep508(package)) is None:
67
+ continue # from_pep508 logged a warning
68
+ yield from ensure_packages(
69
+ host,
70
+ [pkg_info],
71
+ current_packages,
72
+ present,
73
+ install_command=install_command,
74
+ uninstall_command=uninstall_command,
75
+ upgrade_command=upgrade_command,
76
+ latest=latest,
77
+ )
78
+
79
+
80
+ @operation()
81
+ def upgrade_all():
82
+ """
83
+ Upgrade all pipx packages.
84
+ """
85
+ yield "pipx upgrade-all"
86
+
87
+
88
+ @operation()
89
+ def ensure_path():
90
+ """
91
+ Ensure pipx bin dir is in the PATH.
92
+ """
93
+
94
+ # Fetch the current user's PATH
95
+ path = host.get_fact(Path)
96
+ # Fetch the pipx environment variables
97
+ pipx_env = host.get_fact(PipxEnvironment)
98
+
99
+ # If the pipx bin dir is already in the user's PATH, we're done
100
+ if "PIPX_BIN_DIR" in pipx_env and pipx_env["PIPX_BIN_DIR"] in path.split(":"):
101
+ host.noop("pipx bin dir is already in the PATH")
102
+ else:
103
+ yield "pipx ensurepath"
@@ -0,0 +1,71 @@
1
+ """
2
+ Manage BSD packages and repositories. Note that BSD package names are case-sensitive.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pyinfra import host
8
+ from pyinfra.api import operation
9
+ from pyinfra.facts.files import File
10
+ from pyinfra.facts.pkg import PkgPackages
11
+ from pyinfra.facts.server import Arch, Os, OsVersion, Which
12
+
13
+ from .util.packaging import ensure_packages
14
+
15
+
16
+ @operation()
17
+ def packages(packages: str | list[str] | None = None, present=True, pkg_path: str | None = None):
18
+ """
19
+ Install/remove/update pkg packages. This will use ``pkg ...`` where available
20
+ (FreeBSD) and the ``pkg_*`` variants elsewhere.
21
+
22
+ + packages: list of packages to ensure
23
+ + present: whether the packages should be installed
24
+ + pkg_path: the PKG_PATH environment variable to set
25
+
26
+ pkg_path:
27
+ By default this is autogenerated as follows (tested/working for OpenBSD):
28
+ ``http://ftp.<OS>.org/pub/<OS>/<VERSION>/packages/<ARCH>/``. Note that OpenBSD's
29
+ official mirrors only hold the latest two versions packages.
30
+
31
+ NetBSD/FreeBSD helpfully use their own directory structures, so the default won't
32
+ work.
33
+
34
+ **Example:**
35
+
36
+ .. code:: python
37
+
38
+ from pyinfra.operations import pkg
39
+ pkg.packages(
40
+ name="Install Vim and Vim Addon Manager",
41
+ packages=["vim-addon-manager", "vim"],
42
+ )
43
+
44
+ """
45
+
46
+ if present is True:
47
+ if not pkg_path and not host.get_fact(File, path="/etc/installurl"):
48
+ host_os = host.get_fact(Os) or ""
49
+ pkg_path = "http://ftp.{http}.org/pub/{os}/{version}/packages/{arch}/".format(
50
+ http=host_os.lower(),
51
+ os=host_os,
52
+ version=host.get_fact(OsVersion),
53
+ arch=host.get_fact(Arch),
54
+ )
55
+
56
+ # FreeBSD used "pkg ..." and OpenBSD uses "pkg_[add|delete]"
57
+ is_pkg = host.get_fact(Which, command="pkg")
58
+ install_command = "pkg install -y" if is_pkg else "pkg_add"
59
+ uninstall_command = "pkg delete -y" if is_pkg else "pkg_delete"
60
+
61
+ if pkg_path:
62
+ install_command = "PKG_PATH={0} {1}".format(pkg_path, install_command)
63
+
64
+ yield from ensure_packages(
65
+ host,
66
+ packages,
67
+ host.get_fact(PkgPackages),
68
+ present,
69
+ install_command=install_command,
70
+ uninstall_command=uninstall_command,
71
+ )
@@ -0,0 +1,92 @@
1
+ """
2
+ Manage pkgin packages.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pyinfra import host
8
+ from pyinfra.api import operation
9
+ from pyinfra.facts.pkgin import PkginPackages
10
+
11
+ from .util.packaging import ensure_packages
12
+
13
+
14
+ @operation(is_idempotent=False)
15
+ def upgrade():
16
+ """
17
+ Upgrades all pkgin packages.
18
+ """
19
+
20
+ yield "pkgin -y upgrade"
21
+
22
+
23
+ _upgrade = upgrade._inner # noqa: E305
24
+
25
+
26
+ @operation(is_idempotent=False)
27
+ def update():
28
+ """
29
+ Updates pkgin repositories.
30
+ """
31
+
32
+ yield "pkgin -y update"
33
+
34
+
35
+ _update = update._inner # noqa: E305
36
+
37
+
38
+ @operation()
39
+ def packages(
40
+ packages: str | list[str] | None = None,
41
+ present=True,
42
+ latest=False,
43
+ update=False,
44
+ upgrade=False,
45
+ ):
46
+ """
47
+ Add/remove/update pkgin packages.
48
+
49
+ + packages: list of packages to ensure
50
+ + present: whether the packages should be installed
51
+ + latest: whether to upgrade packages without a specified version
52
+ + update: run ``pkgin update`` before installing packages
53
+ + upgrade: run ``pkgin upgrade`` before installing packages
54
+
55
+ **Examples:**
56
+
57
+ .. code:: python
58
+
59
+ from pyinfra.operations import pkgin
60
+ # Update package list and install packages
61
+ pkgin.packages(
62
+ name="Install tmux and Vim",
63
+ packages=["tmux", "vim"],
64
+ update=True,
65
+ )
66
+
67
+ # Install the latest versions of packages (always check)
68
+ pkgin.packages(
69
+ name="Install latest Vim",
70
+ packages=["vim"],
71
+ latest=True,
72
+ )
73
+ """
74
+
75
+ if update:
76
+ yield from _update()
77
+
78
+ if upgrade:
79
+ yield from _upgrade()
80
+
81
+ # TODO support glob for specific versions (it isn't as simple
82
+ # as apt-s, as pkgin supports something like 'mysql-server>=5.6<5.7')
83
+ yield from ensure_packages(
84
+ host,
85
+ packages,
86
+ host.get_fact(PkginPackages),
87
+ present,
88
+ install_command="pkgin -y install",
89
+ uninstall_command="pkgin -y remove",
90
+ upgrade_command="pkgin -y upgrade",
91
+ latest=latest,
92
+ )