···280280 lines := replace_version ~version !lines;
281281 Bos.OS.File.write (Fpath.v path) (String.concat "\n" !lines)
282282 |> Result.map_error (fun (`Msg e) -> e)
283283+284284+ (* Extract the [desc "..."] string from a line. Accepts leading whitespace
285285+ and either double or single quotes. Returns [None] if the line is not a
286286+ [desc] declaration or the quote pair is malformed. *)
287287+ let desc_of_line line =
288288+ let s = Astring.String.trim line in
289289+ if not (Astring.String.is_prefix ~affix:"desc" s) then None
290290+ else
291291+ let rest = Astring.String.with_range ~first:4 s |> Astring.String.trim in
292292+ let len = String.length rest in
293293+ if len < 2 then None
294294+ else
295295+ let q = rest.[0] in
296296+ if q <> '"' && q <> '\'' then None
297297+ else
298298+ match
299299+ String.index_opt (Astring.String.with_range ~first:1 rest) q
300300+ with
301301+ | None -> None
302302+ | Some i -> Some (String.sub rest 1 i)
303303+304304+ let desc_of_file ~path =
305305+ match Bos.OS.File.read (Fpath.v path) with
306306+ | Error _ -> None
307307+ | Ok content ->
308308+ Astring.String.cuts ~sep:"\n" content |> List.find_map desc_of_line
283309end
284310285311module Tap = struct
···377403 in
378404 Log.info (fun m -> m "Tap pushed successfully.");
379405 Ok ()
406406+407407+ let reset_to_remote ~local_path =
408408+ let open Result in
409409+ let ( let* ) = bind in
410410+ let* _ =
411411+ Bos.OS.Cmd.run
412412+ Bos.Cmd.(v "git" % "-C" % local_path % "fetch" % "origin" % "main")
413413+ |> Result.map_error (fun (`Msg e) -> e)
414414+ in
415415+ let* _ =
416416+ Bos.OS.Cmd.run
417417+ Bos.Cmd.(
418418+ v "git" % "-C" % local_path % "reset" % "--hard" % "origin/main")
419419+ |> Result.map_error (fun (`Msg e) -> e)
420420+ in
421421+ let* _ =
422422+ Bos.OS.Cmd.run Bos.Cmd.(v "git" % "-C" % local_path % "clean" % "-fdx")
423423+ |> Result.map_error (fun (`Msg e) -> e)
424424+ in
425425+ Log.info (fun m -> m "Tap reset to origin/main.");
426426+ Ok ()
380427end
+16-1
lib/homebrew.mli
···100100 URLs and SHA256 lines inside the formula file at [path]. Each tuple is
101101 [(platform, new_url, sha256_hex)]. Also updates the [version] declaration.
102102 Non-destructive for other content. *)
103103+104104+ val desc_of_file : path:string -> string option
105105+ (** [desc_of_file ~path] reads the [desc "..."] string from an existing
106106+ formula file. Returns [None] when the file cannot be read or has no [desc]
107107+ line. Useful for building summary listings (e.g. a tap README) over
108108+ formulas that were generated by different sources. *)
103109end
104110105111(** {1 Tap repositories} *)
···119125 val commit_and_push :
120126 local_path:string -> message:string -> (unit, string) result
121127 (** Stage all changes, commit with [message], and push. No-op if the tree is
122122- clean. *)
128128+ clean. Returns an error (carrying git's message) on non-fast-forward
129129+ rejection; the caller should reset with {!reset_to_remote}, regenerate the
130130+ tap's content, and retry. *)
131131+132132+ val reset_to_remote : local_path:string -> (unit, string) result
133133+ (** Fetch and hard-reset the tap checkout to [origin/main], discarding any
134134+ local commits, staged changes, and working-tree modifications. Intended
135135+ for retry loops in tap-update flows, where the local state is fully
136136+ regenerated from the remote plus a fresh set of bottles — the only work
137137+ being discarded is a failed attempt at the same generated content. *)
123138end