Monorepo management for opam overlays
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove redundant dev_repo from config and add verse clone support

- Remove unused dev_repo field from Package_config and package_override
types. URL overrides are now handled by sources.toml in the monorepo
root, making the per-package dev_repo in opamverse.toml redundant.

- Add clone_from_verse_if_needed to enable monopam sync to work in
fresh devcontainers by cloning monorepo and opam-repo from verse
registry if they don't exist locally.

- Update documentation to clarify that sources.toml is the single source
of truth for URL overrides, while opamverse.toml only handles branch
overrides.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+93 -36
+2 -2
bin/main.ml
··· 43 43 List.fold_left 44 44 (fun cfg (name, override) -> 45 45 let open Monopam.Verse_config in 46 - Monopam.Config.with_package_override cfg ~name 47 - ?branch:override.branch ?dev_repo:override.dev_repo ()) 46 + Monopam.Config.with_package_override cfg ~name ?branch:override.branch 47 + ()) 48 48 base_config 49 49 (Monopam.Verse_config.packages verse_config) 50 50 in
+7 -12
lib/config.ml
··· 1 1 module Package_config = struct 2 - type t = { 3 - branch : string option; 4 - dev_repo : string option; (** Override dev-repo URL for vendored packages *) 5 - } 2 + type t = { branch : string option } 6 3 7 4 let branch t = t.branch 8 - let dev_repo t = t.dev_repo 9 5 10 6 let codec : t Tomlt.t = 11 7 Tomlt.( 12 8 Table.( 13 - obj (fun branch dev_repo -> { branch; dev_repo }) 9 + obj (fun branch -> { branch }) 14 10 |> opt_mem "branch" string ~enc:(fun c -> c.branch) 15 - |> opt_mem "dev_repo" string ~enc:(fun c -> c.dev_repo) 16 11 |> finish)) 17 12 end 18 13 ··· 36 31 let create ~opam_repo ~checkouts ~monorepo ?(default_branch = "main") () = 37 32 { opam_repo; checkouts; monorepo; default_branch; packages = [] } 38 33 39 - let with_package_override t ~name ?branch:branch_opt ?dev_repo:dev_repo_opt () = 34 + let with_package_override t ~name ?branch:branch_opt () = 40 35 let existing = List.assoc_opt name t.packages in 41 36 let existing_branch = Option.bind existing Package_config.branch in 42 - let existing_dev_repo = Option.bind existing Package_config.dev_repo in 43 - let new_branch = match branch_opt with Some _ -> branch_opt | None -> existing_branch in 44 - let new_dev_repo = match dev_repo_opt with Some _ -> dev_repo_opt | None -> existing_dev_repo in 45 - let pkg_config = Package_config.{ branch = new_branch; dev_repo = new_dev_repo } in 37 + let new_branch = 38 + match branch_opt with Some _ -> branch_opt | None -> existing_branch 39 + in 40 + let pkg_config = Package_config.{ branch = new_branch } in 46 41 let packages = (name, pkg_config) :: List.remove_assoc name t.packages in 47 42 { t with packages } 48 43
+4 -10
lib/config.mli
··· 14 14 15 15 val branch : t -> string option 16 16 (** [branch t] returns the branch override for this package, if set. *) 17 - 18 - val dev_repo : t -> string option 19 - (** [dev_repo t] returns the dev-repo URL override for this package, if set. 20 - Use this to override the source URL for vendored packages. *) 21 17 end 22 18 23 19 type t ··· 112 108 @param monorepo Path to the monorepo 113 109 @param default_branch Default branch to track (default: "main") *) 114 110 115 - val with_package_override : 116 - t -> name:string -> ?branch:string -> ?dev_repo:string -> unit -> t 117 - (** [with_package_override t ~name ?branch ?dev_repo ()] returns a new config 111 + val with_package_override : t -> name:string -> ?branch:string -> unit -> t 112 + (** [with_package_override t ~name ?branch ()] returns a new config 118 113 with overrides for the named package. 119 114 120 115 @param branch Override the git branch for this package 121 - @param dev_repo Override the dev-repo URL for vendored packages. 122 - Use this when you've forked someone else's project and want the opam-repo 123 - to point to your fork instead of the original. *) 116 + 117 + Note: For dev-repo URL overrides, use [sources.toml] in the monorepo root. *) 124 118 125 119 (** {1 Pretty Printing} *) 126 120
+71 -4
lib/monopam.ml
··· 245 245 in 246 246 247 247 (* URL resolution order: 248 - 1. sources.toml override 249 - 2. dune-project source/homepage 250 - 3. existing opam-repo dev-repo (fallback) *) 251 - (* URL resolution order: 252 248 1. Explicit sources.toml entry for this subtree 253 249 2. dune-project source/homepage 254 250 3. sources.toml default_url_base + subtree name *) ··· 1478 1474 if !updated > 0 then 1479 1475 Log.info (fun m -> m "Regenerated %d opam-repo entries from monorepo" !updated) 1480 1476 1477 + (** Clone monorepo and opam-repo from verse registry if they don't exist locally. 1478 + This enables `monopam sync` to work in a fresh devcontainer. *) 1479 + let clone_from_verse_if_needed ~proc ~fs ~config () = 1480 + let monorepo = Config.Paths.monorepo config in 1481 + let opam_repo = Config.Paths.opam_repo config in 1482 + let monorepo_exists = Git.is_repo ~proc ~fs monorepo in 1483 + let opam_repo_exists = Git.is_repo ~proc ~fs opam_repo in 1484 + 1485 + (* If both exist, nothing to do *) 1486 + if monorepo_exists && opam_repo_exists then Ok () 1487 + else 1488 + (* Try to load verse config to get handle *) 1489 + match Verse_config.load ~fs () with 1490 + | Error _ -> 1491 + (* No verse config - can't clone from registry *) 1492 + Log.debug (fun m -> m "No verse config found, will initialize fresh repos"); 1493 + Ok () 1494 + | Ok verse_config -> 1495 + let handle = Verse_config.handle verse_config in 1496 + Log.info (fun m -> m "Found verse config for handle: %s" handle); 1497 + (* Load registry to look up URLs *) 1498 + match Verse_registry.clone_or_pull ~proc ~fs ~config:verse_config () with 1499 + | Error msg -> 1500 + Log.warn (fun m -> m "Could not load verse registry: %s" msg); 1501 + Ok () (* Continue without cloning - will init fresh *) 1502 + | Ok registry -> 1503 + match Verse_registry.find_member registry ~handle with 1504 + | None -> 1505 + Log.warn (fun m -> m "Handle %s not found in registry" handle); 1506 + Ok () 1507 + | Some member -> 1508 + (* Clone monorepo if needed *) 1509 + let result = 1510 + if monorepo_exists then Ok () 1511 + else begin 1512 + Log.app (fun m -> m "Cloning monorepo from %s..." member.monorepo); 1513 + let url = Uri.of_string member.monorepo in 1514 + let branch = Option.value ~default:"main" member.monorepo_branch in 1515 + match Git.clone ~proc ~fs ~url ~branch monorepo with 1516 + | Ok () -> 1517 + Log.app (fun m -> m "Monorepo cloned successfully"); 1518 + Ok () 1519 + | Error e -> 1520 + Log.err (fun m -> m "Failed to clone monorepo: %a" Git.pp_error e); 1521 + Error (Git_error e) 1522 + end 1523 + in 1524 + match result with 1525 + | Error e -> Error e 1526 + | Ok () -> 1527 + (* Clone opam-repo if needed *) 1528 + if opam_repo_exists then Ok () 1529 + else begin 1530 + Log.app (fun m -> m "Cloning opam-repo from %s..." member.opamrepo); 1531 + let url = Uri.of_string member.opamrepo in 1532 + let branch = Option.value ~default:"main" member.opamrepo_branch in 1533 + match Git.clone ~proc ~fs ~url ~branch opam_repo with 1534 + | Ok () -> 1535 + Log.app (fun m -> m "Opam-repo cloned successfully"); 1536 + Ok () 1537 + | Error e -> 1538 + Log.err (fun m -> m "Failed to clone opam-repo: %a" Git.pp_error e); 1539 + Error (Git_error e) 1540 + end 1541 + 1481 1542 let sync ~proc ~fs ~config ?package ?(remote = false) ?(skip_push = false) 1482 1543 ?(skip_pull = false) () = 1483 1544 let fs_t = fs_typed fs in 1545 + 1546 + (* Clone from verse registry if repos don't exist *) 1547 + match clone_from_verse_if_needed ~proc ~fs:fs_t ~config () with 1548 + | Error e -> Error e 1549 + | Ok () -> 1550 + 1484 1551 (* Update the opam repo first - clone if needed *) 1485 1552 let opam_repo = Config.Paths.opam_repo config in 1486 1553 if (not skip_pull) && Git.is_repo ~proc ~fs:fs_t opam_repo then begin
+3 -5
lib/verse_config.ml
··· 2 2 3 3 (** Package-level override for vendored packages *) 4 4 type package_override = { 5 - dev_repo : string option; (** Override dev-repo URL *) 6 5 branch : string option; (** Override branch *) 7 6 } 8 7 ··· 95 94 handle = "anil.recoil.org" 96 95 knot = "git.recoil.org" 97 96 98 - # Optional package overrides for vendored projects 97 + # Optional package overrides (branch only; URL overrides go in sources.toml) 99 98 [packages.braid] 100 - dev_repo = "git+https://github.com/avsm/braid" 99 + branch = "backport-fix" 101 100 *) 102 101 103 102 type workspace_section = { w_root : Fpath.t } ··· 123 122 let package_override_codec : package_override Tomlt.t = 124 123 Tomlt.( 125 124 Table.( 126 - obj (fun dev_repo branch -> { dev_repo; branch }) 127 - |> opt_mem "dev_repo" string ~enc:(fun p -> p.dev_repo) 125 + obj (fun branch -> { branch }) 128 126 |> opt_mem "branch" string ~enc:(fun p -> p.branch) 129 127 |> finish)) 130 128
+6 -3
lib/verse_config.mli
··· 12 12 13 13 (** {1 Types} *) 14 14 15 - (** Package-level override for vendored packages. *) 15 + (** Package-level override for vendored packages. 16 + 17 + Note: For dev-repo URL overrides, use [sources.toml] in the monorepo root instead. 18 + This type only supports branch overrides. *) 16 19 type package_override = { 17 - dev_repo : string option; (** Override dev-repo URL for opam-repo generation *) 18 20 branch : string option; (** Override git branch *) 19 21 } 20 22 ··· 38 40 Each entry is [(subtree_name, override)] where subtree_name is the 39 41 directory name in the monorepo (e.g., "braid" for mono/braid/). 40 42 41 - Use this to override dev-repo URLs for vendored packages. *) 43 + Use this to override git branches. For dev-repo URL overrides, 44 + use [sources.toml] in the monorepo root instead. *) 42 45 43 46 (** {1 Derived Paths} *) 44 47