# monopam Monorepo manager for OCaml. ## Core Idea A monorepo is a directory of **subtrees**. Each subtree mirrors an external git repository. You edit code in the monorepo, build, test, commit — then sync with upstream. Each subtree has: - **source**: where you pull from (any git repo, defaults to your monorepo) - **origin**: where you push to (always your monorepo repo) Push always goes to your monorepo's git remote — a repo you own. You never accidentally push to someone else's repo. Per-subtree source overrides let you pull from upstream repos you don't own. ## Installation Install with opam: ```sh $ opam install monopam ``` If opam cannot find the package, it may not yet be released in the public `opam-repository`. Add the overlay repository, then install it: ```sh $ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git $ opam update $ opam install monopam ``` ## Quick Start ```sh $ # Initialize a workspace $ monopam init --handle yourname.bsky.social $ # Add packages (pulls from upstream, pushes to your monorepo) $ monopam add https://github.com/mirage/eio.git $ monopam add crowbar # resolve from opam $ # Pull upstream changes $ monopam pull $ # Make changes, build, test, commit $ dune build && dune test $ git add -A && git commit -m "Add feature" $ # Push to your monorepo remote $ monopam push ``` ## Commands | Command | Description | |---------|-------------| | `monopam add [name]` | Add a subtree | | `monopam remove ` | Remove a subtree | | `monopam pull [names...]` | Pull updates from source | | `monopam push [names...]` | Push changes to your monorepo remote | | `monopam status [names...]` | Show sync state | | `monopam diff [names...]` | Show changes | | `monopam publish` | Generate opam overlay | | `monopam init` | Initialize workspace | | `monopam fetch [names...]` | Fetch updates without merging | | `monopam clean` | Remove build artifacts and stale state | | `monopam test [names...]` | Run tests for subtrees | | `monopam lint [names...]` | Run merlint across subtrees | | `monopam verse ...` | Collaborate with verse members | ## sources.toml Subtree metadata lives in one file. The top-level `origin` is your monorepo's git remote — the only place `push` ever writes to. Per-subtree entries override where `pull` reads from. ```toml # Your monorepo remote — push always goes here origin = "git@github.com:me/mono.git" # Subtrees that pull from upstream (override source) [eio] source = "https://github.com/mirage/eio.git" [cohttp] source = "https://github.com/mirage/cohttp.git" branch = "main" # Subtrees with no entry pull from origin and push to origin. # No need to list your own projects. ``` - `add ` adds an entry with `source` override - Subtrees not listed use `origin` as both source and push target - `push` always goes to the top-level `origin` - `pull` uses per-subtree `source` if set, otherwise `origin` A subtree that is itself a monorepo can be marked with `mono = true`: ```toml [open-mono] source = "git@github.com:me/mono.git" mono = true ``` This tells `push` and `pull` to recurse into `open-mono/` and process its own `sources.toml` first (depth-first). See [Layers](#layers). ## Source Resolution The `` argument to `add` can be: - **A URL**: `https://github.com/mirage/eio.git` — used directly - **A package name**: `crowbar` — resolved via opam's `dev-repo` field - **A URL#ref**: `https://github.com/mirage/eio.git#v0.15` — pins to a ref Default branch is the remote's HEAD, not hardcoded. ## Layers A monorepo is just a subtree. You can nest monorepos to create layers. ``` product/ sources.toml origin = "git@private.com:co/product.git" [open-mono] source = "git@github.com:me/mono.git" mono = true [secret-lib] source = "git@private.com:co/secret-lib.git" open-mono/ sources.toml origin = "git@github.com:me/mono.git" [eio] source = "https://github.com/mirage/eio.git" [cohttp] source = "https://github.com/mirage/cohttp.git" eio/ cohttp/ mylib/ secret-lib/ app/ ``` Same tool, same commands, same `sources.toml` at every level. The only thing that changes is which directory you're in. Changes flow one layer at a time: - **Inward** (pull): upstream eio → open-mono → product - **Outward** (push): product → open-mono → your fork → PR to upstream `monopam push` is recursive. From the product directory, it detects that `open-mono/` has its own `sources.toml` and pushes inner layers first (depth-first), then the outer layer. One command propagates changes all the way out. This gives you open-source + closed-source separation naturally. Each layer is a separate git repo with its own access controls. Dune sees all directories and builds everything together. ## Overlays Each layer can have its own opam-repo overlay for publishing package metadata: - `open-mono/` → public opam-repo overlay - `product/` → private opam-repo overlay (references public as base) `monopam publish` generates opam entries for the current monorepo's packages. ## Daily Workflow ```sh $ # Get latest from upstream $ monopam pull $ # Work $ dune build && dune test $ git add -A && git commit -m "Description" $ # Send changes to your repos $ monopam push ``` ## Diff ```sh $ monopam diff # What you would push $ monopam diff --incoming # What pull would bring in $ monopam diff eio # Specific subtree ``` ## Collaboration (Verse) Verse lets you browse and pull from collaborators' monorepos. ```sh $ # See what collaborators have $ monopam verse diff $ # Pull their changes $ monopam verse pull alice.bsky.social $ # Cherry-pick a specific commit $ monopam verse cherrypick $ # List members $ monopam verse members ``` Collaboration is just "pull from a different source." A collaborator's monorepo is another subtree you can pull from. ## Design Principles 1. **Push is always safe.** Push goes to your monorepo remote. Never to someone else's repo. 2. **Pull and push are the only sync verbs.** No `sync` command. Pull first, build and test, then push. These steps should never be combined. 3. **A monorepo is a subtree.** Layers emerge from nesting. Same tool at every level. 4. **One manifest, mostly overrides.** `sources.toml` defines your remote and overrides per-subtree sources. Your own projects need no entry. 5. **Name resolution.** `add crowbar` should work, not just URLs. ## Licence ISC