Change how FarmwareManifests are built

pull/974/head
connor rigby 2019-03-15 14:30:20 -07:00 committed by Connor Rigby
parent 1e7ae7a744
commit 2124c19fcd
No known key found for this signature in database
GPG Key ID: 29A88B24B70456E0
5 changed files with 166 additions and 35 deletions

View File

@ -2,22 +2,50 @@ defmodule FarmbotCore.Asset.FarmwareInstallation.Manifest do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
@acceptable_manifest_version_requirement "2.0.0"
@acceptable_farmware_tools_version_requirement "2.0.0"
alias FarmbotCore.Project
defmodule Config do
use Ecto.Schema
@primary_key false
embedded_schema do
field(:label, :string)
field(:value, :string)
field(:order, :integer)
end
def view(config) do
%{
label: config.label,
value: config.value,
order: config.order
}
end
def changeset(config, params) do
config
|> cast(params, [:label, :value, :order])
|> validate_required([:label, :value, :order])
end
end
embedded_schema do
field(:package, :string)
field(:language, :string)
field(:author, :string)
field(:description, :string)
field(:version, :string)
field(:url, :string)
field(:zip, :string)
field(:executable, :string)
field(:args, {:array, :string})
field(:farmware_tools_version, :string, default: "latest")
# new field
field(:os_version_requirement, :string, default: "~> 8.0")
field(:args, :string)
field(:config, :map, default: %{})
field(:package_version, :string)
field(:farmware_manifest_version_requirement, :string)
field(:farmware_tools_version_requirement, :string, default: ">= 0.0.0")
field(:farmbot_os_version_requirement, :string, default: "~> 8.0")
end
def view(manifest) do
@ -26,14 +54,15 @@ defmodule FarmbotCore.Asset.FarmwareInstallation.Manifest do
language: manifest.language,
author: manifest.author,
description: manifest.description,
version: manifest.version,
url: manifest.url,
zip: manifest.zip,
executable: manifest.executable,
args: manifest.args,
farmware_tools_version: manifest.farmware_tools_version,
# new field
os_version_requirement: manifest.os_version_requirement
config: Map.new(manifest.config, fn({key, config_data}) -> {key, Config.view(config_data)} end),
package_version: manifest.package_version,
farmware_tools_version_requirement: manifest.farmware_tools_version_requirement,
farmware_manifest_version_requirement: manifest.farmware_manifest_version_requirement,
farmbot_os_version_requirement: manifest.farmbot_os_version_requirement,
}
end
@ -44,20 +73,38 @@ defmodule FarmbotCore.Asset.FarmwareInstallation.Manifest do
:language,
:author,
:description,
:version,
:url,
:zip,
:executable,
:args,
:os_version_requirement,
:farmware_tools_version
:config,
:package_version,
:farmware_tools_version_requirement,
:farmware_manifest_version_requirement,
:farmbot_os_version_requirement,
])
|> validate_required([:package, :executable, :args, :zip, :version])
|> validate_required_os_version()
|> validate_required([
:package,
:author,
:zip,
:executable,
:args,
:package_version,
:farmware_manifest_version_requirement,
:farmbot_os_version_requirement
])
|> validate_farmbot_os_version_requirement()
|> validate_farmware_manifest_version_requirement()
|> validate_farmware_tools_version_requirement()
|> validate_package_version()
|> validate_config()
|> validate_url()
|> validate_zip()
end
defp validate_required_os_version(changeset) do
req = get_field(changeset, :os_version_requirement)
defp validate_farmbot_os_version_requirement(%{valid?: false} = change), do: change
defp validate_farmbot_os_version_requirement(changeset) do
req = get_field(changeset, :farmbot_os_version_requirement)
cur = Project.version()
match =
@ -72,7 +119,98 @@ defmodule FarmbotCore.Asset.FarmwareInstallation.Manifest do
changeset
_ ->
add_error(changeset, :os_version_requirement, "Version requirement not met")
add_error(changeset, :farmbot_os_version_requirement, "Version requirement not met")
end
end
defp validate_farmware_manifest_version_requirement(%{valid?: false} = change), do: change
defp validate_farmware_manifest_version_requirement(changeset) do
req = get_field(changeset, :farmware_manifest_version_requirement)
match =
try do
Version.match?(@acceptable_manifest_version_requirement, req)
rescue
Version.InvalidRequirementError -> :invalid_version
end
case match do
true ->
changeset
_ ->
add_error(changeset, :farmware_manifest_version_requirement, "Version requirement not met")
end
end
# Validates the version of farmware_tools required
defp validate_farmware_tools_version_requirement(%{valid?: false} = change), do: change
defp validate_farmware_tools_version_requirement(changeset) do
req = get_field(changeset, :farmware_tools_version_requirement)
match =
try do
Version.match?(@acceptable_farmware_tools_version_requirement, req)
rescue
Version.InvalidRequirementError -> :invalid_version
end
case match do
true ->
changeset
_ ->
add_error(changeset, :farmware_tools_version_requirement, "Version requirement not met")
end
end
defp validate_package_version(%{valid?: false} = change), do: change
defp validate_package_version(changeset) do
version = get_field(changeset, :package_version)
case Version.parse(version) do
{:ok, _} -> changeset
:error ->
add_error(changeset, :package_version, "not a valid semver string")
end
end
defp validate_config(%{valid?: false} = change), do: change
defp validate_config(changeset) do
config = get_field(changeset, :config)
Enum.reduce(config, changeset, fn
{name, %{} = params}, changeset when is_binary(name) ->
case Config.changeset(%Config{}, params) do
%{valid?: true} = config_changeset ->
validated_config_data = Ecto.Changeset.apply_changes(config_changeset)
new_config = Map.put(config, name, validated_config_data)
put_change(changeset, :config, new_config)
%{valid?: false} ->
add_error(changeset, :config, "invalid config item")
end
{_, _}, changeset ->
add_error(changeset, :config, "invalid config item")
end)
end
defp validate_url(%{valid?: false} = change), do: change
defp validate_url(changeset) do
# `url` is optional
url = get_field(changeset, :url)
if url do
parse_uri(url, changeset, :url)
else
changeset
end
end
defp validate_zip(%{valid?: false} = change), do: change
defp validate_zip(changeset) do
zip = get_field(changeset, :zip)
parse_uri(zip, changeset, :zip)
end
defp parse_uri(url, changeset, key) do
case URI.parse(url) do
%{scheme: s} when s in ["file", "https", "http"] -> changeset
_ -> add_error(changeset, key, "invalid uri")
end
end
end

View File

@ -71,11 +71,15 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FarmwareInstallation do
# Installed is newer than remote.
:gt ->
success_log(updated, "up to date.")
BotState.report_farmware_installed(updated.manifest.package, Manifest.view(updated.manifest))
{:noreply, updated}
# No difference between installed and remote.
:eq ->
success_log(updated, "up to date.")
BotState.report_farmware_installed(updated.manifest.package, Manifest.view(updated.manifest))
{:noreply, updated}
# Installed version is older than remote
@ -86,6 +90,7 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.FarmwareInstallation do
:ok <- install_zip(updated, zip_binary),
:ok <- install_farmware_tools(updated),
:ok <- write_manifest(updated) do
BotState.report_farmware_installed(updated.manifest.package, Manifest.view(updated.manifest))
{:noreply, updated}
else
er ->

View File

@ -81,8 +81,6 @@ defmodule FarmbotCore.BotState.FileSystem do
def serialize_state(%{} = bot_state, prefix, acc) do
Enum.reduce(bot_state, acc, fn {key, value}, acc ->
cond do
# :( Please FIXME
key == :farmwares -> acc
is_map(value) && map_size(value) == 0 -> [Path.join(prefix, to_string(key)) | acc]
is_list(value) -> raise("Arrays can not be serialized to filesystem nodes")
is_map(value) -> serialize_state(value, Path.join(prefix, to_string(key)), acc)

View File

@ -18,7 +18,7 @@ defmodule FarmbotCore.BotStateNG do
embeds_one(:informational_settings, InformationalSettings, on_replace: :update)
embeds_one(:configuration, Configuration, on_replace: :update)
field(:user_env, {:map, {:string, :any}}, default: %{})
field(:farmwares, {:map, {:string, :map}}, default: %{})
field(:process_info, {:map, {:string, :any}}, default: %{farmwares: %{}})
field(:pins, {:map, {:integer, :map}}, default: %{})
field(:jobs, {:map, {:string, :map}}, default: %{})
end
@ -35,7 +35,7 @@ defmodule FarmbotCore.BotStateNG do
def changeset(bot_state, params \\ %{}) do
bot_state
|> cast(params, [:user_env, :pins, :jobs, :farmwares])
|> cast(params, [:user_env, :pins, :jobs, :process_info])
|> cast_embed(:mcu_params, [])
|> cast_embed(:location_data, [])
|> cast_embed(:informational_settings, [])
@ -47,8 +47,8 @@ defmodule FarmbotCore.BotStateNG do
mcu_params: McuParams.view(bot_state.mcu_params),
location_data: LocationData.view(bot_state.location_data),
informational_settings: InformationalSettings.view(bot_state.informational_settings),
process_info: %{farmwares: bot_state.farmwares},
configuration: Configuration.view(bot_state.configuration),
process_info: bot_state.process_info,
user_env: bot_state.user_env,
pins: bot_state.pins,
jobs: bot_state.jobs
@ -67,16 +67,15 @@ defmodule FarmbotCore.BotStateNG do
put_change(cs, :pins, new_pins)
end
# :( Please FIXME
@doc "Add or update a farmware to state.farmwares"
def add_or_update_farmware(state, name, %{} = manifest) do
cs = changeset(state, %{})
new_farmwares =
cs
|> get_field(:farmwares)
|> Map.put(name, manifest)
put_change(cs, :farmwares, new_farmwares)
|> get_field(:process_info)
|> Map.get(:farmwares)
put_change(cs, :process_info, %{farmwares: new_farmwares})
end
def set_user_env(state, key, value) do

View File

@ -20,15 +20,6 @@ defmodule FarmbotCore.BotStateNG.ChangeGenerator do
|> changes(path, acc)
end
# :( Please FIXME
def changes([{:farmwares, farmwares} | rest], path, acc) do
acc = Enum.reduce(farmwares, acc, fn
{farmware_name, manifest}, acc ->
[{add_paths(path, "process_info.farmwares.#{farmware_name}"), manifest} | acc]
end)
changes(rest, path, acc)
end
def changes([{key, change} | rest], path, acc) do
cond do
is_number(change) ->