Skip to content

Commit

Permalink
feat: show tip of the day
Browse files Browse the repository at this point in the history
  • Loading branch information
mnishiguchi committed Jun 15, 2023
1 parent a98902d commit 8d975ea
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 1 deletion.
22 changes: 21 additions & 1 deletion lib/nerves_motd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule NervesMOTD do
\e[38;5;24m███▌ \e[38;5;74m▀▀████\e[0m
"""

alias NervesMOTD.Tips
alias NervesMOTD.Utils

@excluded_ifnames [~c"lo", ~c"lo0"]
Expand Down Expand Up @@ -47,6 +48,7 @@ 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.
"""
@spec print([option()]) :: :ok
def print(opts \\ []) do
Expand All @@ -63,7 +65,8 @@ defmodule NervesMOTD do
"\n",
"""
Nerves CLI help: https://hexdocs.pm/nerves/iex-with-nerves.html
"""
""",
tips(opts)
]
|> IO.ANSI.format()
|> IO.puts()
Expand All @@ -81,6 +84,23 @@ defmodule NervesMOTD do
Keyword.get(opts, :logo, @logo)
end

@spec tips([option()]) :: IO.ANSI.ansidata()
defp tips(opts) do
if opts[:show_tip] do
{title, content} = Tips.random()

"""
==========================================================================
[Tip of the day] #{title}
--------------------------------------------------------------------------
#{String.strip(content)}
==========================================================================
"""
else
[]
end
end

@spec rows(map(), list()) :: [[cell()]]
defp rows(apps, opts) do
[
Expand Down
210 changes: 210 additions & 0 deletions lib/nerves_motd/tips.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
defmodule NervesMOTD.Tips do
@moduledoc false

@tips [
{
"Toolshed",
"""
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
"""
},
{
"Kernek log message",
"""
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",
"""
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
"""
},
{
"Disable automatic reboot",
"""
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
"""
},
{
"Whats in a firmware?",
"""
Use `mix firmware.unpack` to decompress a local copy of your firmware on
host and inspect the files within before installing on device
"""
},
{
"Elixir Circuits libraries",
"""
See if someone has already implemented support for a sensor or other
hardware device that you have by checking
https://elixir-circuits.github.io/.
"""
},
{
"Read-only filesystems",
"""
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`
"""
},
{
"SASL log messages",
"""
Configuring the Elixir Logger to show SASL reports can help debug
unexpected GenServer restarts.
https://hexdocs.pm/logger/Logger.html#module-erlang-otp-integration
"""
},
{
"Viewing the file system",
"""
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
"""
},
{
"NervesPack",
"""
Get all of the default packages for starting a new Nerves project by
depending on `:nerves_pack`
https://github.com/nerves-project/nerves_pack
"""
},
{
"Hardware watchdogs",
"""
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.
"""
},
{
"Copy and paste",
"""
Make small code changes to your running application by copy/pasting
Elixir source files at the IEx prompt.
"""
},
{
"Read-only filesystems",
"""
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`
"""
},
{
"Device serial numbers",
"""
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.
"""
},
{
"Need an example?",
"""
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
"""
},
{
"Logs",
"""
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
"""
},
{
"Eye from the Sky",
"""
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
"""
},
{
"Revert to the previously loaded firmware",
"""
Run `Nerves.Runtime.revert` to go back to the previously loaded firmware.
"""
},
{
"Compiling systems",
"""
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
"""
},
{
"Order matters",
"""
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!)
"""
},
{
"RamoopsLogger",
"""
Use the `RamoopsLogger` backend to store log messages in DRAM that can
survive unexpected reboots.
https://github.com/smartrent/ramoops_logger
"""
}
]

@type tip :: {title :: String.t(), content :: String.t()}

@doc """
List all Nerves tips
"""
@spec all() :: [tip]
def all(), do: @tips

@doc """
Pick one Nerves tip randomly
"""
@spec random() :: tip
def random(), do: Enum.random(@tips)
end
13 changes: 13 additions & 0 deletions test/nerves_motd/tips_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule NervesMOTD.TipsTest do
use ExUnit.Case
alias NervesMOTD.Tips
doctest NervesMOTD.Tips

test "all" do
assert [{_title, _content} | _] = Tips.all()
end

test "random" do
assert {_title, _content} = Tips.random()
end
end
19 changes: 19 additions & 0 deletions test/nerves_motd_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,23 @@ defmodule NervesMOTDTest do

assert capture_motd() == ""
end

describe "tip of the day" do
setup _context do
NervesMOTD.MockRuntime
|> Mox.expect(:applications, 1, default_applications_code())
|> Mox.expect(:active_partition, 1, fn -> "A" end)
|> Mox.expect(:firmware_validity, 1, fn -> :valid end)

:ok
end

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

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

0 comments on commit 8d975ea

Please sign in to comment.