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.
Summary
Types
How to download a file
Download validation strategy
Specify how download files are processed
Per-package information that's kept in the BuildPlan
The Nerves.BuildPlan struct has the following fields
Functions
Return the interpolated value for the specified key
Return a map of environment variables with all values interpolated
Return the interpolated value for the specified key
Find build plan information about a package
Merge a new set of OS environment variables into the plan
Prepend a path to the PATH environment variable
Replace the specified package info
Validate the build plan to detect issues that may cause problems later
Types
@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:
GITHUB_TOKENenvironment variableGH_TOKENenvironment variable:custom_auth_tokenoption- Calling out to
ghif the:use_gh_cli?option istrue
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 theghCLI utility to be used to get auth tokens. Defaults totrue
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:
GITEA_TOKENenvironment variable:tokenoption
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 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:
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:
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:
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
@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)
@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 bymix nerves.artifact.buildandmix 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
@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.tarfiles 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 overriddenfwup.confpath:fwup_provisioning_conf- an optional user-app overridden provisioning config for fwup:fwup_compression- use:best(default) or:fastcompression. 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- eithertypeor{type, mkfs_options}tuple. Thetypecan be:squashfs,:erofs, or:ext4.optionsis a string list and ifnilor omitted uses Nerves' defaults:host_tuple- the compiler target tuple for making binaries on the computer running the build. SeeNerves.TargetTuple
Functions
Return the interpolated value for the specified key
This is a non-raising version of fetch_interpolated_env/2.
Return a map of environment variables with all values interpolated
Raises KeyError on undefined variables or self-referential variables
Return the interpolated value for the specified key
Raises KeyError on undefined variables or self-referential variables
@spec find_package(t(), atom()) :: package_info() | nil
Find build plan information about a package
@spec merge_config(t(), Enumerable.t()) :: t()
@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 a path to the PATH environment variable
If the path already exists in $PATH, then this is a no-op.
@spec replace_package(t(), package_info()) :: t()
Replace the specified package info
@spec run_release_actions(t(), Mix.Release.t(), atom()) :: Mix.Release.t()
Validate the build plan to detect issues that may cause problems later
Returns the build plan for use in pipelines