commit eba069be0fa41b8c50e8fb580a9bb8cfeef254a9 Author: James Harton Date: Thu Sep 28 17:48:20 2023 +1300 chore: `mix nerves.new`. diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..8aab4bc --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,8 @@ +# Used by "mix format" +[ + inputs: [ + "{mix,.formatter}.exs", + "{config,lib,test}/**/*.{ex,exs}", + "rootfs_overlay/etc/iex.exs" + ] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b03e8b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e4259d --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Bivouac + +**TODO: Add description** + +## Targets + +Nerves applications produce images for hardware targets based on the +`MIX_TARGET` environment variable. If `MIX_TARGET` is unset, `mix` builds an +image that runs on the host (e.g., your laptop). This is useful for executing +logic tests, running utilities, and debugging. Other targets are represented by +a short name like `rpi3` that maps to a Nerves system image for that platform. +All of this logic is in the generated `mix.exs` and may be customized. For more +information about targets see: + +https://hexdocs.pm/nerves/targets.html#content + +## Getting Started + +To start your Nerves app: + * `export MIX_TARGET=my_target` or prefix every command with + `MIX_TARGET=my_target`. For example, `MIX_TARGET=rpi3` + * Install dependencies with `mix deps.get` + * Create firmware with `mix firmware` + * Burn to an SD card with `mix burn` + +## Learn more + + * Official docs: https://hexdocs.pm/nerves/getting-started.html + * Official website: https://nerves-project.org/ + * Forum: https://elixirforum.com/c/nerves-forum + * Discussion Slack elixir-lang #nerves ([Invite](https://elixir-slackin.herokuapp.com/)) + * Source: https://github.com/nerves-project/nerves diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..993a63e --- /dev/null +++ b/config/config.exs @@ -0,0 +1,27 @@ +# This file is responsible for configuring your application and its +# dependencies. +# +# This configuration file is loaded before any dependency and is restricted to +# this project. +import Config + +# Enable the Nerves integration with Mix +Application.start(:nerves_bootstrap) + +config :bivouac, target: Mix.target() + +# Customize non-Elixir parts of the firmware. See +# https://hexdocs.pm/nerves/advanced-configuration.html for details. + +config :nerves, :firmware, rootfs_overlay: "rootfs_overlay" + +# Set the SOURCE_DATE_EPOCH date for reproducible builds. +# See https://reproducible-builds.org/docs/source-date-epoch/ for more information + +config :nerves, source_date_epoch: "1695876461" + +if Mix.target() == :host do + import_config "host.exs" +else + import_config "target.exs" +end diff --git a/config/host.exs b/config/host.exs new file mode 100644 index 0000000..166f24d --- /dev/null +++ b/config/host.exs @@ -0,0 +1,21 @@ +import Config + +# Add configuration that is only needed when running on the host here. + +config :nerves_runtime, + kv_backend: + {Nerves.Runtime.KVBackend.InMemory, + contents: %{ + # The KV store on Nerves systems is typically read from UBoot-env, but + # this allows us to use a pre-populated InMemory store when running on + # host for development and testing. + # + # https://hexdocs.pm/nerves_runtime/readme.html#using-nerves_runtime-in-tests + # https://hexdocs.pm/nerves_runtime/readme.html#nerves-system-and-firmware-metadata + + "nerves_fw_active" => "a", + "a.nerves_fw_architecture" => "generic", + "a.nerves_fw_description" => "N/A", + "a.nerves_fw_platform" => "host", + "a.nerves_fw_version" => "0.0.0" + }} diff --git a/config/target.exs b/config/target.exs new file mode 100644 index 0000000..6786058 --- /dev/null +++ b/config/target.exs @@ -0,0 +1,95 @@ +import Config + +# Use Ringlogger as the logger backend and remove :console. +# See https://hexdocs.pm/ring_logger/readme.html for more information on +# configuring ring_logger. + +config :logger, backends: [RingLogger] + +# Use shoehorn to start the main application. See the shoehorn +# library documentation for more control in ordering how OTP +# applications are started and handling failures. + +config :shoehorn, init: [:nerves_runtime, :nerves_pack] + +# Erlinit can be configured without a rootfs_overlay. See +# https://github.com/nerves-project/erlinit/ for more information on +# configuring erlinit. + +# Advance the system clock on devices without real-time clocks. +config :nerves, :erlinit, update_clock: true + +# Configure the device for SSH IEx prompt access and firmware updates +# +# * See https://hexdocs.pm/nerves_ssh/readme.html for general SSH configuration +# * See https://hexdocs.pm/ssh_subsystem_fwup/readme.html for firmware updates + +keys = + [ + Path.join([System.user_home!(), ".ssh", "id_rsa.pub"]), + Path.join([System.user_home!(), ".ssh", "id_ecdsa.pub"]), + Path.join([System.user_home!(), ".ssh", "id_ed25519.pub"]) + ] + |> Enum.filter(&File.exists?/1) + +if keys == [], + do: + Mix.raise(""" + No SSH public keys found in ~/.ssh. An ssh authorized key is needed to + log into the Nerves device and update firmware on it using ssh. + See your project's config.exs for this error message. + """) + +config :nerves_ssh, + authorized_keys: Enum.map(keys, &File.read!/1) + +# Configure the network using vintage_net +# See https://github.com/nerves-networking/vintage_net for more information +config :vintage_net, + regulatory_domain: "US", + config: [ + {"usb0", %{type: VintageNetDirect}}, + {"eth0", + %{ + type: VintageNetEthernet, + ipv4: %{method: :dhcp} + }}, + {"wlan0", %{type: VintageNetWiFi}} + ] + +config :mdns_lite, + # The `hosts` key specifies what hostnames mdns_lite advertises. `:hostname` + # advertises the device's hostname.local. For the official Nerves systems, this + # is "nerves-<4 digit serial#>.local". The `"nerves"` host causes mdns_lite + # to advertise "nerves.local" for convenience. If more than one Nerves device + # is on the network, it is recommended to delete "nerves" from the list + # because otherwise any of the devices may respond to nerves.local leading to + # unpredictable behavior. + + hosts: [:hostname, "nerves"], + ttl: 120, + + # Advertise the following services over mDNS. + services: [ + %{ + protocol: "ssh", + transport: "tcp", + port: 22 + }, + %{ + protocol: "sftp-ssh", + transport: "tcp", + port: 22 + }, + %{ + protocol: "epmd", + transport: "tcp", + port: 4369 + } + ] + +# Import target specific config. This must remain at the bottom +# of this file so it overrides the configuration defined above. +# Uncomment to use target specific configurations + +# import_config "#{Mix.target()}.exs" diff --git a/lib/bivouac.ex b/lib/bivouac.ex new file mode 100644 index 0000000..f9fc6fe --- /dev/null +++ b/lib/bivouac.ex @@ -0,0 +1,18 @@ +defmodule Bivouac do + @moduledoc """ + Documentation for Bivouac. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Bivouac.hello + :world + + """ + def hello do + :world + end +end diff --git a/lib/bivouac/application.ex b/lib/bivouac/application.ex new file mode 100644 index 0000000..df62dd1 --- /dev/null +++ b/lib/bivouac/application.ex @@ -0,0 +1,44 @@ +defmodule Bivouac.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + @impl true + def start(_type, _args) do + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Bivouac.Supervisor] + + children = + [ + # Children for all targets + # Starts a worker by calling: Bivouac.Worker.start_link(arg) + # {Bivouac.Worker, arg}, + ] ++ children(target()) + + Supervisor.start_link(children, opts) + end + + # List all child processes to be supervised + def children(:host) do + [ + # Children that only run on the host + # Starts a worker by calling: Bivouac.Worker.start_link(arg) + # {Bivouac.Worker, arg}, + ] + end + + def children(_target) do + [ + # Children for all targets except host + # Starts a worker by calling: Bivouac.Worker.start_link(arg) + # {Bivouac.Worker, arg}, + ] + end + + def target() do + Application.get_env(:bivouac, :target) + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..37e418d --- /dev/null +++ b/mix.exs @@ -0,0 +1,87 @@ +defmodule Bivouac.MixProject do + use Mix.Project + + @app :bivouac + @version "0.1.0" + @all_targets [ + :rpi, + :rpi0, + :rpi2, + :rpi3, + :rpi3a, + :rpi4, + :bbb, + :osd32mp1, + :x86_64, + :grisp2, + :mangopi_mq_pro + ] + + def project do + [ + app: @app, + version: @version, + elixir: "~> 1.11", + archives: [nerves_bootstrap: "~> 1.11"], + start_permanent: Mix.env() == :prod, + deps: deps(), + releases: [{@app, release()}], + preferred_cli_target: [run: :host, test: :host] + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + mod: {Bivouac.Application, []}, + extra_applications: [:logger, :runtime_tools] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # Dependencies for all targets + {:nerves, "~> 1.10", runtime: false}, + {:shoehorn, "~> 0.9.1"}, + {:ring_logger, "~> 0.10.0"}, + {:toolshed, "~> 0.3.0"}, + + # Allow Nerves.Runtime on host to support development, testing and CI. + # See config/host.exs for usage. + {:nerves_runtime, "~> 0.13.0"}, + + # Dependencies for all targets except :host + {:nerves_pack, "~> 0.7.0", targets: @all_targets}, + + # Dependencies for specific targets + # NOTE: It's generally low risk and recommended to follow minor version + # bumps to Nerves systems. Since these include Linux kernel and Erlang + # version updates, please review their release notes in case + # changes to your application are needed. + {:nerves_system_rpi, "~> 1.19", runtime: false, targets: :rpi}, + {:nerves_system_rpi0, "~> 1.19", runtime: false, targets: :rpi0}, + {:nerves_system_rpi2, "~> 1.19", runtime: false, targets: :rpi2}, + {:nerves_system_rpi3, "~> 1.19", runtime: false, targets: :rpi3}, + {:nerves_system_rpi3a, "~> 1.19", runtime: false, targets: :rpi3a}, + {:nerves_system_rpi4, "~> 1.19", runtime: false, targets: :rpi4}, + {:nerves_system_bbb, "~> 2.14", runtime: false, targets: :bbb}, + {:nerves_system_osd32mp1, "~> 0.10", runtime: false, targets: :osd32mp1}, + {:nerves_system_x86_64, "~> 1.19", runtime: false, targets: :x86_64}, + {:nerves_system_grisp2, "~> 0.3", runtime: false, targets: :grisp2}, + {:nerves_system_mangopi_mq_pro, "~> 0.4", runtime: false, targets: :mangopi_mq_pro} + ] + end + + def release do + [ + overwrite: true, + # Erlang distribution is not started automatically. + # See https://hexdocs.pm/nerves_pack/readme.html#erlang-distribution + cookie: "#{@app}_cookie", + include_erts: &Nerves.Release.erts/0, + steps: [&Nerves.Release.init/1, :assemble], + strip_beams: Mix.env() == :prod or [keep: ["Docs"]] + ] + end +end diff --git a/rel/vm.args.eex b/rel/vm.args.eex new file mode 100644 index 0000000..3fb4e40 --- /dev/null +++ b/rel/vm.args.eex @@ -0,0 +1,60 @@ +## Customize flags given to the VM: http://erlang.org/doc/man/erl.html + +## Do not set -name or -sname here. Prefer configuring them at runtime +## Configure -setcookie in the mix.exs release section or at runtime + +## Number of dirty schedulers doing IO work (file, sockets, and others) +##+SDio 5 + +## Increase number of concurrent ports/sockets +##+Q 65536 + +## Tweak GC to run more often +##-env ERL_FULLSWEEP_AFTER 10 + +## Use Ctrl-C to interrupt the current shell rather than invoking the emulator's +## break handler and possibly exiting the VM. ++Bc + +# Allow time warps so that the Erlang system time can more closely match the +# OS system time. ++C multi_time_warp + +## Load code at system startup +## See http://erlang.org/doc/system_principles/system_principles.html#code-loading-strategy +-mode embedded + +# Load code as per the boot script since not using archives +# See https://www.erlang.org/doc/man/init.html#command-line-flags +-code_path_choice strict + +## Disable scheduler busy wait to reduce idle CPU usage and avoid delaying +## other OS processes. See http://erlang.org/doc/man/erl.html#+sbwt ++sbwt none ++sbwtdcpu none ++sbwtdio none + +## Save the shell history between reboots +## See http://erlang.org/doc/man/kernel_app.html for additional options +-kernel shell_history enabled + +## Enable heartbeat monitoring of the Erlang runtime system +-heart -env HEART_BEAT_TIMEOUT 30 + +## Start the Elixir shell + +-noshell +-user elixir +-run elixir start_iex + + +## Enable colors in the shell +-elixir ansi_enabled true + +## Options added after -extra are interpreted as plain arguments and can be +## retrieved using :init.get_plain_arguments(). Options before the "--" are +## interpreted by Elixir and anything afterwards is left around for other IEx +## and user applications. +-extra --no-halt +-- +--dot-iex /etc/iex.exs diff --git a/rootfs_overlay/etc/iex.exs b/rootfs_overlay/etc/iex.exs new file mode 100644 index 0000000..74ee8d6 --- /dev/null +++ b/rootfs_overlay/etc/iex.exs @@ -0,0 +1,4 @@ +NervesMOTD.print() + +# Add Toolshed helpers to the IEx session +use Toolshed diff --git a/test/bivouac_test.exs b/test/bivouac_test.exs new file mode 100644 index 0000000..34717ff --- /dev/null +++ b/test/bivouac_test.exs @@ -0,0 +1,8 @@ +defmodule BivouacTest do + use ExUnit.Case + doctest Bivouac + + test "greets the world" do + assert Bivouac.hello() == :world + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start()