last changes in rendering pipeline
@@ -92,4 +92,4 @@ Activities bind via AIDL (`ICastSenderService` / `ICastReceiverService`) for sta
|
|||||||
We use a **green `master`** plus integration branch **`next`**: features merge to `next` first; releases merge `next` → `master` and are tagged there; production hotfixes branch from `master` and sync back into `next`.
|
We use a **green `master`** plus integration branch **`next`**: features merge to `next` first; releases merge `next` → `master` and are tagged there; production hotfixes branch from `master` and sync back into `next`.
|
||||||
|
|
||||||
- Narrative: [docs/GIT_FLOW.md](docs/GIT_FLOW.md)
|
- Narrative: [docs/GIT_FLOW.md](docs/GIT_FLOW.md)
|
||||||
- PDF: [docs/GIT_FLOW.pdf](docs/GIT_FLOW.pdf) — `pip install -r docs/_pdf_build/requirements-pdf.txt` + `rsvg-convert`, then `python3 docs/_pdf_build/build_git_flow_pdf.py` (SVG sources alongside for vector view)
|
- PDF: [docs/GIT_FLOW.pdf](docs/GIT_FLOW.pdf) — `pip install -r docs/_pdf_build/requirements-pdf.txt`, then `python3 docs/_pdf_build/build_git_flow_pdf.py`
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Android Cast — Git flow (green `master`)
|
# Android Cast — Git flow (green `master`)
|
||||||
|
|
||||||
|
This document defines how we develop, integrate, release, and hotfix. **Cursor agents and humans follow the same rules.**
|
||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| **Version** | 1.1 |
|
| Version | 1.3 |
|
||||||
| **Published** | 2026-05-21 |
|
| Published | 2026-05-23 |
|
||||||
| **Author(s)** | Anton Afanaasyeu |
|
| Author(s) | Anton Afanaasyeu |
|
||||||
| **Markdown source** | `docs/GIT_FLOW.md` |
|
| Markdown source | `docs/GIT_FLOW.md` |
|
||||||
| **PDF** | `docs/GIT_FLOW.pdf` — `python3 docs/_pdf_build/build_git_flow_pdf.py` |
|
| PDF | `docs/GIT_FLOW.pdf` |
|
||||||
| **Diagrams (vector)** | `docs/_pdf_build/git_flow_diagram_*.svg` — open in Okular for crisp zoom |
|
| Diagram sources | `docs/_pdf_build/git_flow_diagram_*.mmd` (render via `build_git_flow_pdf.py`) |
|
||||||
|
|
||||||
This document defines how we develop, integrate, release, and hotfix. **Cursor agents and humans follow the same rules.**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Build docs/GIT_FLOW.pdf from structured content and Mermaid diagrams.
|
"""Build docs/GIT_FLOW.pdf from structured content and Mermaid diagrams.
|
||||||
|
|
||||||
Pipeline: Mermaid (.mmd) → SVG → high-DPI PNG (rsvg-convert) → PDF with correct aspect ratio.
|
Diagrams: Mermaid CLI renders PNG via headless Chromium (text + layout correct).
|
||||||
Vector sources: docs/_pdf_build/git_flow_diagram_*.svg (open in Okular for crisp zoom).
|
Do NOT pipe Mermaid SVG through rsvg-convert — Mermaid uses foreignObject HTML labels
|
||||||
|
that librsvg drops (boxes only, no text).
|
||||||
|
|
||||||
Requires: reportlab, npx (@mermaid-js/mermaid-cli), rsvg-convert (librsvg).
|
Requires: reportlab, npx (@mermaid-js/mermaid-cli), Chromium (puppeteer bundled).
|
||||||
Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py
|
Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -33,40 +33,47 @@ from reportlab.platypus import (
|
|||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
BUILD = Path(__file__).resolve().parent
|
BUILD = Path(__file__).resolve().parent
|
||||||
|
CONFIG = BUILD / "mermaid-config.json"
|
||||||
OUT_PDF = ROOT / "docs" / "GIT_FLOW.pdf"
|
OUT_PDF = ROOT / "docs" / "GIT_FLOW.pdf"
|
||||||
|
|
||||||
# (mmd, svg, png, caption, mermaid w/h px, max width cm in PDF)
|
# (mmd, png, caption, mermaid width px, height px, scale, max width cm in PDF)
|
||||||
DIAGRAMS = [
|
DIAGRAMS = [
|
||||||
("git_flow_diagram_branches.mmd", "git_flow_diagram_branches.svg", "git_flow_diagram_branches.png",
|
("git_flow_diagram_branches.mmd", "git_flow_diagram_branches.png",
|
||||||
"Branch model (green master)", 1600, 720, 15.5),
|
"Branch model (green master)", 1800, 900, 2, 15.5),
|
||||||
("git_flow_diagram_feature.mmd", "git_flow_diagram_feature.svg", "git_flow_diagram_feature.png",
|
("git_flow_diagram_feature.mmd", "git_flow_diagram_feature.png",
|
||||||
"Daily development", 1100, 1300, 14.0),
|
"Daily development", 2400, 2000, 2, 14.0),
|
||||||
("git_flow_diagram_release.mmd", "git_flow_diagram_release.svg", "git_flow_diagram_release.png",
|
("git_flow_diagram_release.mmd", "git_flow_diagram_release.png",
|
||||||
"Release: next → master", 1100, 1300, 14.0),
|
"Release: next → master", 2400, 2000, 2, 14.0),
|
||||||
("git_flow_diagram_hotfix.mmd", "git_flow_diagram_hotfix.svg", "git_flow_diagram_hotfix.png",
|
("git_flow_diagram_hotfix.mmd", "git_flow_diagram_hotfix.png",
|
||||||
"Hotfix decision and flow", 1200, 1000, 14.5),
|
"Hotfix decision and flow", 1300, 1100, 2, 14.5),
|
||||||
]
|
]
|
||||||
|
|
||||||
MAX_DIAGRAM_HEIGHT_CM = 16.5
|
MAX_DIAGRAM_HEIGHT_CM = 16.5
|
||||||
RSVG_DPI = 200
|
|
||||||
|
|
||||||
DOC_VERSION = "1.1"
|
DOC_VERSION = "1.3"
|
||||||
DOC_DATE = "2026-05-21"
|
DOC_DATE = "2026-05-23"
|
||||||
DOC_AUTHOR = "Anton Afanaasyeu"
|
DOC_AUTHOR = "Anton Afanaasyeu"
|
||||||
DOC_SOURCE = "docs/GIT_FLOW.md"
|
DOC_SOURCE = "docs/GIT_FLOW.md"
|
||||||
|
|
||||||
|
|
||||||
def render_mermaid() -> None:
|
def render_mermaid() -> None:
|
||||||
for mmd, svg_name, _, _, w_px, h_px, _ in DIAGRAMS:
|
if not CONFIG.exists():
|
||||||
|
raise FileNotFoundError(CONFIG)
|
||||||
|
for mmd, png_name, _, w_px, h_px, scale, _ in DIAGRAMS:
|
||||||
src = BUILD / mmd
|
src = BUILD / mmd
|
||||||
dst = BUILD / svg_name
|
dst = BUILD / png_name
|
||||||
if not src.exists():
|
if not src.exists():
|
||||||
raise FileNotFoundError(src)
|
raise FileNotFoundError(src)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
"npx", "--yes", "@mermaid-js/mermaid-cli@11.4.0",
|
"npx", "--yes", "@mermaid-js/mermaid-cli@11.4.0",
|
||||||
"-i", str(src), "-o", str(dst),
|
"-i", str(src),
|
||||||
"-b", "white", "-w", str(w_px), "-H", str(h_px),
|
"-o", str(dst),
|
||||||
|
"-c", str(CONFIG),
|
||||||
|
"-b", "white",
|
||||||
|
"-w", str(w_px),
|
||||||
|
"-H", str(h_px),
|
||||||
|
"-s", str(scale),
|
||||||
],
|
],
|
||||||
cwd=str(ROOT),
|
cwd=str(ROOT),
|
||||||
check=True,
|
check=True,
|
||||||
@@ -74,24 +81,7 @@ def render_mermaid() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def svg_to_png(svg_path: Path, png_path: Path) -> None:
|
|
||||||
if not shutil.which("rsvg-convert"):
|
|
||||||
raise RuntimeError("rsvg-convert not found (install librsvg / gnome-base/rsvg)")
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
"rsvg-convert",
|
|
||||||
"-d", str(RSVG_DPI),
|
|
||||||
"-p", str(RSVG_DPI),
|
|
||||||
"-o", str(png_path),
|
|
||||||
str(svg_path),
|
|
||||||
],
|
|
||||||
check=True,
|
|
||||||
timeout=60,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def diagram_size_cm(png_path: Path, max_width_cm: float) -> tuple[float, float]:
|
def diagram_size_cm(png_path: Path, max_width_cm: float) -> tuple[float, float]:
|
||||||
"""Width/height in cm preserving aspect ratio, capped by page frame."""
|
|
||||||
w_px, h_px = ImageReader(str(png_path)).getSize()
|
w_px, h_px = ImageReader(str(png_path)).getSize()
|
||||||
max_w = max_width_cm * cm
|
max_w = max_width_cm * cm
|
||||||
max_h = MAX_DIAGRAM_HEIGHT_CM * cm
|
max_h = MAX_DIAGRAM_HEIGHT_CM * cm
|
||||||
@@ -159,17 +149,15 @@ def build_pdf() -> None:
|
|||||||
["Published", DOC_DATE],
|
["Published", DOC_DATE],
|
||||||
["Author(s)", DOC_AUTHOR],
|
["Author(s)", DOC_AUTHOR],
|
||||||
["Markdown source", DOC_SOURCE],
|
["Markdown source", DOC_SOURCE],
|
||||||
["Diagrams (PDF)", f"Mermaid → SVG → PNG @ {RSVG_DPI} DPI"],
|
["Diagrams", "Mermaid CLI → PNG (Chromium); proportional embed in PDF"],
|
||||||
["Diagrams (vector)", "docs/_pdf_build/git_flow_diagram_*.svg"],
|
|
||||||
],
|
],
|
||||||
[4.0 * cm, 12.5 * cm],
|
[4.0 * cm, 12.5 * cm],
|
||||||
)
|
)
|
||||||
story.append(Spacer(1, 0.25 * cm))
|
story.append(Spacer(1, 0.25 * cm))
|
||||||
story.append(
|
story.append(
|
||||||
p(
|
p(
|
||||||
"PDF diagrams are high-resolution raster with correct aspect ratio (not stretched). "
|
"Diagrams are rendered by headless Chromium (not rsvg/SVG rasterize) so labels stay visible. "
|
||||||
"For infinite zoom, open the SVG files in Okular or a browser. "
|
"Regenerate after editing docs/_pdf_build/git_flow_diagram_*.mmd.",
|
||||||
"Okular: View → Zoom for PDF; SVG opens as native vector.",
|
|
||||||
small,
|
small,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -202,7 +190,7 @@ def build_pdf() -> None:
|
|||||||
[3 * cm, 8.5 * cm, 4 * cm],
|
[3 * cm, 8.5 * cm, 4 * cm],
|
||||||
)
|
)
|
||||||
story.append(Spacer(1, 0.3 * cm))
|
story.append(Spacer(1, 0.3 * cm))
|
||||||
add_diagram(story, DIAGRAMS[0][2], DIAGRAMS[0][3], h2, DIAGRAMS[0][6])
|
add_diagram(story, DIAGRAMS[0][1], DIAGRAMS[0][2], h2, DIAGRAMS[0][6])
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
|
|
||||||
story.append(p("3. Daily development", h1))
|
story.append(p("3. Daily development", h1))
|
||||||
@@ -230,7 +218,7 @@ def build_pdf() -> None:
|
|||||||
body,
|
body,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
add_diagram(story, DIAGRAMS[1][2], DIAGRAMS[1][3], h2, DIAGRAMS[1][6])
|
add_diagram(story, DIAGRAMS[1][1], DIAGRAMS[1][2], h2, DIAGRAMS[1][6])
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
|
|
||||||
story.append(p("4. Release (next → master)", h1))
|
story.append(p("4. Release (next → master)", h1))
|
||||||
@@ -243,7 +231,7 @@ def build_pdf() -> None:
|
|||||||
"Continue feature branches from updated next",
|
"Continue feature branches from updated next",
|
||||||
]:
|
]:
|
||||||
story.append(p(f"• {line}", bullet))
|
story.append(p(f"• {line}", bullet))
|
||||||
add_diagram(story, DIAGRAMS[2][2], DIAGRAMS[2][3], h2, DIAGRAMS[2][6])
|
add_diagram(story, DIAGRAMS[2][1], DIAGRAMS[2][2], h2, DIAGRAMS[2][6])
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
|
|
||||||
story.append(p("5. Hotfixes — recommended strategy", h1))
|
story.append(p("5. Hotfixes — recommended strategy", h1))
|
||||||
@@ -271,7 +259,7 @@ def build_pdf() -> None:
|
|||||||
body,
|
body,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
add_diagram(story, DIAGRAMS[3][2], DIAGRAMS[3][3], h2, DIAGRAMS[3][6])
|
add_diagram(story, DIAGRAMS[3][1], DIAGRAMS[3][2], h2, DIAGRAMS[3][6])
|
||||||
|
|
||||||
story.append(p("5.1 Sync cheat sheet", h2))
|
story.append(p("5.1 Sync cheat sheet", h2))
|
||||||
add_table(
|
add_table(
|
||||||
@@ -304,9 +292,8 @@ def build_pdf() -> None:
|
|||||||
story.append(
|
story.append(
|
||||||
p(
|
p(
|
||||||
"<b>Force-push on next:</b> keep disabled by default. Allow only when you intentionally "
|
"<b>Force-push on next:</b> keep disabled by default. Allow only when you intentionally "
|
||||||
"rewrite integration history (e.g. squash fixups before release) — never on master. "
|
"rewrite integration history — never on master. Prefer fixing on topic branches before "
|
||||||
"Prefer merge/rebase on topic branches instead. If you force-push next, coordinate with "
|
"merging to next.",
|
||||||
"anyone else using the repo and re-sync their clones.",
|
|
||||||
body,
|
body,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -322,10 +309,7 @@ def build_pdf() -> None:
|
|||||||
story.append(p(f"• {line}", bullet))
|
story.append(p(f"• {line}", bullet))
|
||||||
story.append(Spacer(1, 0.4 * cm))
|
story.append(Spacer(1, 0.4 * cm))
|
||||||
story.append(
|
story.append(
|
||||||
p(
|
p("Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py", small),
|
||||||
"Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py",
|
|
||||||
small,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
doc = SimpleDocTemplate(
|
doc = SimpleDocTemplate(
|
||||||
@@ -343,17 +327,12 @@ def build_pdf() -> None:
|
|||||||
def main() -> int:
|
def main() -> int:
|
||||||
try:
|
try:
|
||||||
render_mermaid()
|
render_mermaid()
|
||||||
for _mmd, svg_name, png_name, *_ in DIAGRAMS:
|
|
||||||
svg_to_png(BUILD / svg_name, BUILD / png_name)
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print("Diagram render failed:", e, file=sys.stderr)
|
print("Mermaid render failed:", e, file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
print(e, file=sys.stderr)
|
print(e, file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
except RuntimeError as e:
|
|
||||||
print(e, file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
build_pdf()
|
build_pdf()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
|
%% Branch model — render PNG via mermaid-cli (Chromium). Do not rasterize SVG with rsvg.
|
||||||
flowchart TB
|
flowchart TB
|
||||||
subgraph legend[" "]
|
|
||||||
direction LR
|
|
||||||
L1[Long-lived]
|
|
||||||
L2[Short-lived]
|
|
||||||
end
|
|
||||||
|
|
||||||
M["master\n(always green, tagged releases)"]
|
M["master\n(always green, tagged releases)"]
|
||||||
N["next\n(integration / release candidate)"]
|
N["next\n(integration / release candidate)"]
|
||||||
F["feature/* fix/*\n(fork next)"]
|
F["feature/* fix/*\n(fork next)"]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
@@ -11,4 +11,4 @@ sequenceDiagram
|
|||||||
Dev->>CI: run tests
|
Dev->>CI: run tests
|
||||||
CI-->>Dev: pass
|
CI-->>Dev: pass
|
||||||
Dev->>Next: merge topic branch
|
Dev->>Next: merge topic branch
|
||||||
Note over Next: server may delete merged branch on origin
|
Note over Next: delete branch on merge<br/>(origin server setting)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 149 KiB |
@@ -5,7 +5,7 @@ sequenceDiagram
|
|||||||
participant CI as Tests / CI
|
participant CI as Tests / CI
|
||||||
participant Tag as tag vX.Y.Z
|
participant Tag as tag vX.Y.Z
|
||||||
|
|
||||||
Note over Next: feature complete, QA ok
|
Note over Next: feature complete,<br/>QA ok
|
||||||
Next->>CI: full test suite
|
Next->>CI: full test suite
|
||||||
CI-->>Next: green
|
CI-->>Next: green
|
||||||
Next->>Master: merge next to master
|
Next->>Master: merge next to master
|
||||||
@@ -13,4 +13,4 @@ sequenceDiagram
|
|||||||
CI-->>Master: green
|
CI-->>Master: green
|
||||||
Master->>Tag: annotated tag on master
|
Master->>Tag: annotated tag on master
|
||||||
Master->>Next: merge master into next (sync)
|
Master->>Next: merge master into next (sync)
|
||||||
Note over Next: continue features from synced next
|
Note over Next: continue features<br/>from synced next
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
17
docs/_pdf_build/mermaid-config.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"theme": "neutral",
|
||||||
|
"flowchart": {
|
||||||
|
"htmlLabels": true,
|
||||||
|
"curve": "basis"
|
||||||
|
},
|
||||||
|
"sequence": {
|
||||||
|
"diagramMarginX": 120,
|
||||||
|
"diagramMarginY": 28,
|
||||||
|
"actorMargin": 90,
|
||||||
|
"noteMargin": 24,
|
||||||
|
"messageMargin": 48,
|
||||||
|
"boxMargin": 14,
|
||||||
|
"mirrorActors": false,
|
||||||
|
"useMaxWidth": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
reportlab>=4.0
|
reportlab>=4.0
|
||||||
|
|
||||||
# PDF diagrams: Mermaid CLI (npx) + rsvg-convert (system package librsvg).
|
# Diagrams: npx @mermaid-js/mermaid-cli (uses bundled Chromium).
|
||||||
|
# Do NOT use rsvg-convert on Mermaid SVG — HTML labels are dropped.
|
||||||
|
|||||||