docs: add upstream-PR workflow + lnbits branch-model reference
Two new docs under docs/: - upstream-prs.md — how to send a PR upstream using the ~/dev/upstream-prs/ worktree flow. Opens with a "Primer" section for anyone new to forks / pull requests (the three remote roles — upstream / github-fork / origin — explained as a table), then the per-project prerequisites, the prb/prc helper-driven workflow, and a "Common pitfalls" section. - lnbits-upstream-flow.md — reference for how lnbits/lnbits actually moves: the dev/main branch split, the squash-merge PR convention, the non-FF release merge from dev into main, and how to read the history with --first-parent. Adapted from internal aiolabs notes with the fork-versioning specifics stripped; closing section is generic guidance for anyone maintaining their own long-lived fork. README links both from a new "Further reading" section. The prior "Contributing" section is retitled "Contributing to this scaffold" to avoid colliding with the new upstream-PR doc on the term. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3f88551f1b
commit
30fbd0cef9
3 changed files with 278 additions and 1 deletions
128
docs/lnbits-upstream-flow.md
Normal file
128
docs/lnbits-upstream-flow.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# `lnbits/lnbits` — Upstream Development Flow
|
||||
|
||||
Reference for [`github.com/lnbits/lnbits`](https://github.com/lnbits/lnbits).
|
||||
Verified against git history on 2026-05-18; bump the date when you
|
||||
re-verify, and patch this doc if the model has drifted.
|
||||
|
||||
## Branch model
|
||||
|
||||
Two long-lived branches:
|
||||
|
||||
| Branch | Role | How it moves |
|
||||
|---|---|---|
|
||||
| `dev` | Integration / staging | One squash-merge commit per PR. Linear. |
|
||||
| `main` | Release | A non-fast-forward merge of `dev` at each release. |
|
||||
|
||||
`main` never receives feature PRs directly. **Every change reaches
|
||||
`main` through `dev`.**
|
||||
|
||||
## PR flow → `dev`
|
||||
|
||||
1. Contributor opens a PR targeting `dev` (not `main`).
|
||||
2. Maintainer squash-merges via the GitHub UI.
|
||||
3. The resulting commit on `dev` has:
|
||||
- exactly one parent (linear history),
|
||||
- subject ending in `(#NNNN)` — the squash-merge signature,
|
||||
- lowercase conventional-commit prefix: `feat:`, `fix:`, `chore:`,
|
||||
`chore(deps):`, `docs:`, `ci:`, `test:`, `refactor:`.
|
||||
4. CI runs on the PR; nothing else is required between merge and the
|
||||
commit appearing on `dev`.
|
||||
|
||||
Example chain on `dev`:
|
||||
|
||||
```
|
||||
c9c68bd8 Fix: Use default reaction on bootstrap (#3965)
|
||||
810a1372 fix: tighten agents file (#3966)
|
||||
36d696b2 Fix: wrong use of `in` operator (#3960)
|
||||
8b426efa test: add pyinstrument profiler (#3955)
|
||||
```
|
||||
|
||||
Each is a single squashed commit. No merge commits inside `dev`.
|
||||
|
||||
## Release flow → `main`
|
||||
|
||||
When `dev` is ready to ship:
|
||||
|
||||
1. A release-candidate version bump lands on `dev` as a normal PR:
|
||||
`chore: update to version vX.Y.Z-rcN (#NNNN)`.
|
||||
2. Validation happens against the RC.
|
||||
3. A final version-bump PR lands on `dev`:
|
||||
`chore: update to version vX.Y.Z (#NNNN)`.
|
||||
4. A maintainer runs the release merge locally:
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull --ff-only origin main
|
||||
git merge dev # true (non-FF) merge, default message
|
||||
git push origin main
|
||||
git tag vX.Y.Z # tag the merge commit or the bump commit
|
||||
git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
5. The resulting merge commit on `main` has:
|
||||
- **two parents** (prior `main` tip + `dev` tip),
|
||||
- subject exactly `Merge branch 'dev'` (git default when on `main`),
|
||||
- **not** authored via the GitHub PR-merge UI (that would produce
|
||||
`Merge pull request #N from …`).
|
||||
|
||||
Because `main` typically carries a stray release-bump commit that isn't
|
||||
on `dev`, the histories have diverged and git is forced into a true
|
||||
merge. `--no-ff` is not needed for that reason.
|
||||
|
||||
## Reading the history
|
||||
|
||||
```bash
|
||||
# Release log (one entry per release):
|
||||
git log main --first-parent --oneline
|
||||
|
||||
# Full changelog leading into the next release:
|
||||
git log dev --oneline
|
||||
|
||||
# Verify a merge is a true non-FF merge:
|
||||
git log <sha> -1 --format='%P' # two parent hashes = true merge
|
||||
```
|
||||
|
||||
## Diagram
|
||||
|
||||
```
|
||||
(PRs squash-merged one at a time)
|
||||
│
|
||||
▼
|
||||
dev: ── A ── B ── C ── D ── E ── F (rc1) ── G ── H (v1.5.4)
|
||||
╲
|
||||
╲ (true merge)
|
||||
╲
|
||||
main: ────────── prev release ─────────────── X ─────────── M
|
||||
▲
|
||||
│
|
||||
Merge branch 'dev'
|
||||
tag: v1.5.4
|
||||
```
|
||||
|
||||
## Implications for contributors
|
||||
|
||||
- **Base PRs on `dev`.** PRs against `main` will not be accepted.
|
||||
- **Use lowercase conventional-commit titles.** Verified stable across
|
||||
the last 25+ merged PRs.
|
||||
- **Don't expect `main` to move between releases.** It only advances
|
||||
when a maintainer cuts a release merge.
|
||||
- **Tagging is on `main`.** Consumers pinning to a tag get the
|
||||
released state, never a `dev` snapshot.
|
||||
|
||||
## Adapting this in your own fork
|
||||
|
||||
If you maintain a long-lived fork of `lnbits/lnbits` for production use,
|
||||
you'll likely want to:
|
||||
|
||||
1. Mirror upstream's `dev` / `main` split — easier to track upstream
|
||||
merges back into your fork.
|
||||
2. Adopt a version-suffix convention that surfaces fork identity in
|
||||
tags and the packaged `pyproject.toml` version, e.g.
|
||||
`v<upstream>-<your-tag>.<N>`. This makes it unambiguous in logs and
|
||||
deployed-package metadata that you're running fork-modified code.
|
||||
3. Decide whether you also need pre-release channels (`-rcN`, `-devN`)
|
||||
on top of upstream's; useful if your fork ships to staging hosts
|
||||
before promotion.
|
||||
|
||||
Specifics depend entirely on your team's needs — this scaffold
|
||||
deliberately doesn't prescribe a fork-versioning scheme.
|
||||
Reference in a new issue