This repository has been archived on 2026-06-22. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
lnbits-sensei/docs/lnbits-upstream-flow.md
Padreug 30fbd0cef9 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>
2026-05-25 20:07:03 +02:00

4.7 KiB

lnbits/lnbits — Upstream Development Flow

Reference for 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:

    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

# 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.