From 632f244e795fe5416453356981eacbe9e8ce31e2 Mon Sep 17 00:00:00 2001 From: Kian-Meng Ang Date: Sat, 20 Mar 2021 17:36:19 +0800 Subject: [PATCH] Misc doc changes Besides other documentation changes, this commit ensures the generated HTML doc for HexDocs.pm will become the main source doc for this Elixir library which leverage on latest ExDoc features. --- .formatter.exs | 3 +- .gitignore | 24 ++++++++----- CHANGELOG.md | 8 +++-- README.md | 55 +++++++++++++++++++----------- lib/swarm/distribution/strategy.ex | 15 ++++---- lib/swarm/logger.ex | 2 +- lib/swarm/registry.ex | 2 +- lib/swarm/tracker/crdt.ex | 18 +++++----- lib/swarm/tracker/tracker.ex | 24 +++++++++---- mix.exs | 50 ++++++++++++++++++--------- mix.lock | 24 +++++++------ 11 files changed, 141 insertions(+), 84 deletions(-) diff --git a/.formatter.exs b/.formatter.exs index 2bed17c..d2cda26 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,3 +1,4 @@ +# Used by "mix format" [ - inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] ] diff --git a/.gitignore b/.gitignore index 632d0cc..e04498b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,17 @@ -# vscode elixir_ls plugin artifacts -/.elixir_ls - # The directory Mix will write compiled artifacts to. -/_build +/_build/ # If you run "mix test --cover", coverage assets end up here. -/cover +/cover/ # The directory Mix downloads your dependencies sources to. -/deps +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ -# Where 3rd-party dependencies like ExDoc output generated docs. -/doc +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch # If the VM crashes, it generates a dump, let's ignore it too. erl_crash.dump @@ -19,6 +19,12 @@ erl_crash.dump # Also ignore archive artifacts (built via "mix archive.build"). *.ez +# Ignore package tarball (built via "mix hex.build"). +swarm-*.tar + +# Temporary files for e.g. tests. +/tmp/ + # Intellij Files /.idea/ -/swarm.iml \ No newline at end of file +/swarm.iml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c73ceb..96bf4e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## Next Release +# Changelog + +## 3.2.0 (2018-02-01) ### Changed @@ -16,7 +18,7 @@ N/A Don't attempt to hand-off or restart processes started with `Swarm.register_name/2` ([#63](https://github.com/bitwalker/swarm/pull/63)). Fixes #62. -## 3.1 +## 3.1.0 (2017-11-15) ### Changed @@ -37,7 +39,7 @@ Don't attempt to hand-off or restart processes started with `Swarm.register_name - Add local registration when restarted named process is already started but unknown locally ([#46](https://github.com/bitwalker/swarm/pull/46)). - Retry starting remote process when module not yet available on target node ([#56](https://github.com/bitwalker/swarm/pull/56)). -## 2.0 +## 2.0.0 (2016-10-01) ### Removed diff --git a/README.md b/README.md index 390782c..a71b861 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ # Swarm -[![Hex.pm Version](http://img.shields.io/hexpm/v/swarm.svg?style=flat)](https://hex.pm/packages/swarm) [![Build Status](https://travis-ci.com/bitwalker/swarm.svg?branch=master)](https://travis-ci.com/bitwalker/swarm) - -**NOTE**: If you are upgrading from 1.0, be aware that the autoclustering functionality has been extracted -to its own package, which you will need to depend on if you use that feature. -The package is [libcluster](http://github.com/bitwalker/libcluster) and is available on -[Hex](https://hex.pm/packages/libcluster). Please be sure to read over the README to make sure your -config is properly updated. +[![Build Status](https://travis-ci.com/bitwalker/swarm.svg?branch=master)](https://travis-ci.com/bitwalker/swarm) +[![Module Version](https://img.shields.io/hexpm/v/swarm.svg)](https://hex.pm/packages/swarm) +[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/swarm/) +[![Total Download](https://img.shields.io/hexpm/dt/swarm.svg)](https://hex.pm/packages/swarm) +[![License](https://img.shields.io/hexpm/l/swarm.svg)](https://github.com/bitwalker/swarm/blob/master/LICENSE) +[![Last Updated](https://img.shields.io/github/last-commit/bitwalker/swarm.svg)](https://github.com/bitwalker/swarm/commits/master) + +> **NOTE**: If you are upgrading from 1.0, be aware that the autoclustering +> functionality has been extracted to its own package, which you will need to +> depend on if you use that feature. The package is +> [libcluster](http://github.com/bitwalker/libcluster) and is available on +> [Hex](https://hex.pm/packages/libcluster). Please be sure to read over the +> README to make sure your config is properly updated. Swarm is a global distributed registry, offering a feature set similar to that of `gproc`, but architected to handle dynamic node membership and large volumes of process registrations @@ -31,19 +37,27 @@ for you! View the docs [here](https://hexdocs.pm/swarm). -**PLEASE READ**: If you are giving Swarm a spin, it is important to understand that you can concoct scenarios whereby -the registry appears to be out of sync temporarily, this is a side effect of an eventually consistent model and does not mean that -Swarm is not working correctly, rather you need to ensure that applications you build on top of Swarm are written to embrace eventual -consistency, such that periods of inconsistency are tolerated. For the most part though, the registry replicates extremely -quickly, so noticeable inconsistency is more of an exception than a rule, but a proper distributed system should always be designed to -tolerate the exceptions, as they become more and more common as you scale up. If however you notice extreme inconsistency or delayed -replication, then it is possible it may be a bug, or performance issue, so feel free to open an issue if you are unsure, and we will gladly look into it. +> **PLEASE READ**: If you are giving Swarm a spin, it is important to +> understand that you can concoct scenarios whereby the registry appears to be +> out of sync temporarily, this is a side effect of an eventually consistent +> model and does not mean that Swarm is not working correctly, rather you need +> to ensure that applications you build on top of Swarm are written to embrace +> eventual consistency, such that periods of inconsistency are tolerated. For +> the most part though, the registry replicates extremely quickly, so +> noticeable inconsistency is more of an exception than a rule, but a proper +> distributed system should always be designed to tolerate the exceptions, as +> they become more and more common as you scale up. If however you notice +> extreme inconsistency or delayed replication, then it is possible it may be a +> bug, or performance issue, so feel free to open an issue if you are unsure, +> and we will gladly look into it. ## Installation ```elixir defp deps do - [{:swarm, "~> 3.0"}] + [ + {:swarm, "~> 3.0"} + ] end ``` @@ -320,10 +334,6 @@ defmodule MyApp.ExampleUsage do end ``` -## License - -MIT - ## Testing `mix test` runs a variety of tests, most of them use a cluster of @@ -343,8 +353,13 @@ If you don't have `epmd` running you can start it using the following command: epmd -daemon - ## TODO - automated testing (some are present) - QuickCheck model + +## Copyright and License + +Copyright (c) 2016 Paul Schoenfelder + +Released under the MIT License, which can be found in the repository in [`LICENSE.md`](https://github.com/bitwalker/swarm/blob/master/LICENSE.md). diff --git a/lib/swarm/distribution/strategy.ex b/lib/swarm/distribution/strategy.ex index 2a458af..541aca4 100644 --- a/lib/swarm/distribution/strategy.ex +++ b/lib/swarm/distribution/strategy.ex @@ -5,13 +5,15 @@ defmodule Swarm.Distribution.Strategy do via the `libring` library. Custom strategies are expected to return a datastructure or pid which will be - passed along to any functions which need to manipulate the current distribution state. - This can be either a plain datastructure (as is the case with the libring-based strategy), - or a pid which your strategy module then uses to call a process in your own supervision tree. + passed along to any functions which need to manipulate the current + distribution state. This can be either a plain datastructure (as is the case + with the libring-based strategy), or a pid which your strategy module then + uses to call a process in your own supervision tree. - For efficiency reasons, it is highly recommended to use plain datastructures rather than a - process for storing the distribution state, because it has the potential to become a bottleneck otherwise, - however this is really up to the needs of your situation, just know that you can go either way. + For efficiency reasons, it is highly recommended to use plain datastructures + rather than a process for storing the distribution state, because it has the + potential to become a bottleneck otherwise, however this is really up to the + needs of your situation, just know that you can go either way. """ alias Swarm.Distribution.Ring, as: RingStrategy @@ -56,6 +58,7 @@ defmodule Swarm.Distribution.Strategy do @doc """ Adds a list of nodes to the state of the current distribution strategy. + The node list can be composed of both node names (atoms) or tuples containing a node name and a weight for that node. """ diff --git a/lib/swarm/logger.ex b/lib/swarm/logger.ex index e91c502..e4c18bf 100644 --- a/lib/swarm/logger.ex +++ b/lib/swarm/logger.ex @@ -2,7 +2,7 @@ defmodule Swarm.Logger do @moduledoc false @doc """ - Formats a log message to include info on which node swarm is running on + Formats a log message to include info on which node swarm is running on. """ @spec format(String.t()) :: String.t() def format(message), do: "[swarm on #{Node.self()}] #{message}" diff --git a/lib/swarm/registry.ex b/lib/swarm/registry.ex index 2a4a98f..b466fdf 100644 --- a/lib/swarm/registry.ex +++ b/lib/swarm/registry.ex @@ -111,7 +111,7 @@ defmodule Swarm.Registry do end @doc """ - Inserts a new registration, and returns true if successful, or false if not + Inserts a new registration, and returns true if successful, or false if not. """ @spec new(Entry.entry()) :: boolean def new(entry() = reg) do diff --git a/lib/swarm/tracker/crdt.ex b/lib/swarm/tracker/crdt.ex index b687a63..b3f9cc4 100644 --- a/lib/swarm/tracker/crdt.ex +++ b/lib/swarm/tracker/crdt.ex @@ -16,7 +16,7 @@ defmodule Swarm.IntervalTreeClock do | {int_tuple, int_tuple} @doc """ - Creates a new interval tree clock + Creates a new interval tree clock. """ @spec seed() :: __MODULE__.t() def seed(), do: {1, 0} @@ -45,7 +45,7 @@ defmodule Swarm.IntervalTreeClock do def peek({_i, e}), do: {0, e} @doc """ - Records an event on the given clock + Records an event on the given clock. """ @spec event(__MODULE__.t()) :: __MODULE__.t() def event({i, e}) do @@ -69,10 +69,10 @@ defmodule Swarm.IntervalTreeClock do @doc """ Compares two clocks. - If :eq is returned, the two clocks are causally equivalent - If :lt is returned, the first clock is causally dominated by the second - If :gt is returned, the second clock is causally dominated by the first - If :concurrent is returned, the two clocks are concurrent (conflicting) + If :eq is returned, the two clocks are causally equivalent. + If :lt is returned, the first clock is causally dominated by the second. + If :gt is returned, the second clock is causally dominated by the first. + If :concurrent is returned, the two clocks are concurrent (conflicting). """ @spec compare(__MODULE__.t(), __MODULE__.t()) :: :lt | :gt | :eq | :concurrent def compare(a, b) do @@ -88,13 +88,13 @@ defmodule Swarm.IntervalTreeClock do end @doc """ - Encodes the clock as a binary + Encodes the clock as a binary. """ @spec encode(__MODULE__.t()) :: binary def encode({i, e}), do: :erlang.term_to_binary({i, e}) @doc """ - Decodes the clock from a binary + Decodes the clock from a binary. """ @spec decode(binary) :: {:ok, __MODULE__.t()} | {:error, {:invalid_clock, term}} def decode(b) when is_binary(b) do @@ -108,7 +108,7 @@ defmodule Swarm.IntervalTreeClock do end @doc """ - Returns the length of the encoded binary representation of the clock + Returns the length of the encoded binary representation of the clock. """ @spec len(__MODULE__.t()) :: non_neg_integer def len(d), do: :erlang.size(encode(d)) diff --git a/lib/swarm/tracker/tracker.ex b/lib/swarm/tracker/tracker.ex index 920a912..b3833c7 100644 --- a/lib/swarm/tracker/tracker.ex +++ b/lib/swarm/tracker/tracker.ex @@ -60,7 +60,8 @@ defmodule Swarm.Tracker do @doc """ Hand off all the processes running on the given worker to the remaining nodes in the cluster. This can be used to gracefully shut down a node. - Note that if you don't shut down the node after the handoff a rebalance can lead to processes being scheduled on it again. + Note that if you don't shut down the node after the handoff a rebalance can + lead to processes being scheduled on it again. In other words the handoff doesn't blacklist the node for further rebalances. """ def handoff(worker_name, state), @@ -68,6 +69,7 @@ defmodule Swarm.Tracker do @doc """ Tracks a process (pid) with the given name. + Tracking processes with this function will *not* restart the process when its parent node goes down, or shift the process to other nodes if the cluster topology changes. It is strictly for global name registration. @@ -77,12 +79,21 @@ defmodule Swarm.Tracker do @doc """ Tracks a process created via the provided module/function/args with the given name. - The process will be distributed on the cluster based on the implementation of the configured distribution strategy. - If the process' parent node goes down, it will be restarted on the new node which owns its keyspace. + + The process will be distributed on the cluster based on the implementation of + the configured distribution strategy. + + If the process' parent node goes down, it will be restarted on the new node + which owns its keyspace. + If the cluster topology changes, and the owner of its keyspace changes, it will be shifted to the new owner, after initiating the handoff process as described in the documentation. - A track call will return an error tagged tuple, `{:error, :no_node_available}`, if there is no node available to start the process. - Provide a timeout value to limit the track call duration. A value of `:infinity` can be used to block indefinitely. + + A track call will return an error tagged tuple, `{:error, + :no_node_available}`, if there is no node available to start the process. + + Provide a timeout value to limit the track call duration. A value of + `:infinity` can be used to block indefinitely. """ def track(name, m, f, a, timeout) when is_atom(m) and is_atom(f) and is_list(a), do: GenStateMachine.call(__MODULE__, {:track, name, %{mfa: {m, f, a}}}, timeout) @@ -94,7 +105,8 @@ defmodule Swarm.Tracker do do: GenStateMachine.call(__MODULE__, {:untrack, pid}, :infinity) @doc """ - Adds some metadata to the given process (pid). This is primarily used for tracking group membership. + Adds some metadata to the given process (pid). This is primarily used for + tracking group membership. """ def add_meta(key, value, pid) when is_pid(pid), do: GenStateMachine.call(__MODULE__, {:add_meta, key, value, pid}, :infinity) diff --git a/mix.exs b/mix.exs index 5ee05b7..aa8df9d 100644 --- a/mix.exs +++ b/mix.exs @@ -12,35 +12,35 @@ end defmodule Swarm.Mixfile do use Mix.Project + @source_url "https://github.com/bitwalker/swarm" + @version "3.4.0" + def project do [ app: :swarm, - version: "3.4.0", - elixir: "~> 1.3", + version: @version, + elixir: "~> 1.6", elixirc_paths: elixirc_paths(Mix.env()), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, - description: - "A fast, multi-master, distributed global process registry, with automatic distribution of worker processes.", package: package(), docs: docs(), deps: deps(), aliases: aliases(), - dialyzer: [ - plt_add_apps: [:inets], - plt_add_deps: :transitive, - flags: ~w(-Wunmatched_returns -Werror_handling -Wrace_conditions -Wunderspecs) - ] + dialyzer: dialyzer() ] end def application do - [extra_applications: [:logger, :crypto], mod: {Swarm, []}] + [ + extra_applications: [:logger, :crypto], + mod: {Swarm, []} + ] end defp deps do [ - {:ex_doc, "~> 0.13", only: :dev}, + {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, {:dialyxir, "~> 0.3", only: :dev}, {:benchee, "~> 0.4", only: :dev}, {:porcelain, "~> 2.0", only: [:dev, :test]}, @@ -51,24 +51,32 @@ defmodule Swarm.Mixfile do defp package do [ - files: ["lib", "src", "mix.exs", "README.md", "LICENSE.md"], + description: """ + A fast, multi-master, distributed global process registry, with + automatic distribution of worker processes. + """, + files: ["lib", "src", "mix.exs", "README.md", "LICENSE.md", "CHANGELOG.md"], maintainers: ["Paul Schoenfelder"], licenses: ["MIT"], - links: %{Github: "https://github.com/bitwalker/swarm"} + links: %{ + Changelog: "https:hexdocs.pm/swarm/changelog.html", + GitHub: @source_url + } ] end defp docs do [ + extras: ["CHANGELOG.md", "README.md"], main: "readme", + source_url: @source_url, + source_ref: @version, formatter_opts: [gfm: true], - extras: [ - "README.md" - ] + formatters: ["html"] ] end - defp aliases() do + defp aliases do if System.get_env("SWARM_TEST_DEBUG") do [test: "test --no-start --trace"] else @@ -76,6 +84,14 @@ defmodule Swarm.Mixfile do end end + defp dialyzer do + [ + plt_add_apps: [:inets], + plt_add_deps: :transitive, + flags: ~w(-Wunmatched_returns -Werror_handling -Wrace_conditions -Wunderspecs) + ] + end + defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_), do: ["lib"] end diff --git a/mix.lock b/mix.lock index 1d9e9bc..f391beb 100644 --- a/mix.lock +++ b/mix.lock @@ -1,14 +1,16 @@ %{ - "benchee": {:hex, :benchee, "0.13.2", "30cd4ff5f593fdd218a9b26f3c24d580274f297d88ad43383afe525b1543b165", [:mix], [{:deep_merge, "~> 0.1", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm"}, - "deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"}, - "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, - "gen_state_machine": {:hex, :gen_state_machine, "2.0.3", "477ea51b466a749ab23a0d6090e9e84073f41f9aa28c7efc40eac18f3d4a9f77", [:mix], [], "hexpm"}, - "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"}, - "makeup": {:hex, :makeup, "0.5.1", "966c5c2296da272d42f1de178c1d135e432662eca795d6dc12e5e8787514edf7", [:mix], [{:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.8.0", "1204a2f5b4f181775a0e456154830524cf2207cf4f9112215c05e0b76e4eca8b", [:mix], [{:makeup, "~> 0.5.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.2.2", "d526b23bdceb04c7ad15b33c57c4526bf5f50aaa70c7c141b4b4624555c68259", [:mix], [], "hexpm"}, + "benchee": {:hex, :benchee, "0.13.2", "30cd4ff5f593fdd218a9b26f3c24d580274f297d88ad43383afe525b1543b165", [:mix], [{:deep_merge, "~> 0.1", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "d8b3f1720073413c36a21e56a1d1112a4d67a9ad0ec900437efed08b39e515b2"}, + "deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm", "e3bf435a54ed27b0ba3a01eb117ae017988804e136edcbe8a6a14c310daa966e"}, + "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"}, + "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm", "b42a23e9bd92d65d16db2f75553982e58519054095356a418bb8320bbacb58b1"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"}, + "ex_doc": {:hex, :ex_doc, "0.24.0", "2df14354835afaabdf87cb2971ea9485d8a36ff590e4b6c250b4f60c8fdf9143", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "a0f4bcff21ceebea48414e49885d2a3e542200f76a2facf3f8faa54935eeb721"}, + "gen_state_machine": {:hex, :gen_state_machine, "2.0.3", "477ea51b466a749ab23a0d6090e9e84073f41f9aa28c7efc40eac18f3d4a9f77", [:mix], [], "hexpm", "0418b2c2b2da3118349fa344497c37e58c46dd8ef865c1ea86e9c1831471fc28"}, + "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm", "1feaf05ee886815ad047cad7ede17d6910710986148ae09cf73eee2989717b81"}, + "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []}, - "porcelain": {:hex, :porcelain, "2.0.3", "2d77b17d1f21fed875b8c5ecba72a01533db2013bd2e5e62c6d286c029150fdc", [:mix], []}, + "porcelain": {:hex, :porcelain, "2.0.3", "2d77b17d1f21fed875b8c5ecba72a01533db2013bd2e5e62c6d286c029150fdc", [:mix], [], "hexpm", "dc996ab8fadbc09912c787c7ab8673065e50ea1a6245177b0c24569013d23620"}, }