123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247(*---------------------------------------------------------------------------
Copyright (c) 2020 The b0 programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)openB0_stdopenResult.Syntaxletis_vcs~allfind(_,dir)=let*vcs=find?dir:(Somedir)()inmatchvcswith|None->Okfalse|Somevcs->ifallthenOktrueelseB00_vcs.is_dirtyvcsletget_scopescrootexcludesk=(* XXX shouldn't we rather save them in `B0_def.Scopes` ? *)Log.if_error~use:B0_driver.Exit.no_b0_file@@let*b0_file=B0_driver.Conf.get_b0_filecinLog.if_error'~header:""~use:B0_driver.Exit.b0_file_error@@let*s=Os.File.readb0_fileinlet*src=B0_file.of_string~file:b0_filesinlet*incs=matchrootwith|true->Ok(B0_file.b0_includessrc)|false->let*e=B0_file.expandsrcinOk(B0_file.expanded_b0_includese)inletinc_to_scope((n,_),(p,_))=n,Fpath.parentpinletroot=("."(* XXX what should we use here ? *),Fpath.parentb0_file)inletscopes=root::List.sortcompare(List.mapinc_to_scopeincs)ink(List.filter(fun(n,_)->not(List.memnexcludes))scopes)letexec_whencondcrootexcludeskeep_goingcmd=leterr(_,dir)e=Log.err(funm->m"@[%a: %s@]"Fpath.ppdire);OkB00_cli.Exit.some_erroringet_scopescrootexcludes@@functionscopes->letrecloop=function|[]->OkB00_cli.Exit.ok|(n,pass)::ss->matchcondswith|Errore->errse|Okfalse->loopss|Oktrue->Log.appbeginfunm->m"@[%a: %a@]"Fmt.(codestring)n(Fmt.tty[`Faint]Fpath.pp)pend;matchOs.Cmd.run~cwd:pcmdwith|Errorewhennotkeep_going->errse|Error_|Ok()->Log.app(funm->m"");loopssinloopscopesletlistrootexcludesformatpathc=get_scopescrootexcludes@@functionscopes->letpp_scope=matchpathwith|true->funppf(_,dir)->Fpath.pp_unquotedppfdir|false->matchformatwith|`Short->funppf(n,_)->Fmt.(codestring)ppfn|`Normal|`Long->funppf(n,dir)->Fmt.pfppf"@[%a %a@]"Fmt.(codestring)nFpath.pp_unquoteddirinLog.app(funm->m"@[<v>%a@]"Fmt.(listpp_scope)scopes);OkB00_cli.Exit.okletexecrootexcludeskeep_goingtooltool_argsc=letcmd=tool::tool_argsinexec_when(fun_->Oktrue)crootexcludeskeep_going(Cmd.listcmd)letgitrootexcludesallkeep_goingfull_cmdsubcmdsubcmd_argsc=letcmd=subcmd::subcmd_argsinletcmd=iffull_cmdthenCmd.listcmdelseCmd.(atom"git"%%listcmd)inexec_when(is_vcs~allB00_vcs.Git.find)crootexcludeskeep_goingcmdlethgrootexcludesallkeep_goingfull_cmdsubcmdsubcmd_argsc=letcmd=subcmd::subcmd_argsinletcmd=iffull_cmdthenCmd.listcmdelseCmd.(atom"hg"%%listcmd)inexec_when(is_vcs~allB00_vcs.Hg.find)crootexcludeskeep_goingcmd(* Command line interface *)openCmdlinerletroot=letdoc="Only consider scopes included by the root B0 file. Those \
recursively included by these are excluded."inArg.(value&flag&info["root"]~doc)letexcludes=letdoc="Exclude scope $(docv) from the request. Repeatable."inArg.(value&opt_allstring[]&info["x";"exclude"]~doc~docv:"SCOPE")letkeep_going=letdoc="Do not stop if a tool invocation exits with non zero."inArg.(value&flag&info["k";"keep-going"]~doc)letfull_cmd=letdoc="Specify a full command rather than a subcommand of the VCS."inArg.(value&flag&info["c";"full-cmd"]~doc)lettool=letdoc="Invoke tool $(docv)."inArg.(required&pos0(somestring)None&info[]~doc~docv:"TOOL")letall=letdoc="Apply command to all VCS scopes, not only those that are dirty."inArg.(value&flag&info["a";"all"]~doc)letvcs_subcmd=letdoc="Invoke VCS subcommand $(docv)."inArg.(required&pos0(somestring)None&info[]~doc~docv:"SUBCMD")lettool_args=letdoc="Argument for the tool. Start with a $(b,--) \
token otherwise options get interpreted by $(mname)."inArg.(value&pos_right0string[]&info[]~doc~docv:"ARG")letlist_term=letpath=letdoc="Only print the scope paths."inArg.(value&flag&info["path"]~doc)inTerm.(constlist$root$excludes$B0_b0.Cli.format$path)letvcs_syn="$(mname) $(b,scope) $(tname) [$(i,OPTION)]… $(b,--) $(i,SUBCMD) [$(i,ARG)]…"(* Commands *)letexec=letdoc="Execute a tool in scope directories"inletsynopsis=`P"$(mname) $(b,scope) $(tname) [$(i,OPTION)]… $(b,--) \
$(i,TOOL) [$(i,ARG)]…"inletdescr=`P"$(tname) executes $(i,TOOL) with given arguments in the \
directory of each of the scopes. The process is stopped \
if $(i,TOOL) returns with a non zero exit code, use the \
option $(b,--keep-going) to prevent that."inB0_b0.Cli.subcmd_with_driver_conf"exec"~doc~synopsis~descrTerm.(constexec$root$excludes$keep_going$tool$tool_args)lethg=letdoc="Execute $(b,hg) in dirty Mercurial managed scopes"inletsynopsis=`Pvcs_syninletdescr=`P"$(tname) works exactly like $(b,b0 scope git) but with the \
Mercurial VCS, see $(mname) $(b,scope git --help) for
more information"inB0_b0.Cli.subcmd_with_driver_conf"hg"~doc~synopsis~descrTerm.(consthg$root$excludes$all$keep_going$full_cmd$vcs_subcmd$tool_args)letgit=letdoc="Execute $(b,git) in dirty Git managed scopes"inletsynopsis=`Pvcs_syninletdescr=`Blocks[`P"$(tname) executes the Git subcommand $(i,SUBCMD) \
with given arguments in the directory of each of the scopes
which are found to be managed by Git and dirty;
or all of them if $(b,--all) is specified.";`P"If $(b,--full-cmd) is specified the positional arguments specify a
full command like $(b,scope exec) does, not a VCS subcommand.";`P"The process is stopped if an execution returns with a non zero exit
code, use the option $(b,--keep-going) to prevent that."]inB0_b0.Cli.subcmd_with_driver_conf"git"~doc~synopsis~descrTerm.(constgit$root$excludes$all$keep_going$full_cmd$vcs_subcmd$tool_args)letlist=letdoc="List scopes (default command)"inletdescr=`P"$(tname) lists scope names and their location. \
If $(b,--path) is specified only paths are listed."inletenvs=B0_b0.Cli.pager_envsinB0_b0.Cli.subcmd_with_driver_conf"list"~doc~descr~envslist_termletsubs=[exec;hg;git;list;]letcmd=letdoc="Operate on B0 scopes"inletdescr=`Blocks[`P"$(tname) operates on scopes. The default command is $(tname) \
$(b,list).";`P"$(tname) can fold over scope directories and bulk operate \
their VCSs (if applicable) when repositories are dirty. \
Typical usage:";`P"$(b,> b0)";`Noblank;`P"Error: ...";`Noblank;`P"$(b,> ... # Fix errors)";`Noblank;`P"$(b,> b0)";`Noblank;`P"$(b,> b0 scope git -- status)";`Noblank;`P"$(b,> b0 scope git -- add -p)";`Noblank;`P"$(b,> b0 scope git -- commit -m 'Cope with changes!')";`Noblank;`P"$(b,> b0 scope git --all -- push)";`P"To invoke arbitrary tools in scopes use $(b,b0 scope exec). Options
$(b,--root) and $(b,-x) allow to prune the list of scopes.";]inletdefault=list_terminB0_b0.Cli.cmd_group_with_driver_conf"scope"~doc~descr~defaultsubs(*
let scope
c details path root excludes all full_cmd keep_going action action_args
=
match action with
| `List -> list c root excludes details path
| `Exec -> exec c root excludes keep_going action_args
| `Git -> git c root excludes all keep_going full_cmd action_args
| `Hg -> hg c root excludes all keep_going full_cmd action_args
let action =
let action = [ "list", `List; "exec", `Exec; "git", `Git; "hg", `Hg; ] in
let doc =
let alts = Arg.doc_alts_enum action in
Fmt.str "The action to perform. $(docv) must be one of %s." alts
in
let action = Arg.enum action in
Arg.(required & pos 0 (some action) None & info [] ~doc ~docv:"ACTION")
*)(*---------------------------------------------------------------------------
Copyright (c) 2020 The b0 programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)