Repository: iterative/dvc

Issue:
Mermaid lexical error with `dvc dag --md` and `foreach` stages
# Bug Report

<!--
## Issue name

Issue names must follow the pattern `command: description` where the command is the dvc command that you are trying to run. The description should describe the consequence of the bug. 

Example: `repro: doesn't detect input changes`
-->

## Description

<!--
A clear and concise description of what the bug is.
-->

Since `foreach` pipeline stages generate stages of the format `stage@{$item}`, the `@` sign is unable to be read by mermaid in GitHub :  

### Reproduce

<!--
Step list of how to reproduce the bug
-->
1. Have a [foreach stage](https://dvc.org/doc/user-guide/project-structure/pipelines-files#foreach-stages) in your pipeline
2. Run `dvc dag --md > foo.md`
3. Push `foo.md` to GitHub
4. See error when loading foo.md on GitHub, mine was:

```Error rendering embedded code

Lexical error on line 5. Unrecognized text.
...] node4[make_inputs@category] node5[ma
----------------------^

For more information, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams#creating-mermaid-diagrams
```

<!--
Example:

1. dvc init
6. Copy dataset.zip to the directory
7. dvc add dataset.zip
8. dvc run -d dataset.zip -o model ./train.sh
9. modify dataset.zip
10. dvc repro
-->

### Expected

<!--
A clear and concise description of what you expect to happen.
-->
Mermaid graph showing with `stage@{$item}` nodes

### Environment information

<!--
This is required to ensure that we can reproduce the bug.
-->

**Output of `dvc doctor`:**

```console
$ dvc doctor
DVC version: 2.10.2 (pip)
---------------------------------
Platform: Python 3.7.6 on Linux-5.4.0-100-generic-x86_64-with-debian-bullseye-sid
```

**Additional Information (if any):**

<!--
Please check https://github.com/iterative/dvc/wiki/Debugging-DVC on ways to gather more information regarding the issue.

If applicable, please also provide a `--verbose` output of the command, eg: `dvc add --verbose`.
If the issue is regarding the performance, please attach the profiling information and the benchmark comparisons.
-->
Thank you!



Discussion:

Resolving patch:
diff --git a/dvc/commands/dag.py b/dvc/commands/dag.py
--- a/dvc/commands/dag.py
+++ b/dvc/commands/dag.py
@@ -42,7 +42,7 @@ def _show_mermaid(G, markdown: bool = False):
         for node in nodes:
             total_nodes += 1
             node_id = f"node{total_nodes}"
-            graph += f"\n\t{node_id}[{node}]"
+            graph += f'\n\t{node_id}["{node}"]'
             node_ids[node] = node_id
         edges = sorted((str(a), str(b)) for b, a in pipeline.edges)
         for a, b in edges:


Test patch:
diff --git a/tests/unit/command/test_dag.py b/tests/unit/command/test_dag.py
--- a/tests/unit/command/test_dag.py
+++ b/tests/unit/command/test_dag.py
@@ -165,12 +165,12 @@ def test_show_mermaid(repo):
         line.rstrip() for line in _show_mermaid(repo.index.graph).splitlines()
     ] == [
         "flowchart TD",
-        "\tnode1[stage: '1']",
-        "\tnode2[stage: '2']",
-        "\tnode3[stage: '3']",
-        "\tnode4[stage: '4']",
-        "\tnode5[stage: 'a.dvc']",
-        "\tnode6[stage: 'b.dvc']",
+        "\tnode1[\"stage: '1'\"]",
+        "\tnode2[\"stage: '2'\"]",
+        "\tnode3[\"stage: '3'\"]",
+        "\tnode4[\"stage: '4'\"]",
+        "\tnode5[\"stage: 'a.dvc'\"]",
+        "\tnode6[\"stage: 'b.dvc'\"]",
         "\tnode3-->node4",
         "\tnode5-->node1",
         "\tnode5-->node3",
@@ -190,12 +190,12 @@ def test_show_mermaid_markdown(repo, dvc, capsys, mocker):
     ] == [
         "```mermaid",
         "flowchart TD",
-        "\tnode1[stage: '1']",
-        "\tnode2[stage: '2']",
-        "\tnode3[stage: '3']",
-        "\tnode4[stage: '4']",
-        "\tnode5[stage: 'a.dvc']",
-        "\tnode6[stage: 'b.dvc']",
+        "\tnode1[\"stage: '1'\"]",
+        "\tnode2[\"stage: '2'\"]",
+        "\tnode3[\"stage: '3'\"]",
+        "\tnode4[\"stage: '4'\"]",
+        "\tnode5[\"stage: 'a.dvc'\"]",
+        "\tnode6[\"stage: 'b.dvc'\"]",
         "\tnode3-->node4",
         "\tnode5-->node1",
         "\tnode5-->node3",