EB of git flow
@@ -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**.
|
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**.
|
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.
|
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.
|
8. When working on a named branch, call **SetActiveBranch** with that branch name.
|
||||||
|
|
||||||
## Quick commands
|
## Quick commands
|
||||||
|
|||||||
@@ -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 (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)
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
# Android Cast — Git flow (green `master`)
|
# Android Cast — Git flow (green `master`)
|
||||||
|
|
||||||
**Version:** 1.0 · **Last updated:** 2026-05-21
|
| Field | Value |
|
||||||
**PDF:** regenerate with `python3 docs/_pdf_build/build_git_flow_pdf.py` → `docs/GIT_FLOW.pdf`
|
|-------|-------|
|
||||||
|
| **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.**
|
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.
|
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).
|
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.
|
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
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
@@ -166,6 +172,7 @@ flowchart TD
|
|||||||
| No direct pushes of WIP | ✓ | ✓ | — |
|
| No direct pushes of WIP | ✓ | ✓ | — |
|
||||||
| Only maintainers merge to `master` | ✓ | optional | — |
|
| Only maintainers merge to `master` | ✓ | optional | — |
|
||||||
| Tag only on `master` | ✓ | — | — |
|
| 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.
|
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`
|
#### What to enable on `master` and `next`
|
||||||
|
|
||||||
1. **No force-push** (no `git push --force` rewriting history).
|
1. **`master`: never allow force-push** — production history must not be rewritten.
|
||||||
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).
|
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. **Require CI green** before merge (if you have Actions/GitLab CI/Gitea Actions).
|
3. **Delete branch on merge** on the server — merged `feature/*` / `fix/*` disappear from `origin` without a manual delete step.
|
||||||
4. **Restrict who can push** to you (and CI deploy key if needed).
|
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.
|
`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.
|
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`**.
|
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).
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +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.
|
||||||
|
|
||||||
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
|
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
|
||||||
@@ -16,6 +20,7 @@ from reportlab.lib.enums import TA_LEFT
|
|||||||
from reportlab.lib.pagesizes import A4
|
from reportlab.lib.pagesizes import A4
|
||||||
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
|
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
|
from reportlab.lib.utils import ImageReader
|
||||||
from reportlab.platypus import (
|
from reportlab.platypus import (
|
||||||
Image,
|
Image,
|
||||||
PageBreak,
|
PageBreak,
|
||||||
@@ -30,35 +35,38 @@ ROOT = Path(__file__).resolve().parents[2]
|
|||||||
BUILD = Path(__file__).resolve().parent
|
BUILD = Path(__file__).resolve().parent
|
||||||
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)
|
||||||
DIAGRAMS = [
|
DIAGRAMS = [
|
||||||
("git_flow_diagram_branches.mmd", "git_flow_diagram_branches.png", "Branch model (green master)"),
|
("git_flow_diagram_branches.mmd", "git_flow_diagram_branches.svg", "git_flow_diagram_branches.png",
|
||||||
("git_flow_diagram_feature.mmd", "git_flow_diagram_feature.png", "Daily development"),
|
"Branch model (green master)", 1600, 720, 15.5),
|
||||||
("git_flow_diagram_release.mmd", "git_flow_diagram_release.png", "Release: next → master"),
|
("git_flow_diagram_feature.mmd", "git_flow_diagram_feature.svg", "git_flow_diagram_feature.png",
|
||||||
("git_flow_diagram_hotfix.mmd", "git_flow_diagram_hotfix.png", "Hotfix decision and flow"),
|
"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:
|
def render_mermaid() -> None:
|
||||||
for mmd, png, _ in DIAGRAMS:
|
for mmd, svg_name, _, _, w_px, h_px, _ in DIAGRAMS:
|
||||||
src = BUILD / mmd
|
src = BUILD / mmd
|
||||||
dst = BUILD / png
|
dst = BUILD / svg_name
|
||||||
if not src.exists():
|
if not src.exists():
|
||||||
raise FileNotFoundError(src)
|
raise FileNotFoundError(src)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
"npx",
|
"npx", "--yes", "@mermaid-js/mermaid-cli@11.4.0",
|
||||||
"--yes",
|
"-i", str(src), "-o", str(dst),
|
||||||
"@mermaid-js/mermaid-cli@11.4.0",
|
"-b", "white", "-w", str(w_px), "-H", str(h_px),
|
||||||
"-i",
|
|
||||||
str(src),
|
|
||||||
"-o",
|
|
||||||
str(dst),
|
|
||||||
"-b",
|
|
||||||
"white",
|
|
||||||
"-w",
|
|
||||||
"1200",
|
|
||||||
"-H",
|
|
||||||
"800",
|
|
||||||
],
|
],
|
||||||
cwd=str(ROOT),
|
cwd=str(ROOT),
|
||||||
check=True,
|
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:
|
def p(text: str, style) -> Paragraph:
|
||||||
return Paragraph(text.replace("\n", "<br/>"), style)
|
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)
|
story.append(t)
|
||||||
|
|
||||||
|
|
||||||
def add_diagram(story, mmd_png: str, caption: str, h2, iw: float = 16 * cm) -> None:
|
def add_diagram(story, png_name: str, caption: str, h2, max_width_cm: float) -> None:
|
||||||
path = BUILD / mmd_png
|
path = BUILD / png_name
|
||||||
if path.exists():
|
if not path.exists():
|
||||||
story.append(p(caption, h2))
|
return
|
||||||
story.append(Image(str(path), width=iw, height=iw * 0.55))
|
iw, ih = diagram_size_cm(path, max_width_cm)
|
||||||
story.append(Spacer(1, 0.35 * 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:
|
def build_pdf() -> None:
|
||||||
styles = getSampleStyleSheet()
|
styles = getSampleStyleSheet()
|
||||||
title = ParagraphStyle(
|
title = ParagraphStyle(
|
||||||
"DocTitle",
|
"DocTitle", parent=styles["Title"], fontSize=18, spaceAfter=10,
|
||||||
parent=styles["Title"],
|
|
||||||
fontSize=18,
|
|
||||||
spaceAfter=10,
|
|
||||||
textColor=colors.HexColor("#1565C0"),
|
textColor=colors.HexColor("#1565C0"),
|
||||||
)
|
)
|
||||||
h1 = ParagraphStyle("H1", parent=styles["Heading1"], fontSize=14, spaceBefore=12, spaceAfter=6)
|
h1 = ParagraphStyle("H1", parent=styles["Heading1"], fontSize=14, spaceBefore=12, spaceAfter=6)
|
||||||
@@ -118,16 +150,31 @@ def build_pdf() -> None:
|
|||||||
|
|
||||||
story: list = []
|
story: list = []
|
||||||
story.append(p("Android Cast — Git flow (green master)", title))
|
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(
|
story.append(
|
||||||
p(
|
p(
|
||||||
"Development cycles, branch roles, release and hotfix strategy. "
|
"PDF diagrams are high-resolution raster with correct aspect ratio (not stretched). "
|
||||||
"Version 1.0 · 2026-05-21. Markdown source: docs/GIT_FLOW.md",
|
"For infinite zoom, open the SVG files in Okular or a browser. "
|
||||||
|
"Okular: View → Zoom for PDF; SVG opens as native vector.",
|
||||||
small,
|
small,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
story.append(Spacer(1, 0.3 * cm))
|
story.append(Spacer(1, 0.3 * cm))
|
||||||
|
|
||||||
# 1 Goals
|
|
||||||
story.append(p("1. Goals", h1))
|
story.append(p("1. Goals", h1))
|
||||||
add_table(
|
add_table(
|
||||||
story,
|
story,
|
||||||
@@ -142,7 +189,6 @@ def build_pdf() -> None:
|
|||||||
)
|
)
|
||||||
story.append(Spacer(1, 0.4 * cm))
|
story.append(Spacer(1, 0.4 * cm))
|
||||||
|
|
||||||
# 2 Branches
|
|
||||||
story.append(p("2. Branches", h1))
|
story.append(p("2. Branches", h1))
|
||||||
add_table(
|
add_table(
|
||||||
story,
|
story,
|
||||||
@@ -156,31 +202,37 @@ 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][1], DIAGRAMS[0][2], h2)
|
add_diagram(story, DIAGRAMS[0][2], DIAGRAMS[0][3], h2, DIAGRAMS[0][6])
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
|
|
||||||
# 3 Daily dev
|
|
||||||
story.append(p("3. Daily development", h1))
|
story.append(p("3. Daily development", h1))
|
||||||
for line in [
|
for line in [
|
||||||
"1. git fetch && git checkout next && git pull",
|
"git fetch && git checkout next && git pull",
|
||||||
"2. git checkout -b feature/short-description",
|
"git checkout -b feature/short-description",
|
||||||
"3. Commit on topic branch; run tests before merge",
|
"Commit on topic branch; run tests before merge",
|
||||||
"4. Merge topic → next (after CI green)",
|
"Merge topic → next (after CI green)",
|
||||||
"5. Delete topic branch",
|
|
||||||
]:
|
]:
|
||||||
story.append(p(f"• {line}", bullet))
|
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(
|
story.append(
|
||||||
p(
|
p(
|
||||||
"<b>Agents:</b> never push directly to master or next unless the user explicitly "
|
"<b>Agents:</b> never push directly to master or next unless the user explicitly "
|
||||||
"requests a release or hotfix merge.",
|
"requests a release or hotfix merge.",
|
||||||
body,
|
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())
|
story.append(PageBreak())
|
||||||
|
|
||||||
# 4 Release
|
|
||||||
story.append(p("4. Release (next → master)", h1))
|
story.append(p("4. Release (next → master)", h1))
|
||||||
for line in [
|
for line in [
|
||||||
"Confirm next is QA-ready and CI green",
|
"Confirm next is QA-ready and CI green",
|
||||||
@@ -191,10 +243,9 @@ 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][1], DIAGRAMS[2][2], h2, iw=15 * cm)
|
add_diagram(story, DIAGRAMS[2][2], DIAGRAMS[2][3], h2, DIAGRAMS[2][6])
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
|
|
||||||
# 5 Hotfix
|
|
||||||
story.append(p("5. Hotfixes — recommended strategy", h1))
|
story.append(p("5. Hotfixes — recommended strategy", h1))
|
||||||
story.append(
|
story.append(
|
||||||
p(
|
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 "
|
"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.",
|
"<b>merge master → next</b> so integration does not miss the fix.",
|
||||||
body,
|
body,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
story.append(Spacer(1, 0.15 * cm))
|
story.append(Spacer(1, 0.15 * cm))
|
||||||
story.append(
|
story.append(
|
||||||
@@ -210,7 +261,7 @@ def build_pdf() -> None:
|
|||||||
"<b>Exception (option 2):</b> bug exists only on next — branch fix/* from next, "
|
"<b>Exception (option 2):</b> bug exists only on next — branch fix/* from next, "
|
||||||
"merge to next only; ships with the next normal release.",
|
"merge to next only; ships with the next normal release.",
|
||||||
body,
|
body,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
story.append(Spacer(1, 0.15 * cm))
|
story.append(Spacer(1, 0.15 * cm))
|
||||||
story.append(
|
story.append(
|
||||||
@@ -218,9 +269,9 @@ def build_pdf() -> None:
|
|||||||
"<b>Why not hotfix only on next?</b> next often contains unreleased features; "
|
"<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.",
|
"merging next to master for a patch would ship extra code or block the fix.",
|
||||||
body,
|
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))
|
story.append(p("5.1 Sync cheat sheet", h2))
|
||||||
add_table(
|
add_table(
|
||||||
@@ -236,7 +287,6 @@ def build_pdf() -> None:
|
|||||||
)
|
)
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
|
|
||||||
# 6 Protection + agents
|
|
||||||
story.append(p("6. Branch protection and CI", h1))
|
story.append(p("6. Branch protection and CI", h1))
|
||||||
add_table(
|
add_table(
|
||||||
story,
|
story,
|
||||||
@@ -245,10 +295,21 @@ def build_pdf() -> None:
|
|||||||
["CI required before merge", "yes", "yes"],
|
["CI required before merge", "yes", "yes"],
|
||||||
["No WIP direct push", "yes", "yes"],
|
["No WIP direct push", "yes", "yes"],
|
||||||
["Annotated release tags", "yes", "no"],
|
["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],
|
[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(Spacer(1, 0.35 * cm))
|
||||||
story.append(p("7. Cursor / agent checklist", h1))
|
story.append(p("7. Cursor / agent checklist", h1))
|
||||||
for line in [
|
for line in [
|
||||||
@@ -256,17 +317,15 @@ def build_pdf() -> None:
|
|||||||
"Run tests before recommending merge to next",
|
"Run tests before recommending merge to next",
|
||||||
"Releases and tags only with explicit user approval",
|
"Releases and tags only with explicit user approval",
|
||||||
"Hotfix released code from master; sync master into next after tag",
|
"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(p(f"• {line}", bullet))
|
||||||
story.append(Spacer(1, 0.4 * cm))
|
story.append(Spacer(1, 0.4 * cm))
|
||||||
story.append(
|
story.append(
|
||||||
p(
|
p(
|
||||||
"Full narrative and Mermaid sources: docs/GIT_FLOW.md, "
|
"Regenerate: python3 docs/_pdf_build/build_git_flow_pdf.py",
|
||||||
"docs/_pdf_build/git_flow_diagram_*.mmd · "
|
|
||||||
"Regenerate PDF: python3 docs/_pdf_build/build_git_flow_pdf.py",
|
|
||||||
small,
|
small,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
doc = SimpleDocTemplate(
|
doc = SimpleDocTemplate(
|
||||||
@@ -284,12 +343,17 @@ 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("Mermaid render failed:", e, file=sys.stderr)
|
print("Diagram 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
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 9.4 KiB |
1
docs/_pdf_build/git_flow_diagram_branches.reportlab.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
1
docs/_pdf_build/git_flow_diagram_branches.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
@@ -11,5 +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: next stays integration line
|
Note over Next: server may delete merged branch on origin
|
||||||
Dev->>Topic: delete branch
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 44 KiB |
1
docs/_pdf_build/git_flow_diagram_feature.reportlab.svg
Normal file
|
After Width: | Height: | Size: 27 KiB |
1
docs/_pdf_build/git_flow_diagram_feature.svg
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 18 KiB |
1
docs/_pdf_build/git_flow_diagram_hotfix.reportlab.svg
Normal file
|
After Width: | Height: | Size: 149 KiB |
1
docs/_pdf_build/git_flow_diagram_hotfix.svg
Normal file
|
After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 50 KiB |
1
docs/_pdf_build/git_flow_diagram_release.reportlab.svg
Normal file
|
After Width: | Height: | Size: 28 KiB |
1
docs/_pdf_build/git_flow_diagram_release.svg
Normal file
|
After Width: | Height: | Size: 28 KiB |
3
docs/_pdf_build/requirements-pdf.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
reportlab>=4.0
|
||||||
|
|
||||||
|
# PDF diagrams: Mermaid CLI (npx) + rsvg-convert (system package librsvg).
|
||||||