离线构建十六进制注册表文件
building the hex registry file offline
使 Elixir 及其生态系统中的所有工具适应不同的构建系统。
在这个系统中,包和它们的依赖是分开管理的,Hex 是在离线模式下工作的。 (抓住压缩包)
它有一个警告:每次导入新包时,我还需要从 hexpm 导入最新的注册表文件,并且我不能使用未通过 hex 发布的包,除非它们位于 deps 的顶层链.
给定一堆 tarball(并假设它们之间的依赖关系得到满足,如何构建一个与它们一起工作的十六进制注册表文件。
我目前拥有的:
- 查看注册表文件格式,发现它是一个 ets 文件。可以加载和检查它;现在我需要生成
- 查看了该网站如何构建注册表文件,但它对我的需要来说超级复杂
- 我有点难以理解为什么需要注册表文件(如果有,为什么每个包都不能在元数据中包含所需的信息,从而不再需要中央注册表)
无论如何,如果有人玩过 Hex 并且可以提供一些关于如何做到这一点的指导,我将不胜感激。
如果没有关于您的用例的更多信息,很难提供好的信息和建议。您能否详细说明您在做什么以及为什么要这样做?不过我会尽力回答这个问题。
注册表格式规范如下:https://github.com/hexpm/specifications/blob/master/registry.md.
格式相当简单,自己构建 ETS 文件不需要太多代码。
I struggle a bit to understand why there is a need for a registry file (and if there is, why can't each package contain the needed info in the metadata, making the need for a central registry obsolete)
Hex 客户端中的依赖项解析需要注册表。解析器可能会尝试许多不同版本的包,如果客户端必须获取每个包版本以查看它是否已解析,则必须发出大量无用的 HTTP 请求。注册表是一种优化,因此我们只需获取一个文件即可完成完整解析。
我认为您可能想要直接依赖于本地包 tarball,因为您暗示您自己进行依赖性解析。那是对的吗?我已经在客户端上打开了一个问题来支持这个: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 table。参见:http://erlang.org/doc/man/ets.html
- 此处概述了注册表格式:
https://github.com/hexpm/specifications/blob/master/registry.md
- 本模块中的 private 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
使 Elixir 及其生态系统中的所有工具适应不同的构建系统。
在这个系统中,包和它们的依赖是分开管理的,Hex 是在离线模式下工作的。 (抓住压缩包)
它有一个警告:每次导入新包时,我还需要从 hexpm 导入最新的注册表文件,并且我不能使用未通过 hex 发布的包,除非它们位于 deps 的顶层链.
给定一堆 tarball(并假设它们之间的依赖关系得到满足,如何构建一个与它们一起工作的十六进制注册表文件。
我目前拥有的:
- 查看注册表文件格式,发现它是一个 ets 文件。可以加载和检查它;现在我需要生成
- 查看了该网站如何构建注册表文件,但它对我的需要来说超级复杂
- 我有点难以理解为什么需要注册表文件(如果有,为什么每个包都不能在元数据中包含所需的信息,从而不再需要中央注册表)
无论如何,如果有人玩过 Hex 并且可以提供一些关于如何做到这一点的指导,我将不胜感激。
如果没有关于您的用例的更多信息,很难提供好的信息和建议。您能否详细说明您在做什么以及为什么要这样做?不过我会尽力回答这个问题。
注册表格式规范如下:https://github.com/hexpm/specifications/blob/master/registry.md.
格式相当简单,自己构建 ETS 文件不需要太多代码。
I struggle a bit to understand why there is a need for a registry file (and if there is, why can't each package contain the needed info in the metadata, making the need for a central registry obsolete)
Hex 客户端中的依赖项解析需要注册表。解析器可能会尝试许多不同版本的包,如果客户端必须获取每个包版本以查看它是否已解析,则必须发出大量无用的 HTTP 请求。注册表是一种优化,因此我们只需获取一个文件即可完成完整解析。
我认为您可能想要直接依赖于本地包 tarball,因为您暗示您自己进行依赖性解析。那是对的吗?我已经在客户端上打开了一个问题来支持这个: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 table。参见:http://erlang.org/doc/man/ets.html
- 此处概述了注册表格式: https://github.com/hexpm/specifications/blob/master/registry.md
- 本模块中的 private 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