Skip to content
This repository has been archived by the owner on Oct 3, 2024. It is now read-only.

Commit

Permalink
Move the lint errors into a dedicated module
Browse files Browse the repository at this point in the history
Enables sharing the type between the interface and implementation
without duplication.
  • Loading branch information
shonfeder committed Aug 21, 2024
1 parent a01c993 commit 7c0fa3d
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 143 deletions.
148 changes: 5 additions & 143 deletions lib/lint.ml
Original file line number Diff line number Diff line change
@@ -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 =
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ->
Expand Down
141 changes: 141 additions & 0 deletions lib/lint_error.ml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions lib/opam_helpers.ml
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 7c0fa3d

Please sign in to comment.