Skip to content

Commit 903256c

Browse files
docs: restore loop cycle error guidance (#1767)
Co-authored-by: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com>
1 parent 78d6023 commit 903256c

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

docs/deploy-process.rst

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,84 @@ Since this gets executed before nginx is installed by ``apt.packages`` operation
183183
reloaded=True,
184184
_if=remove_default_site.did_change,
185185
)
186+
187+
.. _loops-cycle-errors:
188+
189+
Loops & Cycle Errors
190+
~~~~~~~~~~~~~~~~~~~~
191+
192+
In CLI mode ``pyinfra`` builds a single DAG to determine the order in which
193+
operations are executed. This usually produces the order a deploy author
194+
expects, but loops can make the same operation line appear multiple times with
195+
different per-host paths through the deploy. When those paths disagree about
196+
which operation must run first, the ordering graph contains a cycle and
197+
``pyinfra`` raises a cycle error.
198+
199+
Use ``host.loop`` when a loop contains operations. This gives ``pyinfra`` the
200+
loop position as an ordering hint:
201+
202+
.. code:: python
203+
204+
from pyinfra import host
205+
from pyinfra.operations import server
206+
207+
for i in host.loop(range(0, 2)):
208+
server.shell(
209+
name=f"Do a thing {i}",
210+
commands="ls",
211+
)
212+
213+
For example, this deploy can generate a cycle because the first operation only
214+
appears on ``@local`` during the first loop iteration:
215+
216+
.. code:: python
217+
218+
from pyinfra import host
219+
from pyinfra.operations import server
220+
221+
for i in range(0, 2):
222+
if i > 0 or (i == 0 and host.name == "@local"):
223+
server.shell(
224+
name="A",
225+
commands="ls",
226+
)
227+
228+
server.shell(
229+
name="B",
230+
commands="ls",
231+
)
232+
233+
The resulting per-host order is inconsistent:
234+
235+
.. code:: shell
236+
237+
# @local: A -> B -> A-1 -> B-1
238+
# Other: B -> A -> B-1
239+
240+
Combining those host orders means ``A`` must run before ``B`` and ``B`` must
241+
run before ``A``. Switching the loop to ``host.loop`` includes the loop position
242+
in the operation order and removes the ambiguity:
243+
244+
.. code:: python
245+
246+
from pyinfra import host
247+
from pyinfra.operations import server
248+
249+
for i in host.loop(range(0, 2)):
250+
if i > 0 or (i == 0 and host.name == "@local"):
251+
server.shell(
252+
name="A",
253+
commands="ls",
254+
)
255+
256+
server.shell(
257+
name="B",
258+
commands="ls",
259+
)
260+
261+
The graph can then be resolved consistently:
262+
263+
.. code:: shell
264+
265+
# @local: 0A -> 0B -> 1A -> 1B
266+
# Other: 0B -> 1A -> 1B

0 commit comments

Comments
 (0)