Skip to content

Commit 887cf46

Browse files
authored
chore: release 1.1.4
2 parents cb1ed39 + a8c4c61 commit 887cf46

File tree

8 files changed

+74
-7
lines changed

8 files changed

+74
-7
lines changed

CHANGES.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818
Changes
1919
=======
2020

21+
`1.1.4 <https://github.com/SwissDataScienceCenter/renku-python/compare/v1.1.3...v1.1.4>`__ (2022-03-28)
22+
-------------------------------------------------------------------------------------------------------
23+
24+
This is a bugfix release fixing an issue with cycle detection in workflows.
25+
26+
Bug Fixes
27+
~~~~~~~~~
28+
29+
- **core:** prevent creating cycles when creating/executing workflows. Fix color in `workflow visualize`.
30+
(`#2785 <https://github.com/SwissDataScienceCenter/renku-python/pull/2785>`__)
31+
2132
`1.1.3 <https://github.com/SwissDataScienceCenter/renku-python/compare/v1.1.2...v1.1.3>`__ (2022-03-25)
2233
-------------------------------------------------------------------------------------------------------
2334

helm-chart/renku-core/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ appVersion: "1.0"
33
description: A Helm chart for Kubernetes
44
name: renku-core
55
icon: https://avatars0.githubusercontent.com/u/53332360?s=400&u=a4311d22842343604ef61a8c8a1e5793209a67e9&v=4
6-
version: 1.1.3
6+
version: 1.1.4

helm-chart/renku-core/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ versions:
109109
fullnameOverride: ""
110110
image:
111111
repository: renku/renku-core
112-
tag: "v1.1.3"
112+
tag: "v1.1.4"
113113
pullPolicy: IfNotPresent
114114
v8:
115115
name: v8

renku/core/commands/view_model/activity_graph.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def _add_edges_to_canvas(
146146

147147
colors = {e.color for e in intersecting_edges}
148148

149-
if len(colors) < len(EdgeShape.EDGE_COLORS):
149+
if len(colors) < len(EdgeShape.COLORS):
150150
while edge_color in colors:
151151
edge_color = EdgeShape.next_color()
152152
for e in new_edges:

renku/core/errors.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ class GitCommandError(GitError):
334334
"""Raised when a Git command fails."""
335335

336336
def __init__(self, message="Git command failed.", command=None, stdout=None, stderr=None, status=None):
337+
"""Build a custom message."""
337338
super().__init__(message)
338339
self.command = command
339340
self.stdout = stdout
@@ -551,7 +552,12 @@ class GraphCycleError(RenkuException):
551552
def __init__(self, cycles: List[List[str]]):
552553
"""Embed exception and build a custom message."""
553554
cycles = "), (".join(", ".join(cycle) for cycle in cycles)
554-
super().__init__(f"Cycles detected in execution graph: ({cycles})")
555+
super().__init__(
556+
f"Cycles detected in execution graph: ({cycles})\nCircular workflows are not supported in renku\n"
557+
"If this happened as part of a 'renku run' or 'renku workflow execute', please git reset and clean"
558+
"the project and try again. This might be due to renku erroneously detecting an input as an output, "
559+
"if so, please specify the wrongly detected output as an explicit input using '--input'."
560+
)
555561

556562

557563
class NothingToExecuteError(RenkuException):

renku/core/management/workflow/activity.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,11 @@ def remove_overridden_activities():
205205

206206
connect_nodes_based_on_dependencies()
207207

208-
if not networkx.algorithms.dag.is_directed_acyclic_graph(graph):
209-
raise ValueError("Cannot find execution order: Project has cyclic dependencies.")
208+
cycles = list(networkx.algorithms.cycles.simple_cycles(graph))
209+
210+
if cycles:
211+
cycles = [map(lambda x: getattr(x, "id", x), cycle) for cycle in cycles]
212+
raise errors.GraphCycleError(cycles)
210213

211214
connect_nodes_by_execution_order()
212215
remove_overridden_activities()
@@ -215,7 +218,7 @@ def remove_overridden_activities():
215218

216219

217220
def sort_activities(activities: List[Activity], remove_overridden_parents=True) -> List[Activity]:
218-
"""Returns a sorted list of activities based on their dependencies and execution order."""
221+
"""Return a sorted list of activities based on their dependencies and execution order."""
219222
graph = create_activity_graph(activities, remove_overridden_parents)
220223

221224
return list(networkx.topological_sort(graph))

renku/core/metadata/gateway/activity_gateway.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# limitations under the License.
1818
"""Renku activity database gateway implementation."""
1919

20+
from itertools import chain
2021
from pathlib import Path
2122
from typing import List, Optional, Set, Tuple, Union
2223

@@ -26,6 +27,7 @@
2627
from renku.core.management.interface.activity_gateway import IActivityGateway
2728
from renku.core.management.interface.database_dispatcher import IDatabaseDispatcher
2829
from renku.core.management.interface.plan_gateway import IPlanGateway
30+
from renku.core.management.workflow.activity import create_activity_graph
2931
from renku.core.metadata.gateway.database_gateway import ActivityDownstreamRelation
3032
from renku.core.models.provenance.activity import Activity, ActivityCollection
3133
from renku.core.models.workflow.plan import Plan
@@ -148,6 +150,19 @@ def add(self, activity: Activity):
148150
plan_gateway = inject.instance(IPlanGateway)
149151
plan_gateway.add(activity.association.plan)
150152

153+
# NOTE: Check for a cycle if this activity
154+
upstream_chains = self.get_upstream_activity_chains(activity)
155+
downstream_chains = self.get_downstream_activity_chains(activity)
156+
157+
all_activities = set()
158+
159+
for activity_chain in chain(upstream_chains, downstream_chains):
160+
for current_activity in activity_chain:
161+
all_activities.add(current_activity)
162+
163+
# NOTE: This call raises an exception if there is a cycle
164+
create_activity_graph(list(all_activities), with_inputs_outputs=True)
165+
151166
def add_activity_collection(self, activity_collection: ActivityCollection):
152167
"""Add an ``ActivityCollection`` to storage."""
153168
database = self.database_dispatcher.current_database

tests/cli/test_workflow.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,3 +902,35 @@ def test_workflow_iterate(runner, run_shell, client, workflow, parameters, provi
902902
# check whether parameters setting was effective
903903
for o in outputs:
904904
assert Path(o).resolve().exists()
905+
906+
907+
def test_workflow_cycle_detection(run_shell, project, capsys, client):
908+
"""Test creating a cycle is not possible with renku run or workflow execute."""
909+
input = client.path / "input"
910+
911+
with client.commit():
912+
input.write_text("test")
913+
914+
result = run_shell("renku run --name run1 -- cp input output")
915+
916+
# Assert expected empty stdout.
917+
assert b"" == result[0]
918+
# Assert not allocated stderr.
919+
assert result[1] is None
920+
921+
result = run_shell("renku run --name run2 -- wc output > input")
922+
923+
assert b"Cycles detected in execution graph" in result[0]
924+
925+
run_shell("git clean -fd && git reset --hard")
926+
927+
result = run_shell("renku run --name run2 -- wc output > wordcount")
928+
929+
# Assert expected empty stdout.
930+
assert b"" == result[0]
931+
# Assert not allocated stderr.
932+
assert result[1] is None
933+
934+
result = run_shell("renku workflow execute --set output-2=input run2")
935+
936+
assert b"Cycles detected in execution graph" in result[0]

0 commit comments

Comments
 (0)