Composer.Lint (composer v0.2.0)

Copy Markdown View Source

Checks a cookbook, or your local recipes, for mix composer.cookbook.lint.

Lint is a set of plain functions: it takes discovered cookbooks, prints nothing and never raises. Every problem is a Composer.Lint.Diagnostic, located where it is written (see Composer.Error.anchor/1 and Composer.Recipe.Locator), in a stable order. Recipes are checked in two passes: each entry of each recipe on its own (so every bad reference is reported), then each recipe as a whole (for cycles and conflicting installs); a problem is reported once, in the recipe that has it.

Errors, for a package cookbook (run/3):

  • every step has a summary, an action and a stability
  • every step has a test at test/composer/<cookbook>/steps/<id>_test.exs
  • every step reference in requires resolves
  • the committed manifest holds the same data as a freshly built one
  • the recipe group rules below
  • every recipe resolves (with every include followed); this also reports a cookbook alias named like an installed cookbook
  • a leftover cookbook-level templates/ directory
  • a file under steps/ that no step owns: it is not in a step's template directory (steps/_<id>/, beside steps/<id>.ex)
  • a step's templates in an unprefixed steps/<id>/ directory
  • a .ex file under steps/ that is not a compiled step module
  • a template/1 path that does not exist

Files under steps/ that git ignores are skipped.

Errors, for local (local/2):

  • a steps/ or templates/ directory (local holds recipes only)
  • any other entry besides recipes/ that composer does not recognize
  • the recipe group rules below
  • every recipe resolves

Recipe group rules:

  • a subdirectory under recipes/ (groups are files)
  • a group file with an invalid name
  • a group file that is not a keyword list of recipes
  • a duplicate recipe name within a group
  • a recipe missing shortdoc or steps, with an unknown key, or with a value of the wrong type; a shortdoc on more than one line
  • an if: flag that the recipe does not declare in its own flags
  • an invalid, reserved (core, hex, local), duplicate or atom-valued cookbook alias, or one whose target is not a package cookbook name
  • a recipe named like a group of the same cookbook

Warnings:

  • an empty group
  • a cookbook alias that none of the recipe's own references use
  • an alias that names different cookbooks in two recipes of one group
  • a step and a recipe share an id, which is allowed but confusing
  • a file in a step's template directory that its template/1 calls never refer to (skipped for a step whose template/1 path is computed at run time)

A file belongs to the step named by the first directory on its path that starts with _: steps/ash/_postgres/x/y belongs to ash/postgres. Step path segments never start with _, so a steps/<id>/ directory only ever holds child steps.

Summary

Functions

Returns the recipes that include recipe ref (or any of a list of refs), directly or through other includes, as %{ref: ref, path: group_file}, sorted by ref. The given refs are left out. Takes the :local option of files/3.

Lints recipe group files: for each of paths, the diagnostics located in it.

Lints the local config directory dir, resolving references against cookbooks (which need not include local).

Finds the cookbook a recipe group file belongs to: local when it is under the local config directory's recipes/ (the :local option), otherwise the cookbook in cookbooks whose recipes/ holds it.

Lints cookbook, resolving references against cookbooks. root is the project root, where step tests are looked for.

Types

result()

@type result() :: %{diagnostics: [Composer.Lint.Diagnostic.t()]}

Functions

dependents(cookbooks, ref, opts \\ [])

@spec dependents([Composer.Cookbook.t()], String.t() | [String.t()], keyword()) :: [
  %{ref: String.t(), path: Path.t()}
]

Returns the recipes that include recipe ref (or any of a list of refs), directly or through other includes, as %{ref: ref, path: group_file}, sorted by ref. The given refs are left out. Takes the :local option of files/3.

files(cookbooks, paths, opts \\ [])

@spec files([Composer.Cookbook.t()], [Path.t()], keyword()) :: %{
  required(Path.t()) => [Composer.Lint.Diagnostic.t()]
}

Lints recipe group files: for each of paths, the diagnostics located in it.

Every cookbook's groups are checked, since includes cross files, but only the recipes in paths are resolved as a whole. Each {:use, ...} in those files whose target, or anything it includes, has problems gets one :include_problems diagnostic, with the problems as related locations.

Files are matched by path, following symbolic links; results are keyed, and their files given, as in paths.

Options

  • :local - the local config directory; its recipes are linted as the local cookbook, in place of any in cookbooks
  • :root - the project root

local(dir, cookbooks)

@spec local(Path.t(), [Composer.Cookbook.t()]) :: result()

Lints the local config directory dir, resolving references against cookbooks (which need not include local).

owner(cookbooks, path, opts \\ [])

@spec owner([Composer.Cookbook.t()], Path.t(), keyword()) ::
  {:ok, Composer.Cookbook.t()} | {:error, term()}

Finds the cookbook a recipe group file belongs to: local when it is under the local config directory's recipes/ (the :local option), otherwise the cookbook in cookbooks whose recipes/ holds it.

run(cookbook, cookbooks, root)

Lints cookbook, resolving references against cookbooks. root is the project root, where step tests are looked for.