From 8885969e90dd99a90b6f1c36df2f334a89446eff Mon Sep 17 00:00:00 2001 From: Marc Beninca Date: Mon, 16 Dec 2024 19:00:32 +0100 Subject: [PATCH 1/5] squash --- .forgejo/workflows/main.yaml | 18 +++ .gitignore | 1 + build.py | 41 ++++++ index.gv | 262 +++++++++++++++++++++++++++++++++++ readme.md | 49 +++++++ 5 files changed, 371 insertions(+) create mode 100644 .forgejo/workflows/main.yaml create mode 100644 .gitignore create mode 100755 build.py create mode 100644 index.gv create mode 100644 readme.md diff --git a/.forgejo/workflows/main.yaml b/.forgejo/workflows/main.yaml new file mode 100644 index 0000000..78facd9 --- /dev/null +++ b/.forgejo/workflows/main.yaml @@ -0,0 +1,18 @@ +on: [push] +jobs: + job: + container: + image: ${{vars.DOCKER}}debian:bookworm + steps: + - name: spcd + env: + SPCD: ${{vars.SPCD}} + SPCD_SSH_HOSTS: ${{vars.SPCD_SSH_HOSTS}} + SPCD_SSH_KEY: ${{secrets.SPCD_SSH_KEY}} + SPCD_TXT_LOCALE: ${{vars.SPCD_TXT_LOCALE}} + run: ${{vars.SPCD}} + + - run: spcd-check-project + - run: spcd-build-project + - run: spcd-browse-workspace + - run: spcd-synchronize diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2e7327 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/out diff --git a/build.py b/build.py new file mode 100755 index 0000000..81ffcd9 --- /dev/null +++ b/build.py @@ -0,0 +1,41 @@ +#! /usr/bin/env python3 +"""Build web.""" + +from pathlib import Path + +from rwx.fs import make_directory, read_file_text, write +from rwx.ps import run + +if __name__ == "__main__": + root = Path(__file__).parent + out = root / "out" / "web" + gv = root / "index.gv" + svg = out / "index.svg" + make_directory(out) + run("dot", str(gv), "-Tsvg", "-o", str(svg)) + text = read_file_text(svg) + write( + out / "index.css", + """\ +html { +background-color: #202020; +} +""", + ) + write( + out / "index.html", + f"""\ + + + + + +todo.rwx.work + + + +{text} + + +""", + ) diff --git a/index.gv b/index.gv new file mode 100644 index 0000000..ac4ab30 --- /dev/null +++ b/index.gv @@ -0,0 +1,262 @@ +# ╭───────╮ +# │ Graph │ +# ╰───────╯ +digraph "index" { +bgcolor="transparent" +color="#C0C000" +fontcolor="#FF8000" +fontname="DejaVu Sans" +penwidth="2" +rankdir="RL" + +# ╭───────┬──────╮ +# │ Graph │ Edge │ +# ╰───────┴──────╯ +edge [ +fontcolor="#FF4040" +fontname="DejaVu Sans" +style="filled" +] + +# ╭───────┬──────╮ +# │ Graph │ Edge │ +# ╰───────┴──────╯ +node [ +color="#C0C0C0" +fontcolor="#FFFFFF" +fontname="DejaVu Sans" +penwidth="2" +shape="record" +style="filled" +] + +{ rank="max" +"" [style="invis"] +} + +edge [color="#008000"] +node [fillcolor="#408040"] + +# ╭────────╮ +# │ DevOps │ +# ╰────────╯ + +subgraph "cluster/devops" { +label="DevOps" + +"devops/dev/plan" -> "devops/ops/release" [style="invis"] +"devops/dev/test" -> "devops/ops/monitor" [style="invis"] + +# ╭────────┬─────╮ +# │ DevOps │ Dev │ +# ╰────────┴─────╯ + +subgraph "cluster/devops/dev" { +label="Dev" + +"devops/dev/plan" [label="Plan"] +"devops/dev/code" [label="Code"] +"devops/dev/build" [label="Build"] +"devops/dev/test" [label="Test"] + +} + +"devops/dev/plan" -> "devops/dev/code" -> "devops/dev/build" -> "devops/dev/test" -> "devops/dev/plan" + +# ╭────────┬─────╮ +# │ DevOps │ Ops │ +# ╰────────┴─────╯ + +subgraph "cluster/devops/ops" { +label="Ops" + +"devops/ops/release" [label="Release"] +"devops/ops/deploy" [label="Deploy"] +"devops/ops/operate" [label="Operate"] +"devops/ops/monitor" [label="Monitor"] + +} + +"devops/ops/release" -> "devops/ops/deploy" -> "devops/ops/operate" -> "devops/ops/monitor" -> "devops/ops/deploy" + +# ╭────────╮ +# │ DevOps │ +# ╰────────╯ + +"devops/dev/test" -> "devops/ops/release" +"devops/ops/monitor" -> "devops/dev/plan" + +} + +edge [color="#FF0000"] +node [fillcolor="#303030"] + +# ╭─────╮ +# │ DNS │ +# ╰─────╯ + +subgraph "cluster/dns" { +label="Domains" + +# ╭─────┬─────╮ +# │ DNS │ RWX │ +# ╰─────┴─────╯ + +subgraph "cluster/dns/rwx" { +label="rwx.work" +URL="https://rwx.work" + +"dns/rwx/blog" [ +fontcolor="#8080FF" +label="Blog" +URL="https://blog.rwx.work" +] + +"dns/rwx/forge" [ +fontcolor="#8080FF" +label="Forge" +URL="https://forge.rwx.work" +] + +"dns/rwx/ilos" [ +fontcolor="#8080FF" +label="ILOS:\nIncremental Live\nOperating System" +URL="https://ilos.rwx.work" +] + +subgraph "cluster/dns/rwx/lsgm" { +label="LSGM:\nLive Scan Grub Menu" +URL="https://lsgm.rwx.work" + +"dns/rwx/lsgm/python" [label="Python"] +"dns/rwx/lsgm/shell" [label="Shell"] + +} + +"dns/rwx/ofsp" [ +fontcolor="#8080FF" +label="OFSP:\nOperating File\nSystem Profile" +URL="https://ofsp.rwx.work" +] + +"dns/rwx/prj" [ +fontcolor="#8080FF" +label="PRJ:\nPRJ" +URL="https://prj.rwx.work" +] + +"dns/rwx/rtfd" [ +fontcolor="#8080FF" +label="RTFD:\nRead The\nFancy Docs" +URL="https://rtfd.rwx.work" +] + +subgraph "cluster/dns/rwx/rwx" { +label="RWX:\nRead Write eXecute" +URL="https://rwx.rwx.work" + +"dns/rwx/rwx/python" [label="Python"] +"dns/rwx/rwx/shell" [label="Shell"] + +} + +"dns/rwx/spcd" [ +fontcolor="#8080FF" +label="SPCD:\nShell to Python\nContinuous Deployment" +URL="https://spcd.rwx.work" +] + +"dns/rwx/srmp" [ +fontcolor="#8080FF" +label="SRMP:\nSoftware Repositories\nMirror Profile" +URL="https://srmp.rwx.work" +] + +"dns/rwx/todo" [ +fontcolor="#8080FF" +label="TODO:\nTO\nDO" +URL="https://todo.rwx.work" +] + +} + +# ╭─────┬─────┬───────╮ +# │ DNS │ RWX │ Forge │ +# ╰─────┴─────┴───────╯ + +"dns/rwx/forge/marc" [ +fontcolor="#8080FF" +label="Marc" +URL="https://forge.rwx.work/marc.beninca" +] +"dns/rwx/forge/marc" -> "dns/rwx/forge" + +# ╭─────┬─────┬──────╮ +# │ DNS │ RWX │ OFSP │ +# ╰─────┴─────┴──────╯ + +{ +"dns/rwx/ofsp/python" [label="Python"] +"dns/rwx/ofsp/shell" [label="Shell"] +} -> "dns/rwx/ofsp" + +# ╭─────┬─────┬──────╮ +# │ DNS │ RWX │ RTFD │ +# ╰─────┴─────┴──────╯ + +{ +"dns/rwx/rtfd/forgejo" [label="Forgejo"] +} -> "dns/rwx/rtfd" + +# ╭─────┬─────┬─────┬────────╮ +# │ DNS │ RWX │ RWX │ Python │ +# ╰─────┴─────┴─────┴────────╯ +{ +"dns/rwx/rwx/python/codium" [label="Codium"] +"dns/rwx/rwx/python/ffmpeg" [label="FFMPEG"] +"dns/rwx/rwx/python/freetube" [label="FreeTube"] +"dns/rwx/rwx/python/gource" [label="Gource"] +"dns/rwx/rwx/python/logo" [label="Logo"] +"dns/rwx/rwx/python/venvs" [label="Build VEnvs"] +} -> "dns/rwx/rwx/python" +# ╭─────┬─────┬─────┬───────╮ +# │ DNS │ RWX │ RWX │ Shell │ +# ╰─────┴─────┴─────┴───────╯ +{ +"dns/rwx/rwx/shell/cs" [label="CryptSetup"] +"dns/rwx/rwx/shell/ffmpeg" [label="FFMPEG"] +"dns/rwx/rwx/shell/git" [label="Git"] +"dns/rwx/rwx/shell/venv" [label="Use VEnv"] +} -> "dns/rwx/rwx/shell" + +# ╭─────┬─────┬──────╮ +# │ DNS │ RWX │ SRMP │ +# ╰─────┴─────┴──────╯ +{ +"dns/rwx/srmp/alma" [label="Alma"] +"dns/rwx/srmp/debian" [label="Debian"] +"dns/rwx/srmp/docker" [label="Docker"] +"dns/rwx/srmp/msys" [label="MSys"] +"dns/rwx/srmp/pypi" [label="PyPI"] +"dns/rwx/srmp/python" [label="Python"] +} -> "dns/rwx/srmp" + +# ╭─────┬─────┬──────┬────────╮ +# │ DNS │ RWX │ SRMP │ Debian │ +# ╰─────┴─────┴──────┴────────╯ +{ +"dns/rwx/srmp/debian/codium" [label="Codium"] +"dns/rwx/srmp/debian/incus" [label="Incus"] +} -> "dns/rwx/srmp/debian" + +# ╭─────┬─────┬──────╮ +# │ DNS │ RWX │ TODO │ +# ╰─────┴─────┴──────╯ +{ +"dns/rwx/todo/name" [label="Name"] +"dns/rwx/todo/subdomain" [label="Subdomain"] +} -> "dns/rwx/todo" + +} + +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2c784a4 --- /dev/null +++ b/readme.md @@ -0,0 +1,49 @@ +# Track Ongoing DevOps + +A graph to track things to do. + +--- + +## Why + +Dependencies between tasks of projects. + +--- + +## How + +A tree to identify leafs. + +--- + +## What + +Generate: + +* [X] SVG with GraphViz +* [X] HTML including SVG + +--- + +## Authors + +* [Marc Beninca](https://marc.beninca.link) + +--- + +## Where + +### Chat + +* [Discord](https://discord.com/channels/983145051985154108/1315476519325274112) +* [IRC](ircs://irc.libera.chat/#todo) + +### Forge + +* [Repository](https://forge.rwx.work/rwx.work/todo) +* [RSS](https://forge.rwx.work/rwx.work/todo.rss) +* [Workflows](https://forge.rwx.work/rwx.work/todo/actions) + +### Deployment + +* [Site](https://todo.rwx.work) From 52ed2e1db21dea9242164f9aeadc1fcb26b6f567 Mon Sep 17 00:00:00 2001 From: Marc Beninca Date: Mon, 16 Dec 2024 19:28:12 +0100 Subject: [PATCH 2/5] refactor --- index.gv | 190 ++++++++++++++++++++++++++----------------------------- 1 file changed, 91 insertions(+), 99 deletions(-) diff --git a/index.gv b/index.gv index ac4ab30..2863b6c 100644 --- a/index.gv +++ b/index.gv @@ -41,50 +41,50 @@ node [fillcolor="#408040"] # │ DevOps │ # ╰────────╯ -subgraph "cluster/devops" { +subgraph cluster_devops { label="DevOps" -"devops/dev/plan" -> "devops/ops/release" [style="invis"] -"devops/dev/test" -> "devops/ops/monitor" [style="invis"] +devops_dev_plan -> devops_ops_release [style="invis"] +devops_dev_test -> devops_ops_monitor [style="invis"] # ╭────────┬─────╮ # │ DevOps │ Dev │ # ╰────────┴─────╯ -subgraph "cluster/devops/dev" { +subgraph cluster_devops_dev { label="Dev" -"devops/dev/plan" [label="Plan"] -"devops/dev/code" [label="Code"] -"devops/dev/build" [label="Build"] -"devops/dev/test" [label="Test"] +devops_dev_plan [label="Plan"] +devops_dev_code [label="Code"] +devops_dev_build [label="Build"] +devops_dev_test [label="Test"] } -"devops/dev/plan" -> "devops/dev/code" -> "devops/dev/build" -> "devops/dev/test" -> "devops/dev/plan" +devops_dev_plan -> devops_dev_code -> devops_dev_build -> devops_dev_test -> devops_dev_plan # ╭────────┬─────╮ # │ DevOps │ Ops │ # ╰────────┴─────╯ -subgraph "cluster/devops/ops" { +subgraph cluster_devops_ops { label="Ops" -"devops/ops/release" [label="Release"] -"devops/ops/deploy" [label="Deploy"] -"devops/ops/operate" [label="Operate"] -"devops/ops/monitor" [label="Monitor"] +devops_ops_release [label="Release"] +devops_ops_deploy [label="Deploy"] +devops_ops_operate [label="Operate"] +devops_ops_monitor [label="Monitor"] } -"devops/ops/release" -> "devops/ops/deploy" -> "devops/ops/operate" -> "devops/ops/monitor" -> "devops/ops/deploy" +devops_ops_release -> devops_ops_deploy -> devops_ops_operate -> devops_ops_monitor -> devops_ops_deploy # ╭────────╮ # │ DevOps │ # ╰────────╯ -"devops/dev/test" -> "devops/ops/release" -"devops/ops/monitor" -> "devops/dev/plan" +devops_dev_test -> devops_ops_release +devops_ops_monitor -> devops_dev_plan } @@ -92,87 +92,80 @@ edge [color="#FF0000"] node [fillcolor="#303030"] # ╭─────╮ -# │ DNS │ +# │ RWX │ # ╰─────╯ -subgraph "cluster/dns" { -label="Domains" - -# ╭─────┬─────╮ -# │ DNS │ RWX │ -# ╰─────┴─────╯ - -subgraph "cluster/dns/rwx" { +subgraph cluster_rwx { label="rwx.work" URL="https://rwx.work" -"dns/rwx/blog" [ +rwx_blog [ fontcolor="#8080FF" label="Blog" URL="https://blog.rwx.work" ] -"dns/rwx/forge" [ +rwx_forge [ fontcolor="#8080FF" label="Forge" URL="https://forge.rwx.work" ] -"dns/rwx/ilos" [ +rwx_ilos [ fontcolor="#8080FF" label="ILOS:\nIncremental Live\nOperating System" URL="https://ilos.rwx.work" ] -subgraph "cluster/dns/rwx/lsgm" { +subgraph cluster_rwx_lsgm { label="LSGM:\nLive Scan Grub Menu" URL="https://lsgm.rwx.work" -"dns/rwx/lsgm/python" [label="Python"] -"dns/rwx/lsgm/shell" [label="Shell"] +rwx_lsgm_python [label="Python"] +rwx_lsgm_shell [label="Shell"] } -"dns/rwx/ofsp" [ +rwx_ofsp [ fontcolor="#8080FF" label="OFSP:\nOperating File\nSystem Profile" URL="https://ofsp.rwx.work" ] -"dns/rwx/prj" [ +rwx_prj [ fontcolor="#8080FF" label="PRJ:\nPRJ" URL="https://prj.rwx.work" ] -"dns/rwx/rtfd" [ +rwx_rtfd [ fontcolor="#8080FF" label="RTFD:\nRead The\nFancy Docs" URL="https://rtfd.rwx.work" ] -subgraph "cluster/dns/rwx/rwx" { +subgraph cluster_rwx_rwx { label="RWX:\nRead Write eXecute" URL="https://rwx.rwx.work" -"dns/rwx/rwx/python" [label="Python"] -"dns/rwx/rwx/shell" [label="Shell"] +rwx_rwx_python [label="Python"] +rwx_rwx_shell [label="Shell"] } -"dns/rwx/spcd" [ +rwx_spcd [ fontcolor="#8080FF" label="SPCD:\nShell to Python\nContinuous Deployment" URL="https://spcd.rwx.work" ] -"dns/rwx/srmp" [ +rwx_srmp [ fontcolor="#8080FF" label="SRMP:\nSoftware Repositories\nMirror Profile" URL="https://srmp.rwx.work" ] -"dns/rwx/todo" [ +rwx_todo [ fontcolor="#8080FF" label="TODO:\nTO\nDO" URL="https://todo.rwx.work" @@ -180,83 +173,82 @@ URL="https://todo.rwx.work" } -# ╭─────┬─────┬───────╮ -# │ DNS │ RWX │ Forge │ -# ╰─────┴─────┴───────╯ +# ╭─────┬───────╮ +# │ RWX │ Forge │ +# ╰─────┴───────╯ -"dns/rwx/forge/marc" [ +rwx_forge_marc [ fontcolor="#8080FF" label="Marc" URL="https://forge.rwx.work/marc.beninca" ] -"dns/rwx/forge/marc" -> "dns/rwx/forge" -# ╭─────┬─────┬──────╮ -# │ DNS │ RWX │ OFSP │ -# ╰─────┴─────┴──────╯ +rwx_forge_marc -> rwx_forge + +# ╭─────┬──────╮ +# │ RWX │ OFSP │ +# ╰─────┴──────╯ { -"dns/rwx/ofsp/python" [label="Python"] -"dns/rwx/ofsp/shell" [label="Shell"] -} -> "dns/rwx/ofsp" +rwx_ofsp_python [label="Python"] +rwx_ofsp_shell [label="Shell"] +} -> rwx_ofsp -# ╭─────┬─────┬──────╮ -# │ DNS │ RWX │ RTFD │ -# ╰─────┴─────┴──────╯ +# ╭─────┬──────╮ +# │ RWX │ RTFD │ +# ╰─────┴──────╯ { -"dns/rwx/rtfd/forgejo" [label="Forgejo"] -} -> "dns/rwx/rtfd" +rwx_rtfd_forgejo [label="Forgejo"] +} -> rwx_rtfd -# ╭─────┬─────┬─────┬────────╮ -# │ DNS │ RWX │ RWX │ Python │ -# ╰─────┴─────┴─────┴────────╯ +# ╭─────┬─────┬────────╮ +# │ RWX │ RWX │ Python │ +# ╰─────┴─────┴────────╯ { -"dns/rwx/rwx/python/codium" [label="Codium"] -"dns/rwx/rwx/python/ffmpeg" [label="FFMPEG"] -"dns/rwx/rwx/python/freetube" [label="FreeTube"] -"dns/rwx/rwx/python/gource" [label="Gource"] -"dns/rwx/rwx/python/logo" [label="Logo"] -"dns/rwx/rwx/python/venvs" [label="Build VEnvs"] -} -> "dns/rwx/rwx/python" -# ╭─────┬─────┬─────┬───────╮ -# │ DNS │ RWX │ RWX │ Shell │ -# ╰─────┴─────┴─────┴───────╯ +rwx_rwx_python_codium [label="Codium"] +rwx_rwx_python_ffmpeg [label="FFMPEG"] +rwx_rwx_python_freetube [label="FreeTube"] +rwx_rwx_python_gource [label="Gource"] +rwx_rwx_python_logo [label="Logo"] +rwx_rwx_python_venvs [label="Build VEnvs"] +} -> rwx_rwx_python +# ╭─────┬─────┬───────╮ +# │ RWX │ RWX │ Shell │ +# ╰─────┴─────┴───────╯ { -"dns/rwx/rwx/shell/cs" [label="CryptSetup"] -"dns/rwx/rwx/shell/ffmpeg" [label="FFMPEG"] -"dns/rwx/rwx/shell/git" [label="Git"] -"dns/rwx/rwx/shell/venv" [label="Use VEnv"] -} -> "dns/rwx/rwx/shell" +rwx_rwx_shell_cs [label="CryptSetup"] +rwx_rwx_shell_ffmpeg [label="FFMPEG"] +rwx_rwx_shell_git [label="Git"] +rwx_rwx_shell_venv [label="Use VEnv"] +} -> rwx_rwx_shell -# ╭─────┬─────┬──────╮ -# │ DNS │ RWX │ SRMP │ -# ╰─────┴─────┴──────╯ +# ╭─────┬──────╮ +# │ RWX │ SRMP │ +# ╰─────┴──────╯ { -"dns/rwx/srmp/alma" [label="Alma"] -"dns/rwx/srmp/debian" [label="Debian"] -"dns/rwx/srmp/docker" [label="Docker"] -"dns/rwx/srmp/msys" [label="MSys"] -"dns/rwx/srmp/pypi" [label="PyPI"] -"dns/rwx/srmp/python" [label="Python"] -} -> "dns/rwx/srmp" +rwx_srmp_alma [label="Alma"] +rwx_srmp_debian [label="Debian"] +rwx_srmp_docker [label="Docker"] +rwx_srmp_msys [label="MSys"] +rwx_srmp_pypi [label="PyPI"] +rwx_srmp_python [label="Python"] +} -> rwx_srmp -# ╭─────┬─────┬──────┬────────╮ -# │ DNS │ RWX │ SRMP │ Debian │ -# ╰─────┴─────┴──────┴────────╯ +# ╭─────┬──────┬────────╮ +# │ RWX │ SRMP │ Debian │ +# ╰─────┴──────┴────────╯ { -"dns/rwx/srmp/debian/codium" [label="Codium"] -"dns/rwx/srmp/debian/incus" [label="Incus"] -} -> "dns/rwx/srmp/debian" +rwx_srmp_debian_codium [label="Codium"] +rwx_srmp_debian_incus [label="Incus"] +} -> rwx_srmp_debian -# ╭─────┬─────┬──────╮ -# │ DNS │ RWX │ TODO │ -# ╰─────┴─────┴──────╯ +# ╭─────┬──────╮ +# │ RWX │ TODO │ +# ╰─────┴──────╯ { -"dns/rwx/todo/name" [label="Name"] -"dns/rwx/todo/subdomain" [label="Subdomain"] -} -> "dns/rwx/todo" - -} +rwx_todo_name [label="Name"] +rwx_todo_subdomain [label="Subdomain"] +} -> rwx_todo } From 3de4b8042d8fc26d35a29b00d2684f7ba6d90d97 Mon Sep 17 00:00:00 2001 From: Marc Beninca Date: Fri, 27 Dec 2024 21:31:38 +0100 Subject: [PATCH 3/5] rwx/dev --- index.gv | 1 + 1 file changed, 1 insertion(+) diff --git a/index.gv b/index.gv index 2863b6c..497a381 100644 --- a/index.gv +++ b/index.gv @@ -148,6 +148,7 @@ subgraph cluster_rwx_rwx { label="RWX:\nRead Write eXecute" URL="https://rwx.rwx.work" +rwx_rwx_dev [label="Proper\ndev branch"] rwx_rwx_python [label="Python"] rwx_rwx_shell [label="Shell"] From 29101f49cd4e02db1fe29410f48a7a551d788567 Mon Sep 17 00:00:00 2001 From: Marc Beninca Date: Sat, 17 May 2025 01:57:09 +0200 Subject: [PATCH 4/5] forge,todo --- index.gv | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.gv b/index.gv index 497a381..7b65b83 100644 --- a/index.gv +++ b/index.gv @@ -88,8 +88,11 @@ devops_ops_monitor -> devops_dev_plan } -edge [color="#FF0000"] node [fillcolor="#303030"] +edge [color="#FF0000"] + +rwx_forge -> devops_dev_code +rwx_todo -> devops_dev_plan # ╭─────╮ # │ RWX │ From 138da6363b09ab7b4723305326313f523d055352 Mon Sep 17 00:00:00 2001 From: Marc Beninca Date: Sat, 17 May 2025 01:59:05 +0200 Subject: [PATCH 5/5] circo --- build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.py b/build.py index 81ffcd9..0c30abb 100755 --- a/build.py +++ b/build.py @@ -12,7 +12,7 @@ if __name__ == "__main__": gv = root / "index.gv" svg = out / "index.svg" make_directory(out) - run("dot", str(gv), "-Tsvg", "-o", str(svg)) + run("dot", str(gv), "-Kcirco", "-Tsvg", "-o", str(svg)) text = read_file_text(svg) write( out / "index.css",