···11+# repo-tool
22+33+A CLI tool that generates an opam repository and monorepo from a list of git repositories.
44+55+## Overview
66+77+`repo-tool` reads a text file containing git repository URLs and:
88+99+1. Clones each repository into a `vendor/` directory
1010+2. Generates an opam repository structure in `opam-repository/`
1111+3. Creates a `setup.sh` script to pin packages and install dependencies
1212+4. Sets up dune to build everything as a monorepo
1313+1414+This is useful for creating a local development environment with multiple interdependent packages that may not yet be published to opam.
1515+1616+## Installation
1717+1818+```bash
1919+opam install . --deps-only
2020+dune build
2121+dune install
2222+```
2323+2424+Or run directly:
2525+2626+```bash
2727+dune exec repo-tool -- <args>
2828+```
2929+3030+## Usage
3131+3232+```bash
3333+repo-tool INPUT_FILE [-o OUTPUT_DIR] [-v]
3434+```
3535+3636+### Arguments
3737+3838+- `INPUT_FILE` - Path to a text file containing git repository URLs (one per line)
3939+- `-o, --output DIR` - Output directory (default: `opam-repository`)
4040+- `-v, --verbose` - Enable verbose output
4141+4242+### Input File Format
4343+4444+```
4545+# Comments start with #
4646+https://github.com/user/repo1.git
4747+https://github.com/user/repo2.git main
4848+https://tangled.org/user/repo3
4949+```
5050+5151+Each line contains a git URL, optionally followed by a branch name.
5252+5353+## Output Structure
5454+5555+```
5656+output-dir/
5757+├── dune-project # Dune project file
5858+├── dune # Top-level dune config
5959+├── setup.sh # Setup script for opam switch
6060+├── opam-repository/
6161+│ ├── repo # opam repository metadata
6262+│ └── packages/
6363+│ └── <pkg>/
6464+│ └── <pkg>.dev/
6565+│ └── opam # Package opam file with url stanza
6666+└── vendor/
6767+ ├── dune # Lists vendor subdirectories
6868+ ├── repo1/ # Cloned source code
6969+ ├── repo2/
7070+ └── ...
7171+```
7272+7373+## Setting Up the Monorepo
7474+7575+After running `repo-tool`, set up the development environment:
7676+7777+```bash
7878+cd output-dir
7979+./setup.sh
8080+```
8181+8282+The setup script will:
8383+1. Create a local opam switch with OCaml 5.4.0
8484+2. Pin all vendor packages
8585+3. Install dependencies (including test dependencies)
8686+4. Run `dune build`
8787+8888+Alternatively, run the steps manually:
8989+9090+```bash
9191+cd output-dir
9292+opam switch create . 5.4.0 -y
9393+opam pin add -ny <pkg1> vendor/<repo1>
9494+opam pin add -ny <pkg2> vendor/<repo2>
9595+# ... for each package
9696+opam install -y --deps-only --with-test <pkg1> <pkg2> ...
9797+opam exec -- dune build --root .
9898+```
9999+100100+## Using the opam Repository as an Overlay
101101+102102+You can also use just the generated opam repository as an overlay:
103103+104104+```bash
105105+opam repository add local /path/to/output-dir/opam-repository
106106+opam update
107107+opam install <package-name>
108108+```
109109+110110+## Incremental Updates
111111+112112+Running `repo-tool` again on an existing output directory will:
113113+- Update existing repositories with `git pull`
114114+- Clone any new repositories
115115+- Regenerate the opam repository and setup script
116116+117117+## Example
118118+119119+```bash
120120+# Create a repos.txt file
121121+cat > repos.txt << EOF
122122+https://github.com/user/ocaml-foo
123123+https://github.com/user/ocaml-bar
124124+https://tangled.org/user/ocaml-baz
125125+EOF
126126+127127+# Generate the monorepo
128128+repo-tool repos.txt -o my-monorepo -v
129129+130130+# Set up and build
131131+cd my-monorepo
132132+./setup.sh
133133+```
···11+open Cmdliner
22+33+let input_file =
44+ let doc = "Path to the input file containing git repository URLs (one per line)." in
55+ Arg.(required & pos 0 (some file) None & info [] ~docv:"INPUT_FILE" ~doc)
66+77+let output_dir =
88+ let doc = "Output directory for the generated opam repository." in
99+ Arg.(value & opt string "opam-repository" & info [ "o"; "output" ] ~docv:"DIR" ~doc)
1010+1111+let verbose =
1212+ let doc = "Enable verbose output." in
1313+ Arg.(value & flag & info [ "v"; "verbose" ] ~doc)
1414+1515+let run input_file output_dir verbose =
1616+ let exit_code = Repo_tool.run ~input_file ~output_dir ~verbose in
1717+ exit exit_code
1818+1919+let run_t = Term.(const run $ input_file $ output_dir $ verbose)
2020+2121+let cmd =
2222+ let doc = "Generate an opam repository from git repositories" in
2323+ let man =
2424+ [
2525+ `S Manpage.s_description;
2626+ `P
2727+ "$(tname) reads a text file containing a list of git repository URLs \
2828+ and generates an opam repository structure. Each line in the input \
2929+ file should contain a git URL, optionally followed by a branch name.";
3030+ `S Manpage.s_examples;
3131+ `P "Create an opam repository from repos.txt:";
3232+ `Pre " $(tname) repos.txt -o my-opam-repo";
3333+ `P "Input file format:";
3434+ `Pre
3535+ " https://github.com/user/repo1.git\n\
3636+ \ https://github.com/user/repo2.git main\n\
3737+ \ # This is a comment";
3838+ `S Manpage.s_bugs;
3939+ `P "Report bugs at https://github.com/mtelvers/repo-tool/issues";
4040+ ]
4141+ in
4242+ let info = Cmd.info "repo-tool" ~version:"0.1.0" ~doc ~man in
4343+ Cmd.v info run_t
4444+4545+let () = exit (Cmd.eval cmd)