Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hook for post-processing filesystem #1042

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions guides/advanced/advanced-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,25 @@ You can modify the kernel parameters for your application or custom Nerves
system by copying the default `sysctl.conf` file to your `rootfs_overlay/etc`
directory and making the desired changes. Use `System.cmd/3` to run `sysctl` to
change settings after initialization.

## Post-processing or signing the root filesystem

When generating a Nerves firmware there is stage where `mix firmware` takes
your application code, makes a release and adds that release into the root
filesystem. After this the root filesystem is complete. This is the point where
you could generate signatures, hashes and such for a tool like dm-verity. To
allow this Nerves provides an entrypoint for a script that you can add to your
Nerves firmware config, such as `config/target.exs` or for the specific target.

```
config :nerves,
firmware: [
post_processing_script: Path.expand("./scripts/sign.sh")
]
```

Create your desired `sign.sh` in a `scripts` directory in your Nerves project
or put it wherever you prefer. The configuration file is not running with the
Nerves environment variables. The script will have access to them however. The
script will receive the filepath of the filesystem as the first argument and
this allows you to sign it or otherwise amend it.
55 changes: 37 additions & 18 deletions lib/mix/tasks/firmware.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,35 +109,27 @@ defmodule Mix.Tasks.Firmware do

write_erlinit_config(build_rootfs_overlay)

project_rootfs_overlay =
case firmware_config[:rootfs_overlay] || firmware_config[:rootfs_additions] do
nil ->
[]

overlays when is_list(overlays) ->
overlays

overlay ->
[Path.expand(overlay)]
end

project_rootfs_overlay = config_arg(:rootfs_overlay, firmware_config)
prevent_overlay_overwrites!(project_rootfs_overlay)

rootfs_overlays =
[build_rootfs_overlay | project_rootfs_overlay]
|> Enum.map(&["-a", &1])
|> List.flatten()

fwup_conf =
case firmware_config[:fwup_conf] do
nil -> []
fwup_conf -> ["-c", Path.join(File.cwd!(), fwup_conf)]
end
fwup_conf = config_arg(:fwup_conf, firmware_config)

post_processing_script = config_arg(:post_processing_script, firmware_config)

fw = ["-f", fw_out]
release_path = Path.join(Mix.Project.build_path(), "rel/#{otp_app}")
output = [release_path]
args = args ++ fwup_conf ++ rootfs_overlays ++ fw ++ rootfs_priorities ++ output

args =
args ++
fwup_conf ++
rootfs_overlays ++ fw ++ rootfs_priorities ++ post_processing_script ++ output

env = [{"MIX_BUILD_PATH", Mix.Project.build_path()} | standard_fwup_variables(config)]

set_provisioning(firmware_config[:provisioning])
Expand Down Expand Up @@ -296,4 +288,31 @@ defmodule Mix.Tasks.Firmware do
""")
end
end

defp config_arg(:rootfs_overlay, firmware_config) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
defp config_arg(:rootfs_overlay, firmware_config) do
defp rootfs_overlay_arg(firmware_config) do

LGTM! Though I would probably suggest descriptive purpose built functions for these args rather than a generic config_arg that has variable key arg

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked the single function where all the config keys were exposed to find. But since I had to reduce complexity for the cyclomat I wanted to do something that made it visually simpler but kept the keys visible. Okay to do the suggested change but it becomes a bit of a hunt to figure out what's needed or involved when diving in I think.

case firmware_config[:rootfs_overlay] || firmware_config[:rootfs_additions] do
nil ->
[]

overlays when is_list(overlays) ->
overlays

overlay ->
[Path.expand(overlay)]
end
end

defp config_arg(:fwup_conf, firmware_config) do
case firmware_config[:fwup_conf] do
nil -> []
fwup_conf -> ["-c", Path.join(File.cwd!(), fwup_conf)]
end
end

defp config_arg(:post_processing_script, firmware_config) do
case firmware_config[:post_processing_script] do
nil -> []
script when is_binary(script) -> ["-s", script]
end
end
end