Построение шестнадцатеричного реестра
Адаптация Elixir и всех инструментов его экосистемы для работы с другой системой сборки.
В этой системе пакеты и их зависимости управляются отдельно, а Hex настроен для работы в автономном режиме. (возьмите тарболы)
Это работает с одним предупреждением: каждый раз, когда я импортирую новый пакет, мне также необходимо импортировать последний файл реестра из hexpm, и я не могу использовать пакеты, которые не публикуются через hex, если они не находятся на верхнем уровне в цепочке deps.
Учитывая кучу тарболлов (и предполагая, что зависимости между ними удовлетворены, как можно было бы построить файл шестнадцатеричного реестра, который работает с ними.
Что у меня так далеко:
- посмотрел формат файла реестра и увидел, что это файл ETS. Можно загрузить и проверить его; теперь мне нужно сформироваться
- посмотрел, как сайт строит файл реестра, но он очень сложный для моих нужд
- Я немного пытаюсь понять, почему существует необходимость в файле реестра (и если есть, то почему каждый пакет не может содержать необходимую информацию в метаданных, что делает необходимость в центральном реестре устаревшей)
Во всяком случае, если кто-то играл с Hex и может дать некоторые рекомендации о том, как это сделать, я был бы признателен.
2 ответа
Немного сложно дать хорошую информацию и советы без дополнительной информации о вашем случае использования. Не могли бы вы подробнее рассказать о том, что вы делаете и почему вы это делаете? Я сделаю все возможное, чтобы ответить на вопрос, хотя.
Вот спецификация для формата реестра: https://github.com/hexpm/specifications/blob/master/registry.md.
Формат довольно прост, и он не потребует слишком много кода для создания файла ETS самостоятельно.
Я немного пытаюсь понять, почему существует необходимость в файле реестра (и если есть, то почему каждый пакет не может содержать необходимую информацию в метаданных, что делает необходимость в центральном реестре устаревшей)
Реестр необходим для разрешения зависимостей в шестнадцатеричном клиенте. Средство распознавания может попробовать много разных версий пакетов, если клиенту придется извлекать каждую версию пакета, чтобы посмотреть, разрешит ли он много бесполезных HTTP-запросов. Реестр предназначен для оптимизации, поэтому нам нужно только выбрать один файл, чтобы выполнить полное разрешение.
Я думаю, что вы можете захотеть напрямую зависеть от локальных тарболлов пакетов, поскольку вы подразумеваете, что сами выполняете разрешение зависимостей. Это верно? Я открыл вопрос на клиенте для поддержки этого: https://github.com/hexpm/hex/issues/261
Для будущих поколений, которые оказались здесь, вот работающий конструктор реестра:
defp string_files(files) do
Enum.into(files, %{}, fn {name, binary} ->
{List.to_string(name), binary}
end)
end
defp decode(string) when is_binary(string) do
string = String.to_char_list(string)
case :safe_erl_term.string(string) do
{:ok, tokens, _line} ->
try do
terms = :safe_erl_term.terms(tokens)
result = Enum.into(terms, %{})
{:ok, result}
rescue
FunctionClauseError ->
{:error, "invalid terms"}
ArgumentError ->
{:error, "not in key-value format"}
end
{:error, reason} ->
{:error, inspect reason}
end
end
def build_registry(hex_home) do
# find the tars
tars = Path.wildcard(Path.join(hex_home,"packages/*.tar"))
# initialize the ets table used to build the registry
:ets.new(:myr, [:named_table])
:ets.insert(:myr, {:"$$version$$", 4})
# go through the tars, extract the info needed and populate
# the registry
Enum.each(tars, fn filename ->
{:ok, files} = :erl_tar.extract(String.to_char_list(filename), [:memory])
files = string_files(files)
{:ok, metadata} = decode(files["metadata.config"])
name = metadata["app"]
version = metadata["version"]
build_tools = metadata["build_tools"]
checksum = files["CHECKSUM"]
deps = []
if metadata["requirements"], do: deps = metadata["requirements"]
reg_deps = Enum.map(deps, fn
{name, depa} ->
depa = Enum.into(depa, %{})
[name, depa["requirement"], depa["optional"], depa["app"]]
depa ->
depa = Enum.into(depa, %{})
[depa["name"], depa["requirement"], depa["optional"], depa["app"]]
end)
IO.puts "adding dependency"
IO.inspect {name, [[version]]}
IO.inspect {{name, version}, [reg_deps, checksum, build_tools]}
:ets.insert(:myr, {name, [[version]]})
:ets.insert(:myr, {{name, version}, [reg_deps, checksum, build_tools]})
end)
# persist the registry to disk and remove the table
registry_file = Path.join(hex_home, "registry.ets")
IO.puts "Writing registry to: #{registry_file}"
:ets.tab2file(:myr, String.to_char_list(registry_file))
:ets.delete(:myr)
registry_file_gzip = registry_file <> ".gz"
IO.puts "Gzipping registry to: #{registry_file_gzip}"
gzipped_content = File.read!(registry_file) |> :zlib.gzip
File.write!(registry_file_gzip, gzipped_content)
end
Для большего контекста:
- реестр является таблицей ETS. Смотрите: http://erlang.org/doc/man/ets.html
- формат реестра описан здесь: https://github.com/hexpm/specifications/blob/master/registry.md
- частные fn в этом модуле вдохновлены: https://github.com/hexpm/hex_web/blob/master/scripts/rebuild_tarballs.exs
- safe_erl_term.xrl (должен быть в src/) заимствован из: https://raw.githubusercontent.com/hexpm/hex_web/master/src/safe_erl_term.xrl
- xrl является входом для leex. смотрите здесь (берет спецификацию, генерирует код erlang): http://erlang.org/doc/man/leex.html