1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 07:39:15 +03:00

git flow, infra descriptions, cast flow changes (mobile side)

This commit is contained in:
Anton Afanasyeu
2026-05-23 12:18:43 +02:00
parent 41aa6659f5
commit a5ee5d7140
28 changed files with 1426 additions and 25 deletions

View File

@@ -0,0 +1,298 @@
#!/usr/bin/env python3
"""Build docs/GIT_FLOW.pdf from structured content and Mermaid diagrams.
Source of truth for edits: docs/GIT_FLOW.md (narrative) + this script (PDF layout).
Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py
"""
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import cm
from reportlab.platypus import (
Image,
PageBreak,
Paragraph,
SimpleDocTemplate,
Spacer,
Table,
TableStyle,
)
ROOT = Path(__file__).resolve().parents[2]
BUILD = Path(__file__).resolve().parent
OUT_PDF = ROOT / "docs" / "GIT_FLOW.pdf"
DIAGRAMS = [
("git_flow_diagram_branches.mmd", "git_flow_diagram_branches.png", "Branch model (green master)"),
("git_flow_diagram_feature.mmd", "git_flow_diagram_feature.png", "Daily development"),
("git_flow_diagram_release.mmd", "git_flow_diagram_release.png", "Release: next → master"),
("git_flow_diagram_hotfix.mmd", "git_flow_diagram_hotfix.png", "Hotfix decision and flow"),
]
def render_mermaid() -> None:
for mmd, png, _ in DIAGRAMS:
src = BUILD / mmd
dst = BUILD / png
if not src.exists():
raise FileNotFoundError(src)
subprocess.run(
[
"npx",
"--yes",
"@mermaid-js/mermaid-cli@11.4.0",
"-i",
str(src),
"-o",
str(dst),
"-b",
"white",
"-w",
"1200",
"-H",
"800",
],
cwd=str(ROOT),
check=True,
timeout=180,
)
def p(text: str, style) -> Paragraph:
return Paragraph(text.replace("\n", "<br/>"), style)
def add_table(story, headers: list[str], rows: list[list[str]], col_widths) -> None:
data = [headers] + rows
t = Table(data, colWidths=col_widths)
t.setStyle(
TableStyle(
[
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1565C0")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("GRID", (0, 0), (-1, -1), 0.5, colors.lightgrey),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor("#F5F5F5")]),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
]
)
)
story.append(t)
def add_diagram(story, mmd_png: str, caption: str, h2, iw: float = 16 * cm) -> None:
path = BUILD / mmd_png
if path.exists():
story.append(p(caption, h2))
story.append(Image(str(path), width=iw, height=iw * 0.55))
story.append(Spacer(1, 0.35 * cm))
def build_pdf() -> None:
styles = getSampleStyleSheet()
title = ParagraphStyle(
"DocTitle",
parent=styles["Title"],
fontSize=18,
spaceAfter=10,
textColor=colors.HexColor("#1565C0"),
)
h1 = ParagraphStyle("H1", parent=styles["Heading1"], fontSize=14, spaceBefore=12, spaceAfter=6)
h2 = ParagraphStyle("H2", parent=styles["Heading2"], fontSize=11, spaceBefore=8, spaceAfter=4)
body = ParagraphStyle("Body", parent=styles["Normal"], fontSize=10, leading=14, alignment=TA_LEFT)
small = ParagraphStyle("Small", parent=body, fontSize=9, textColor=colors.grey)
bullet = ParagraphStyle("Bullet", parent=body, leftIndent=14, bulletIndent=6)
story: list = []
story.append(p("Android Cast — Git flow (green master)", title))
story.append(
p(
"Development cycles, branch roles, release and hotfix strategy. "
"Version 1.0 · 2026-05-21. Markdown source: docs/GIT_FLOW.md",
small,
)
)
story.append(Spacer(1, 0.3 * cm))
# 1 Goals
story.append(p("1. Goals", h1))
add_table(
story,
["Goal", "Mechanism"],
[
["Always shippable master", "Merge to master only when CI/tests pass"],
["Safe integration", "Features land on next first"],
["Clear production line", "Release tags only on master"],
["Fast production patches", "hotfix/* from master, then sync to next"],
],
[4.5 * cm, 12 * cm],
)
story.append(Spacer(1, 0.4 * cm))
# 2 Branches
story.append(p("2. Branches", h1))
add_table(
story,
["Branch", "Role", "Direct commits?"],
[
["master", "Production; green; annotated tags vX.Y.Z", "No — merge only"],
["next", "Integration / release candidate", "No — merge only"],
["feature/*, fix/*", "Short-lived work", "Yes; merge → next"],
["hotfix/*", "Urgent fix for released code", "Yes; merge → master first"],
],
[3 * cm, 8.5 * cm, 4 * cm],
)
story.append(Spacer(1, 0.3 * cm))
add_diagram(story, DIAGRAMS[0][1], DIAGRAMS[0][2], h2)
story.append(PageBreak())
# 3 Daily dev
story.append(p("3. Daily development", h1))
for line in [
"1. git fetch && git checkout next && git pull",
"2. git checkout -b feature/short-description",
"3. Commit on topic branch; run tests before merge",
"4. Merge topic → next (after CI green)",
"5. Delete topic branch",
]:
story.append(p(f"{line}", bullet))
story.append(Spacer(1, 0.2 * cm))
story.append(
p(
"<b>Agents:</b> never push directly to master or next unless the user explicitly "
"requests a release or hotfix merge.",
body,
)
)
add_diagram(story, DIAGRAMS[1][1], DIAGRAMS[1][2], h2, iw=15 * cm)
story.append(PageBreak())
# 4 Release
story.append(p("4. Release (next → master)", h1))
for line in [
"Confirm next is QA-ready and CI green",
"Merge next → master",
"Smoke / CI on master",
"Tag on master: git tag -a v0.1.2 -m \"Release v0.1.2\"",
"Sync: git checkout next && git merge master && git push",
"Continue feature branches from updated next",
]:
story.append(p(f"{line}", bullet))
add_diagram(story, DIAGRAMS[2][1], DIAGRAMS[2][2], h2, iw=15 * cm)
story.append(PageBreak())
# 5 Hotfix
story.append(p("5. Hotfixes — recommended strategy", h1))
story.append(
p(
"<b>Default (option 1):</b> branch hotfix/* from <b>master</b> when the bug affects "
"released code. After fix and tests: merge → master, tag patch (e.g. v0.1.3), then "
"<b>merge master → next</b> so integration does not miss the fix.",
body,
)
)
story.append(Spacer(1, 0.15 * cm))
story.append(
p(
"<b>Exception (option 2):</b> bug exists only on next — branch fix/* from next, "
"merge to next only; ships with the next normal release.",
body,
)
)
story.append(Spacer(1, 0.15 * cm))
story.append(
p(
"<b>Why not hotfix only on next?</b> next often contains unreleased features; "
"merging next to master for a patch would ship extra code or block the fix.",
body,
)
)
add_diagram(story, DIAGRAMS[3][1], DIAGRAMS[3][2], h2, iw=14 * cm)
story.append(p("5.1 Sync cheat sheet", h2))
add_table(
story,
["Event", "Action"],
[
["After release + tag", "merge master → next"],
["After hotfix + tag", "merge master → next"],
["Feature done", "merge topic → next only"],
["Wrong base branch", "rebase topic onto latest next"],
],
[4.5 * cm, 12 * cm],
)
story.append(PageBreak())
# 6 Protection + agents
story.append(p("6. Branch protection and CI", h1))
add_table(
story,
["Rule", "master", "next"],
[
["CI required before merge", "yes", "yes"],
["No WIP direct push", "yes", "yes"],
["Annotated release tags", "yes", "no"],
["Force-push", "forbidden", "forbidden"],
],
[5.5 * cm, 5 * cm, 5 * cm],
)
story.append(Spacer(1, 0.35 * cm))
story.append(p("7. Cursor / agent checklist", h1))
for line in [
"Branch from next for features and non-production fixes",
"Run tests before recommending merge to next",
"Releases and tags only with explicit user approval",
"Hotfix released code from master; sync master into next after tag",
"Never force-push master or next",
]:
story.append(p(f"{line}", bullet))
story.append(Spacer(1, 0.4 * cm))
story.append(
p(
"Full narrative and Mermaid sources: docs/GIT_FLOW.md, "
"docs/_pdf_build/git_flow_diagram_*.mmd · "
"Regenerate PDF: python3 docs/_pdf_build/build_git_flow_pdf.py",
small,
)
)
doc = SimpleDocTemplate(
str(OUT_PDF),
pagesize=A4,
leftMargin=2 * cm,
rightMargin=2 * cm,
topMargin=2 * cm,
bottomMargin=2 * cm,
)
doc.build(story)
print(f"Wrote {OUT_PDF}")
def main() -> int:
try:
render_mermaid()
except subprocess.CalledProcessError as e:
print("Mermaid render failed:", e, file=sys.stderr)
return 1
except FileNotFoundError as e:
print(e, file=sys.stderr)
return 1
build_pdf()
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,21 @@
flowchart TB
subgraph legend[" "]
direction LR
L1[Long-lived]
L2[Short-lived]
end
M["master\n(always green, tagged releases)"]
N["next\n(integration / release candidate)"]
F["feature/* fix/*\n(fork next)"]
H["hotfix/*\n(fork master)"]
F -->|"merge + CI green"| N
N -->|"release: merge + CI green"| M
H -->|"urgent patch + CI green"| M
M -->|"sync after release or hotfix"| N
style M fill:#c8e6c9,stroke:#2e7d32
style N fill:#bbdefb,stroke:#1565c0
style F fill:#fff9c4,stroke:#f9a825
style H fill:#ffccbc,stroke:#e64a19

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,15 @@
sequenceDiagram
autonumber
participant Dev as Developer or agent
participant Topic as feature/* or fix/*
participant Next as next
participant CI as Tests / CI
Dev->>Next: fetch, checkout, pull
Dev->>Topic: create branch from next
Dev->>Topic: commits
Dev->>CI: run tests
CI-->>Dev: pass
Dev->>Next: merge topic branch
Note over Next: next stays integration line
Dev->>Topic: delete branch

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,21 @@
flowchart TD
Start([Defect found])
Q{Affects released code\non master / latest tag?}
Start --> Q
Q -->|Yes| A[Branch hotfix/* from master]
A --> B[Fix + tests]
B --> C[Merge to master]
C --> D[Tag patch on master e.g. v0.1.3]
D --> E[Merge master into next]
E --> End([Done])
Q -->|No, only on next| F[Branch fix/* from next]
F --> G[Fix + tests]
G --> H[Merge to next only]
H --> End2([Ships with next release])
style C fill:#c8e6c9
style D fill:#c8e6c9
style E fill:#bbdefb
style H fill:#bbdefb

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,16 @@
sequenceDiagram
autonumber
participant Next as next
participant Master as master
participant CI as Tests / CI
participant Tag as tag vX.Y.Z
Note over Next: feature complete, QA ok
Next->>CI: full test suite
CI-->>Next: green
Next->>Master: merge next to master
Master->>CI: smoke on master
CI-->>Master: green
Master->>Tag: annotated tag on master
Master->>Next: merge master into next (sync)
Note over Next: continue features from synced next

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB