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

EB of git flow

This commit is contained in:
Anton Afanasyeu
2026-05-23 12:36:31 +02:00
parent a5ee5d7140
commit aa05e8e39c
19 changed files with 340 additions and 177 deletions

View File

@@ -24,7 +24,7 @@ Follow [docs/GIT_FLOW.md](docs/GIT_FLOW.md) and [docs/GIT_FLOW.pdf](docs/GIT_FLO
4. **Release:** only after user confirms — merge `next` → `master`, CI green, **tag on master**, then **merge master → next**.
5. **Hotfix (production):** branch from **master**, not next; after tag, **merge master into next**.
6. **Hotfix (next-only bug):** `fix/*` from **next** → merge to **next** only.
7. **Never** force-push `master` or `next`.
7. **Never** force-push `master`; force-push `next` only when the user explicitly requests history rewrite on integration.
8. When working on a named branch, call **SetActiveBranch** with that branch name.
## Quick commands

View File

@@ -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`.
- Narrative: [docs/GIT_FLOW.md](docs/GIT_FLOW.md)
- PDF (diagrams): [docs/GIT_FLOW.pdf](docs/GIT_FLOW.pdf) — regenerate: `python3 docs/_pdf_build/build_git_flow_pdf.py`
- 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)

View File

@@ -1,7 +1,13 @@
# Android Cast — Git flow (green `master`)
**Version:** 1.0 · **Last updated:** 2026-05-21
**PDF:** regenerate with `python3 docs/_pdf_build/build_git_flow_pdf.py``docs/GIT_FLOW.pdf`
| Field | Value |
|-------|-------|
| **Version** | 1.1 |
| **Published** | 2026-05-21 |
| **Author(s)** | Anton Afanaasyeu |
| **Markdown source** | `docs/GIT_FLOW.md` |
| **PDF** | `docs/GIT_FLOW.pdf``python3 docs/_pdf_build/build_git_flow_pdf.py` |
| **Diagrams (vector)** | `docs/_pdf_build/git_flow_diagram_*.svg` — open in Okular for crisp zoom |
This document defines how we develop, integrate, release, and hotfix. **Cursor agents and humans follow the same rules.**
@@ -66,7 +72,7 @@ flowchart LR
3. **Commit** in small logical chunks on the topic branch.
4. **Before merge:** run project checks (Android: `./gradlew :app:testDebugUnitTest` or CI equivalent; backend: relevant tests/lint).
5. **Merge to `next`:** prefer merge commit or squash per team habit; **do not** merge unfinished work.
6. **Delete** topic branch after merge (local + remote).
6. **Topic branch cleanup:** optional locally (`git branch -d feature/...`). On the server, prefer **delete branch on merge** (Gitea/GitLab/GitHub) so merged `feature/*` / `fix/*` are removed on `origin` automatically.
```mermaid
sequenceDiagram
@@ -166,6 +172,7 @@ flowchart TD
| No direct pushes of WIP | ✓ | ✓ | — |
| Only maintainers merge to `master` | ✓ | optional | — |
| Tag only on `master` | ✓ | — | — |
| Force-push | **never** | **maintainer only, rare** | allowed on topic branches |
See **[§7.1 How to protect branches](#71-how-to-protect-branches-not-local-hooks-alone)** below.
@@ -182,10 +189,12 @@ See **[§7.1 How to protect branches](#71-how-to-protect-branches-not-local-hook
#### What to enable on `master` and `next`
1. **No force-push** (no `git push --force` rewriting history).
2. **No direct push** (optional but strong): changes only via merge from `feature/*` or from PR/MR — you merge locally or on the web, but the server rejects `git push origin master` with a normal commit on top without review (exact option name varies).
3. **Require CI green** before merge (if you have Actions/GitLab CI/Gitea Actions).
4. **Restrict who can push** to you (and CI deploy key if needed).
1. **`master`: never allow force-push** — production history must not be rewritten.
2. **`next`: force-push off by default** — enable only for maintainers when you intentionally clean integration history (squash/rebase of already-merged work). Prefer fixing history on `feature/*` before merging to `next`. After a rare `next` force-push, anyone with a clone must reset: `git fetch origin && git reset --hard origin/next`.
3. **Delete branch on merge** on the server — merged `feature/*` / `fix/*` disappear from `origin` without a manual delete step.
4. **No direct push** (optional but strong): changes only via merge from `feature/*` or from PR/MR.
5. **Require CI green** before merge (if you have Actions/GitLab CI/Gitea Actions).
6. **Restrict who can push** to you (and CI deploy key if needed).
`feature/*` branches stay **unprotected** — push freely.
@@ -252,7 +261,7 @@ Agents working in this repo **must**:
4. **Releases:** only describe merging `next` → `master` and tagging after user confirms QA.
5. **Hotfixes on released code:** branch from `master`, merge back to `master`, tag, then **merge `master` into `next`**.
6. **Set active branch** metadata when using a named topic branch (project convention).
7. **Do not** force-push `master` / `next`.
7. **Do not** force-push `master`; avoid force-push on `next` unless the user explicitly requests integration history rewrite.
---

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,16 @@
#!/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).
Pipeline: Mermaid (.mmd) → SVG → high-DPI PNG (rsvg-convert) → PDF with correct aspect ratio.
Vector sources: docs/_pdf_build/git_flow_diagram_*.svg (open in Okular for crisp zoom).
Requires: reportlab, npx (@mermaid-js/mermaid-cli), rsvg-convert (librsvg).
Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py
"""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
@@ -16,6 +20,7 @@ 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.lib.utils import ImageReader
from reportlab.platypus import (
Image,
PageBreak,
@@ -30,35 +35,38 @@ ROOT = Path(__file__).resolve().parents[2]
BUILD = Path(__file__).resolve().parent
OUT_PDF = ROOT / "docs" / "GIT_FLOW.pdf"
# (mmd, svg, png, caption, mermaid w/h px, max width cm in 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"),
("git_flow_diagram_branches.mmd", "git_flow_diagram_branches.svg", "git_flow_diagram_branches.png",
"Branch model (green master)", 1600, 720, 15.5),
("git_flow_diagram_feature.mmd", "git_flow_diagram_feature.svg", "git_flow_diagram_feature.png",
"Daily development", 1100, 1300, 14.0),
("git_flow_diagram_release.mmd", "git_flow_diagram_release.svg", "git_flow_diagram_release.png",
"Release: next → master", 1100, 1300, 14.0),
("git_flow_diagram_hotfix.mmd", "git_flow_diagram_hotfix.svg", "git_flow_diagram_hotfix.png",
"Hotfix decision and flow", 1200, 1000, 14.5),
]
MAX_DIAGRAM_HEIGHT_CM = 16.5
RSVG_DPI = 200
DOC_VERSION = "1.1"
DOC_DATE = "2026-05-21"
DOC_AUTHOR = "Anton Afanaasyeu"
DOC_SOURCE = "docs/GIT_FLOW.md"
def render_mermaid() -> None:
for mmd, png, _ in DIAGRAMS:
for mmd, svg_name, _, _, w_px, h_px, _ in DIAGRAMS:
src = BUILD / mmd
dst = BUILD / png
dst = BUILD / svg_name
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",
"npx", "--yes", "@mermaid-js/mermaid-cli@11.4.0",
"-i", str(src), "-o", str(dst),
"-b", "white", "-w", str(w_px), "-H", str(h_px),
],
cwd=str(ROOT),
check=True,
@@ -66,6 +74,31 @@ 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]:
"""Width/height in cm preserving aspect ratio, capped by page frame."""
w_px, h_px = ImageReader(str(png_path)).getSize()
max_w = max_width_cm * cm
max_h = MAX_DIAGRAM_HEIGHT_CM * cm
scale = min(max_w / w_px, max_h / h_px)
return w_px * scale, h_px * scale
def p(text: str, style) -> Paragraph:
return Paragraph(text.replace("\n", "<br/>"), style)
@@ -93,21 +126,20 @@ def add_table(story, headers: list[str], rows: list[list[str]], col_widths) -> N
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 add_diagram(story, png_name: str, caption: str, h2, max_width_cm: float) -> None:
path = BUILD / png_name
if not path.exists():
return
iw, ih = diagram_size_cm(path, max_width_cm)
story.append(p(caption, h2))
story.append(Image(str(path), width=iw, height=ih))
story.append(Spacer(1, 0.35 * cm))
def build_pdf() -> None:
styles = getSampleStyleSheet()
title = ParagraphStyle(
"DocTitle",
parent=styles["Title"],
fontSize=18,
spaceAfter=10,
"DocTitle", parent=styles["Title"], fontSize=18, spaceAfter=10,
textColor=colors.HexColor("#1565C0"),
)
h1 = ParagraphStyle("H1", parent=styles["Heading1"], fontSize=14, spaceBefore=12, spaceAfter=6)
@@ -118,16 +150,31 @@ def build_pdf() -> None:
story: list = []
story.append(p("Android Cast — Git flow (green master)", title))
story.append(Spacer(1, 0.15 * cm))
add_table(
story,
["Field", "Value"],
[
["Version", DOC_VERSION],
["Published", DOC_DATE],
["Author(s)", DOC_AUTHOR],
["Markdown source", DOC_SOURCE],
["Diagrams (PDF)", f"Mermaid → SVG → PNG @ {RSVG_DPI} DPI"],
["Diagrams (vector)", "docs/_pdf_build/git_flow_diagram_*.svg"],
],
[4.0 * cm, 12.5 * cm],
)
story.append(Spacer(1, 0.25 * cm))
story.append(
p(
"Development cycles, branch roles, release and hotfix strategy. "
"Version 1.0 · 2026-05-21. Markdown source: docs/GIT_FLOW.md",
"PDF diagrams are high-resolution raster with correct aspect ratio (not stretched). "
"For infinite zoom, open the SVG files in Okular or a browser. "
"Okular: View → Zoom for PDF; SVG opens as native vector.",
small,
)
),
)
story.append(Spacer(1, 0.3 * cm))
# 1 Goals
story.append(p("1. Goals", h1))
add_table(
story,
@@ -142,7 +189,6 @@ def build_pdf() -> None:
)
story.append(Spacer(1, 0.4 * cm))
# 2 Branches
story.append(p("2. Branches", h1))
add_table(
story,
@@ -156,31 +202,37 @@ def build_pdf() -> None:
[3 * cm, 8.5 * cm, 4 * cm],
)
story.append(Spacer(1, 0.3 * cm))
add_diagram(story, DIAGRAMS[0][1], DIAGRAMS[0][2], h2)
add_diagram(story, DIAGRAMS[0][2], DIAGRAMS[0][3], h2, DIAGRAMS[0][6])
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",
"git fetch && git checkout next && git pull",
"git checkout -b feature/short-description",
"Commit on topic branch; run tests before merge",
"Merge topic → next (after CI green)",
]:
story.append(p(f"{line}", bullet))
story.append(Spacer(1, 0.2 * cm))
story.append(Spacer(1, 0.15 * cm))
story.append(
p(
"<b>Topic branch cleanup:</b> optional locally. On the server, enable "
"<i>delete branch on merge</i> (Gitea/GitLab/GitHub) so merged feature/* "
"branches are removed automatically on origin.",
body,
),
)
story.append(Spacer(1, 0.15 * 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)
add_diagram(story, DIAGRAMS[1][2], DIAGRAMS[1][3], h2, DIAGRAMS[1][6])
story.append(PageBreak())
# 4 Release
story.append(p("4. Release (next → master)", h1))
for line in [
"Confirm next is QA-ready and CI green",
@@ -191,10 +243,9 @@ def build_pdf() -> None:
"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)
add_diagram(story, DIAGRAMS[2][2], DIAGRAMS[2][3], h2, DIAGRAMS[2][6])
story.append(PageBreak())
# 5 Hotfix
story.append(p("5. Hotfixes — recommended strategy", h1))
story.append(
p(
@@ -202,7 +253,7 @@ def build_pdf() -> None:
"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(
@@ -210,7 +261,7 @@ def build_pdf() -> None:
"<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(
@@ -218,9 +269,9 @@ def build_pdf() -> None:
"<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)
add_diagram(story, DIAGRAMS[3][2], DIAGRAMS[3][3], h2, DIAGRAMS[3][6])
story.append(p("5.1 Sync cheat sheet", h2))
add_table(
@@ -236,7 +287,6 @@ def build_pdf() -> None:
)
story.append(PageBreak())
# 6 Protection + agents
story.append(p("6. Branch protection and CI", h1))
add_table(
story,
@@ -245,10 +295,21 @@ def build_pdf() -> None:
["CI required before merge", "yes", "yes"],
["No WIP direct push", "yes", "yes"],
["Annotated release tags", "yes", "no"],
["Force-push", "forbidden", "forbidden"],
["Force-push", "never", "maintainer only, rare"],
["Delete branch on merge", "n/a", "yes (topic branches)"],
],
[5.5 * cm, 5 * cm, 5 * cm],
)
story.append(Spacer(1, 0.2 * cm))
story.append(
p(
"<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. "
"Prefer merge/rebase on topic branches instead. If you force-push next, coordinate with "
"anyone else using the repo and re-sync their clones.",
body,
),
)
story.append(Spacer(1, 0.35 * cm))
story.append(p("7. Cursor / agent checklist", h1))
for line in [
@@ -256,17 +317,15 @@ def build_pdf() -> None:
"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",
"Never force-push master; avoid force-push on next unless user requests",
]:
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",
"Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py",
small,
)
),
)
doc = SimpleDocTemplate(
@@ -284,12 +343,17 @@ def build_pdf() -> None:
def main() -> int:
try:
render_mermaid()
for _mmd, svg_name, png_name, *_ in DIAGRAMS:
svg_to_png(BUILD / svg_name, BUILD / png_name)
except subprocess.CalledProcessError as e:
print("Mermaid render failed:", e, file=sys.stderr)
print("Diagram render failed:", e, file=sys.stderr)
return 1
except FileNotFoundError as e:
print(e, file=sys.stderr)
return 1
except RuntimeError as e:
print(e, file=sys.stderr)
return 1
build_pdf()
return 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -11,5 +11,4 @@ sequenceDiagram
Dev->>CI: run tests
CI-->>Dev: pass
Dev->>Next: merge topic branch
Note over Next: next stays integration line
Dev->>Topic: delete branch
Note over Next: server may delete merged branch on origin

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 149 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 50 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,3 @@
reportlab>=4.0
# PDF diagrams: Mermaid CLI (npx) + rsvg-convert (system package librsvg).