Skip to content

Commit

Permalink
Adjust mix nerves.system.shell` for OTP 26
Browse files Browse the repository at this point in the history
OTP 26 made some big changes around serial interface and it breaks the
`mix nerves.system.shell` task. In the end, the main issue is that we
no longer have an easy way to take over the STDIN of the task to forward
it to a shell running in the system build directory.

Many attempts were made to recapture STDIN, but decided that for now we
need to move on. Instead, this adjusts the task to do _all_ the setup
for the build directory as before and outputs a warning to the user for
the command that can be manually run in order to get the same shell that
was previously started with `mix nerves.system.shell`
  • Loading branch information
jjcarstens committed Sep 12, 2023
1 parent a502829 commit f9789f2
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 19 deletions.
60 changes: 48 additions & 12 deletions lib/nerves/artifact/build_runners/docker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -125,30 +125,62 @@ defmodule Nerves.Artifact.BuildRunners.Docker do

@doc """
Connect to a system configuration shell in a Docker container
Unsupported in >= OTP 26. However, the Docker env will still be created
and a command output to IO which can be used manually.
"""
@spec system_shell(Nerves.Package.t()) :: :ok
def system_shell(pkg) do
_ = preflight(pkg)
{_, image} = config(pkg)
platform_config = pkg.config[:platform_config][:defconfig]
defconfig = Path.join("/nerves/env/#{pkg.app}", platform_config)

initial_input = [
"echo Updating build directory.",
"echo This will take a while if it is the first time...",
"/nerves/env/platform/create-build.sh #{defconfig} #{@working_dir} >/dev/null"
]

mounts = Enum.join(mounts(pkg), " ")
ssh_agent = Enum.join(ssh_agent(), " ")
env_vars = Enum.join(env(), " ")

cmd =
shell =
"docker run --rm -it -w #{@working_dir} #{env_vars} #{mounts} #{ssh_agent} #{image} /bin/bash"

set_volume_permissions(pkg)

Mix.Nerves.Shell.open(cmd, initial_input)
create_build = create_build_cmd(pkg) |> Enum.join(" ")

if String.to_integer(System.otp_release()) < 26 do
# Legacy shell support
initial_input = [
"echo Updating build directory.",
"echo This will take a while if it is the first time...",
"#{create_build} >/dev/null"
]

Mix.Nerves.Shell.open(shell, initial_input)
else
Mix.Nerves.IO.shell_warn("shell start deprecated", """
OTP 26 made several changes to the serial interface handling. Unfortunately, this
is a regression in preventing the Nerves tooling from starting a system sub-shell.
However, the Docker environment has been configured and can be used the same as
before by manually running the command below which creates the build directory
and gives you a shell running in the container to interact with the system:
""")

exec_input = [
"echo -e '\\e[25F\\e[0J\\e[1;7m\\n Preparing Nerves Shell \\e[0m'",
"echo -e '\\e]0;Nerves Shell\\a'",
"echo \\\"PS1='\\e[1;7m Nerves \\e[0;1m \\W > \\e[0m'\\\" >> ~/.bashrc",
"echo \\\"PS2='\\e[1;7m Nerves \\e[0;1m \\W ..\\e[0m'\\\" >> ~/.bashrc",
"echo 'Updating build directory.'",
"echo 'This will take a while if it is the first time...'",
"#{create_build} >/dev/null",
"echo -e '\\nUse \\e[33mctrl+d\\e[0m or \\e[33mexit\\e[0m to leave the container shell\\n'",
"exec /bin/bash"
]

# >= OTP 26 will just output this command to the user for them to run
# With Docker, we want create-build.sh to run and then to exec the shell
# to keep the docker container open as an interactive shell
Mix.shell().info(~s(#{shell} -c "#{Enum.join(exec_input, " ; ")}"))
end
end

defp preflight(pkg) do
Expand All @@ -162,10 +194,14 @@ defmodule Nerves.Artifact.BuildRunners.Docker do

# Build Commands

defp create_build(pkg, stream) do
defp create_build_cmd(pkg) do
platform_config = pkg.config[:platform_config][:defconfig]
defconfig = Path.join("/nerves/env/#{pkg.app}", platform_config)
cmd = ["/nerves/env/platform/create-build.sh", defconfig, @working_dir]
["/nerves/env/platform/create-build.sh", defconfig, @working_dir]
end

defp create_build(pkg, stream) do
cmd = create_build_cmd(pkg)
shell_info("Starting Build... (this may take a while)")
run(pkg, cmd, stream)
end
Expand Down
31 changes: 24 additions & 7 deletions lib/nerves/artifact/build_runners/local.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ defmodule Nerves.Artifact.BuildRunners.Local do

@doc """
Connect to a system configuration sub-shell
Unsupported in >= OTP 26
"""
@spec system_shell(Nerves.Package.t()) :: :ok
def system_shell(pkg) do
Expand All @@ -67,13 +69,28 @@ defmodule Nerves.Artifact.BuildRunners.Local do
platform_config = pkg.config[:platform_config][:defconfig]
defconfig = Path.join("#{pkg.path}", platform_config)

initial_input = [
"echo Updating build directory.",
"echo This will take a while if it is the first time...",
"#{script} #{defconfig} #{dest} >/dev/null",
"cd #{dest}"
]
if String.to_integer(System.otp_release()) < 26 do
initial_input = [
"echo Updating build directory.",
"echo This will take a while if it is the first time...",
"#{script} #{defconfig} #{dest} >/dev/null",
"cd #{dest}"
]

Mix.Nerves.Shell.open(shell, initial_input)
else
Mix.Nerves.IO.shell_warn("shell start deprecated", """
OTP 26 made several changes to the serial interface handling. Unfortunately, this
is a regression in preventing the Nerves tooling from starting a system sub-shell.
However, compilation is supported on this host and this native shell can be used.
Run the commands below to create the build directory and perform all the same
interactions as before within it:
""")

Mix.Nerves.Shell.open(shell, initial_input)
# Leave as it's own info line so users could pipe/eval this as a workaround
# i.e eval "$(mix nerves.system.shell | tail -n 1)"
Mix.shell().info(" #{script} #{defconfig} #{dest} >/dev/null && cd #{dest}")
end
end
end

0 comments on commit f9789f2

Please sign in to comment.