diff --git a/lib/lint.ml b/lib/lint.ml index 458e149..4f50f4d 100644 --- a/lib/lint.ml +++ b/lib/lint.ml @@ -1,45 +1,12 @@ module D = Dir_helpers +module O = Opam_helpers let ( // ) = Filename.concat - -let path_from_pkg ~repo_dir pkg = - repo_dir // "packages" - // OpamPackage.Name.to_string (OpamPackage.name pkg) - // OpamPackage.to_string pkg - let get_files dir = dir |> Sys.readdir |> Array.to_list -module Checks = struct - (** If either a restricted prefix or conflict class exists, - then the corresponding other must also exist. *) - type prefix_conflict_class_mismatch = - | WrongPrefix of { conflict_class : string; required_prefix : string } - | WrongConflictClass of { - prefix : string; - required_conflict_class : string; - } - - type error = - | UnnecessaryField of string - | UnmatchedName of OpamPackage.Name.t - | UnmatchedVersion of OpamPackage.Version.t - | DubiousDuneSubst - | DuneProjectMissing - | DuneConstraintMissing - | DuneIsBuild - | BadDuneConstraint of string * string - | UnexpectedFile of string - | ForbiddenPerm of string - | OpamLint of (int * [ `Warning | `Error ] * string) - | FailedToDownload of string - | NameCollision of string - | WeakChecksum of string - | PinDepends - | ExtraFiles - | RestrictedPrefix of string - | PrefixConflictClassMismatch of prefix_conflict_class_mismatch - | ParseError +include Lint_error +module Checks = struct module Prefix = struct (* For context, see https://github.com/ocurrent/opam-repo-ci/pull/316#issuecomment-2160069803 *) let prefix_conflict_class_map = @@ -111,17 +78,6 @@ module Checks = struct let name = OpamPackage.name_to_string pkg in check_prefix_without_conflict_class ~pkg name conflict_classes @ check_conflict_class_without_prefix ~pkg name conflict_classes - - let msg_of_prefix_conflict_class_mismatch ~pkg = function - | WrongPrefix { conflict_class; required_prefix } -> - Printf.sprintf - "Error in %s: package with conflict class '%s' requires name \ - prefix '%s'" - pkg conflict_class required_prefix - | WrongConflictClass { prefix; required_conflict_class } -> - Printf.sprintf - "Error in %s: package with prefix '%s' requires conflict class '%s'" - pkg prefix required_conflict_class end let check_name_field ~pkg opam = @@ -315,7 +271,7 @@ module Checks = struct | _ -> false let check_package_dir ~repo_dir ~pkg _opam = - let dir = path_from_pkg ~repo_dir pkg in + let dir = O.path_from_pkg ~repo_dir pkg in let check_file = function | "opam" -> let path = dir // "opam" in @@ -402,106 +358,12 @@ module Checks = struct let parse_error pkg = (pkg, ParseError) end -let msg_of_error (package, (err : Checks.error)) = - let pkg = OpamPackage.to_string package in - match err with - | UnnecessaryField field -> - Printf.sprintf - "Warning in %s: Unnecessary field '%s'. It is suggested to remove it." - pkg field - | UnmatchedName value -> - Printf.sprintf - "Error in %s: The field 'name' that doesn't match its context. Field \ - 'name' has value '%s' but was expected of value '%s'." - pkg - (OpamPackage.Name.to_string value) - (OpamPackage.Name.to_string (OpamPackage.name package)) - | UnmatchedVersion value -> - Printf.sprintf - "Error in %s: The field 'version' that doesn't match its context. \ - Field 'version' has value '%s' but was expected of value '%s'." - pkg - (OpamPackage.Version.to_string value) - (OpamPackage.Version.to_string (OpamPackage.version package)) - | DubiousDuneSubst -> - Printf.sprintf - "Warning in %s: Dubious use of 'dune subst'. 'dune subst' should \ - always only be called with {dev} (i.e. [\"dune\" \"subst\"] {dev}) If \ - your opam file has been autogenerated by dune, you need to upgrade \ - your dune-project to at least (lang dune 2.7)." - pkg - | WeakChecksum msg -> - Printf.sprintf - "Error in %s: Weak checksum algorithm(s) provided. Please use SHA-256 \ - or SHA-512. Details: %s" - pkg msg - | PinDepends -> - Printf.sprintf - "Error in %s: pin-depends present. This is not allowed in the \ - opam-repository." - pkg - | ExtraFiles -> - Printf.sprintf - "Error in %s: extra-files present. This is not allowed in the \ - opam-repository. Please use extra-source instead." - pkg - | FailedToDownload msg -> - Printf.sprintf "Error in %s: Failed to download the archive. Details: %s" - pkg msg - | DuneProjectMissing -> - Printf.sprintf - "Warning in %s: The package seems to use dune but the dune-project \ - file is missing." - pkg - | DuneConstraintMissing -> - Printf.sprintf - "Warning in %s: The package has a dune-project file but no explicit \ - dependency on dune was found." - pkg - | BadDuneConstraint (dep, ver) -> - Printf.sprintf - "Error in %s: Your dune-project file indicates that this package \ - requires at least dune %s but your opam file only requires dune >= \ - %s. Please check which requirement is the right one, and fix the \ - other." - pkg ver dep - | DuneIsBuild -> - Printf.sprintf - "Warning in %s: The package tagged dune as a build dependency. Due to \ - a bug in dune (https://github.com/ocaml/dune/issues/2147) this should \ - never be the case. Please remove the {build} tag from its filter." - pkg - | RestrictedPrefix prefix -> - Printf.sprintf "Warning in %s: package name has restricted prefix '%s'" - pkg prefix - | PrefixConflictClassMismatch mismatch -> - Checks.Prefix.msg_of_prefix_conflict_class_mismatch ~pkg mismatch - | NameCollision other_pkg -> - Printf.sprintf "Warning in %s: Possible name collision with package '%s'" - pkg other_pkg - | UnexpectedFile file -> - Printf.sprintf "Error in %s: Unexpected file in %s/%s" pkg - (path_from_pkg ~repo_dir:"" package) - file - | ForbiddenPerm file -> - Printf.sprintf - "Error in %s: Forbidden permission for file %s/%s. All files should \ - have permissions 644." - pkg - (path_from_pkg ~repo_dir:"" package) - (Filename.basename file) - | OpamLint warn -> - let warn = OpamFileTools.warns_to_string [ warn ] in - Printf.sprintf "Error in %s: %s" pkg warn - | ParseError -> - Printf.sprintf "Error in %s: Failed to parse the opam file" pkg - let get_packages repo_dir = get_files (repo_dir // "packages") |> List.sort String.compare let run_package_lint ~newly_published ~repo_dir pkg = let pkg = OpamPackage.of_string pkg in - let opam_path = path_from_pkg ~repo_dir pkg // "opam" in + let opam_path = O.path_from_pkg ~repo_dir pkg // "opam" in (* NOTE: We use OpamFile.OPAM.read_from_channel instead of OpamFile.OPAM.file to prevent the name and version fields being automatically added *) In_channel.with_open_text opam_path (fun ic -> diff --git a/lib/lint_error.ml b/lib/lint_error.ml new file mode 100644 index 0000000..c07e5fb --- /dev/null +++ b/lib/lint_error.ml @@ -0,0 +1,141 @@ +(** Some package name prefixes must be used along with specific conflict classes + + If either a restricted prefix or conflict class exists, then the + corresponding other must also exist. *) +type prefix_conflict_class_mismatch = + | WrongPrefix of { conflict_class : string; required_prefix : string } + | WrongConflictClass of { prefix : string; required_conflict_class : string } + +(** Errors detected during linting. + + Use {!msg_of_error} to produce descriptions the errors *) +type error = + | UnnecessaryField of string + | UnmatchedName of OpamPackage.Name.t + | UnmatchedVersion of OpamPackage.Version.t + | DubiousDuneSubst + | DuneProjectMissing + | DuneConstraintMissing + | DuneIsBuild + | BadDuneConstraint of string * string + | UnexpectedFile of string + | ForbiddenPerm of string + | OpamLint of (int * [ `Warning | `Error ] * string) + | FailedToDownload of string + | NameCollision of string + | WeakChecksum of string + | PinDepends + | ExtraFiles + | RestrictedPrefix of string + | PrefixConflictClassMismatch of prefix_conflict_class_mismatch + | ParseError + +(**/**) + +let msg_of_prefix_conflict_class_mismatch ~pkg = function + | WrongPrefix { conflict_class; required_prefix } -> + Printf.sprintf + "Error in %s: package with conflict class '%s' requires name prefix \ + '%s'" + pkg conflict_class required_prefix + | WrongConflictClass { prefix; required_conflict_class } -> + Printf.sprintf + "Error in %s: package with prefix '%s' requires conflict class '%s'" pkg + prefix required_conflict_class + +(**/**) + +(** [msg_of_error (pkg, err)] is a string describing a linting [err] found for the [pkg] *) +let msg_of_error (package, (err : error)) = + let pkg = OpamPackage.to_string package in + match err with + | UnnecessaryField field -> + Printf.sprintf + "Warning in %s: Unnecessary field '%s'. It is suggested to remove it." + pkg field + | UnmatchedName value -> + Printf.sprintf + "Error in %s: The field 'name' that doesn't match its context. Field \ + 'name' has value '%s' but was expected of value '%s'." + pkg + (OpamPackage.Name.to_string value) + (OpamPackage.Name.to_string (OpamPackage.name package)) + | UnmatchedVersion value -> + Printf.sprintf + "Error in %s: The field 'version' that doesn't match its context. \ + Field 'version' has value '%s' but was expected of value '%s'." + pkg + (OpamPackage.Version.to_string value) + (OpamPackage.Version.to_string (OpamPackage.version package)) + | DubiousDuneSubst -> + Printf.sprintf + "Warning in %s: Dubious use of 'dune subst'. 'dune subst' should \ + always only be called with {dev} (i.e. [\"dune\" \"subst\"] {dev}) If \ + your opam file has been autogenerated by dune, you need to upgrade \ + your dune-project to at least (lang dune 2.7)." + pkg + | WeakChecksum msg -> + Printf.sprintf + "Error in %s: Weak checksum algorithm(s) provided. Please use SHA-256 \ + or SHA-512. Details: %s" + pkg msg + | PinDepends -> + Printf.sprintf + "Error in %s: pin-depends present. This is not allowed in the \ + opam-repository." + pkg + | ExtraFiles -> + Printf.sprintf + "Error in %s: extra-files present. This is not allowed in the \ + opam-repository. Please use extra-source instead." + pkg + | FailedToDownload msg -> + Printf.sprintf "Error in %s: Failed to download the archive. Details: %s" + pkg msg + | DuneProjectMissing -> + Printf.sprintf + "Warning in %s: The package seems to use dune but the dune-project \ + file is missing." + pkg + | DuneConstraintMissing -> + Printf.sprintf + "Warning in %s: The package has a dune-project file but no explicit \ + dependency on dune was found." + pkg + | BadDuneConstraint (dep, ver) -> + Printf.sprintf + "Error in %s: Your dune-project file indicates that this package \ + requires at least dune %s but your opam file only requires dune >= \ + %s. Please check which requirement is the right one, and fix the \ + other." + pkg ver dep + | DuneIsBuild -> + Printf.sprintf + "Warning in %s: The package tagged dune as a build dependency. Due to \ + a bug in dune (https://github.com/ocaml/dune/issues/2147) this should \ + never be the case. Please remove the {build} tag from its filter." + pkg + | RestrictedPrefix prefix -> + Printf.sprintf "Warning in %s: package name has restricted prefix '%s'" + pkg prefix + | PrefixConflictClassMismatch mismatch -> + msg_of_prefix_conflict_class_mismatch ~pkg mismatch + | NameCollision other_pkg -> + Printf.sprintf "Warning in %s: Possible name collision with package '%s'" + pkg other_pkg + | UnexpectedFile file -> + Printf.sprintf "Error in %s: Unexpected file in %s/%s" pkg + (Opam_helpers.path_from_pkg ~repo_dir:"" package) + file + | ForbiddenPerm file -> + Printf.sprintf + "Error in %s: Forbidden permission for file %s/%s. All files should \ + have permissions 644." + pkg + (Opam_helpers.path_from_pkg ~repo_dir:"" package) + (Filename.basename file) + | OpamLint warn -> + let warn = OpamFileTools.warns_to_string [ warn ] in + Printf.sprintf "Error in %s: %s" pkg warn + | ParseError -> + Printf.sprintf "Error in %s: Failed to parse the opam file" pkg diff --git a/lib/opam_helpers.ml b/lib/opam_helpers.ml new file mode 100644 index 0000000..21087d4 --- /dev/null +++ b/lib/opam_helpers.ml @@ -0,0 +1,6 @@ +let ( // ) = Filename.concat + +let path_from_pkg ~repo_dir pkg = + repo_dir // "packages" + // OpamPackage.Name.to_string (OpamPackage.name pkg) + // OpamPackage.to_string pkg