ChoreRunner (chore_runner v0.5.0)

A framework and library for productively writing and running code "Chores".

A "Chore" can really be anything, but most commonly it is just some infrequently, manually run code which achieve a business or development goal.

For example: updating a config value in a database that does not yet have a UI (perhaps due to time constraints) is a great use for a chore. A chore could be created that accepts the desired value and runs the update query.

Usually, the alternative to this would be a direct prod-shell or prod-db connection, which is inherently insecure and dangerous. Many fast-moving startups or companies are ok with this access for developers, and that's fine.

But many companies have regulations that they must follow, or do not want to take the risk of a developer mistake while working in these environments.

In these cases, ChoreRunner allows the rapid creation, testing, and reviewing of code chores, along with a bundled UI for running them that accepts a variety of input types, with the goal of finding a "sweet spot" of safety and speed when solving such problems.

Getting Started

Add ChoreRunner to your supervision tree, after your app's PubSub:

children = [
  {Phoenix.PubSub, [name: MyApp.PubSub]},
  {ChoreRunner, [pubsub: MyApp.PubSub]},
]

Writing a chore

defmodule MyApp.MyChore do
  use ChoreRunner.Chore

  input :my_file, :file

  def run(%{my_file: path}}) do
    path
    |> File.read!()
    |> parse_file()
    |> do_stuff()
  end
end

Example of running this Chore:

iex> ChoreRunner.run_chore(MyApp.MyChore, %{my_file: file}, :infinity)
{:ok, %Chore{}}

Link to this section Summary

Functions

Returns the pubsub topic used for a specific chore, or all chores if given the atom :all

Returns the pubsub topic used for file downloads

List the currently running chores on all nodes.

Runs the given chore module as a chore. Accepts an input map with either string or atom keys as well as a keyword list of options. Returns a %ChoreRunner.Chore{} struct.

Stops the provided chore by terminating both the chore task and the reporter. Returns :ok if successful, and :error if not successful

Link to this section Functions

Link to this function

chore_pubsub_topic(arg1)

Specs

chore_pubsub_topic(ChoreRunner.Chore.t() | :all) :: String.t()

Returns the pubsub topic used for a specific chore, or all chores if given the atom :all

Link to this function

downloads_pubsub_topic()

Returns the pubsub topic used for file downloads

Link to this function

list_running_chores()

Specs

list_running_chores() :: [ChoreRunner.Chore.t()]

List the currently running chores on all nodes.

Link to this function

run_chore(chore_mod, input, opts \\ [])

Specs

run_chore(module(), map(), Keyword.t()) ::
  {:ok, ChoreRunner.Chore.t()} | {:error, any()}

Runs the given chore module as a chore. Accepts an input map with either string or atom keys as well as a keyword list of options. Returns a %ChoreRunner.Chore{} struct.

Input map keys must match one of the inputs defined in the provided chore module. If not, the input under the unmatched key is discarded. Matched input will have default validations run on them, as well custom validations declared in the chore module. If any inputs fail validation, the chore will not run, and instead an error tuple will be returned. If all validations pass, the chore will then be run.

Opts

  • extra_data: Map of arbitrary data to be forwarded to telemetry events and result handlers. Useful for storing chore session information, such as identifying who or what ran the chore.
  • result_handler: Single arity anonymous function or MFA of a single arity function that is called once the chore is finished. The function will take the %Chore{}.
Link to this function

stop_chore(chore)

Specs

stop_chore(ChoreRunner.Chore.t()) :: :ok | :error

Stops the provided chore by terminating both the chore task and the reporter. Returns :ok if successful, and :error if not successful