pyinfra 0.11.dev3__py3-none-any.whl → 3.5.1__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 (203) hide show
  1. pyinfra/__init__.py +9 -12
  2. pyinfra/__main__.py +4 -0
  3. pyinfra/api/__init__.py +18 -3
  4. pyinfra/api/arguments.py +406 -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 +67 -18
  12. pyinfra/api/facts.py +253 -202
  13. pyinfra/api/host.py +413 -50
  14. pyinfra/api/inventory.py +121 -160
  15. pyinfra/api/operation.py +432 -262
  16. pyinfra/api/operations.py +273 -260
  17. pyinfra/api/state.py +302 -248
  18. pyinfra/api/util.py +291 -368
  19. pyinfra/connectors/base.py +173 -0
  20. pyinfra/connectors/chroot.py +212 -0
  21. pyinfra/connectors/docker.py +381 -0
  22. pyinfra/connectors/dockerssh.py +297 -0
  23. pyinfra/connectors/local.py +238 -0
  24. pyinfra/connectors/scp/__init__.py +1 -0
  25. pyinfra/connectors/scp/client.py +204 -0
  26. pyinfra/connectors/ssh.py +670 -0
  27. pyinfra/connectors/ssh_util.py +114 -0
  28. pyinfra/connectors/sshuserclient/client.py +309 -0
  29. pyinfra/connectors/sshuserclient/config.py +102 -0
  30. pyinfra/connectors/terraform.py +135 -0
  31. pyinfra/connectors/util.py +410 -0
  32. pyinfra/connectors/vagrant.py +183 -0
  33. pyinfra/context.py +145 -0
  34. pyinfra/facts/__init__.py +7 -6
  35. pyinfra/facts/apk.py +22 -7
  36. pyinfra/facts/apt.py +117 -60
  37. pyinfra/facts/brew.py +100 -15
  38. pyinfra/facts/bsdinit.py +23 -0
  39. pyinfra/facts/cargo.py +37 -0
  40. pyinfra/facts/choco.py +47 -0
  41. pyinfra/facts/crontab.py +195 -0
  42. pyinfra/facts/deb.py +94 -0
  43. pyinfra/facts/dnf.py +48 -0
  44. pyinfra/facts/docker.py +96 -23
  45. pyinfra/facts/efibootmgr.py +113 -0
  46. pyinfra/facts/files.py +630 -58
  47. pyinfra/facts/flatpak.py +77 -0
  48. pyinfra/facts/freebsd.py +70 -0
  49. pyinfra/facts/gem.py +19 -6
  50. pyinfra/facts/git.py +59 -14
  51. pyinfra/facts/gpg.py +150 -0
  52. pyinfra/facts/hardware.py +313 -167
  53. pyinfra/facts/iptables.py +72 -62
  54. pyinfra/facts/launchd.py +44 -0
  55. pyinfra/facts/lxd.py +17 -4
  56. pyinfra/facts/mysql.py +122 -86
  57. pyinfra/facts/npm.py +17 -9
  58. pyinfra/facts/openrc.py +71 -0
  59. pyinfra/facts/opkg.py +246 -0
  60. pyinfra/facts/pacman.py +50 -7
  61. pyinfra/facts/pip.py +24 -7
  62. pyinfra/facts/pipx.py +82 -0
  63. pyinfra/facts/pkg.py +15 -6
  64. pyinfra/facts/pkgin.py +35 -0
  65. pyinfra/facts/podman.py +54 -0
  66. pyinfra/facts/postgres.py +178 -0
  67. pyinfra/facts/postgresql.py +6 -147
  68. pyinfra/facts/rpm.py +105 -0
  69. pyinfra/facts/runit.py +77 -0
  70. pyinfra/facts/selinux.py +161 -0
  71. pyinfra/facts/server.py +746 -285
  72. pyinfra/facts/snap.py +88 -0
  73. pyinfra/facts/systemd.py +139 -0
  74. pyinfra/facts/sysvinit.py +59 -0
  75. pyinfra/facts/upstart.py +35 -0
  76. pyinfra/facts/util/__init__.py +17 -0
  77. pyinfra/facts/util/databases.py +4 -6
  78. pyinfra/facts/util/packaging.py +37 -6
  79. pyinfra/facts/util/units.py +30 -0
  80. pyinfra/facts/util/win_files.py +99 -0
  81. pyinfra/facts/vzctl.py +20 -13
  82. pyinfra/facts/xbps.py +35 -0
  83. pyinfra/facts/yum.py +34 -40
  84. pyinfra/facts/zfs.py +77 -0
  85. pyinfra/facts/zypper.py +42 -0
  86. pyinfra/local.py +45 -83
  87. pyinfra/operations/__init__.py +12 -0
  88. pyinfra/operations/apk.py +98 -0
  89. pyinfra/operations/apt.py +488 -0
  90. pyinfra/operations/brew.py +231 -0
  91. pyinfra/operations/bsdinit.py +59 -0
  92. pyinfra/operations/cargo.py +45 -0
  93. pyinfra/operations/choco.py +61 -0
  94. pyinfra/operations/crontab.py +191 -0
  95. pyinfra/operations/dnf.py +210 -0
  96. pyinfra/operations/docker.py +446 -0
  97. pyinfra/operations/files.py +1939 -0
  98. pyinfra/operations/flatpak.py +94 -0
  99. pyinfra/operations/freebsd/__init__.py +12 -0
  100. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  101. pyinfra/operations/freebsd/pkg.py +219 -0
  102. pyinfra/operations/freebsd/service.py +116 -0
  103. pyinfra/operations/freebsd/sysrc.py +92 -0
  104. pyinfra/operations/gem.py +47 -0
  105. pyinfra/operations/git.py +419 -0
  106. pyinfra/operations/iptables.py +311 -0
  107. pyinfra/operations/launchd.py +45 -0
  108. pyinfra/operations/lxd.py +68 -0
  109. pyinfra/operations/mysql.py +609 -0
  110. pyinfra/operations/npm.py +57 -0
  111. pyinfra/operations/openrc.py +63 -0
  112. pyinfra/operations/opkg.py +88 -0
  113. pyinfra/operations/pacman.py +81 -0
  114. pyinfra/operations/pip.py +205 -0
  115. pyinfra/operations/pipx.py +102 -0
  116. pyinfra/operations/pkg.py +70 -0
  117. pyinfra/operations/pkgin.py +91 -0
  118. pyinfra/operations/postgres.py +436 -0
  119. pyinfra/operations/postgresql.py +30 -0
  120. pyinfra/operations/puppet.py +40 -0
  121. pyinfra/operations/python.py +72 -0
  122. pyinfra/operations/runit.py +184 -0
  123. pyinfra/operations/selinux.py +189 -0
  124. pyinfra/operations/server.py +1099 -0
  125. pyinfra/operations/snap.py +117 -0
  126. pyinfra/operations/ssh.py +216 -0
  127. pyinfra/operations/systemd.py +149 -0
  128. pyinfra/operations/sysvinit.py +141 -0
  129. pyinfra/operations/upstart.py +68 -0
  130. pyinfra/operations/util/__init__.py +12 -0
  131. pyinfra/operations/util/docker.py +251 -0
  132. pyinfra/operations/util/files.py +247 -0
  133. pyinfra/operations/util/packaging.py +336 -0
  134. pyinfra/operations/util/service.py +46 -0
  135. pyinfra/operations/vzctl.py +137 -0
  136. pyinfra/operations/xbps.py +77 -0
  137. pyinfra/operations/yum.py +210 -0
  138. pyinfra/operations/zfs.py +175 -0
  139. pyinfra/operations/zypper.py +192 -0
  140. pyinfra/progress.py +44 -32
  141. pyinfra/py.typed +0 -0
  142. pyinfra/version.py +9 -1
  143. pyinfra-3.5.1.dist-info/METADATA +141 -0
  144. pyinfra-3.5.1.dist-info/RECORD +159 -0
  145. {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info}/WHEEL +1 -2
  146. pyinfra-3.5.1.dist-info/entry_points.txt +12 -0
  147. {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info/licenses}/LICENSE.md +1 -1
  148. pyinfra_cli/__init__.py +1 -0
  149. pyinfra_cli/cli.py +780 -0
  150. pyinfra_cli/commands.py +66 -0
  151. pyinfra_cli/exceptions.py +155 -65
  152. pyinfra_cli/inventory.py +233 -89
  153. pyinfra_cli/log.py +39 -43
  154. pyinfra_cli/main.py +26 -495
  155. pyinfra_cli/prints.py +215 -156
  156. pyinfra_cli/util.py +172 -105
  157. pyinfra_cli/virtualenv.py +25 -20
  158. pyinfra/api/connectors/__init__.py +0 -21
  159. pyinfra/api/connectors/ansible.py +0 -99
  160. pyinfra/api/connectors/docker.py +0 -178
  161. pyinfra/api/connectors/local.py +0 -169
  162. pyinfra/api/connectors/ssh.py +0 -402
  163. pyinfra/api/connectors/sshuserclient/client.py +0 -105
  164. pyinfra/api/connectors/sshuserclient/config.py +0 -90
  165. pyinfra/api/connectors/util.py +0 -63
  166. pyinfra/api/connectors/vagrant.py +0 -155
  167. pyinfra/facts/init.py +0 -176
  168. pyinfra/facts/util/files.py +0 -102
  169. pyinfra/hook.py +0 -41
  170. pyinfra/modules/__init__.py +0 -11
  171. pyinfra/modules/apk.py +0 -64
  172. pyinfra/modules/apt.py +0 -272
  173. pyinfra/modules/brew.py +0 -122
  174. pyinfra/modules/files.py +0 -711
  175. pyinfra/modules/gem.py +0 -30
  176. pyinfra/modules/git.py +0 -115
  177. pyinfra/modules/init.py +0 -344
  178. pyinfra/modules/iptables.py +0 -271
  179. pyinfra/modules/lxd.py +0 -45
  180. pyinfra/modules/mysql.py +0 -347
  181. pyinfra/modules/npm.py +0 -47
  182. pyinfra/modules/pacman.py +0 -60
  183. pyinfra/modules/pip.py +0 -99
  184. pyinfra/modules/pkg.py +0 -43
  185. pyinfra/modules/postgresql.py +0 -245
  186. pyinfra/modules/puppet.py +0 -20
  187. pyinfra/modules/python.py +0 -37
  188. pyinfra/modules/server.py +0 -524
  189. pyinfra/modules/ssh.py +0 -150
  190. pyinfra/modules/util/files.py +0 -52
  191. pyinfra/modules/util/packaging.py +0 -118
  192. pyinfra/modules/vzctl.py +0 -133
  193. pyinfra/modules/yum.py +0 -171
  194. pyinfra/pseudo_modules.py +0 -64
  195. pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
  196. pyinfra-0.11.dev3.dist-info/METADATA +0 -135
  197. pyinfra-0.11.dev3.dist-info/RECORD +0 -95
  198. pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
  199. pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
  200. pyinfra_cli/__main__.py +0 -40
  201. pyinfra_cli/config.py +0 -92
  202. /pyinfra/{modules/util → connectors}/__init__.py +0 -0
  203. /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
@@ -0,0 +1,419 @@
1
+ """
2
+ Manage git repositories and configuration.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import re
8
+
9
+ from pyinfra import host
10
+ from pyinfra.api import OperationError, operation
11
+ from pyinfra.facts.files import Directory, File
12
+ from pyinfra.facts.git import GitBranch, GitConfig, GitTag, GitTrackingBranch
13
+
14
+ from . import files, ssh
15
+ from .util.files import chown, unix_path_join
16
+
17
+
18
+ @operation()
19
+ def config(key: str, value: str, multi_value=False, repo: str | None = None, system=False):
20
+ """
21
+ Manage git config at repository, user or system level.
22
+
23
+ + key: the key of the config to ensure
24
+ + value: the value this key should have
25
+ + multi_value: Add the value rather than set it for settings that can have multiple values
26
+ + repo: specify the git repo path to edit local config (defaults to global)
27
+ + system: whether, when ``repo`` is unspecified, to work at system level (or default to global)
28
+
29
+ **Examples:**
30
+
31
+ .. code:: python
32
+
33
+ git.config(
34
+ name="Always prune specified repo",
35
+ key="fetch.prune",
36
+ value="true",
37
+ repo="/usr/local/src/pyinfra",
38
+ )
39
+
40
+ git.config(
41
+ name="Ensure user name is set for all repos of specified user",
42
+ key="user.name",
43
+ value="Anon E. Mouse",
44
+ _sudo=True,
45
+ _sudo_user="anon"
46
+ )
47
+
48
+ git.config(
49
+ name="Ensure same date format for all users",
50
+ key="log.date",
51
+ value="iso",
52
+ system=True
53
+ )
54
+
55
+ """
56
+
57
+ existing_config = {}
58
+
59
+ if not repo:
60
+ existing_config = host.get_fact(GitConfig, system=system)
61
+
62
+ # Only get the config if the repo exists at this stage
63
+ elif host.get_fact(Directory, path=unix_path_join(repo, ".git")):
64
+ existing_config = host.get_fact(GitConfig, repo=repo)
65
+
66
+ if repo is None:
67
+ base_command = "git config" + (" --system" if system else " --global")
68
+ else:
69
+ base_command = "cd {0} && git config --local".format(repo)
70
+
71
+ if not multi_value and existing_config.get(key) != [value]:
72
+ yield '{0} {1} "{2}"'.format(base_command, key, value)
73
+
74
+ elif multi_value and value not in existing_config.get(key, []):
75
+ yield '{0} --add {1} "{2}"'.format(base_command, key, value)
76
+
77
+ else:
78
+ host.noop("git config {0} is set to {1}".format(key, value))
79
+
80
+
81
+ @operation()
82
+ def repo(
83
+ src: str,
84
+ dest: str,
85
+ branch: str | None = None,
86
+ pull=True,
87
+ rebase=False,
88
+ user: str | None = None,
89
+ group: str | None = None,
90
+ ssh_keyscan=False,
91
+ update_submodules=False,
92
+ recursive_submodules=False,
93
+ ):
94
+ """
95
+ Clone/pull git repositories.
96
+
97
+ + src: the git source URL
98
+ + dest: directory to clone to
99
+ + branch: branch to pull/checkout
100
+ + pull: pull any changes for the branch
101
+ + rebase: when pulling, use ``--rebase``
102
+ + user: chown files to this user after
103
+ + group: chown files to this group after
104
+ + ssh_keyscan: keyscan the remote host if not in known_hosts before clone/pull
105
+ + update_submodules: update any git submodules
106
+ + recursive_submodules: update git submodules recursively
107
+
108
+ **Example:**
109
+
110
+ .. code:: python
111
+
112
+ git.repo(
113
+ name="Clone repo",
114
+ src="https://github.com/Fizzadar/pyinfra.git",
115
+ dest="/usr/local/src/pyinfra",
116
+ )
117
+ """
118
+
119
+ # Ensure our target directory exists
120
+ yield from files.directory._inner(dest)
121
+
122
+ # Do we need to scan for the remote host key?
123
+ if ssh_keyscan:
124
+ # Attempt to parse the domain from the git repository
125
+ domain = re.match(r"^[a-zA-Z0-9]+@([0-9a-zA-Z\.\-]+)", src)
126
+
127
+ if domain:
128
+ yield from ssh.keyscan._inner(domain.group(1))
129
+ else:
130
+ raise OperationError(
131
+ "Could not parse domain (to SSH keyscan) from: {0}".format(src),
132
+ )
133
+
134
+ # Store git commands for directory prefix
135
+ git_commands = []
136
+ git_dir = unix_path_join(dest, ".git")
137
+ is_repo = host.get_fact(Directory, path=git_dir)
138
+
139
+ # Cloning new repo?
140
+ if not is_repo:
141
+ if branch:
142
+ git_commands.append("clone {0} --branch {1} .".format(src, branch))
143
+ else:
144
+ git_commands.append("clone {0} .".format(src))
145
+ # Ensuring existing repo
146
+ else:
147
+ is_tag = False
148
+ if branch and host.get_fact(GitBranch, repo=dest) != branch:
149
+ git_commands.append("fetch") # fetch to ensure we have the branch locally
150
+ git_commands.append("checkout {0}".format(branch))
151
+ if branch and branch in (host.get_fact(GitTag, repo=dest) or []):
152
+ git_commands.append("checkout {0}".format(branch))
153
+ is_tag = True
154
+ if pull and not is_tag:
155
+ if rebase:
156
+ git_commands.append("pull --rebase")
157
+ else:
158
+ git_commands.append("pull")
159
+
160
+ if update_submodules:
161
+ if recursive_submodules:
162
+ git_commands.append("submodule update --init --recursive")
163
+ else:
164
+ git_commands.append("submodule update --init")
165
+
166
+ # Attach prefixes for directory
167
+ command_prefix = "cd {0} && git".format(dest)
168
+ git_commands = ["{0} {1}".format(command_prefix, command) for command in git_commands]
169
+
170
+ for cmd in git_commands:
171
+ yield cmd
172
+
173
+ # Apply any user or group if we did anything
174
+ if git_commands and (user or group):
175
+ yield chown(dest, user, group, recursive=True)
176
+
177
+
178
+ @operation()
179
+ def worktree(
180
+ worktree: str,
181
+ repo: str | None = None,
182
+ detached=False,
183
+ new_branch: str | None = None,
184
+ commitish: str | None = None,
185
+ pull=True,
186
+ rebase=False,
187
+ from_remote_branch: tuple[str, str] | None = None,
188
+ present=True,
189
+ assume_repo_exists=False,
190
+ force=False,
191
+ user: str | None = None,
192
+ group: str | None = None,
193
+ ):
194
+ """
195
+ Manage git worktrees.
196
+
197
+ + worktree: git working tree directory
198
+ + repo: git main repository directory
199
+ + detached: create a working tree with a detached HEAD
200
+ + new_branch: local branch name created at the same time than the worktree
201
+ + commitish: from which git commit, branch, ... the worktree is created
202
+ + pull: pull any changes from a remote branch if set
203
+ + rebase: when pulling, use ``--rebase``
204
+ + from_remote_branch: a 2-tuple ``(remote, branch)`` that identifies a remote branch
205
+ + present: whether the working tree should exist
206
+ + assume_repo_exists: whether to assume the main repo exists
207
+ + force: whether to use ``--force`` when adding/removing worktrees
208
+ + user: chown files to this user after
209
+ + group: chown files to this group after
210
+
211
+ **Examples:**
212
+
213
+ .. code:: python
214
+
215
+ git.worktree(
216
+ name="Create a worktree from the current repo `HEAD`",
217
+ repo="/usr/local/src/pyinfra/master",
218
+ worktree="/usr/local/src/pyinfra/hotfix"
219
+ )
220
+
221
+ git.worktree(
222
+ name="Create a worktree from the commit `4e091aa0`",
223
+ repo="/usr/local/src/pyinfra/master",
224
+ worktree="/usr/local/src/pyinfra/hotfix",
225
+ commitish="4e091aa0"
226
+ )
227
+
228
+ git.worktree(
229
+ name="Create a worktree from the tag `4e091aa0`, even if already registered",
230
+ repo="/usr/local/src/pyinfra/master",
231
+ worktree="/usr/local/src/pyinfra/2.x",
232
+ commitish="2.x",
233
+ force=True
234
+ )
235
+
236
+ git.worktree(
237
+ name="Create a worktree with a new local branch `v1.0`",
238
+ repo="/usr/local/src/pyinfra/master",
239
+ worktree="/usr/local/src/pyinfra/hotfix",
240
+ new_branch="v1.0",
241
+ )
242
+
243
+ git.worktree(
244
+ name="Create a worktree from the commit 4e091aa0 with the new local branch `v1.0`",
245
+ repo="/usr/local/src/pyinfra/master",
246
+ worktree="/usr/local/src/pyinfra/hotfix",
247
+ new_branch="v1.0",
248
+ commitish="4e091aa0"
249
+ )
250
+
251
+ git.worktree(
252
+ name="Create a worktree with a detached `HEAD`",
253
+ repo="/usr/local/src/pyinfra/master",
254
+ worktree="/usr/local/src/pyinfra/hotfix",
255
+ detached=True,
256
+ )
257
+
258
+ git.worktree(
259
+ name="Create a worktree with a detached `HEAD` from commit `4e091aa0`",
260
+ repo="/usr/local/src/pyinfra/master",
261
+ worktree="/usr/local/src/pyinfra/hotfix",
262
+ commitish="4e091aa0",
263
+ detached=True,
264
+ )
265
+
266
+ git.worktree(
267
+ name="Create a worktree from the existing local branch `v1.0`",
268
+ repo="/usr/local/src/pyinfra/master",
269
+ worktree="/usr/local/src/pyinfra/hotfix",
270
+ commitish="v1.0"
271
+ )
272
+
273
+ git.worktree(
274
+ name="Create a worktree with a new branch `v1.0` that tracks `origin/v1.0`",
275
+ repo="/usr/local/src/pyinfra/master",
276
+ worktree="/usr/local/src/pyinfra/hotfix",
277
+ new_branch="v1.0",
278
+ commitish="v1.0"
279
+ )
280
+
281
+ git.worktree(
282
+ name="Idempotent worktree creation, never pulls",
283
+ repo="/usr/local/src/pyinfra/master",
284
+ worktree="/usr/local/src/pyinfra/hotfix",
285
+ new_branch="v1.0",
286
+ commitish="v1.0",
287
+ pull=False
288
+ )
289
+
290
+ git.worktree(
291
+ name="Pull an existing worktree already linked to a tracking branch",
292
+ repo="/usr/local/src/pyinfra/master",
293
+ worktree="/usr/local/src/pyinfra/hotfix"
294
+ )
295
+
296
+ git.worktree(
297
+ name="Pull an existing worktree from a specific remote branch",
298
+ repo="/usr/local/src/pyinfra/master",
299
+ worktree="/usr/local/src/pyinfra/hotfix",
300
+ from_remote_branch=("origin", "master")
301
+ )
302
+
303
+ git.worktree(
304
+ name="Remove a worktree",
305
+ worktree="/usr/local/src/pyinfra/hotfix",
306
+ present=False,
307
+ )
308
+
309
+ git.worktree(
310
+ name="Remove an unclean worktree",
311
+ worktree="/usr/local/src/pyinfra/hotfix",
312
+ present=False,
313
+ force=True,
314
+ )
315
+ """
316
+
317
+ # Doesn't exist & we want it
318
+ if not host.get_fact(Directory, path=worktree) and present:
319
+ # be sure that `repo` is a GIT repository
320
+ if not assume_repo_exists and not host.get_fact(
321
+ Directory,
322
+ path=unix_path_join(repo, ".git"),
323
+ ):
324
+ raise OperationError(
325
+ "The following folder is not a valid GIT repository : {0}".format(repo),
326
+ )
327
+
328
+ command_parts = ["cd {0} && git worktree add".format(repo)]
329
+
330
+ if new_branch:
331
+ command_parts.append("-b {0}".format(new_branch))
332
+ elif detached:
333
+ command_parts.append("--detach")
334
+
335
+ if force:
336
+ command_parts.append("--force")
337
+
338
+ command_parts.append(worktree)
339
+
340
+ if commitish:
341
+ command_parts.append(commitish)
342
+
343
+ yield " ".join(command_parts)
344
+
345
+ # Apply any user or group
346
+ if user or group:
347
+ yield chown(worktree, user, group, recursive=True)
348
+
349
+ # It exists and we don't want it
350
+ elif host.get_fact(Directory, path=worktree) and not present:
351
+ command = "cd {0} && git worktree remove .".format(worktree)
352
+
353
+ if force:
354
+ command += " --force"
355
+
356
+ yield command
357
+
358
+ # It exists and we still want it => pull/rebase it
359
+ elif host.get_fact(Directory, path=worktree) and present:
360
+ if not pull:
361
+ host.noop("Pull is disabled")
362
+
363
+ # pull the worktree only if it's already linked to a tracking branch or
364
+ # if a remote branch is set
365
+ elif host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
366
+ command = "cd {0} && git pull".format(worktree)
367
+
368
+ if rebase:
369
+ command += " --rebase"
370
+
371
+ if from_remote_branch:
372
+ if len(from_remote_branch) != 2 or type(from_remote_branch) not in (tuple, list):
373
+ raise OperationError(
374
+ "The remote branch must be a 2-tuple (remote, branch) such as "
375
+ '("origin", "master")',
376
+ )
377
+ command += " {0} {1}".format(*from_remote_branch)
378
+
379
+ yield command
380
+
381
+
382
+ @operation()
383
+ def bare_repo(
384
+ path: str,
385
+ user: str | None = None,
386
+ group: str | None = None,
387
+ present=True,
388
+ ):
389
+ """
390
+ Create bare git repositories.
391
+
392
+ + path: path to the folder
393
+ + present: whether the bare repository should exist
394
+ + user: chown files to this user after
395
+ + group: chown files to this group after
396
+
397
+ **Example:**
398
+
399
+ .. code:: python
400
+
401
+ git.bare_repo(
402
+ name="Create bare repo",
403
+ path="/home/git/test.git",
404
+ )
405
+ """
406
+
407
+ yield from files.directory._inner(path, present=present)
408
+
409
+ if present:
410
+ head_filename = unix_path_join(path, "HEAD")
411
+ head_file = host.get_fact(File, path=head_filename)
412
+
413
+ if not head_file:
414
+ yield "git init --bare {0}".format(path)
415
+ if user or group:
416
+ yield chown(path, user, group, recursive=True)
417
+ else:
418
+ if (user and head_file["user"] != user) or (group and head_file["group"] != group):
419
+ yield chown(path, user, group, recursive=True)