Compute and present the opam file differences as a unified diff #7
4 changed files with 35 additions and 70 deletions
29
lib/views.ml
29
lib/views.ml
|
@ -828,33 +828,8 @@ let duniverse_diffs diffs =
|
||||||
let opam_diffs diffs =
|
let opam_diffs diffs =
|
||||||
List.concat_map (fun pd ->
|
List.concat_map (fun pd ->
|
||||||
H.h4 [ txtf "%a" Opamdiff.pp_opam_diff pd ] ::
|
H.h4 [ txtf "%a" Opamdiff.pp_opam_diff pd ] ::
|
||||||
(match pd.Opamdiff.build with None -> [] | Some a ->
|
H.h5 [ H.txt "diff" ] ::
|
||||||
let l, r = Opamdiff.commands_to_strings a in
|
H.code [ H.txt pd.diff ; H.br () ] :: [])
|
||||||
[
|
|
||||||
H.h5 [ H.txt "build instruction (without common prefix) \
|
|
||||||
modifications, old:" ] ;
|
|
||||||
H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) l) ;
|
|
||||||
H.h5 [ H.txt "new" ] ;
|
|
||||||
H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) r)
|
|
||||||
]) @
|
|
||||||
(match pd.Opamdiff.install with None -> [] | Some a ->
|
|
||||||
let l, r = Opamdiff.commands_to_strings a in
|
|
||||||
[
|
|
||||||
H.h5 [ H.txt "install instruction (without common prefix) \
|
|
||||||
modifications, old:" ] ;
|
|
||||||
H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) l) ;
|
|
||||||
H.h5 [ H.txt "new" ] ;
|
|
||||||
H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) r)
|
|
||||||
]) @
|
|
||||||
(match pd.Opamdiff.url with None -> [] | Some a ->
|
|
||||||
let l, r = Opamdiff.opt_url_to_string a in
|
|
||||||
[
|
|
||||||
H.h5 [ H.txt "URL" ] ;
|
|
||||||
txtf "old: %s" l;
|
|
||||||
H.br ();
|
|
||||||
txtf "new: %s" r
|
|
||||||
]) @
|
|
||||||
[ H.br () ])
|
|
||||||
diffs
|
diffs
|
||||||
|
|
||||||
let compare_builds
|
let compare_builds
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
(library
|
(library
|
||||||
(name opamdiff)
|
(name opamdiff)
|
||||||
(libraries opam-core opam-format yojson))
|
(libraries opam-core opam-format yojson bos))
|
||||||
|
|
|
@ -168,10 +168,8 @@ let pp_version_diff ppf { name; version_left; version_right } =
|
||||||
|
|
||||||
type opam_diff = {
|
type opam_diff = {
|
||||||
pkg : OpamPackage.t ;
|
pkg : OpamPackage.t ;
|
||||||
build : (OpamTypes.command list * OpamTypes.command list) option ;
|
effectively_equal : bool ;
|
||||||
install : (OpamTypes.command list * OpamTypes.command list) option ;
|
diff : string ;
|
||||||
url : (OpamFile.URL.t option * OpamFile.URL.t option) option ;
|
|
||||||
otherwise_equal : bool ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let commands_to_strings (l, r) =
|
let commands_to_strings (l, r) =
|
||||||
|
@ -186,46 +184,40 @@ let opt_url_to_string (l, r) =
|
||||||
in
|
in
|
||||||
url_to_s l, url_to_s r
|
url_to_s l, url_to_s r
|
||||||
reynir marked this conversation as resolved
|
|||||||
|
|
||||||
let pp_opam_diff ppf { pkg ; otherwise_equal ; _ } =
|
let pp_opam_diff ppf { pkg ; effectively_equal ; _ } =
|
||||||
Format.fprintf ppf "%a%s"
|
Format.fprintf ppf "%a%s"
|
||||||
pp_opampackage pkg
|
pp_opampackage pkg
|
||||||
(if otherwise_equal then "" else " (and additional changes)")
|
(if effectively_equal then "" else " (effectively equal)")
|
||||||
|
|
||||||
let rec strip_common_prefix a b =
|
|
||||||
match a, b with
|
|
||||||
| hd::tl, hd'::tl' ->
|
|
||||||
if hd = hd' then
|
|
||||||
strip_common_prefix tl tl'
|
|
||||||
else
|
|
||||||
a, b
|
|
||||||
| a, b -> a, b
|
|
||||||
|
|
||||||
let detailed_opam_diff pkg l r =
|
let detailed_opam_diff pkg l r =
|
||||||
let no_build_install_url p =
|
let ( let* ) = Result.bind in
|
||||||
OpamFile.OPAM.with_url_opt None
|
let opaml = OpamFile.OPAM.write_to_string l in
|
||||||
(OpamFile.OPAM.with_install []
|
let opamr =
|
||||||
(OpamFile.OPAM.with_build [] p))
|
let o = OpamFile.make (OpamFilename.raw "opam") in
|
||||||
|
OpamFile.OPAM.to_string_with_preserved_format ~format_from_string:opaml o r
|
||||||
reynir marked this conversation as resolved
reynir
commented
I think the files are only deleted The files are usually quite small so I imagine it would usually take years before the dangling temporary files become a problem. I think the files are only deleted `at_exit`. There are functions `Bos.OS.File.with_tmp_output` / `Bos.OS.File.with_tmp_oc` that ensure the file is deleted when the callback returns - maybe worth using one of them?
The files are usually quite small so I imagine it would usually take years before the dangling temporary files become a problem.
hannes
commented
Yes, you're right. I'll adapt. Yes, you're right. I'll adapt.
|
|||||||
in
|
in
|
||||||
let otherwise_equal =
|
let effectively_equal =
|
||||||
|
let no_build_install_url p =
|
||||||
|
OpamFile.OPAM.with_url_opt None
|
||||||
|
(OpamFile.OPAM.with_install []
|
||||||
|
(OpamFile.OPAM.with_build [] p))
|
||||||
|
in
|
||||||
OpamFile.OPAM.effectively_equal
|
OpamFile.OPAM.effectively_equal
|
||||||
(no_build_install_url l) (no_build_install_url r)
|
(no_build_install_url l) (no_build_install_url r)
|
||||||
reynir marked this conversation as resolved
Outdated
reynir
commented
Maybe we should log the error message instead? I think from a security point of view there could be error messages that we don't want to display to the client (e.g. information about the filesystem). Maybe we should log the error message instead? I think from a security point of view there could be error messages that we don't want to display to the client (e.g. information about the filesystem).
hannes
commented
Indeed. Indeed.
|
|||||||
and build =
|
|
||||||
if OpamFile.OPAM.build l = OpamFile.OPAM.build r then
|
|
||||||
None
|
|
||||||
else
|
|
||||||
Some (strip_common_prefix (OpamFile.OPAM.build l) (OpamFile.OPAM.build r))
|
|
||||||
and install =
|
|
||||||
if OpamFile.OPAM.install l = OpamFile.OPAM.install r then
|
|
||||||
None
|
|
||||||
else
|
|
||||||
Some (strip_common_prefix (OpamFile.OPAM.install l) (OpamFile.OPAM.install r))
|
|
||||||
and url =
|
|
||||||
if OpamFile.OPAM.url l = OpamFile.OPAM.url r then
|
|
||||||
None
|
|
||||||
else
|
|
||||||
Some (OpamFile.OPAM.url l, OpamFile.OPAM.url r)
|
|
||||||
in
|
in
|
||||||
{ pkg ; build ; install ; url ; otherwise_equal }
|
let diff =
|
||||||
|
let* tmpl = Bos.OS.File.tmp "opaml_%s" in
|
||||||
|
let* () = Bos.OS.File.write tmpl opaml in
|
||||||
|
let* tmpr = Bos.OS.File.tmp "opamr_%s" in
|
||||||
|
let* () = Bos.OS.File.write tmpr opamr in
|
||||||
|
let cmd = Bos.Cmd.(v "diff" % "-u" % p tmpl % p tmpr) in
|
||||||
|
Bos.OS.Cmd.(run_out cmd |> out_string)
|
||||||
reynir
commented
I think we can write I think we can write `match Result.join (Result.join diff) with` to avoid the nested matches, but this is fine, too.
|
|||||||
|
in
|
||||||
|
let diff = match diff with
|
||||||
|
| Ok (s, _) -> s
|
||||||
|
| Error `Msg m -> "error when comparing the opam files: " ^ m
|
||||||
|
in
|
||||||
|
{ pkg ; effectively_equal ; diff }
|
||||||
|
|
||||||
let detailed_opam_diffs left right pkgs =
|
let detailed_opam_diffs left right pkgs =
|
||||||
OpamPackage.Set.fold (fun p acc ->
|
OpamPackage.Set.fold (fun p acc ->
|
||||||
|
@ -299,9 +291,9 @@ let compare left right =
|
||||||
let opam_diff_to_json opam_diff =
|
let opam_diff_to_json opam_diff =
|
||||||
`List (List.map (fun (diff : opam_diff) ->
|
`List (List.map (fun (diff : opam_diff) ->
|
||||||
`Assoc [
|
`Assoc [
|
||||||
|
|
||||||
("package_version", `String (OpamPackage.to_string diff.pkg));
|
("package_version", `String (OpamPackage.to_string diff.pkg));
|
||||||
("otherwise_equal", `Bool diff.otherwise_equal)
|
("effectively_equal", `Bool diff.effectively_equal);
|
||||||
|
("diff", `String diff.diff);
|
||||||
]
|
]
|
||||||
|
|
||||||
) opam_diff)
|
) opam_diff)
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
type opam_diff = {
|
type opam_diff = {
|
||||||
pkg : OpamPackage.t ;
|
pkg : OpamPackage.t ;
|
||||||
build : (OpamTypes.command list * OpamTypes.command list) option ;
|
effectively_equal : bool ;
|
||||||
install : (OpamTypes.command list * OpamTypes.command list) option ;
|
diff : string ;
|
||||||
url : (OpamFile.URL.t option * OpamFile.URL.t option) option ;
|
|
||||||
otherwise_equal : bool ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type version_diff = {
|
type version_diff = {
|
||||||
|
|
Loading…
Reference in a new issue
So this dance here is to try to get the same formatting as for
l
/opaml
? I think that makes sense. Maybe worth attaching a comment?I am curious what the behavior is if the two packages differ too much. I guess since
OpamFile.OPAM.write_to_string
is likely to use a fixed formatting style the style will be consistent even with a larger difference in packages? (this doesn't have to be answered IMO)in my experience, using the "with_preserved_format" is good -- apart from potentially dangling comments (in the last line). I also think a comment is worth it. Of course we can leave the opam files as is and do a direct diff, i.e. do not care about the "minimal" diff...