Monorepo management for opam overlays
0
fork

Configure Feed

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

fix(merlint): show cwd in project root log message

Display working directory alongside project root in -v output to help
debug workspace resolution issues when running merlint on external
projects.

+347 -161
+113 -152
lib/opam_sync.ml
··· 137 137 | [] -> all_pkgs 138 138 | names -> 139 139 List.filter 140 - (fun p -> 141 - List.exists 142 - (fun name -> Pkg.name p = name || Pkg.subtree p = name) 143 - names) 140 + (fun p -> List.exists (fun n -> Pkg.matches_name n p) names) 144 141 all_pkgs 145 142 in 146 143 let total = List.length pkgs in ··· 194 191 195 192 (** {1 CWD-based Export} *) 196 193 197 - (** Discover packages from a standalone project directory. Similar to 198 - Pkg.discover but works without config, scanning for dune-project and opam 199 - files in the source directory. *) 194 + (** Derive dev_repo and url_src from a dune-project and sources registry. *) 195 + let derive_urls ~sources ~subtree dune_proj = 196 + match 197 + (Dune_project.dev_repo_url dune_proj, Dune_project.url_with_branch dune_proj) 198 + with 199 + | Ok dr, Ok us -> (dr, us) 200 + | _ -> ( 201 + match Sources_registry.derive_url sources ~subtree with 202 + | Some url -> (url, url ^ "#main") 203 + | None -> ("", "")) 204 + 205 + (** Load opam packages from a directory given metadata. *) 206 + let load_opam_pkgs dir_eio ~subtree ~dev_repo ~url_src = 207 + let opam_files = 208 + try 209 + Eio.Path.read_dir dir_eio 210 + |> List.filter (fun name -> Filename.check_suffix name ".opam") 211 + with Eio.Io _ -> [] 212 + in 213 + List.filter_map 214 + (fun opam_file -> 215 + let pkg_name = Filename.chop_suffix opam_file ".opam" in 216 + let opam_path = Eio.Path.(dir_eio / opam_file) in 217 + try 218 + let raw_content = Eio.Path.load opam_path in 219 + let opam_content = 220 + Opam_transform.transform ~content:raw_content ~dev_repo ~url_src 221 + in 222 + Some (Pkg.v ~pkg_name ~subtree ~dev_repo ~url_src ~opam_content) 223 + with Eio.Io _ -> None) 224 + opam_files 225 + 226 + (** Scan subdirectories for packages (each subdir with its own dune-project). *) 227 + let scan_subdirectories ~source_eio ~sources = 228 + let subdirs = 229 + try 230 + Eio.Path.read_dir source_eio 231 + |> List.filter (fun name -> 232 + let child = Eio.Path.(source_eio / name) in 233 + match Eio.Path.kind ~follow:false child with 234 + | `Directory -> true 235 + | _ -> false) 236 + with Eio.Io _ -> [] 237 + in 238 + List.fold_left 239 + (fun acc subtree -> 240 + let subtree_path = Eio.Path.(source_eio / subtree) in 241 + let dune_path = Eio.Path.(subtree_path / "dune-project") in 242 + match Eio.Path.kind ~follow:false dune_path with 243 + | `Regular_file -> ( 244 + let content = 245 + try Some (Eio.Path.load dune_path) with Eio.Io _ -> None 246 + in 247 + match content with 248 + | None -> acc 249 + | Some content -> ( 250 + match Dune_project.parse content with 251 + | Error _ -> acc 252 + | Ok dune_proj -> 253 + let dev_repo, url_src = 254 + derive_urls ~sources ~subtree dune_proj 255 + in 256 + let pkgs = 257 + load_opam_pkgs subtree_path ~subtree ~dev_repo ~url_src 258 + in 259 + pkgs @ acc)) 260 + | _ -> acc 261 + | exception Eio.Io _ -> acc) 262 + [] subdirs 263 + |> List.rev 264 + 265 + (** Discover packages from a project directory. Scans both root-level opam files 266 + (if a dune-project exists) and subdirectory packages (for workspace roots). 267 + *) 200 268 let discover_from_cwd ~fs ~source = 201 269 let source_eio = Eio.Path.(fs / Fpath.to_string source) in 202 270 let sources = load_sources ~fs ~dir:source in 203 - (* First check if source itself is a package (has dune-project) *) 271 + let sub_pkgs = scan_subdirectories ~source_eio ~sources in 204 272 let dune_project_path = Eio.Path.(source_eio / "dune-project") in 205 273 match Eio.Path.kind ~follow:false dune_project_path with 206 274 | `Regular_file -> ( 207 - (* Single project - discover packages from this directory *) 208 275 let content = 209 276 try Some (Eio.Path.load dune_project_path) with Eio.Io _ -> None 210 277 in 211 278 match content with 212 - | None -> Error (`Config_error "Cannot read dune-project") 279 + | None -> Ok sub_pkgs 213 280 | Some content -> ( 214 281 match Dune_project.parse content with 215 - | Error msg -> Error (`Config_error msg) 282 + | Error _ -> Ok sub_pkgs 216 283 | Ok dune_proj -> 217 284 let subtree = Fpath.basename source in 218 - let dev_repo, url_src = 219 - match 220 - ( Dune_project.dev_repo_url dune_proj, 221 - Dune_project.url_with_branch dune_proj ) 222 - with 223 - | Ok dev_repo, Ok url_src -> (dev_repo, url_src) 224 - | _ -> ( 225 - match Sources_registry.derive_url sources ~subtree with 226 - | Some url -> (url, url ^ "#main") 227 - | None -> ("", "")) 228 - in 229 - let opam_files = 230 - try 231 - Eio.Path.read_dir source_eio 232 - |> List.filter (fun name -> 233 - Filename.check_suffix name ".opam") 234 - with Eio.Io _ -> [] 285 + let dev_repo, url_src = derive_urls ~sources ~subtree dune_proj in 286 + let root_pkgs = 287 + load_opam_pkgs source_eio ~subtree ~dev_repo ~url_src 235 288 in 236 - let pkgs = 237 - List.filter_map 238 - (fun opam_file -> 239 - let pkg_name = Filename.chop_suffix opam_file ".opam" in 240 - let opam_path = Eio.Path.(source_eio / opam_file) in 241 - try 242 - let raw_content = Eio.Path.load opam_path in 243 - let opam_content = 244 - Opam_transform.transform ~content:raw_content ~dev_repo 245 - ~url_src 246 - in 247 - Some 248 - (Pkg.make ~pkg_name ~subtree ~dev_repo ~url_src 249 - ~opam_content) 250 - with Eio.Io _ -> None) 251 - opam_files 252 - in 253 - Ok pkgs)) 254 - | _ -> 255 - (* Directory of subtrees - scan subdirectories like monorepo *) 256 - let subdirs = 257 - try 258 - Eio.Path.read_dir source_eio 259 - |> List.filter (fun name -> 260 - let child = Eio.Path.(source_eio / name) in 261 - match Eio.Path.kind ~follow:false child with 262 - | `Directory -> true 263 - | _ -> false) 264 - with Eio.Io _ -> [] 265 - in 266 - let packages = 267 - List.fold_left 268 - (fun acc subtree -> 269 - let subtree_path = Eio.Path.(source_eio / subtree) in 270 - let dune_path = Eio.Path.(subtree_path / "dune-project") in 271 - match Eio.Path.kind ~follow:false dune_path with 272 - | `Regular_file -> ( 273 - let content = 274 - try Some (Eio.Path.load dune_path) with Eio.Io _ -> None 275 - in 276 - match content with 277 - | None -> acc 278 - | Some content -> ( 279 - match Dune_project.parse content with 280 - | Error _ -> acc 281 - | Ok dune_proj -> 282 - let dev_repo, url_src = 283 - match 284 - ( Dune_project.dev_repo_url dune_proj, 285 - Dune_project.url_with_branch dune_proj ) 286 - with 287 - | Ok dr, Ok us -> (dr, us) 288 - | _ -> ( 289 - match 290 - Sources_registry.derive_url sources ~subtree 291 - with 292 - | Some url -> (url, url ^ "#main") 293 - | None -> ("", "")) 294 - in 295 - let opam_files = 296 - try 297 - Eio.Path.read_dir subtree_path 298 - |> List.filter (fun name -> 299 - Filename.check_suffix name ".opam") 300 - with Eio.Io _ -> [] 301 - in 302 - let pkgs = 303 - List.filter_map 304 - (fun opam_file -> 305 - let pkg_name = 306 - Filename.chop_suffix opam_file ".opam" 307 - in 308 - let opam_path = 309 - Eio.Path.(subtree_path / opam_file) 310 - in 311 - try 312 - let raw_content = Eio.Path.load opam_path in 313 - let opam_content = 314 - Opam_transform.transform ~content:raw_content 315 - ~dev_repo ~url_src 316 - in 317 - Some 318 - (Pkg.make ~pkg_name ~subtree ~dev_repo 319 - ~url_src ~opam_content) 320 - with Eio.Io _ -> None) 321 - opam_files 322 - in 323 - pkgs @ acc)) 324 - | _ -> acc 325 - | exception Eio.Io _ -> acc) 326 - [] subdirs 327 - in 328 - Ok (List.rev packages) 289 + Ok (root_pkgs @ sub_pkgs))) 290 + | _ -> Ok sub_pkgs 291 + | exception Eio.Io _ -> Ok sub_pkgs 329 292 330 293 (** List packages in a standalone opam-repo. *) 331 294 let list_opam_repo_packages_at ~fs opam_repo = ··· 378 341 `Synced pkg_name 379 342 end 380 343 344 + (** Partition sync results into synced and unchanged lists. *) 345 + let partition_sync_results results = 346 + List.fold_left 347 + (fun (s, u) r -> 348 + match r with `Synced n -> (n :: s, u) | `Unchanged n -> (s, n :: u)) 349 + ([], []) results 350 + 351 + (** Commit changes in an opam-repo if needed. *) 352 + let commit_if_needed ~fs ~target ~no_commit ~dry_run result = 353 + if 354 + (not no_commit) && (not dry_run) 355 + && (result.synced <> [] || result.orphaned <> []) 356 + then begin 357 + let repo = Git.Repository.open_repo ~fs target in 358 + let msg = commit_message result in 359 + match Git_cli.global_git_user () with 360 + | Some user -> ( 361 + match 362 + Git.Repository.commit_index repo ~author:user ~committer:user 363 + ~message:msg () 364 + with 365 + | Ok _ -> () 366 + | Error (`Msg e) -> Log.warn (fun m -> m "Failed to commit: %s" e)) 367 + | None -> Log.warn (fun m -> m "No git user config found, skipping commit") 368 + end 369 + 381 370 let run_from_cwd ~fs ~proc:_ ~source ~target ?(packages = []) 382 371 ?(no_commit = false) ?(dry_run = false) () = 383 372 match discover_from_cwd ~fs ~source with ··· 388 377 | [] -> all_pkgs 389 378 | names -> 390 379 List.filter 391 - (fun p -> 392 - List.exists 393 - (fun name -> Pkg.name p = name || Pkg.subtree p = name) 394 - names) 380 + (fun p -> List.exists (fun n -> Pkg.matches_name n p) names) 395 381 all_pkgs 396 382 in 397 383 if pkgs = [] then begin ··· 410 396 pkgs 411 397 in 412 398 Tty.Progress.finish progress; 413 - let synced, unchanged = 414 - List.fold_left 415 - (fun (s, u) r -> 416 - match r with 417 - | `Synced n -> (n :: s, u) 418 - | `Unchanged n -> (s, n :: u)) 419 - ([], []) sync_results 420 - in 399 + let synced, unchanged = partition_sync_results sync_results in 421 400 let generated_names = 422 401 List.map Pkg.name pkgs |> List.sort_uniq String.compare 423 402 in 424 - (* Only delete orphaned when syncing all packages and not dry-run *) 425 403 let deleted = 426 404 if packages = [] && not dry_run then begin 427 405 let existing = list_opam_repo_packages_at ~fs target in ··· 447 425 orphaned = deleted; 448 426 } 449 427 in 450 - if 451 - (not no_commit) && (not dry_run) 452 - && (result.synced <> [] || result.orphaned <> []) 453 - then begin 454 - let repo = Git.Repository.open_repo ~fs target in 455 - let msg = commit_message result in 456 - match Git_cli.global_git_user () with 457 - | Some user -> ( 458 - match 459 - Git.Repository.commit_index repo ~author:user ~committer:user 460 - ~message:msg () 461 - with 462 - | Ok _ -> () 463 - | Error (`Msg e) -> Log.warn (fun m -> m "Failed to commit: %s" e) 464 - ) 465 - | None -> 466 - Log.warn (fun m -> m "No git user config found, skipping commit") 467 - end; 428 + commit_if_needed ~fs ~target ~no_commit ~dry_run result; 468 429 Ok result 469 430 end
+1
lib/package.ml
··· 27 27 28 28 let checkout_dir ~checkouts_root t = Fpath.(checkouts_root / repo_name t) 29 29 let subtree_prefix t = repo_name t 30 + let matches_name name t = t.name = name || repo_name t = name 30 31 let compare a b = String.compare a.name b.name 31 32 let equal a b = String.equal a.name b.name 32 33 let same_repo a b = Uri.equal a.dev_repo b.dev_repo
+6
lib/package.mli
··· 74 74 This is the repository name (same as [repo_name t]), so multiple packages 75 75 from the same repository share the same subtree directory. *) 76 76 77 + (** {1 Matching} *) 78 + 79 + val matches_name : string -> t -> bool 80 + (** [matches_name name t] returns true if [name] matches either the opam package 81 + name or the repository name (subtree prefix). *) 82 + 77 83 (** {1 Comparison} *) 78 84 79 85 val compare : t -> t -> int
+2 -1
lib/pkg.ml
··· 12 12 opam_content : string; 13 13 } 14 14 15 - let make ~pkg_name ~subtree ~dev_repo ~url_src ~opam_content = 15 + let v ~pkg_name ~subtree ~dev_repo ~url_src ~opam_content = 16 16 { pkg_name; subtree; dev_repo; url_src; opam_content } 17 17 18 18 let pp ppf t = Fmt.pf ppf "@[<v>%s (subtree: %s)@]" t.pkg_name t.subtree 19 19 let name t = t.pkg_name 20 20 let subtree t = t.subtree 21 + let matches_name name t = t.pkg_name = name || t.subtree = name 21 22 let dev_repo t = t.dev_repo 22 23 let url_src t = t.url_src 23 24 let opam_content t = t.opam_content
+5 -1
lib/pkg.mli
··· 3 3 type t 4 4 (** Package metadata from a monorepo subtree. *) 5 5 6 - val make : 6 + val v : 7 7 pkg_name:string -> 8 8 subtree:string -> 9 9 dev_repo:string -> ··· 20 20 21 21 val subtree : t -> string 22 22 (** Subtree directory name within the monorepo. *) 23 + 24 + val matches_name : string -> t -> bool 25 + (** [matches_name name t] returns true if [name] matches either the opam package 26 + name or the subtree directory name. *) 23 27 24 28 val dev_repo : t -> string 25 29 (** Development repository URL. *)
+4 -1
lib/pull.ml
··· 120 120 match packages with 121 121 | [] -> all_pkgs 122 122 | names -> 123 - List.filter (fun p -> List.mem (Package.name p) names) all_pkgs 123 + List.filter 124 + (fun p -> 125 + List.exists (fun n -> Package.matches_name n p) names) 126 + all_pkgs 124 127 in 125 128 if pkgs = [] && packages <> [] then 126 129 Error (Ctx.Package_not_found (List.hd packages))
+3 -1
lib/push.ml
··· 138 138 match packages with 139 139 | [] -> all_pkgs 140 140 | names -> 141 - List.filter (fun p -> List.mem (Package.name p) names) all_pkgs 141 + List.filter 142 + (fun p -> List.exists (fun n -> Package.matches_name n p) names) 143 + all_pkgs 142 144 in 143 145 if pkgs = [] && packages <> [] then 144 146 Error (Ctx.Package_not_found (List.hd packages))
+149
test/publish.t
··· 1 + Publish command tests 2 + ===================== 3 + 4 + Setup: configure git and disable colors 5 + 6 + $ export NO_COLOR=1 7 + $ export GIT_AUTHOR_NAME="Test User" 8 + $ export GIT_AUTHOR_EMAIL="test@example.com" 9 + $ export GIT_AUTHOR_DATE="2025-01-01T00:00:00+00:00" 10 + $ export GIT_COMMITTER_NAME="Test User" 11 + $ export GIT_COMMITTER_EMAIL="test@example.com" 12 + $ export GIT_COMMITTER_DATE="2025-01-01T00:00:00+00:00" 13 + $ export HOME="$PWD/home" 14 + $ mkdir -p "$HOME" 15 + $ TROOT=$(pwd) 16 + 17 + Helper that strips trailing whitespace: 18 + 19 + $ publish () { monopam publish "$@" 2>&1 | grep -v '^ *$'; } 20 + 21 + Publish from a single project 22 + ------------------------------ 23 + 24 + Create a project with dune-project and opam file: 25 + 26 + $ mkdir -p project 27 + $ cd project 28 + $ git init -q 29 + 30 + $ cat > dune-project << 'EOF' 31 + > (lang dune 3.21) 32 + > (name mylib) 33 + > (source (github user/mylib)) 34 + > (package (name mylib) (synopsis "A library")) 35 + > EOF 36 + 37 + $ cat > mylib.opam << 'EOF' 38 + > opam-version: "2.0" 39 + > name: "mylib" 40 + > version: "dev" 41 + > synopsis: "A library" 42 + > EOF 43 + 44 + $ git add . && git commit -q -m "initial" 45 + 46 + Create target opam-repo: 47 + 48 + $ mkdir -p "$TROOT/opam-repo/packages" 49 + $ cd "$TROOT/opam-repo" && git init -q && git commit -q --allow-empty -m "init" && cd "$TROOT/project" 50 + 51 + Dry-run publish discovers the package: 52 + 53 + $ publish --dry-run --opam-repo "$TROOT/opam-repo" --no-checkouts 54 + Dry run: publishing from $TESTCASE_ROOT/project to $TESTCASE_ROOT/opam-repo 55 + Synced 1 package: 56 + mylib 57 + 58 + $ cd "$TROOT" 59 + 60 + Publish from a workspace root with subtrees 61 + --------------------------------------------- 62 + 63 + Create a monorepo-like workspace with a root dune-project and subtrees. 64 + This tests that `discover_from_cwd` finds packages in subdirectories even 65 + when the root has its own dune-project (workspace root pattern): 66 + 67 + $ mkdir -p workspace/my-lib workspace/my-tool 68 + $ cd workspace 69 + $ git init -q 70 + 71 + Root dune-project (workspace root, like a monorepo): 72 + 73 + $ cat > dune-project << 'EOF' 74 + > (lang dune 3.21) 75 + > (name root) 76 + > (package (name root) (allow_empty)) 77 + > EOF 78 + 79 + $ cat > root.opam << 'EOF' 80 + > opam-version: "2.0" 81 + > name: "root" 82 + > EOF 83 + 84 + Subtree 1: my-lib (pkg name "mylib" differs from dir name "my-lib") 85 + 86 + $ cat > my-lib/dune-project << 'EOF' 87 + > (lang dune 3.21) 88 + > (name mylib) 89 + > (source (github user/my-lib)) 90 + > (package (name mylib) (synopsis "A library")) 91 + > EOF 92 + 93 + $ cat > my-lib/mylib.opam << 'EOF' 94 + > opam-version: "2.0" 95 + > name: "mylib" 96 + > version: "dev" 97 + > synopsis: "A library" 98 + > EOF 99 + 100 + Subtree 2: my-tool 101 + 102 + $ cat > my-tool/dune-project << 'EOF' 103 + > (lang dune 3.21) 104 + > (name mytool) 105 + > (source (github user/my-tool)) 106 + > (package (name mytool) (synopsis "A tool")) 107 + > EOF 108 + 109 + $ cat > my-tool/mytool.opam << 'EOF' 110 + > opam-version: "2.0" 111 + > name: "mytool" 112 + > version: "dev" 113 + > synopsis: "A tool" 114 + > EOF 115 + 116 + $ git add . && git commit -q -m "initial" 117 + 118 + Create target opam-repo: 119 + 120 + $ mkdir -p "$TROOT/ws-opam-repo/packages" 121 + $ cd "$TROOT/ws-opam-repo" && git init -q && git commit -q --allow-empty -m "init" && cd "$TROOT/workspace" 122 + 123 + Publish all discovers both root and subtree packages: 124 + 125 + $ publish --dry-run --opam-repo "$TROOT/ws-opam-repo" --no-checkouts 126 + Dry run: publishing from $TESTCASE_ROOT/workspace to $TESTCASE_ROOT/ws-opam-repo 127 + Synced 3 packages: 128 + root 129 + mylib 130 + mytool 131 + 132 + 133 + Publish by subtree name (the bug fix: "my-lib" matches subtree, not pkg name): 134 + 135 + $ publish my-lib --dry-run --opam-repo "$TROOT/ws-opam-repo" --no-checkouts 136 + Dry run: publishing from $TESTCASE_ROOT/workspace to $TESTCASE_ROOT/ws-opam-repo 137 + Synced 1 package: 138 + mylib 139 + 140 + 141 + Publish by package name also works: 142 + 143 + $ publish mylib --dry-run --opam-repo "$TROOT/ws-opam-repo" --no-checkouts 144 + Dry run: publishing from $TESTCASE_ROOT/workspace to $TESTCASE_ROOT/ws-opam-repo 145 + Synced 1 package: 146 + mylib 147 + 148 + 149 + $ cd "$TROOT"
+1 -1
test/test.ml
··· 16 16 [ Test_fork_join.suite ]; 17 17 [ Test_forks.suite ]; 18 18 [ Test_git_cli.suite ]; 19 - [ Test_monorepo_pkg.suite ]; 19 + [ Test_pkg.suite ]; 20 20 [ Test_opam_repo.suite ]; 21 21 [ Test_opam_sync.suite ]; 22 22 [ Test_opam_transform.suite ];
-3
test/test_monorepo_pkg.ml
··· 1 - (* Tests for monorepo_pkg *) 2 - 3 - let suite = ("monorepo_pkg", [])
-1
test/test_monorepo_pkg.mli
··· 1 - val suite : string * unit Alcotest.test_case list
+25
test/test_package.ml
··· 125 125 Alcotest.(check bool) "same repo" true (Package.same_repo pkg1 pkg2); 126 126 Alcotest.(check bool) "different repo" false (Package.same_repo pkg1 pkg3) 127 127 128 + (* Test matches_name *) 129 + 130 + let test_matches_name_by_name () = 131 + let dev_repo = Uri.of_string "https://github.com/user/ocaml-d3t.git" in 132 + let pkg = Package.v ~name:"d3t" ~version:"dev" ~dev_repo () in 133 + Alcotest.(check bool) "match by name" true (Package.matches_name "d3t" pkg) 134 + 135 + let test_matches_name_by_repo () = 136 + let dev_repo = Uri.of_string "https://github.com/user/ocaml-d3t.git" in 137 + let pkg = Package.v ~name:"d3t" ~version:"dev" ~dev_repo () in 138 + Alcotest.(check bool) 139 + "match by repo name" true 140 + (Package.matches_name "ocaml-d3t" pkg) 141 + 142 + let test_matches_name_no_match () = 143 + let dev_repo = Uri.of_string "https://github.com/user/ocaml-d3t.git" in 144 + let pkg = Package.v ~name:"d3t" ~version:"dev" ~dev_repo () in 145 + Alcotest.(check bool) "no match" false (Package.matches_name "other" pkg) 146 + 128 147 let suite = 129 148 [ 130 149 ( "Package: creation", ··· 154 173 Alcotest.test_case "compare" `Quick test_compare; 155 174 Alcotest.test_case "equal" `Quick test_equal; 156 175 Alcotest.test_case "same_repo" `Quick test_same_repo; 176 + ] ); 177 + ( "Package: matches_name", 178 + [ 179 + Alcotest.test_case "by name" `Quick test_matches_name_by_name; 180 + Alcotest.test_case "by repo name" `Quick test_matches_name_by_repo; 181 + Alcotest.test_case "no match" `Quick test_matches_name_no_match; 157 182 ] ); 158 183 ]
+36
test/test_pkg.ml
··· 1 + (* Tests for Pkg module *) 2 + 3 + module Pkg = Monopam.Pkg 4 + 5 + let test_matches_name_by_pkg_name () = 6 + let pkg = 7 + Pkg.v ~pkg_name:"d3t" ~subtree:"ocaml-d3t" ~dev_repo:"" ~url_src:"" 8 + ~opam_content:"" 9 + in 10 + Alcotest.(check bool) "match by pkg name" true (Pkg.matches_name "d3t" pkg) 11 + 12 + let test_matches_name_by_subtree () = 13 + let pkg = 14 + Pkg.v ~pkg_name:"d3t" ~subtree:"ocaml-d3t" ~dev_repo:"" ~url_src:"" 15 + ~opam_content:"" 16 + in 17 + Alcotest.(check bool) 18 + "match by subtree" true 19 + (Pkg.matches_name "ocaml-d3t" pkg) 20 + 21 + let test_matches_name_no_match () = 22 + let pkg = 23 + Pkg.v ~pkg_name:"d3t" ~subtree:"ocaml-d3t" ~dev_repo:"" ~url_src:"" 24 + ~opam_content:"" 25 + in 26 + Alcotest.(check bool) "no match" false (Pkg.matches_name "other" pkg) 27 + 28 + let suite = 29 + ( "Pkg", 30 + [ 31 + Alcotest.test_case "matches by pkg name" `Quick 32 + test_matches_name_by_pkg_name; 33 + Alcotest.test_case "matches by subtree" `Quick 34 + test_matches_name_by_subtree; 35 + Alcotest.test_case "no match" `Quick test_matches_name_no_match; 36 + ] )
+2
test/test_pkg.mli
··· 1 + val suite : string * unit Alcotest.test_case list 2 + (** Alcotest suite for Pkg module. *)