# `MixAffected.Config`
[🔗](https://github.com/mnox/mix_affected/blob/v0.1.0/lib/mix_affected/config.ex#L1)

The `affected:` settings of the consuming project, plus the test layout
(`:test_paths`, `:test_pattern`) Mix itself uses to find test files.

    def project do
      [
        app: :my_app,
        affected: [
          # replaces the defaults when given
          global_invalidation: ["config/**", "mix.exs", "mix.lock", "test/test_helper.exs",
                                "priv/repo/migrations/**"],
          # appended to global_invalidation
          extra_global_invalidation: [],
          # changed files that affect no test; default ["*.md", "docs/**/*.md"]
          # (other .md may be runtime data: it falls through as unmapped)
          ignore: ["*.md", "docs/**/*.md"],
          # source glob => test globs to run when a matching file is affected
          mappings: [{"lib/my_app/workers/**", ["test/my_app/workers/**"]}]
        ]
      ]
    end

Every glob is matched by `MixAffected.Glob` against project-root-relative
paths, and checked up front by `validate_glob/2`: a malformed glob, or a
pattern naming a directory (one ending in `/`, or a glob-free pattern that
is a directory under the project root; either would match no file), is an
error rather than a rule that silently never fires.

# `mapping`

```elixir
@type mapping() :: {String.t(), [String.t()]}
```

# `t`

```elixir
@type t() :: %MixAffected.Config{
  global_invalidation: [String.t()],
  ignore: [String.t()],
  mappings: [mapping()],
  test_paths: [String.t()],
  test_pattern: String.t()
}
```

# `default_global_invalidation`

```elixir
@spec default_global_invalidation() :: [String.t()]
```

The default `global_invalidation` globs.

# `from_project`

```elixir
@spec from_project(keyword(), Path.t()) :: {:ok, t()} | {:error, String.t()}
```

Reads the config from a project keyword list (`Mix.Project.config/0`) of
the project at `root`. Unknown `affected:` keys, malformed values and globs
`validate_glob/2` rejects are errors, never ignored.

# `test_file?`

```elixir
@spec test_file?(t(), String.t()) :: boolean()
```

Whether `path` is a test file: under a test path and matching the test pattern.

# `validate_glob`

```elixir
@spec validate_glob(String.t(), Path.t()) :: :ok | {:error, String.t()}
```

Checks a glob from the config or a test's `affected_by` against the project
at `root`: it must compile (`MixAffected.Glob.compile/1`), must be in
normal form and must not name a directory; any of those would leave a rule
that matches no changed file. Changed paths are normalized, so a glob with
a `.`, `..` or empty (`//`) segment never matches; the error gives the
normal form, and a glob reaching outside the root is an error of its own.
A glob ending in `/` (or `/.`, `/..`) names a directory whatever it holds;
a pattern with no glob metacharacter names one when it is a directory
under `root`. A leading `./` is allowed, as `MixAffected.Glob` ignores it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
