Packages
nerves
1.9.3
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.0
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.3
1.9.2
retired
1.9.1
1.9.0
1.8.0
1.7.17
1.7.16
1.7.15
1.7.14
retired
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
retired
1.7.1
retired
1.7.0
retired
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.11.0
0.10.1
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-rc.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
Craft and deploy bulletproof embedded software
Current section
Files
Jump to
Current section
Files
lib/nerves/artifact/resolvers/github_api.ex
defmodule Nerves.Artifact.Resolvers.GithubAPI do
@moduledoc false
@behaviour Nerves.Artifact.Resolver
alias Nerves.{Utils, Utils.HTTPClient}
@base_url "https://api.github.com/"
defstruct artifact_name: nil,
base_url: @base_url,
headers: [],
http_client: HTTPClient,
http_pid: nil,
public?: false,
opts: [],
repo: nil,
tag: "",
token: "",
url: nil,
username: ""
@impl Nerves.Artifact.Resolver
def get({org_proj, opts}) do
opts =
%{struct(__MODULE__, opts) | opts: opts, repo: org_proj}
|> maybe_adjust_token()
|> add_http_opts()
|> maybe_start_http()
result = fetch_artifact(opts)
opts.http_client.stop(opts.http_pid)
result
end
defp add_http_opts(opts) do
headers =
if opts.public? do
[]
else
# make safe values here in case nil was supplied as an option
# The request will fail and error will be reported later on
user = opts.username || ""
token = opts.token || ""
credentials = Base.encode64(user <> ":" <> token)
[{"Authorization", "Basic " <> credentials}]
end
%{
opts
| headers: headers,
url: Path.join([opts.base_url, "repos", opts.repo, "releases", "tags", opts.tag])
}
end
defp maybe_adjust_token(opts) do
token = System.get_env("GITHUB_TOKEN") || System.get_env("GH_TOKEN")
if token do
# Let the env var take precedence
%{opts | token: token}
else
opts
end
end
defp maybe_start_http(%{http_pid: pid} = opts) when is_pid(pid), do: opts
defp maybe_start_http(opts) do
{:ok, http_pid} = opts.http_client.start_link()
%{opts | http_pid: http_pid}
end
defp fetch_artifact(opts) do
info = if System.get_env("NERVES_DEBUG") == "1", do: opts.url, else: opts.artifact_name
Utils.Shell.info([" [GitHub] ", info])
with {:ok, data} <- release_details(opts),
%{"assets" => assets} <- Utils.json_decode(data),
{:ok, asset_url} <- get_asset_url(assets, opts) do
opts.http_client.get(opts.http_pid, asset_url,
headers: [{"Accept", "application/octet-stream"} | opts.headers]
)
end
end
defp release_details(opts) do
case opts.http_client.get(opts.http_pid, opts.url, headers: opts.headers, progress?: false) do
{:error, "Status 404 Not Found"} ->
invalid_token? = is_nil(opts.token) or opts.token == ""
msg =
if not opts.public? and invalid_token? do
"""
Missing token
For private releases, you must authenticate the request to fetch release assets.
You can do this in a few ways:
* export or set GITHUB_TOKEN=<your-token>
* set `token: <get-token-function>` for this GitHub repository in your Nerves system mix.exs
"""
else
"No release"
end
{:error, msg}
result ->
result
end
end
defp get_asset_url([], _opts) do
{:error, "No release artifacts"}
end
defp get_asset_url(assets, %{artifact_name: artifact_name}) do
ret =
Enum.find(assets, fn %{"name" => name} ->
String.equivalent?(artifact_name, name)
end)
case ret do
nil ->
available = for %{"name" => name} <- assets, do: [" * ", name, "\n"]
msg = ["No artifact with valid checksum\n\n Found:\n", available]
{:error, msg}
%{"url" => url} ->
{:ok, url}
end
end
end