# `Nerves.BuildPlan`
[🔗](https://github.com/nerves-project/nerves/blob/v2.0.0-pre.2/lib/nerves/build_plan.ex#L5)

Nerves firmware build plan

This struct gets filled in by Nerves and Nerves-aware packages
before compilation starts. It then gets passed around during the
firmware build process to guide everything from pre-compiled
artifact downloads to building root filesystems and final
firmware assembly. If you'd like to adjust the firmware build process,
this is usually the place to look.

# `download_spec`

```elixir
@type download_spec() ::
  String.t()
  | {:github_releases, keyword()}
  | {:github_api, keyword()}
  | {:gitea_releases, keyword()}
  | {:gitea_api, keyword()}
```

How to download a file

## URL

Raw strings are interpreted as URLs. Schemes may be `file`, `http`, or `https`

## GitHub Releases

Public GitHub repositories may use URLs to download release artifacts, but using
the `:github_releases` or `:github_api` download strategies enables GitHub authentication. In addition to supporting private GitHub repositories, this can also help
get past unauthenticated download errors.

The `:github_releases` strategy supports unauthenticated GitHub downloads. If GitHub
auth tokens aren't available, it still tries. This is good for public repositories.

The `:github_api` strategy uses the GitHub API to find the release artifact. It will
fail with GitHub auth tokens aren't available.

GitHub auth tokens are found in this order:

1. `GITHUB_TOKEN` environment variable
2. `GH_TOKEN` environment variable
3. `:custom_auth_token` option
4. Calling out to `gh` if the `:use_gh_cli?` option is `true`

Options:
  - `:github_url` - the main GitHub URL (defaults to https://github.com)
  - `:github_api_url` - the GitHub API endpoint (defaults to https://api.github.com)
  - `:org` - the GitHub organization (required)
  - `:repo` - the GitHub repository (required)
  - `:tag` - the release tag (required)
  - `:filename` - the filename to download (required)
  - `:custom_auth_token` - specify an auth token. This is rarely needed.
  - `:use_gh_cli?` - allow the `gh` CLI utility to be used to get auth tokens. Defaults to `true`

## Gitea Releases

Gitea support is very similar to GitHub support with the exception of the URLs
and naming of auth tokens. It is similar in that `:gitea_releases` is intended
for public assets that don't require authentication and `:gitea_api` works for
private release assets.

Gitea auth tokens are found in this order:

1. `GITEA_TOKEN` environment variable
2. `:token` option

Options:
  - `:base_url` - the base Gitea URL (required)
  - `:org` - the Gitea organization (required)
  - `:repo` - the Gitea repository (required)
  - `:tag` - the release tag (required)
  - `:filename` - the filename to download (required)
  - `:token` - specify an auth token

# `download_validator`

```elixir
@type download_validator() ::
  :archive | {:openssl_signature, keyword()} | {:skip, keyword()}
```

Download validation strategy

Nerves doesn't allow unvalidated downloads to proceed to subsequent build steps.
One size doesn't fit all when it comes to validation, so Nerves-aware packages
can customize it to their needs.

## Skip

If the download step suffices in getting trusted binaries, then validation isn't
needed. It is still necessary to explicitly skip trusted binaries.

Options:

- `:filename` - the filename to skip validation on

## OpenSSL Signature

This is the simplest type of validation that can be done using the `openssl` command
line tool. To create a public/private key pair, run:

```sh
openssl genrsa -aes128 -passout pass:<passphrase> -out private.pem 2048
openssl rsa -in private.pem -passin pass:<passphrase> -pubout -out public.pem
```

Then to sign a file, run:

```sh
openssl dgst -sha256 -sign $privatekey -out /tmp/$filename.out $filename
openssl base64 -in /tmp/$filename.out -out $filename.sig
rm /tmp/$filename.out
```

To manually verify a file, run:

```sh
openssl base64 -d -in $filename.sig -out /tmp/$filename.out
openssl dgst -sha256 -verify $publickey -signature /tmp/$filename.out $filename
rm /tmp/$filename.out
```

Options:
  - `:filename` - the file to verify (required)
  - `:signature` - the signature file (defaults to `<filename>.sig`)
  - `:public_keys` - a list of public keys in PEM form and as strings

# `extractor_spec`

```elixir
@type extractor_spec() :: {:untar, keyword()}
```

Specify how download files are processed

The normal procedure is to untar the file that was downloaded. This
is not always the case, and this option allows other processing to
be done.

## Untar

Run `tar` to extract the contents into the artifact directory.

Options:
  - `:filename` - the file to untar (required)

# `package_info`

```elixir
@type package_info() :: %{
  app: atom(),
  path: Path.t(),
  version: String.t(),
  deps: [atom()],
  artifact_path: Path.t(),
  download_path: Path.t(),
  downloads: [download_spec()],
  download_validators: [download_validator()],
  extractors: [extractor_spec()],
  source_fingerprint: String.t(),
  artifact_source_files: [Path.t()],
  validated_files: [Path.t()],
  dockerfile: Path.t() | nil
}
```

Per-package information that's kept in the BuildPlan

* `:app` - the name of the package
* `:artifact_path` - Path to where artifacts can be extracted and custom files added
* `:dockerfile` - Dockerfile used by `mix nerves.artifact.build` and
  `mix nerves.artifact.shell`
* `:deps` - List of Nerves-aware packages that this one depends on
* `:download_path` - Path to where to download any files for this package
* `:download_validators` - A list of validators for checking the package's download. Post-download steps may only access validated files.
* `:downloads` - a list of download plans
* `:extractors` - a list of extractors for expanding archives into the artifact_path
* `:path` - path to package source under the deps directory
* `:source_fingerprint` - calculated source fingerprints. The fingerprint catches configs that diverge from artifact inputs.
* `:artifact_source_files` - List of files required to build the artifact.
* `:validated_files` - a map of download files that have passed validation keyed by package name
* `:version` - the version of this package

# `t`

```elixir
@type t() :: %Nerves.BuildPlan{
  actions: [module() | {module(), keyword()}],
  config: map(),
  env: map(),
  erts: Path.t() | boolean(),
  host_build?: boolean(),
  packages: [package_info()],
  rootfs_overlays: [Path.t()]
}
```

The Nerves.BuildPlan struct has the following fields:

* `:config` - a map of key-value pairs containing the overall configuration
* `:env` - a map of key-value pairs that are exported in the OS environment to guide
  cross-compilation. See the Nerves advanced configuration for official environment variables.
* `:host_build?` - true if building for the host
* `:packages` - package-specific plans and config. List is ordered by compilation order.
* `:actions` - a list of build actions for creating the firmware
* `:rootfs_overlays` - a list of paths or `.tar` files that get overlaid to create the root filesystem
* `:erts` - Path to the version of erts to include in releases

The `:config` map should be used to record all configuration. The build plan is intended to be
the sole source of truth. Keys from non-Nerves packages should prepend their package name
to avoid conflicts. The following keys are used:

* `:source_date_epoch` - the Unix date (integer) timestamp to use for all date/time references
* `:fwup_conf` - an optional user-app overridden `fwup.conf` path
* `:fwup_provisioning_conf` - an optional user-app overridden provisioning config for fwup
* `:fwup_compression` - use `:best` (default) or `:fast` compression. Numbers from 1 to 9 are also supported.
* `:rootfs_overlay` - an optional user-app overridden rootfs overlay (TODO? Add to main rootfs_overlays field?)
* `:rootfs_type` - either `type` or `{type, mkfs_options}` tuple. The `type` can be `:squashfs`, `:erofs`, or `:ext4`. `options` is a string list and if `nil` or omitted uses Nerves' defaults
* `:host_tuple` - the compiler target tuple for making binaries on the computer running the build. See `Nerves.TargetTuple`

# `fetch_interpolated_env`

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

Return the interpolated value for the specified key

This is a non-raising version of `fetch_interpolated_env/2`.

# `fetch_interpolated_env!`

```elixir
@spec fetch_interpolated_env!(t()) :: %{required(String.t()) =&gt; String.t()}
```

Return a map of environment variables with all values interpolated

Raises `KeyError` on undefined variables or self-referential variables

# `fetch_interpolated_env!`

```elixir
@spec fetch_interpolated_env!(t(), String.t()) :: String.t()
```

Return the interpolated value for the specified key

Raises `KeyError` on undefined variables or self-referential variables

# `find_package`

```elixir
@spec find_package(t(), atom()) :: package_info() | nil
```

Find build plan information about a package

# `merge_config`

```elixir
@spec merge_config(t(), Enumerable.t()) :: t()
```

# `merge_env`

```elixir
@spec merge_env(t(), Enumerable.t()) :: t()
```

Merge a new set of OS environment variables into the plan

Variables should be passed either as a map or a list of key/value tuples. Variables
overwrite ones with the same keys.

Values support interpolation using `${VAR_NAME}` syntax. Interpolation is postponed
until later so it's fine to set values that can't be interpolated until a future
variable gets added. Unlike a Unix shell, though, values referencing unknown values
raise rather than substitute an empty string.

Except for `$PATH` handling, the user's OS environment is ignored.

# `prepend_path`

```elixir
@spec prepend_path(t(), Path.t()) :: t()
```

Prepend a path to the PATH environment variable

If the path already exists in $PATH, then this is a no-op.

# `replace_package`

```elixir
@spec replace_package(t(), package_info()) :: t()
```

Replace the specified package info

# `run_planning_actions`

```elixir
@spec run_planning_actions(t(), atom()) :: t()
```

# `run_release_actions`

```elixir
@spec run_release_actions(t(), Mix.Release.t(), atom()) :: Mix.Release.t()
```

# `run_simple_actions`

```elixir
@spec run_simple_actions(t(), atom()) :: :ok
```

# `validate!`

```elixir
@spec validate!(t()) :: t()
```

Validate the build plan to detect issues that may cause problems later

Returns the build plan for use in pipelines

---

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