Monorepo management for opam overlays
0
fork

Configure Feed

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

Add monopam devcontainer command

Creates and enters a devcontainer environment for OCaml development:
- If .devcontainer/ doesn't exist: creates directory structure,
fetches devcontainer.json from claude-ocaml-devcontainer template,
builds and starts the container
- If .devcontainer/ exists: starts the container if needed and
opens an interactive bash shell

Usage: monopam devcontainer <path>

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

+80 -1
+80 -1
bin/main.ml
··· 1365 1365 let info = Cmd.info "feature" ~doc ~man in 1366 1366 Cmd.group info [ feature_add_cmd; feature_remove_cmd; feature_list_cmd ] 1367 1367 1368 + (* Devcontainer command *) 1369 + 1370 + let devcontainer_cmd = 1371 + let doc = "Setup and enter a devcontainer environment" in 1372 + let man = 1373 + [ 1374 + `S Manpage.s_description; 1375 + `P 1376 + "Creates and enters a devcontainer environment for OCaml development \ 1377 + with monopam and Claude. If the target directory doesn't have a \ 1378 + .devcontainer configuration, it will be created automatically."; 1379 + `S "WHAT IT DOES"; 1380 + `P "For a new directory (no .devcontainer/):"; 1381 + `I ("1.", "Creates the target directory if needed"); 1382 + `I ("2.", "Creates .devcontainer/ subdirectory"); 1383 + `I ("3.", "Downloads devcontainer.json from the template repository"); 1384 + `I ("4.", "Builds and starts the devcontainer"); 1385 + `I ("5.", "Opens an interactive shell inside the container"); 1386 + `P "For an existing directory with .devcontainer/:"; 1387 + `I ("1.", "Starts the devcontainer if not running"); 1388 + `I ("2.", "Opens an interactive shell inside the container"); 1389 + `S Manpage.s_examples; 1390 + `P "Create a new devcontainer workspace:"; 1391 + `Pre "monopam devcontainer ~/my-ocaml-project"; 1392 + `P "Enter an existing devcontainer:"; 1393 + `Pre "monopam devcontainer ~/my-ocaml-project"; 1394 + ] 1395 + in 1396 + let info = Cmd.info "devcontainer" ~doc ~man in 1397 + let path_arg = 1398 + let doc = "Target directory for the devcontainer workspace." in 1399 + Arg.(required & pos 0 (some string) None & info [] ~docv:"PATH" ~doc) 1400 + in 1401 + let run path () = 1402 + (* Resolve to absolute path *) 1403 + let abs_path = 1404 + if Filename.is_relative path then 1405 + Filename.concat (Sys.getcwd ()) path 1406 + else path 1407 + in 1408 + let devcontainer_dir = Filename.concat abs_path ".devcontainer" in 1409 + let devcontainer_json = Filename.concat devcontainer_dir "devcontainer.json" in 1410 + (* Check if .devcontainer exists *) 1411 + let needs_init = not (Sys.file_exists devcontainer_dir && Sys.is_directory devcontainer_dir) in 1412 + if needs_init then begin 1413 + Fmt.pr "Initializing devcontainer in %s...@." abs_path; 1414 + (* Create directories *) 1415 + (try Unix.mkdir abs_path 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> ()); 1416 + (try Unix.mkdir devcontainer_dir 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> ()); 1417 + (* Fetch devcontainer.json using curl *) 1418 + let url = "https://raw.githubusercontent.com/avsm/claude-ocaml-devcontainer/refs/heads/main/.devcontainer/devcontainer.json" in 1419 + Fmt.pr "Fetching devcontainer.json...@."; 1420 + let curl_cmd = Printf.sprintf "curl -fsSL '%s' -o '%s'" url devcontainer_json in 1421 + let ret = Sys.command curl_cmd in 1422 + if ret <> 0 then begin 1423 + Fmt.epr "Error: Failed to fetch devcontainer.json (curl exit code %d)@." ret; 1424 + exit 1 1425 + end; 1426 + Fmt.pr "Created %s@." devcontainer_json; 1427 + (* Build and start the devcontainer *) 1428 + Fmt.pr "Building devcontainer (this may take a while on first run)...@."; 1429 + let up_cmd = Printf.sprintf "npx @devcontainers/cli up --workspace-folder '%s' --remove-existing-container" abs_path in 1430 + let ret = Sys.command up_cmd in 1431 + if ret <> 0 then begin 1432 + Fmt.epr "Error: Failed to start devcontainer (exit code %d)@." ret; 1433 + exit 1 1434 + end 1435 + end; 1436 + (* Exec into the devcontainer *) 1437 + Fmt.pr "Entering devcontainer...@."; 1438 + let exec_cmd = Printf.sprintf "npx @devcontainers/cli exec --workspace-folder '%s' bash -l" abs_path in 1439 + let ret = Sys.command exec_cmd in 1440 + if ret <> 0 then 1441 + `Error (false, Printf.sprintf "devcontainer exec failed with code %d" ret) 1442 + else 1443 + `Ok () 1444 + in 1445 + Cmd.v info Term.(ret (const run $ path_arg $ logging_term)) 1446 + 1368 1447 (* Main command group *) 1369 1448 1370 1449 let main_cmd = ··· 1460 1539 in 1461 1540 let info = Cmd.info "monopam" ~version:"%%VERSION%%" ~doc ~man in 1462 1541 Cmd.group info 1463 - [ status_cmd; diff_cmd; pull_cmd; cherrypick_cmd; sync_cmd; changes_cmd; opam_cmd; doctor_cmd; verse_cmd; feature_cmd ] 1542 + [ status_cmd; diff_cmd; pull_cmd; cherrypick_cmd; sync_cmd; changes_cmd; opam_cmd; doctor_cmd; verse_cmd; feature_cmd; devcontainer_cmd ] 1464 1543 1465 1544 let () = exit (Cmd.eval main_cmd)