-
Notifications
You must be signed in to change notification settings - Fork 17
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
WIP: support a globally-installable escript #232
base: main
Are you sure you want to change the base?
Conversation
lib/nerves_hub_cli/api.ex
Outdated
|> File.read!() | ||
|> X509.from_pem() | ||
|> Enum.map(&X509.Certificate.to_der/1) | ||
# CAStore uses the priv directory, which does not work with escripts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an interesting issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the OS-supplied certs are returned in the true
clause. It seems like this code could be completely deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, i guess we can just state that you need to keep your os supplied certs up to date
I love where this is going! Regarding setting the org and product, do you have any options/ideas to reduce the need to specify these on every cli use? |
Hey @joshk, thanks for the support! This is a really good question, but I have to start with the disclaimer that I am still quite new to NervesHub, so I haven't spent much time working with multiple orgs/products. But I did spend some time thinking about this use case, and here are several options for how I think we could support these settings:
%{
token: "nh_token_here",
uri: "https://manage.nervescloud.com",
local_config: %{
"/path/to/my/project" => %{
org: "my_org",
product: "my_product"
},
"/path/to/other/project" => %{
org: "my_other_org",
product: "my_other_product"
}
}
However, this solution breaks down when the project is moved, renamed, etc. |
@gworkman I really love this! I'd be happy to merge this in ASAP and for us to continue to improve it. I think we need to address the |
|> File.read!() | ||
|> X509.from_pem() | ||
|> Enum.map(&X509.Certificate.to_der/1) | ||
# TODO: These conditions can probably be removed after successfully testing for some time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: check security considerations of loading the CA certs at compile time into the binary, and the checks here
@gworkman can you also remove Elixir 1.11, 1.12, and 1.13 from the GitHub Actions configs? |
@castore_certs CAStore.file_path() | ||
|> File.read!() | ||
|> X509.from_pem() | ||
|> Enum.map(&X509.Certificate.to_der/1) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fhunleth @jjcarstens do either of you have any concerns with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's ok. This is a really big PR that looks like a clear improvement so I mostly want to get it in and iterate.
@joshk will do! I think it's about ready to merge in. There's one or two more spots I need to highlight for review |
@joshk updated min elixir version and CI config for 1.13, in line with what Frank said on slack that the rest of Nerves is >= 1.13 |
|
For the iterations: If we could set config like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review & merge. Please see the two highlighted points -
nerves_hub device burn
, nerves_hub firmware sign
and nerves_hub firmware publish
will all fail due to references to Mix
module
""" | ||
@spec firmware() :: Path.t() | ||
def firmware do | ||
# TODO: what to replace these paths with? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joshk @fhunleth I don't think I understand the use of :images_path
key here... I'm assuming it is a special key set by Nerves?
Basically, we need to change this because config/0 resolves to Mix.Project.config()
, which we don't have access to at runtime, nor can we call Mix.Project.build_path()
So how should we get this path?
|> OptionParser.to_argv() | ||
|
||
# TODO: remove reference to mix task | ||
Mix.Task.run("burn", burn_args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. Mix tasks had special docs so you could run |
tl;dr
I added support to install NervesHubCLI as an escript, to evaluate whether this provides a better experience for working with NervesHub from the command line without configuring it with every project. It's quite rough around the edges
Problem
I recently was invited to try out the NervesCloud beta by Lars and Josh. This was my first time setting up NervesHub, and unfortunately I found the process to be confusing. While NervesHubLink makes sense to add as a project dependency, I couldn't quite figure out how to work with the mix tasks for the CLI.
I know that 2.0 is under active development and the docs are getting better with every commit, and that definitely would have helped with some of the issues I faced. However, one of the most confusing parts of this was configuring the CLI to correctly talk with the NervesHub instance (NervesCloud in this case), and then realizing that I need to run
mix nerves_hub.user auth
(previously there were no error messages, just a crash that the%NervesHubCLI.API.Auth{}
struct wasnil
). I did get it working eventually, but while talking to Josh and Lars, I realized that NervesHubCLI may have a better user experience if it was a globally-installable CLI tool, with some persistent settings.Potential solutions
There are a number of different ways which this problem could be tackled.
phx_new
generators, ornerves_bootstrap
. Installation could be as simple asmix archive.install hex nerves_hub_cli
#! /usr/bin/env elixir
script, with someMix.install
commands.For the first option of distributing it as a hex archive, I think this makes the most sense, and would be the easiest. However, hex archives are supposed to be lightweight extensions of
Mix
, so unfortunately, it can't have dependencies to prevent conflicts with the project deps. Given that the CLI has web requests and cryptography functions, I don't think it's a good idea to try to go without any dependenciesI made some preliminary experiments with the Elixir script idea, but I found that the
Mix
module was not available in the environment, which meant that I couldn't easily reuse the current mix tasks found in this repo, as they make heavy use of that module.I think escript makes the most sense, as it can be easily distributed and doesn't require a large number of changes.
Changes
To build the dependency as an escript, I basically copied all of the mix tasks into the
NervesHubCLI.CLI
module. I removed references toMix
, and swapped out the task-specific modules used (such aslib/mix/nerves_hub/*.ex
) into variants which didn't haveMix
dependencies. For example, instead of usingMix.Shell.yes?/1
, I created a variant which just reads stdio viaIO.gets/1
and checks to see if it is a positive response.The command structure is now slightly different. Because it is no longer invoked with mix tasks, the command follows a command + subcommand structure. Examples:
$ nerves_hub user whoami
$ nerves_hub device list
$ nerves_hub key create --name dev_key
Additionally, there are some persistent config options which can now be set. I anticipate support for these can be expanded in the future, but the main one is the
uri
option:$ nerves_hub config set uri "https://manage.nervescloud.com"
$ nerves_hub config get uri
What's broken
This PR is definitely not ready. I wanted to showcase this early work, to see if this CLI experience is something which would be a beneficial change for the community. As such, I spent time to get some basic functions to work, but didn't spend a lot of time to polish. Some things which are definitely broken:
:ca_store
dependency stores the cert files in thepriv/
directory - which is not available for escripts. So I commented out portions of the HTTP API interface which used that.Mix
is used. For example, try searching forMix.Project.config()
in thelib/nerves_hub_cli/cli/
folder. If you run a command which callsMix
it will crashorg
orproduct
(or set them as environment variables:NERVES_HUB_ORG
andNERVES_HUB_PRODUCT
), since they aren't read from application config.How to test it out
I'm assuming you are using the NervesCloud instance here, if not change the uri to point to your own instance.
What's next
If this is something which is useful for the community, I'll keep working on this to polish it up. For the most part, I haven't touched anything which breaks the mix tasks (just the removed
:ca_store
, and added global config of theuri
).I'm happy to get some feedback, and also some input on some of my opinionated things (such as naming the executable to
nerves_hub
or the module names -NervesHubCLI.CLI.*
isn't great imo, but I wasn't sure where else to put it).