Rho.Dyn.Seed (rho v0.1.0)

Copy Markdown View Source

The seed: the Dyn sources an install starts from, which the host ships (Operator's default front: a welcome screen, a component library, the front's settings). The host embeds them at build time (Application.app_dir/2 can't resolve priv/ on the device) and hands them to start/2.

start/2 installs it in the background after the launch's Dyn step (Rho.Dyn.seed/3): compiling a big seed whole takes about half a minute on a Moto G 2021. The seed becomes a generation like any other, so its screens are the agent's to change, and reverting to that generation restores the defaults.

A newer seed on a phone that has one (an app update that changed the seed). The install records each seed file's sha256 as it shipped (Rho.Dyn.Store.seed_files/1). When this build's seed differs, it is installed again, merged three ways with what runs now (merge/3): a seed file the user (or the agent) never changed is replaced by the new one (or removed, when the new seed dropped it); a new seed file is added unless the user already has a file at that path; a file the user changed, deleted or wrote stays exactly as it is. So user edits are never lost, at the price that an edited seed file doesn't get the update (reverting to the seed's generation, or asking the agent, does). The merged generation goes through the whole check, compile and selftest; if it fails (an edited file relies on something the new seed changed), nothing changes and that seed is recorded as rejected, so it isn't retried every launch; the next app update with a different seed tries again. An install that recorded nothing (an older app's) is taken to have shipped the :legacy digests, if the host has them (Operator 1.0's); the seed generation's own sources can't stand in for them, since that install merged the user's files into it.

Summary

Functions

Each file's sha256 (hex), as needed/3 and the seed's record compare them.

The sources to install: seed over current (what runs now), given shipped (path => sha256 of the seed files as the installed seed shipped them; %{} for a first install). See the moduledoc for the rules.

What the Dyn store at dir needs for a seed whose files have digests: :install (no seed yet), {:update, shipped} (an older seed is installed; shipped its files' digests as they shipped, legacy when it recorded none) or :none (this seed, or this seed was rejected there).

Is the seed being installed right now?

Installs (or updates) the seed sources in a process of its own (registered under this module's name) unless it's current, or can't be now (safe mode, a proposal pending: the next launch tries again). When it ends, :notify runs (the host's front redraws). Options: :keeper (Rho.Dyn.Keeper), :notify (a 0-arity function, default none), :rationale, :legacy (see needed/3).

Functions

digests(sources)

@spec digests(%{required(String.t()) => String.t()}) :: %{
  required(String.t()) => String.t()
}

Each file's sha256 (hex), as needed/3 and the seed's record compare them.

merge(current, shipped, seed)

@spec merge(
  %{required(String.t()) => String.t()},
  %{required(String.t()) => String.t()},
  %{
    required(String.t()) => String.t()
  }
) :: %{required(String.t()) => String.t()}

The sources to install: seed over current (what runs now), given shipped (path => sha256 of the seed files as the installed seed shipped them; %{} for a first install). See the moduledoc for the rules.

needed(dir, digests, legacy \\ %{})

@spec needed(Path.t(), %{required(String.t()) => String.t()}, %{
  required(String.t()) => String.t()
}) ::
  :install | {:update, %{required(String.t()) => String.t()}} | :none

What the Dyn store at dir needs for a seed whose files have digests: :install (no seed yet), {:update, shipped} (an older seed is installed; shipped its files' digests as they shipped, legacy when it recorded none) or :none (this seed, or this seed was rejected there).

running?()

@spec running?() :: boolean()

Is the seed being installed right now?

start(sources, opts \\ [])

@spec start(%{required(String.t()) => String.t()}, keyword()) ::
  {:ok, pid()} | :ignore

Installs (or updates) the seed sources in a process of its own (registered under this module's name) unless it's current, or can't be now (safe mode, a proposal pending: the next launch tries again). When it ends, :notify runs (the host's front redraws). Options: :keeper (Rho.Dyn.Keeper), :notify (a 0-arity function, default none), :rationale, :legacy (see needed/3).