diff --git a/.agents/references/diagrams.md b/.agents/references/diagrams.md new file mode 100644 index 00000000..87abb5d1 --- /dev/null +++ b/.agents/references/diagrams.md @@ -0,0 +1,230 @@ +--- +description: Architecture diagram conventions for meshstack-hub. Graphviz DOT sources with committed SVGs, the shared colour palette and emoji vocabulary that keep diagrams looking like one family, layout-control attributes (rank=same, constraint=false, splines=ortho), label discipline, and why Mermaid was dropped. +--- + +# Architecture Diagrams + +Diagrams are **Graphviz DOT** source committed next to the Markdown that uses them, with the rendered +SVG committed alongside: + +``` +reference-architectures/azure-kubernetes/ +├── README.md # references the SVG: ![...](azure-kubernetes.svg) +├── azure-kubernetes.dot # the source you edit +└── azure-kubernetes.svg # generated — never hand-edit +``` + +```sh +task diagrams # re-render every *.dot in the repo to a sibling *.svg +task diagrams:check # verify committed SVGs match their sources (what CI runs) +``` + +Graphviz runs from a WASM npm package, so no system `dot` install is needed. CI fails when a `.dot` +is edited without committing the re-rendered SVG. + +Keep the markdown link **relative** so it renders on GitHub. The website generator copies every +relatively-linked image into `website/public/assets/markdown-images//` and rewrites the link +to that served path, so the diagram renders from the website's own origin regardless of whether the +commit has been pushed. This applies to every markdown body the website ships — reference +architectures, platform readmes and a building block's `## How to Use` section — so a diagram or +screenshot can be committed next to any of them. + +--- + +## Composition + +**Separate the systems into top-level clusters.** The single most effective structural choice is one +box per system — the cloud provider's resource hierarchy in one, meshStack's constructs in the other — +with the relationships drawn as dotted edges across the boundary. The reader sees two worlds they +already understand and one set of lines explaining how they connect, instead of one undifferentiated +graph. + +```dot +subgraph cluster_stackit { label=" STACKIT" /* org, folders, projects */ } +subgraph cluster_mesh { label=" meshStack" /* platform, landing zones, BBDs */ } + +FOUND -> PLAT [style=dotted constraint=false xlabel="authenticates"] +``` + +Use a **lifecycle split** instead (catalog / per application team / existing infrastructure) when the +diagram's story is *who does what and when* rather than *how two systems map onto each other*. Do not +mix both splits in one diagram — pick the one that matches the story and let colour carry the rest. + +**Give each cluster its own depth.** A cluster holding one node reads as noise; either merge it into +a neighbouring cluster or let the node stand alone at the top level. Where a system has layers, show +them (platform → landing zone → building block definition). + +--- + +## Palette + +Colour encodes **what kind of thing a box is**, never decoration. Never introduce a new colour to +distinguish two boxes of the same kind. + +| Role | Fill | Border | Use for | +|------|------|--------|---------| +| `definition` | `#ecedfb` | `#9aa2e6` | meshStack constructs: platform, landing zone, building block definition | +| `instance` | `#e5f2ea` | `#85bfa0` | what a tenant ends up with: project, network, namespace, repo | +| `provider` | `#eef2f6` | `#93a7bb` | services the cloud provider runs: managed cluster, registry, model API | +| `structure` | `#ffffff` | `#a2abb8` | containers and grouping objects: organization, folder | +| `muted` | `#f0f1f3` | `#cdd2d8` | shown for context only, not managed by this architecture | + +`muted` nodes also take `style="rounded,filled,dashed"` and `fontcolor="#99a1ac"` so they read as +out-of-scope even in greyscale. + +Supporting greys: cluster border `#d5dae0`, cluster label `#697180`, edge `#8b95a3`, edge label +`#697180`. + +--- + +## Emoji vocabulary + +Every node label opens with an emoji, and the **same concept always gets the same emoji** across +diagrams. This is what makes separate diagrams read as one family, and it lets a reader match a box +in one diagram to a box in another without reading the label. + +| Emoji | Concept | +|-------|---------| +| 🏢 | organization / tenant root | +| 📁 | folder / resource group | +| 🗂️ | project / subscription | +| 🔑 | service account, credentials | +| 🌐 | network area, address plan | +| 🔌 | network, subnet | +| ☸️ | Kubernetes cluster | +| 🗄️ | container registry | +| ⚙️ | CI/CD wiring | +| 🔀 | git repository | +| 🧠 | model serving / AI API | +| 🛰️ | meshStack platform | +| 🛬 | landing zone | +| 📦 | building block definition | + +Extend the table when a diagram needs a concept it does not cover — do not invent a second emoji for +a concept already listed. + +Copy the emoji straight from this table — several of them carry a **U+FE0F variation selector** that +is invisible in the source but decides whether the glyph renders in colour. `☸ ⚙ 🗄 🗂 🛰` default to +*text* presentation and come out as small monochrome symbols without it; `🏢 📁 🔑 🌐 🔌 🔀 🧠 🛬 📦` +are colour by default. If an icon renders monochrome in the SVG, it is missing the selector. + +The **same emoji with a different fill** is deliberate and useful: a green `🔀` repo inside the +application-team box and a provider-coloured `🔀` in the STACKIT box say "same concept, different +owner" in a way two different icons never could. Emoji carries the concept, colour carries the kind. + +--- + +## Preamble + +Start every diagram from this block so spacing, type and stroke weight match across the repo: + +```dot +digraph { + rankdir=TB + splines=ortho + forcelabels=true + bgcolor="white" + nodesep=0.55 + ranksep=0.85 + + node [shape=box style="rounded,filled" fontname="Helvetica" fontsize=11 + fillcolor="#ffffff" color="#a2abb8" penwidth=1.1 margin="0.20,0.11"] + edge [fontname="Helvetica" fontsize=9 fontcolor="#697180" color="#8b95a3" arrowsize=0.7] +} +``` + +Cluster styling, with two leading spaces in the label so it clears the rounded corner: + +```dot +subgraph cluster_team { + label=" Per Application Team" + labeljust=l + fontname="Helvetica" fontsize=12 fontcolor="#697180" + style="rounded" color="#d5dae0" + margin=18 +} +``` + +`bgcolor="white"` is not optional. A transparent background renders dark-on-dark for anyone reading +the diagram in GitHub's dark theme. + +--- + +## Layout control + +These are the attributes Mermaid lacks, and the reason for the render step: + +- **`{ rank=same; A B C }`** pins nodes to one row. This is how sibling folders end up at the same + height instead of staggered down a diagonal. +- **`constraint=false`** draws an edge without letting it influence ranking. Use it for every + cross-cutting relationship — a mapping between two clusters, or a link back up a hierarchy — that + would otherwise distort the layout it is describing. +- **`style=invis`** edges inside a `rank=same` block nudge left-to-right order: + `{ rank=same; DEV -> PROD [style=invis] }`. Treat it as a hint, not a guarantee — always confirm the + rendered order. +- **`splines=ortho`** gives right-angle connectors, which read as a wiring diagram rather than a mind + map. + +Traps, each of which cost a debugging round: + +- Under `splines=ortho`, use **`xlabel`** (with `forcelabels=true`) for edge labels. Plain `label` + gets placed far from its edge, sometimes in open space. +- **Never set `ordering=out`.** It fights rank ordering and silently mirrors rows — a `rank=same` + chain of `dev -> prod` came out as `prod`, `dev`, and reversing the chain changed nothing. +- **Avoid `newrank=true`** unless a `rank=same` must span clusters. It makes cluster boxes overlap. +- A **`constraint=false` edge between two nodes on the same rank is itself a flat edge** and outranks + your invisible ordering chain. `HARBOR -> SKE [constraint=false]` silently pulled `SKE` out of the + declared `SKE -> MODEL -> HARBOR` order. Declare the chain in the direction the real edge implies + instead of fighting it. +- `splines=polyline` is not a substitute for `ortho` — it produces diagonals and more crossings. + +--- + +## Labels + +- Label the **relationship**, not the objects: `provisioned by`, `target folder of`, `deploys to`. +- On parallel edges that mean the same thing (a dev and a prod edge into the same registry), label + **one** of the pair. Repeating it doubles the text for no information and the labels collide. +- Keep labels to three or four words. Push detail into the node's second line, which is quieter: + `label="🔌 Routed Network ×N\nspoke subnet"`. +- Use `×N` / `×1` to show cardinality rather than drawing several identical boxes. + +--- + +## Diagrams in Markdown + +Reference the SVG with a plain relative image link: + +```markdown +![Azure Kubernetes reference architecture](azure-kubernetes.svg) +``` + +Do **not** inline HTML or `` in a Markdown body. The hub website renders these files through +`marked` into Angular's `[innerHTML]`, which strips `
managed by platform team] - ACR["Azure Container Registry"] - end - - subgraph Self-Service Catalog - C[aks/starterkit
Developer Onboarding] - end - - subgraph Per Application Team - D[github/repository
GitHub Repo] - E_dev[aks/github-connector
Dev Namespace CI/CD] - E_prod[aks/github-connector
Prod Namespace CI/CD] - end - - C -->|creates| D - C -->|creates dev project +
namespace + connector| E_dev - C -->|creates prod project +
namespace + connector| E_prod - D -->|repo output| E_dev - D -->|repo output| E_prod - E_dev -->|build & push images| ACR - E_prod -->|build & push images| ACR - B -->|pull access| ACR - E_dev -->|deploys to| B - E_prod -->|deploys to| B -``` +![Azure Kubernetes reference architecture](azure-kubernetes.svg) ## How It Works diff --git a/reference-architectures/azure-kubernetes/azure-kubernetes.dot b/reference-architectures/azure-kubernetes/azure-kubernetes.dot new file mode 100644 index 00000000..4d5fda29 --- /dev/null +++ b/reference-architectures/azure-kubernetes/azure-kubernetes.dot @@ -0,0 +1,83 @@ +/* + * Azure Kubernetes reference architecture. + * Conventions: .agents/references/diagrams.md — render with: task diagrams + */ +digraph azure_kubernetes { + rankdir=TB + splines=ortho + forcelabels=true + bgcolor="white" + nodesep=0.55 + ranksep=0.85 + + node [shape=box style="rounded,filled" fontname="Helvetica" fontsize=11 + fillcolor="#ffffff" color="#a2abb8" penwidth=1.1 margin="0.20,0.11"] + edge [fontname="Helvetica" fontsize=9 fontcolor="#697180" color="#8b95a3" arrowsize=0.7] + + subgraph cluster_azure { + label=" Azure" + labeljust=l + fontname="Helvetica" fontsize=12 fontcolor="#697180" + style="rounded" color="#d5dae0" + margin=18 + + SUB [label="🗂️ Azure Subscription" fillcolor="#ffffff" color="#a2abb8"] + AKS [label="☸️ AKS cluster\nrun by the platform team" fillcolor="#eef2f6" color="#93a7bb"] + ACR [label="🗄️ Azure Container Registry" fillcolor="#eef2f6" color="#93a7bb"] + NS [label="☸️ Namespace ×2\ndev + prod" fillcolor="#e5f2ea" color="#85bfa0"] + + { rank=same; AKS -> ACR [style=invis] } + + SUB -> AKS + SUB -> ACR + AKS -> NS [xlabel="contains"] + NS -> ACR [xlabel="pulls images" style=dashed constraint=false] + } + + subgraph cluster_github { + label=" GitHub" + labeljust=l + fontname="Helvetica" fontsize=12 fontcolor="#697180" + style="rounded" color="#d5dae0" + margin=18 + + ORG [label="🏢 GitHub organization" fillcolor="#ffffff" color="#a2abb8"] + TPL [label="🔀 Template repository\nmaintained by the platform team" fillcolor="#ffffff" color="#a2abb8"] + REPO [label="🔀 App repository ×1\nper application team" fillcolor="#e5f2ea" color="#85bfa0"] + GHA [label="⚙️ GitHub Actions ×2\ndev + prod environment" fillcolor="#e5f2ea" color="#85bfa0"] + + ORG -> TPL + TPL -> REPO [xlabel="cloned into"] + REPO -> GHA [xlabel="runs workflow"] + } + + subgraph cluster_mesh { + label=" meshStack" + labeljust=l + fontname="Helvetica" fontsize=12 fontcolor="#697180" + style="rounded" color="#d5dae0" + margin=18 + + PLAT [label="🛰️ AKS Namespace Platform" fillcolor="#ecedfb" color="#9aa2e6"] + LZ [label="🛬 Landing Zone ×2\ndev + prod" fillcolor="#ecedfb" color="#9aa2e6"] + PROJ [label="🗂️ Project ×2\ndev + prod" fillcolor="#e5f2ea" color="#85bfa0"] + BBD_S [label="📦 aks/starterkit BBD\nself-service catalog entry" fillcolor="#ecedfb" color="#9aa2e6"] + BBD_R [label="📦 github/repository BBD" fillcolor="#ecedfb" color="#9aa2e6"] + BBD_C [label="📦 aks/github-connector BBD" fillcolor="#ecedfb" color="#9aa2e6"] + + { rank=same; BBD_R -> BBD_C [style=invis] } + + PLAT -> LZ + LZ -> PROJ [xlabel="applies to"] + BBD_S -> BBD_R [xlabel="composes"] + BBD_S -> BBD_C + BBD_S -> PROJ [xlabel="creates" style=dotted constraint=false] + } + + PLAT -> AKS [xlabel="replicates into" style=dotted constraint=false] + PROJ -> NS [xlabel="tenant namespace" style=dotted constraint=false] + BBD_R -> REPO [xlabel="provisions" style=dotted constraint=false] + BBD_C -> GHA [xlabel="provisions" style=dotted constraint=false] + GHA -> ACR [xlabel="pushes images" style=dotted constraint=false] + GHA -> NS [xlabel="deploys to" style=dotted constraint=false] +} diff --git a/reference-architectures/azure-kubernetes/azure-kubernetes.svg b/reference-architectures/azure-kubernetes/azure-kubernetes.svg new file mode 100644 index 00000000..58b713f0 --- /dev/null +++ b/reference-architectures/azure-kubernetes/azure-kubernetes.svg @@ -0,0 +1,240 @@ + + + + +azure_kubernetes + + +cluster_azure + +  Azure + + +cluster_github + +  GitHub + + +cluster_mesh + +  meshStack + + + +SUB + +🗂️ Azure Subscription + + + +AKS + +☸️ AKS cluster +run by the platform team + + + +SUB->AKS + + + + + +ACR + +🗄️ Azure Container Registry + + + +SUB->ACR + + + + + + +NS + +☸️ Namespace ×2 +dev + prod + + + +AKS->NS + + +contains + + + +NS->ACR + + +pulls images + + + +ORG + +🏢 GitHub organization + + + +TPL + +🔀 Template repository +maintained by the platform team + + + +ORG->TPL + + + + + +REPO + +🔀 App repository ×1 +per application team + + + +TPL->REPO + + +cloned into + + + +GHA + +⚙️ GitHub Actions ×2 +dev + prod environment + + + +REPO->GHA + + +runs workflow + + + +GHA->ACR + + +pushes images + + + +GHA->NS + + +deploys to + + + +PLAT + +🛰️ AKS Namespace Platform + + + +PLAT->AKS + + +replicates into + + + +LZ + +🛬 Landing Zone ×2 +dev + prod + + + +PLAT->LZ + + + + + +PROJ + +🗂️ Project ×2 +dev + prod + + + +LZ->PROJ + + +applies to + + + +PROJ->NS + + +tenant namespace + + + +BBD_S + +📦 aks/starterkit BBD +self-service catalog entry + + + +BBD_S->PROJ + + +creates + + + +BBD_R + +📦 github/repository BBD + + + +BBD_S->BBD_R + + +composes + + + +BBD_C + +📦 aks/github-connector BBD + + + +BBD_S->BBD_C + + + + + +BBD_R->REPO + + +provisions + + + + +BBD_C->GHA + + +provisions + + + diff --git a/reference-architectures/azure-kubernetes/logo.png b/reference-architectures/azure-kubernetes/logo.png new file mode 100644 index 00000000..94c65032 Binary files /dev/null and b/reference-architectures/azure-kubernetes/logo.png differ diff --git a/reference-architectures/stackit-hub-spoke/logo.png b/reference-architectures/stackit-hub-spoke/logo.png new file mode 100644 index 00000000..0ef884fb Binary files /dev/null and b/reference-architectures/stackit-hub-spoke/logo.png differ diff --git a/reference-architectures/stackit-kubernetes.md b/reference-architectures/stackit-kubernetes/README.md similarity index 85% rename from reference-architectures/stackit-kubernetes.md rename to reference-architectures/stackit-kubernetes/README.md index 64d2b18a..db3bbd8a 100644 --- a/reference-architectures/stackit-kubernetes.md +++ b/reference-architectures/stackit-kubernetes/README.md @@ -36,48 +36,7 @@ a sovereign LLM API, ensuring even AI capabilities remain under full data contro ## Architecture Diagram -```mermaid -flowchart TD - subgraph STACKIT["STACKIT Global Services"] - Git["STACKIT Git
(Forgejo Instance)"] - Harbor["STACKIT Harbor
(Global Registry
Shared Across Customers)"] - ModelServing["STACKIT Model Serving
(Platform-provided API)"] - end - - subgraph Platform Team - SKE[SKE Cluster
Managed by STACKIT] - end - - subgraph Self-Service Catalog - B[ske/ske-starterkit
Developer Onboarding] - end - - subgraph Per Application Team - C[stackit/git-repository
Forgejo Repo] - D_dev[ske/forgejo-connector
Dev Namespace CI/CD] - D_prod[ske/forgejo-connector
Prod Namespace CI/CD] - P_dev[Dev Project + SKE Tenant] - P_prod[Prod Project + SKE Tenant] - end - - B -->|creates| C - B -->|creates| P_dev - B -->|creates| P_prod - B -->|creates dev connector| D_dev - B -->|creates prod connector| D_prod - P_dev -->|namespace| D_dev - P_prod -->|namespace| D_prod - C -->|hosted on| Git - C -->|repo output| D_dev - C -->|repo output| D_prod - D_dev -->|push images to| Harbor - D_prod -->|push images to| Harbor - D_dev -->|wires Model API secret| ModelServing - D_prod -->|wires Model API secret| ModelServing - D_dev -->|deploys to| SKE - D_prod -->|deploys to| SKE - Harbor -->|pull from| SKE -``` +![STACKIT Kubernetes reference architecture](stackit-kubernetes.svg) ## How It Works diff --git a/reference-architectures/stackit-kubernetes/logo.png b/reference-architectures/stackit-kubernetes/logo.png new file mode 100644 index 00000000..57c2c04b Binary files /dev/null and b/reference-architectures/stackit-kubernetes/logo.png differ diff --git a/reference-architectures/stackit-kubernetes/stackit-kubernetes.dot b/reference-architectures/stackit-kubernetes/stackit-kubernetes.dot new file mode 100644 index 00000000..1b63a726 --- /dev/null +++ b/reference-architectures/stackit-kubernetes/stackit-kubernetes.dot @@ -0,0 +1,74 @@ +/* + * STACKIT Kubernetes (SKE) reference architecture. + * Conventions: .agents/references/diagrams.md — render with: task diagrams + */ +digraph stackit_kubernetes { + rankdir=TB + splines=ortho + forcelabels=true + bgcolor="white" + nodesep=0.55 + ranksep=0.85 + + node [shape=box style="rounded,filled" fontname="Helvetica" fontsize=11 + fillcolor="#ffffff" color="#a2abb8" penwidth=1.1 margin="0.20,0.11"] + edge [fontname="Helvetica" fontsize=9 fontcolor="#697180" color="#8b95a3" arrowsize=0.7] + + subgraph cluster_stackit { + label=" STACKIT" + labeljust=l + fontname="Helvetica" fontsize=12 fontcolor="#697180" + style="rounded" color="#d5dae0" + margin=18 + + ORG [label="🏢 STACKIT Organization" fillcolor="#ffffff" color="#a2abb8"] + SKE [label="☸️ SKE cluster\nrun by the platform team" fillcolor="#eef2f6" color="#93a7bb"] + GIT [label="🔀 STACKIT Git\nForgejo instance" fillcolor="#eef2f6" color="#93a7bb"] + HARBOR [label="🗄️ STACKIT Harbor\nregistry shared across customers" fillcolor="#eef2f6" color="#93a7bb"] + MODEL [label="🧠 STACKIT Model Serving\nsovereign LLM API" fillcolor="#eef2f6" color="#93a7bb"] + NS [label="☸️ Namespace ×2\ndev + prod" fillcolor="#e5f2ea" color="#85bfa0"] + REPO [label="🔀 App repository ×1\nper application team" fillcolor="#e5f2ea" color="#85bfa0"] + ACT [label="⚙️ Forgejo Actions ×2\ndev + prod pipeline" fillcolor="#e5f2ea" color="#85bfa0"] + + { rank=same; SKE -> GIT -> HARBOR -> MODEL [style=invis] } + + ORG -> SKE + ORG -> GIT + SKE -> NS [xlabel="contains"] + GIT -> REPO [xlabel="hosts"] + + REPO -> ACT [xlabel="runs pipeline"] + ACT -> HARBOR [xlabel="pushes images" style=dashed constraint=false] + ACT -> NS [xlabel="deploys to" style=dashed constraint=false] + NS -> HARBOR [xlabel="pulls images" style=dashed constraint=false] + NS -> MODEL [xlabel="calls with injected key" style=dashed constraint=false] + } + + subgraph cluster_mesh { + label=" meshStack" + labeljust=l + fontname="Helvetica" fontsize=12 fontcolor="#697180" + style="rounded" color="#d5dae0" + margin=18 + + PLAT [label="🛰️ SKE Platform" fillcolor="#ecedfb" color="#9aa2e6"] + LZ [label="🛬 Landing Zone ×2\ndev + prod" fillcolor="#ecedfb" color="#9aa2e6"] + PROJ [label="🗂️ Project ×2\ndev + prod" fillcolor="#e5f2ea" color="#85bfa0"] + BBD_S [label="📦 ske/ske-starterkit BBD\nself-service catalog entry" fillcolor="#ecedfb" color="#9aa2e6"] + BBD_R [label="📦 stackit/git-repository BBD" fillcolor="#ecedfb" color="#9aa2e6"] + BBD_C [label="📦 ske/forgejo-connector BBD" fillcolor="#ecedfb" color="#9aa2e6"] + + { rank=same; BBD_R -> BBD_C [style=invis] } + + PLAT -> LZ + LZ -> PROJ [xlabel="applies to"] + BBD_S -> BBD_R [xlabel="composes"] + BBD_S -> BBD_C + BBD_S -> PROJ [xlabel="creates" style=dotted constraint=false] + } + + PLAT -> SKE [xlabel="replicates into" style=dotted constraint=false] + PROJ -> NS [xlabel="tenant namespace" style=dotted constraint=false] + BBD_R -> REPO [xlabel="provisions" style=dotted constraint=false] + BBD_C -> ACT [xlabel="provisions" style=dotted constraint=false] +} diff --git a/reference-architectures/stackit-kubernetes/stackit-kubernetes.svg b/reference-architectures/stackit-kubernetes/stackit-kubernetes.svg new file mode 100644 index 00000000..65c22d94 --- /dev/null +++ b/reference-architectures/stackit-kubernetes/stackit-kubernetes.svg @@ -0,0 +1,240 @@ + + + + +stackit_kubernetes + + +cluster_stackit + +  STACKIT + + +cluster_mesh + +  meshStack + + + +ORG + +🏢 STACKIT Organization + + + +SKE + +☸️ SKE cluster +run by the platform team + + + +ORG->SKE + + + + + +GIT + +🔀 STACKIT Git +Forgejo instance + + + +ORG->GIT + + + + + + +NS + +☸️ Namespace ×2 +dev + prod + + + +SKE->NS + + +contains + + + +HARBOR + +🗄️ STACKIT Harbor +registry shared across customers + + + + +REPO + +🔀 App repository ×1 +per application team + + + +GIT->REPO + + +hosts + + + +MODEL + +🧠 STACKIT Model Serving +sovereign LLM API + + + + +NS->HARBOR + + +pulls images + + + +NS->MODEL + + +calls with injected key + + + +ACT + +⚙️ Forgejo Actions ×2 +dev + prod pipeline + + + +REPO->ACT + + +runs pipeline + + + +ACT->HARBOR + + +pushes images + + + +ACT->NS + + +deploys to + + + +PLAT + +🛰️ SKE Platform + + + +PLAT->SKE + + +replicates into + + + +LZ + +🛬 Landing Zone ×2 +dev + prod + + + +PLAT->LZ + + + + + +PROJ + +🗂️ Project ×2 +dev + prod + + + +LZ->PROJ + + +applies to + + + +PROJ->NS + + +tenant namespace + + + +BBD_S + +📦 ske/ske-starterkit BBD +self-service catalog entry + + + +BBD_S->PROJ + + +creates + + + +BBD_R + +📦 stackit/git-repository BBD + + + +BBD_S->BBD_R + + +composes + + + +BBD_C + +📦 ske/forgejo-connector BBD + + + +BBD_S->BBD_C + + + + + +BBD_R->REPO + + +provisions + + + + +BBD_C->ACT + + +provisions + + + diff --git a/reference-architectures/stackit-sandbox-landingzone/logo.png b/reference-architectures/stackit-sandbox-landingzone/logo.png new file mode 100644 index 00000000..0ef884fb Binary files /dev/null and b/reference-architectures/stackit-sandbox-landingzone/logo.png differ diff --git a/tools/diagrams/render-diagrams.mjs b/tools/diagrams/render-diagrams.mjs new file mode 100644 index 00000000..02ab8f43 --- /dev/null +++ b/tools/diagrams/render-diagrams.mjs @@ -0,0 +1,105 @@ +#!/usr/bin/env node +/** + * Renders every `*.dot` diagram in the repo to a sibling `*.svg`. + * + * node tools/diagrams/render-diagrams.mjs # write the SVGs + * node tools/diagrams/render-diagrams.mjs --check # fail if any SVG is stale + * + * Graphviz runs as a WASM module from node_modules, so no system `dot` install + * is required. The `--check` mode is what CI runs: it re-renders in memory and + * compares against what is committed, so a `.dot` edit without a re-render fails + * the build instead of silently shipping a stale picture. + */ + +import { readFileSync, writeFileSync, readdirSync, statSync } from "node:fs"; +import { join, relative, dirname, basename } from "node:path"; +import { fileURLToPath } from "node:url"; +import { Graphviz } from "@hpcc-js/wasm-graphviz"; + +const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", ".."); + +const SKIP_DIRS = new Set([ + ".git", + ".terraform", + "node_modules", + "dist", + ".go", +]); + +function findDotFiles(dir) { + return readdirSync(dir, { withFileTypes: true }).flatMap(entry => { + if (entry.isDirectory()) { + if (SKIP_DIRS.has(entry.name)) return []; + return findDotFiles(join(dir, entry.name)); + } + return entry.isFile() && entry.name.endsWith(".dot") + ? [join(dir, entry.name)] + : []; + }); +} + +/** + * Drops the generator comment so the committed SVG does not churn on every + * Graphviz version bump, and keeps a trailing newline for well-behaved diffs. + */ +function normalize(svg) { + return svg + .replace(/\s*/g, "") + .replace(/\s*/g, "") + .replace(/\s*$/, "\n"); +} + +const check = process.argv.includes("--check"); +const graphviz = await Graphviz.load(); +const dotFiles = findDotFiles(repoRoot).sort(); + +if (dotFiles.length === 0) { + console.log("No .dot diagrams found."); + process.exit(0); +} + +const stale = []; + +for (const dotPath of dotFiles) { + const svgPath = join(dirname(dotPath), `${basename(dotPath, ".dot")}.svg`); + const rel = relative(repoRoot, svgPath); + + let rendered; + try { + rendered = normalize(graphviz.layout(readFileSync(dotPath, "utf8"), "svg", "dot")); + } catch (err) { + console.error(`❌ ${relative(repoRoot, dotPath)}: ${err.message}`); + process.exit(1); + } + + const current = existsAsFile(svgPath) ? readFileSync(svgPath, "utf8") : null; + + if (current === rendered) { + console.log(`✅ ${rel} up to date`); + continue; + } + + if (check) { + stale.push(rel); + console.error(`❌ ${rel} is ${current === null ? "missing" : "stale"}`); + continue; + } + + writeFileSync(svgPath, rendered); + console.log(`✏️ ${rel} ${current === null ? "created" : "updated"}`); +} + +function existsAsFile(p) { + try { + return statSync(p).isFile(); + } catch { + return false; + } +} + +if (stale.length > 0) { + console.error( + `\n${stale.length} diagram(s) out of date. Run \`task diagrams\` and commit the result.` + ); + process.exit(1); +} diff --git a/tools/scorecard/pr-scorecard.sh b/tools/scorecard/pr-scorecard.sh index c7c64c24..04747b2b 100755 --- a/tools/scorecard/pr-scorecard.sh +++ b/tools/scorecard/pr-scorecard.sh @@ -18,8 +18,10 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$REPO_ROOT" +# Only paths inside a module dir (modules///...) map to a module. +# Provider-level files such as modules//logo.png are not modules and are skipped. MODULES=$(git diff --name-only "${BASE_REF}...HEAD" \ - | grep '^modules/' \ + | grep -E '^modules/[^/]+/[^/]+/' \ | sed 's|^modules/\([^/]*/[^/]*\)/.*|\1|' \ | sort -u || true) diff --git a/website/.gitignore b/website/.gitignore index 89dbeffc..96d3f2e7 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -44,6 +44,8 @@ Thumbs.db # Files generated by index.ts /public/assets/building-block-logos /public/assets/logos +/public/assets/reference-architecture-logos +/public/assets/markdown-images /public/assets/platform.json /public/assets/templates.json diff --git a/website/src/app/core/reference-architecture.ts b/website/src/app/core/reference-architecture.ts index 00c53e62..b0f8e2f4 100644 --- a/website/src/app/core/reference-architecture.ts +++ b/website/src/app/core/reference-architecture.ts @@ -10,6 +10,9 @@ export interface ReferenceArchitecture { cloudProviders: string[]; buildingBlocks: ReferenceArchitectureBuildingBlock[]; body: string; + // Own logo, when one is committed for this architecture. Null means the cloud provider logos + // are shown instead. + logo: string | null; sourceUrl: string | null; // Set when this reference architecture ships its own meshstack_integration.tf and can be // imported into meshStack directly, the same way a building block is imported. diff --git a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html index d5f7c549..ced8104c 100644 --- a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html +++ b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html @@ -12,13 +12,19 @@
- -
-
- -
+ +
+
+ +
+
+ +
+
+
diff --git a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts index 41035c55..a21550f4 100644 --- a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts +++ b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts @@ -31,6 +31,7 @@ interface RefArchDetailVm { buildingBlocks: BuildingBlockLink[]; bodyHtml: string; sourceUrl: string | null; + logo: string | null; platformLogos: { platformType: string; imageUrl: string }[]; integrationSourceUrl: string | null; folderUrl: string | null; @@ -157,6 +158,7 @@ export class ReferenceArchitectureDetailComponent implements OnInit, OnDestroy, buildingBlocks, bodyHtml: marked.parse(arch.body) as string, sourceUrl: arch.sourceUrl, + logo: arch.logo, platformLogos: arch.cloudProviders.map(cp => ({ platformType: cp, imageUrl: platforms.find(p => p.platformType === cp)?.logo ?? 'assets/meshstack-logo.png' diff --git a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html index 22f7a25e..b69cc34a 100644 --- a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html +++ b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html @@ -34,14 +34,20 @@

Reference Archite
- +
-
-
- -
+
+
+ +
+
+ +
+
+

{{ card.name }}

diff --git a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts index 9d7f8796..851d9f6f 100644 --- a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts +++ b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts @@ -17,6 +17,7 @@ interface RefArchCard { cloudProviders: string[]; buildingBlockCount: number; integrationSourceUrl: string | null; + logo: string | null; platformLogos: { platformType: string; imageUrl: string }[]; } @@ -62,6 +63,7 @@ export class ReferenceArchitectureListComponent implements OnInit { cloudProviders: arch.cloudProviders, buildingBlockCount: arch.buildingBlocks.length, integrationSourceUrl: arch.integrationSourceUrl, + logo: arch.logo, platformLogos: arch.cloudProviders.map(cp => ({ platformType: cp, imageUrl: platforms.find(p => p.platformType === cp)?.logo ?? 'assets/meshstack-logo.png' diff --git a/website/src/app/features/template-gallery/template-gallery.component.html b/website/src/app/features/template-gallery/template-gallery.component.html index 40ed2dbc..0cbdaf80 100644 --- a/website/src/app/features/template-gallery/template-gallery.component.html +++ b/website/src/app/features/template-gallery/template-gallery.component.html @@ -74,14 +74,20 @@

- +
-
-
- -
+
+
+ +
+
+ +
+
+

{{ card.name }}

diff --git a/website/src/app/features/template-gallery/template-gallery.component.ts b/website/src/app/features/template-gallery/template-gallery.component.ts index 2051241d..2949691c 100644 --- a/website/src/app/features/template-gallery/template-gallery.component.ts +++ b/website/src/app/features/template-gallery/template-gallery.component.ts @@ -21,6 +21,7 @@ interface RefArchCardVm { name: string; description: string; buildingBlockCount: number; + logo: string | null; platformLogos: { platformType: string; imageUrl: string }[]; } @@ -215,6 +216,7 @@ export class TemplateGalleryComponent implements OnInit, OnDestroy { name: arch.name, description: arch.description, buildingBlockCount: arch.buildingBlocks.length, + logo: arch.logo, platformLogos: arch.cloudProviders.map(cp => ({ platformType: cp, imageUrl: platforms.find(p => p.platformType === cp)?.logo ?? 'assets/meshstack-logo.png' diff --git a/yarn.lock b/yarn.lock index 6327f193..2bf4cd66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@hpcc-js/wasm-graphviz@^1.15.0": + version "1.28.0" + resolved "https://registry.npmjs.org/@hpcc-js/wasm-graphviz/-/wasm-graphviz-1.28.0.tgz" + integrity sha512-KqvPy7ckxPc1xv8Kt7H3bo9cj8U4gfOXxQ8grLZvDZxyQQjnXsKxrweJLLJtEoWWtQqAk4biUB0g2qsjkTiMVA== + "@types/node@^25.2.0": version "25.2.0" resolved "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz"