Skip to content

Commit

Permalink
read default from a file and allow extra tips
Browse files Browse the repository at this point in the history
  • Loading branch information
mnishiguchi committed Jul 19, 2023
1 parent 3fa871e commit a5826fd
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 150 deletions.
75 changes: 75 additions & 0 deletions config/default_tips.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Compiling systems on linux or via `mix nerves.system.shell`? Don't forgot about
`make help` and other tips from the Buildroot manual (thanks @ericr3r)
https://buildroot.org/downloads/manual/manual.html#make-tips

Configuring the Elixir Logger to show SASL reports can help debug unexpected
GenServer restarts.
https://hexdocs.pm/logger/Logger.html#module-erlang-otp-integration

Erlinit is a small program that starts the Erlang VM on boot. It has many
options - especially for debugging startup issues.
https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config

Get all of the default packages for starting a new Nerves project by depending
on `:nerves_pack` https://github.com/nerves-project/nerves_pack

Get some insights to Nerves internals with this high level overview of the
Nerves architecture and choice to use the BEAM VM https://youtu.be/VzOaSvTcvU4

Identify your Nerves devices using a unique ID that's already programmed into
the hardware using the boardid command. Details at
https://github.com/nerves-project/boardid/ and the boardid.config file.

Make small code changes to your running application by copy/pasting Elixir
source files at the IEx prompt.

Need more than Elixir logs? Run `dmesg` to see log messages from device drivers
and the Linux kernel. Nerves also routes kernel log messages to the Elixir
logger.

Nerves automatically reboots devices when the Erlang VM exits. This can make
some debugging harder, but you can easily disable it to let those sessions hang
for debugging

Nerves enables hardware watchdogs and connects them to Erlang's heart feature
to detect and recover from the Erlang VM hanging. See
https://embedded-elixir.com/post/2018-12-10-heart/ to learn more about this.

Nerves maintains a set of examples for common use-cases with devices. Things
like running a phoenix app, using SQLite, controlling GPIO, using Zig lang, and
more! https://github.com/nerves-project/nerves_examples

Nerves stores all BEAM files, the Erlang runtime and various support libraries
and apps in a compressed and read-only SquashFS filesystem. The writable
application partition is mounted at `/data`

Nerves stores all BEAM files, the Erlang runtime and various support
libraries/apps in a compressed and read-only SquashFS filesystem. Need to write
to disk? Use the application partition mounted R/W at `/data`

Run `Nerves.Runtime.revert` to go back to the previously loaded firmware.

See if someone has already implemented support for a sensor or other hardware
device that you have by checking https://elixir-circuits.github.io/.

Sometimes when compiling Nerves systems, order matters! Use `make
show-build-order` while in `mix nerves.system.shell` to see the compilation
order and make sure all your 🦆🦆 are in a row (thanks @ericr3r!)

Sometimes, you just need to see whats on your Nerves device's disk. Luckily,
there are a few ways to do that. You can even use VSCode!
https://embedded-elixir.com/post/2021-05-08-nerves-file-peeking/#sshfs

Use `RingLogger.next` to dump the current log buffer to screen. Use
`log_attach` from `Toolshed` to attach the current session to the Elixir logger
for live logs. https://hexdocs.pm/toolshed/Toolshed.Log.html#log_attach/1

Use `mix firmware.unpack` to decompress a local copy of your firmware on host
and inspect the files within before installing on device

Use the `RamoopsLogger` backend to store log messages in DRAM that can survive
unexpected reboots. https://github.com/smartrent/ramoops_logger

Want more traditional shell tools in your Nerves IEx? Use Toolshed! Its
included by default and has may familiar functions, such as `cat`, `ls`, or
even `weather` 🌤️ https://hexdocs.pm/toolshed
19 changes: 10 additions & 9 deletions lib/nerves_motd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ defmodule NervesMOTD do
@typedoc """
MOTD options
"""
@type option() :: {:logo, IO.ANSI.ansidata()} | {:extra_rows, [row()]}
@type option() ::
{:logo, IO.ANSI.ansidata()}
| {:extra_rows, [row()]}
| {:show_tip, boolean()}
| {:extra_tips, [String.t()]}

@typedoc """
One row of information
Expand All @@ -48,7 +52,8 @@ defmodule NervesMOTD do
an empty logo (`""`) to remove it completely.
* `:extra_rows` - a list of custom rows or a callback for returning rows.
The callback can be a 0-arity function reference or MFArgs tuple.
* `:show tip` - a boolean flag to show the tip of the day.
* `:show_tip` - a boolean flag to show the tip of the day.
* `:extra_tips` - additional custom tips as a list of strings.
"""
@spec print([option()]) :: :ok
def print(opts \\ []) do
Expand Down Expand Up @@ -87,14 +92,10 @@ defmodule NervesMOTD do
@spec tips([option()]) :: IO.ANSI.ansidata()
defp tips(opts) do
if opts[:show_tip] do
content = Tips.random() |> String.trim()

"""
==========================================================================
[Tip of the day]
--------------------------------------------------------------------------
#{content}
==========================================================================
=== Tip of the day ===
#{Tips.random(opts)}
"""
else
[]
Expand Down
152 changes: 17 additions & 135 deletions lib/nerves_motd/tips.ex
Original file line number Diff line number Diff line change
@@ -1,147 +1,29 @@
defmodule NervesMOTD.Tips do
@moduledoc false

@tips [
"""
Want more traditional shell tools in your Nerves IEx?
Use Toolshed!
Its included by default and has may familiar functions, such as `cat`,
`ls`, or even `weather` 🌤️
https://hexdocs.pm/toolshed
""",
"""
Need more than Elixir logs? Run `dmesg` to see log messages from device
drivers and the Linux kernel. Nerves also routes kernel log messages to
the Elixir logger.
""",
"""
Erlinit is a small program that starts the Erlang VM on boot. It has many
options - especially for debugging startup issues.
https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config
""",
"""
Nerves automatically reboots devices when the Erlang VM exits. This can
make some debugging harder, but you can easily disable it to let those
sessions hang for debugging
""",
"""
Use `mix firmware.unpack` to decompress a local copy of your firmware on
host and inspect the files within before installing on device
""",
"""
See if someone has already implemented support for a sensor or other
hardware device that you have by checking
https://elixir-circuits.github.io/.
""",
"""
Nerves stores all BEAM files, the Erlang runtime and various support
libraries/apps in a compressed and read-only SquashFS filesystem.
Need to write to disk? Use the application partition mounted R/W at
`/data`
""",
"""
Configuring the Elixir Logger to show SASL reports can help debug
unexpected GenServer restarts.
https://hexdocs.pm/logger/Logger.html#module-erlang-otp-integration
""",
"""
Sometimes, you just need to see whats on your Nerves device's disk.
Luckily, there are a few ways to do that. You can even use VSCode!
https://embedded-elixir.com/post/2021-05-08-nerves-file-peeking/#sshfs
""",
"""
Get all of the default packages for starting a new Nerves project by
depending on `:nerves_pack`
https://github.com/nerves-project/nerves_pack
""",
"""
Nerves enables hardware watchdogs and connects them to Erlang's heart
feature to detect and recover from the Erlang VM hanging.
See https://embedded-elixir.com/post/2018-12-10-heart/ to learn more
about this.
""",
"""
Make small code changes to your running application by copy/pasting
Elixir source files at the IEx prompt.
""",
"""
Nerves stores all BEAM files, the Erlang runtime and various support
libraries and apps in a compressed and read-only SquashFS filesystem.
The writable application partition is mounted at `/data`
""",
"""
Identify your Nerves devices using a unique ID that's already programmed
into the hardware using the boardid command. Details at
https://github.com/nerves-project/boardid/ and the boardid.config file.
""",
"""
Nerves maintains a set of examples for common use-cases with devices.
Things like running a phoenix app, using SQLite, controlling GPIO, using
Zig lang, and more!
https://github.com/nerves-project/nerves_examples
""",
"""
Use `RingLogger.next` to dump the current log buffer to screen.
Use `log_attach` from `Toolshed` to attach the current session to the
Elixir logger for live logs.
https://hexdocs.pm/toolshed/Toolshed.Log.html#log_attach/1
""",
"""
Get some insights to Nerves internals with this high level overview of
the Nerves architecture and choice to use the BEAM VM
https://youtu.be/VzOaSvTcvU4
""",
"""
Run `Nerves.Runtime.revert` to go back to the previously loaded firmware.
""",
"""
Compiling systems on linux or via `mix nerves.system.shell`?
Don't forgot about `make help` and other tips from the Buildroot manual
(thanks @ericr3r)
https://buildroot.org/downloads/manual/manual.html#make-tips
""",
"""
Sometimes when compiling Nerves systems, order matters!
Use `make show-build-order` while in `mix nerves.system.shell` to see the
compilation order and make sure all your 🦆🦆 are in a row (thanks
@ericr3r!)
""",
"""
Use the `RamoopsLogger` backend to store log messages in DRAM that can
survive unexpected reboots.
https://github.com/smartrent/ramoops_logger
"""
]
@divider "\n\n"
@default_tips File.read!("config/default_tips.txt")
|> String.split(@divider, trim: true)
|> Enum.reject(&(&1 == ""))

@type tip :: String.t()

@doc """
List all Nerves tips
List all tips
"""
@spec all() :: [tip]
def all(), do: @tips
@spec all(keyword) :: [tip]
def all(opts \\ []) do
extra_tips = Keyword.get(opts, :extra_tips, [])
@default_tips ++ extra_tips
end

@doc """
Pick one Nerves tip randomly
Pick one tip randomly
"""
@spec random() :: tip
def random(), do: Enum.random(@tips)
@spec random(keyword) :: tip
def random(opts \\ []) do
all(opts)
|> Enum.reject(&(&1 == ""))
|> Enum.random()
end
end
16 changes: 13 additions & 3 deletions test/nerves_motd/tips_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ defmodule NervesMOTD.TipsTest do
alias NervesMOTD.Tips
doctest NervesMOTD.Tips

test "all returns a list of strings" do
@extra_tip """
There are only two hard things in Computer Science: cache invalidation and naming things.
"""

test "all/1 returns a list of default tips" do
assert [tip | _] = Tips.all()
assert is_binary(tip)
end

test "random returns a string" do
assert Tips.random() |> is_binary()
test "all/1 with extra tips returns tips that contain extra tips" do
tips = Tips.all(extra_tips: [@extra_tip])
assert @extra_tip in tips
end

test "random/1 returns a string" do
tip = Tips.random(extra_tips: [@extra_tip])
assert is_binary(tip)
end
end
8 changes: 5 additions & 3 deletions test/nerves_motd_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,14 @@ defmodule NervesMOTDTest do
:ok
end

test "does nothing by default" do
refute capture_motd() =~ ~r/Tip of the day/
@tip_of_the_day_regex ~r/Tip of the day/

test "does not show tip by default" do
refute capture_motd() =~ @tip_of_the_day_regex
end

test "shows tip when enabled" do
assert capture_motd(show_tip: true) =~ ~r/Tip of the day/
assert capture_motd(show_tip: true) =~ @tip_of_the_day_regex
end
end
end

0 comments on commit a5826fd

Please sign in to comment.