# `Pipette.Target`
[🔗](https://github.com/tommeier/pipette-buildkite-plugin/blob/main/lib/pipette/target.ex#L1)

Parses pipeline targets from commit messages and the `CI_TARGET` environment variable.

Targets allow developers to manually select which groups and steps
to run, bypassing file-based scope detection. This is useful for
re-running specific checks or skipping irrelevant CI work.

## Commit Message Syntax

Prefix the commit message with `[ci:<targets>]`:

    [ci:api] Fix login bug            # run :api group
    [ci:api/test] Fix flaky test      # run only :test step in :api
    [ci:api,web] Update shared types  # run :api and :web groups

Group and step names must match `[a-z_]+`.

## `CI_TARGET` Environment Variable

Same format as the tag content (without brackets):

    CI_TARGET=api          # run :api group
    CI_TARGET=api/test     # run only :test step in :api
    CI_TARGET=api,web      # run :api and :web groups

Commit message targets take precedence over `CI_TARGET`.

## Return Format

Parsed targets are returned as a map with two keys:

  * `:groups` — `MapSet` of group name atoms to activate
  * `:steps` — `MapSet` of `{group, step}` tuples for step-level filtering

## Examples

    Pipette.Target.parse_commit_message("[ci:api] Fix login bug")
    #=> {:ok, %{groups: MapSet.new([:api]), steps: MapSet.new()}}

    Pipette.Target.parse_ci_target("api/test")
    #=> {:ok, %{groups: MapSet.new([:api]), steps: MapSet.new([{:api, :test}])}}

    Pipette.Target.resolve(ctx)
    #=> {:ok, %{groups: ..., steps: ...}} or :none

# `target_set`

```elixir
@type target_set() :: %{groups: MapSet.t(atom()), steps: MapSet.t({atom(), atom()})}
```

# `parse_ci_target`

```elixir
@spec parse_ci_target(String.t() | nil) :: {:ok, target_set()} | :none
```

Parse targets from the `CI_TARGET` environment variable value.

## Examples

    iex> Pipette.Target.parse_ci_target("api")
    {:ok, %{groups: MapSet.new([:api]), steps: MapSet.new()}}

    iex> Pipette.Target.parse_ci_target("api,web")
    {:ok, %{groups: MapSet.new([:api, :web]), steps: MapSet.new()}}

    iex> Pipette.Target.parse_ci_target(nil)
    :none

# `parse_commit_message`

```elixir
@spec parse_commit_message(String.t()) :: {:ok, target_set()} | :none
```

Parse targets from a commit message.

Looks for a `[ci:...]` prefix at the start of the message.

## Examples

    iex> Pipette.Target.parse_commit_message("[ci:api] Fix bug")
    {:ok, %{groups: MapSet.new([:api]), steps: MapSet.new()}}

    iex> Pipette.Target.parse_commit_message("[ci:api/test] Fix flaky")
    {:ok, %{groups: MapSet.new([:api]), steps: MapSet.new([{:api, :test}])}}

    iex> Pipette.Target.parse_commit_message("No targets here")
    :none

# `resolve`

```elixir
@spec resolve(Pipette.Context.t()) :: {:ok, target_set()} | :none
```

Resolve targets from the build context.

Checks the commit message first, then falls back to `CI_TARGET`.
Returns `:none` if no targets are found.

## Examples

    ctx = %Pipette.Context{message: "[ci:api] Fix bug", ci_target: nil}
    Pipette.Target.resolve(ctx)
    #=> {:ok, %{groups: MapSet.new([:api]), steps: MapSet.new()}}

---

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