Packages
hex_core
0.20.1
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.0
0.14.1
0.14.0
0.13.0
0.12.2
0.12.1
retired
0.12.0
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
retired
0.2.1
0.2.0
0.1.1
0.1.0
Reference implementation of Hex specifications
Current section
Files
Jump to
Current section
Files
README.md
# hex_core
[](https://github.com/hexpm/hex_core/actions/workflows/main.yml)
Reference implementation of Hex specifications: <https://github.com/hexpm/specifications>.
## Usage
Let's use default config for now. See "Configuration" section below for customization.
```erlang
Config = hex_core:default_config().
```
The built-in httpc-based HTTP client adapter verifies HTTPS server certificates against
the operating system's CA certificates from `public_key:cacerts_get/0`. HTTPS requests fail
on OTP 24, and when those CA certificates can't be loaded, unless you pass `ssl` options with
`{hex_http_httpc, #{http_options => [{ssl, SslOpts}]}}` or use another HTTP adapter.
See "Configuration" and "Wrapper Module" sections for more information on customising your usage of `hex_core`.
### Repository
Get all package names:
```erlang
> hex_repo:get_names(Config).
{ok,{200, ...,
#{packages => [
#{name => <<"package1">>},
#{name => <<"package2">>},
...]}}}
```
Get all package versions from repository:
```erlang
> hex_repo:get_versions(Config).
{ok,{200, ...,
#{packages => [[
#{name => <<"package1">>, retired => [], versions => [<<"1.0.0">>]},
#{name => <<"package2">>, retired => [], versions => [<<"0.5.0">>]},
...]}}}
```
Get package releases from repository:
```erlang
> hex_repo:get_package(Config, <<"package1">>).
{ok,{200, ...,
#{releases => [
#{checksum => ..., version => <<"0.5.0">>, dependencies => []}],
#{checksum => ..., version => <<"1.0.0">>, dependencies => []}],
...]}}}
```
### API
For a full list of all parameters and returned objects for the API, check out the API docs: <https://github.com/hexpm/specifications/blob/master/http_api.md>.
Get package from HTTP API:
```erlang
> hex_api_package:get(Config, <<"package1">>).
{ok, {200, ...,
#{
<<"name">> => <<"package1">>,
<<"meta">> => #{
<<"description">> => ...,
<<"licenses">> => ...,
<<"links">> => ...,
<<"maintainers">> => ...,
},
...,
<<"releases">> => [
#{<<"url">> => ..., <<"version">> => <<"0.5.0">>}],
#{<<"url">> => ..., <<"version">> => <<"1.0.0">>}],
...
]
}}}
```
Get package tarball:
```erlang
{ok, {200, _, Tarball}} = hex_repo:get_tarball(Config, <<"package1">>, <<"1.0.0">>).
```
Publish package tarball:
```erlang
{ok, {200, _Headers, _Body} = hex_api_package:publish(Config, Tarball).
```
### Two-Factor Authentication
When using OAuth tokens, two-factor authentication may be required. If required, the server will return `{error, otp_required}` and you should retry the request with the TOTP code via the `api_otp` configuration option. If the TOTP code is incorrect, the server will return `{error, invalid_totp}` and you should retry with the correct TOTP code.
```erlang
%% First attempt without OTP
case hex_api_release:publish(Config, Tarball) of
{error, otp_required} ->
%% Retry with TOTP code
ConfigWithOTP = Config#{api_otp := <<"123456">>},
hex_api_release:publish(ConfigWithOTP, Tarball);
{error, invalid_totp} ->
%% Retry with correct TOTP code
ConfigWithOTP = Config#{api_otp := <<"123456">>},
hex_api_release:publish(ConfigWithOTP, Tarball);
Result ->
Result
end.
```
API keys don't require TOTP validation.
### Package tarballs
Unpack package tarball:
```erlang
{ok, #{outer_checksum := Checksum, contents := Contents, metadata := Metadata}} = hex_tarball:unpack(Tarball, memory).
```
Remember to verify the outer tarball checksum against the registry checksum
returned from `hex_repo:get_package(Config, Package)`.
Create package tarball:
```erlang
{ok, #{tarball := Tarball,
inner_checksum := InnerChecksum,
outer_checksum := OuterChecksum}} = hex_tarball:create(Metadata, Contents).
```
## Configuration
The default configuration, provided by `hex_core:default_config/0`, uses built-in httpc-based adapter and Hex.pm APIs:
<https://hex.pm/api> and <https://repo.hex.pm>.
HTTP client configuration can be overridden as follows:
```erlang
Config = maps:merge(hex_core:default_config(), #{
http_adapter => {my_hackney_adapter, my_hackney_adapter_config},
http_user_agent_fragment => <<"(my_app/0.1.0) (hackney/1.12.1) ">>
}),
hex_repo:get_names(Config).
%% my_hackney_adapter.erl
-module(my_hackney_adapter).
-behaviour(hex_http).
-exports([request/5]).
request(Method, URI, ReqHeaders, Body, AdapterConfig) ->
%% ...
```
See the [`hex_core` module](src/hex_core.erl) for more information about the configuration.
## Wrapper Module
It's recommended to write a wrapper module because a lot of decisions are left to the user, e.g.:
where to get configuration from, how to handle caching, failures etc.
For a sample, see: [`examples/myapp_hex.erl`](examples/myapp_hex.erl). Here's an excerpt:
```erlang
-module(myapp_hex).
-export([
get_api_package/1,
get_repo_tarball/2,
get_repo_versions/0
]).
%%====================================================================
%% API functions
%%====================================================================
get_api_package(Name) ->
case hex_api_package:get(config(), Name) of
{ok, {200, _Headers, Payload}} ->
{ok, Payload};
Other ->
Other
end.
get_repo_versions() ->
case hex_repo:get_versions(config()) of
{ok, {200, _Headers, Payload}} ->
{ok, maps:get(packages, Payload)};
Other ->
Other
end.
%%====================================================================
%% Internal functions
%%====================================================================
config() ->
Config1 = hex_core:default_config(),
Config2 = put_http_config(Config1),
Config3 = maybe_put_api_key(Config2),
Config3.
put_http_config(Config) ->
maps:put(http_user_agent_fragment, <<"(myapp/1.0.0) (httpc)">>, Config).
maybe_put_api_key(Config) ->
case os:getenv("HEX_API_KEY") of
false -> Config;
Key -> maps:put(api_key, Key, Config)
end.
```
## Installation
### Rebar3
Add to `rebar.config`:
```erlang
{deps, [
{hex_core, "0.20.1"}
]}
```
### Mix
Add to `mix.exs`:
```elixir
defp deps do
[
{:hex_core, "~> 0.20.1"}
]
end
```
## Development
* Run `rebar3 as dev compile` to re-generate protobuf files
* Run `rebar3 as test proper` for property-based tests
* Run `rebar3 ex_doc` to generate documentation