init: and so there was the tree
This commit is contained in:
commit
d0c1fa3719
23 changed files with 519 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
||||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
### Elixir ###
|
||||||
|
/_build
|
||||||
|
/cover
|
||||||
|
/deps
|
||||||
|
/doc
|
||||||
|
/.fetch
|
||||||
|
erl_crash.dump
|
||||||
|
*.ez
|
||||||
|
*.beam
|
||||||
|
/config/*.secret.exs
|
||||||
|
.elixir_ls/
|
||||||
|
.expert/
|
||||||
|
### End of Elixir ###
|
||||||
|
|
||||||
|
### Nix ###
|
||||||
|
# Ignore build outputs from performing a nix-build or `nix build` command
|
||||||
|
result
|
||||||
|
result-*
|
||||||
|
|
||||||
|
# Ignore automatically generated direnv output
|
||||||
|
.direnv
|
||||||
|
### End of Nix ###
|
||||||
|
|
||||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Sunbeam
|
||||||
|
|
||||||
|
An IRCd in Elixir, made as an experiment by `teesh3rt`.
|
||||||
77
flake.lock
generated
Normal file
77
flake.lock
generated
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769996383,
|
||||||
|
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"import-tree": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771045967,
|
||||||
|
"narHash": "sha256-oYO4poyw0Sb/db2PigqugMlDwsvwLg6CSpFrMUWxA3Q=",
|
||||||
|
"owner": "vic",
|
||||||
|
"repo": "import-tree",
|
||||||
|
"rev": "c968d3b54d12cf5d9c13f16f7c545a06c9d1fde6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "vic",
|
||||||
|
"repo": "import-tree",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771008912,
|
||||||
|
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769909678,
|
||||||
|
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "72716169fe93074c333e8d0173151350670b824c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"import-tree": "import-tree",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
13
flake.nix
Normal file
13
flake.nix
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
description = "A very basic flake";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
import-tree.url = "github:vic/import-tree";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = inputs:
|
||||||
|
inputs.flake-parts.lib.mkFlake {inherit inputs;}
|
||||||
|
(inputs.import-tree ./packages/nix);
|
||||||
|
}
|
||||||
17
lib/sunbeam/acceptor_supervisor.ex
Normal file
17
lib/sunbeam/acceptor_supervisor.ex
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
defmodule Sunbeam.AcceptorSupervisor do
|
||||||
|
use Supervisor
|
||||||
|
|
||||||
|
def start_link(socket) do
|
||||||
|
Supervisor.start_link(__MODULE__, socket, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def init(socket) do
|
||||||
|
children =
|
||||||
|
for i <- 1..20 do
|
||||||
|
Supervisor.child_spec({Sunbeam.TcpAcceptor, socket}, id: {Sunbeam.TcpAcceptor, i})
|
||||||
|
end
|
||||||
|
|
||||||
|
options = [strategy: :one_for_all, name: __MODULE__]
|
||||||
|
Supervisor.init(children, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
25
lib/sunbeam/application.ex
Normal file
25
lib/sunbeam/application.ex
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
defmodule Sunbeam.Application do
|
||||||
|
use Application
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def start(_type, _args) do
|
||||||
|
children = [
|
||||||
|
# Channels
|
||||||
|
{Registry, keys: :unique, name: Sunbeam.ChannelRegistry},
|
||||||
|
Sunbeam.ChannelSupervisor,
|
||||||
|
|
||||||
|
# Users
|
||||||
|
{Registry, keys: :unique, name: Sunbeam.UserRegistry},
|
||||||
|
Sunbeam.UserSupervisor,
|
||||||
|
|
||||||
|
# Sockets
|
||||||
|
Sunbeam.SocketSupervisor,
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
Sunbeam.MonitoringWorker
|
||||||
|
]
|
||||||
|
|
||||||
|
opts = [strategy: :one_for_one, name: Sunbeam.RootSupervisor]
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
14
lib/sunbeam/channel.ex
Normal file
14
lib/sunbeam/channel.ex
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule Sunbeam.Channel do
|
||||||
|
use GenServer, restart: :transient
|
||||||
|
|
||||||
|
def start_link(name) do
|
||||||
|
GenServer.start_link(__MODULE__, name,
|
||||||
|
name: {:via, Registry, {Sunbeam.ChannelRegistry, name}}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_args) do
|
||||||
|
{:ok, %{users: MapSet.new()}}
|
||||||
|
end
|
||||||
|
end
|
||||||
21
lib/sunbeam/channel_supervisor.ex
Normal file
21
lib/sunbeam/channel_supervisor.ex
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
defmodule Sunbeam.ChannelSupervisor do
|
||||||
|
use DynamicSupervisor
|
||||||
|
|
||||||
|
# NOTE: Also creates a new channel if it dosent exist
|
||||||
|
def get(name) do
|
||||||
|
case DynamicSupervisor.start_child(__MODULE__, {Sunbeam.Channel, name}) do
|
||||||
|
{:ok, pid} -> {:ok, pid}
|
||||||
|
{:error, {:already_started, pid}} -> {:ok, pid}
|
||||||
|
error -> error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_link(init) do
|
||||||
|
DynamicSupervisor.start_link(__MODULE__, init, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_init) do
|
||||||
|
DynamicSupervisor.init(strategy: :one_for_one)
|
||||||
|
end
|
||||||
|
end
|
||||||
32
lib/sunbeam/connection.ex
Normal file
32
lib/sunbeam/connection.ex
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule Sunbeam.Connection do
|
||||||
|
use GenServer, restart: :temporary
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def start_link(socket) do
|
||||||
|
GenServer.start_link(__MODULE__, socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(socket) do
|
||||||
|
{:ok, user} = Sunbeam.UserSupervisor.create_user(self())
|
||||||
|
Process.link(user)
|
||||||
|
{:ok, %{user: user, socket: socket}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:tcp, socket, data}, state) do
|
||||||
|
:inet.setopts(socket, active: :once)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:tcp_closed, socket}, state) do
|
||||||
|
{:stop, :normal, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:tcp_error, socket, reason}, state) do
|
||||||
|
{:stop, reason, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
16
lib/sunbeam/connection_supervisor.ex
Normal file
16
lib/sunbeam/connection_supervisor.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule Sunbeam.ConnectionSupervisor do
|
||||||
|
use DynamicSupervisor
|
||||||
|
|
||||||
|
def start_link(init) do
|
||||||
|
DynamicSupervisor.start_link(__MODULE__, init, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_conn_from(client_socket) do
|
||||||
|
DynamicSupervisor.start_child(__MODULE__, {Sunbeam.Connection, client_socket})
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_arg) do
|
||||||
|
DynamicSupervisor.init(strategy: :one_for_one)
|
||||||
|
end
|
||||||
|
end
|
||||||
86
lib/sunbeam/debug/supervision_tree.ex
Normal file
86
lib/sunbeam/debug/supervision_tree.ex
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
defmodule Sunbeam.Debug.SupervisionTree do
|
||||||
|
@moduledoc """
|
||||||
|
Prints a supervision tree like the `tree` command with colors.
|
||||||
|
Depth-based coloring: fire-red at root, fading to ash-gray at deepest levels.
|
||||||
|
DynamicSupervisor children registered in a Registry show their name.
|
||||||
|
Limits children per node to avoid crashing the logger.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@colors [
|
||||||
|
# fire red
|
||||||
|
"\e[38;5;196m",
|
||||||
|
# orange
|
||||||
|
"\e[38;5;202m",
|
||||||
|
# yellow
|
||||||
|
"\e[38;5;226m",
|
||||||
|
# light yellow
|
||||||
|
"\e[38;5;190m",
|
||||||
|
# light green
|
||||||
|
"\e[38;5;145m",
|
||||||
|
# cyan
|
||||||
|
"\e[38;5;111m",
|
||||||
|
# ash gray
|
||||||
|
"\e[38;5;250m"
|
||||||
|
]
|
||||||
|
|
||||||
|
@reset "\e[0m"
|
||||||
|
@max_children 20
|
||||||
|
|
||||||
|
# Public entry point
|
||||||
|
def print_tree(supervisor), do: print_tree(supervisor, "", 0)
|
||||||
|
|
||||||
|
# Internal recursive function with depth
|
||||||
|
defp print_tree(supervisor, prefix, depth) do
|
||||||
|
children = get_children(supervisor)
|
||||||
|
color = color_for_depth(depth)
|
||||||
|
|
||||||
|
IO.puts("#{prefix}#{color}#{inspect(supervisor)}#{@reset}")
|
||||||
|
|
||||||
|
Enum.take(children, @max_children)
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.each(fn {{id, child_pid, type, _modules}, index} ->
|
||||||
|
last = index == min(length(children), @max_children) - 1
|
||||||
|
branch = if last, do: "└─ ", else: "├─ "
|
||||||
|
sub_prefix = if last, do: prefix <> " ", else: prefix <> "│ "
|
||||||
|
|
||||||
|
# Lookup registry name for dynamic workers
|
||||||
|
registry_name = get_registry_name(child_pid)
|
||||||
|
|
||||||
|
display_id =
|
||||||
|
cond do
|
||||||
|
id != :undefined -> inspect(id)
|
||||||
|
type == :worker and registry_name != nil -> inspect(registry_name)
|
||||||
|
true -> inspect(child_pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
node_color = color_for_depth(depth + 1)
|
||||||
|
IO.puts("#{prefix}#{branch}#{node_color}#{display_id} (#{type})#{@reset}")
|
||||||
|
|
||||||
|
if type == :supervisor do
|
||||||
|
print_tree(child_pid, sub_prefix, depth + 1)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if length(children) > @max_children do
|
||||||
|
IO.puts("#{prefix}│ ... (#{length(children) - @max_children} more children)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_children(pid) do
|
||||||
|
case :supervisor.which_children(pid) do
|
||||||
|
children when is_list(children) -> children
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_registry_name(pid) when is_pid(pid) do
|
||||||
|
case Registry.keys(Sunbeam.ChannelRegistry, pid) do
|
||||||
|
[name] -> name
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp color_for_depth(depth) do
|
||||||
|
Enum.at(@colors, min(depth, length(@colors) - 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
5
lib/sunbeam/irc/codes.ex
Normal file
5
lib/sunbeam/irc/codes.ex
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
defmodule Sunbeam.Irc.Codes do
|
||||||
|
@rpl_welcome "001"
|
||||||
|
|
||||||
|
def rpl_welcome(), do: @rpl_welcome
|
||||||
|
end
|
||||||
22
lib/sunbeam/monitoring_worker.ex
Normal file
22
lib/sunbeam/monitoring_worker.ex
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
defmodule Sunbeam.MonitoringWorker do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
def start_link(arg) do
|
||||||
|
GenServer.start_link(__MODULE__, arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_arg) do
|
||||||
|
send(self(), :monitor)
|
||||||
|
{:ok, nil}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info(:monitor, state) do
|
||||||
|
IO.write("\e[H\e[2J")
|
||||||
|
Sunbeam.Debug.SupervisionTree.print_tree(Sunbeam.RootSupervisor)
|
||||||
|
Process.send_after(self(), :monitor, 1000 * 1)
|
||||||
|
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
26
lib/sunbeam/socket_supervisor.ex
Normal file
26
lib/sunbeam/socket_supervisor.ex
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
defmodule Sunbeam.SocketSupervisor do
|
||||||
|
use Supervisor
|
||||||
|
|
||||||
|
def start_link(init_arg) do
|
||||||
|
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_arg) do
|
||||||
|
{:ok, socket} =
|
||||||
|
:gen_tcp.listen(6667, [
|
||||||
|
:binary,
|
||||||
|
packet: 0,
|
||||||
|
active: false,
|
||||||
|
reuseaddr: true
|
||||||
|
])
|
||||||
|
|
||||||
|
children = [
|
||||||
|
Sunbeam.ConnectionSupervisor,
|
||||||
|
{Sunbeam.AcceptorSupervisor, socket}
|
||||||
|
]
|
||||||
|
|
||||||
|
options = [strategy: :one_for_one]
|
||||||
|
Supervisor.init(children, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
35
lib/sunbeam/tcp_acceptor.ex
Normal file
35
lib/sunbeam/tcp_acceptor.ex
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
defmodule Sunbeam.TcpAcceptor do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def start_link(listen_socket) do
|
||||||
|
GenServer.start_link(__MODULE__, listen_socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(listen_socket) do
|
||||||
|
send(self(), :accept)
|
||||||
|
{:ok, listen_socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info(:accept, listen_socket) do
|
||||||
|
case :gen_tcp.accept(listen_socket, :infinity) do
|
||||||
|
{:ok, client_socket} ->
|
||||||
|
{:ok, conn} = Sunbeam.ConnectionSupervisor.create_conn_from(client_socket)
|
||||||
|
:gen_tcp.controlling_process(client_socket, conn)
|
||||||
|
:inet.setopts(client_socket, active: :once)
|
||||||
|
send(self(), :accept)
|
||||||
|
{:noreply, listen_socket}
|
||||||
|
|
||||||
|
{:error, :closed} ->
|
||||||
|
{:stop, :normal, listen_socket}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
Logger.error("Accept failed: #{inspect(reason)}")
|
||||||
|
send(self(), :accept)
|
||||||
|
{:noreply, listen_socket}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
30
lib/sunbeam/user.ex
Normal file
30
lib/sunbeam/user.ex
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
defmodule Sunbeam.User do
|
||||||
|
use GenServer, restart: :transient
|
||||||
|
|
||||||
|
def start_link(connection) do
|
||||||
|
GenServer.start_link(__MODULE__, connection)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(connection) do
|
||||||
|
Process.monitor(connection)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
# NOTE: This is not a handle to a raw socket.
|
||||||
|
# What are we, Python???
|
||||||
|
connection: connection,
|
||||||
|
|
||||||
|
# NICK & USER, CAP, JOIN
|
||||||
|
# Can either be :registration, :negotiation or :ready
|
||||||
|
phase: :registration,
|
||||||
|
nick: nil,
|
||||||
|
user: nil
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:DOWN, _, _, _, _}, state) do
|
||||||
|
{:stop, :normal, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
16
lib/sunbeam/user_supervisor.ex
Normal file
16
lib/sunbeam/user_supervisor.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule Sunbeam.UserSupervisor do
|
||||||
|
use DynamicSupervisor
|
||||||
|
|
||||||
|
def start_link(init) do
|
||||||
|
DynamicSupervisor.start_link(__MODULE__, init, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_init) do
|
||||||
|
DynamicSupervisor.init(strategy: :one_for_one)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_user(conn) do
|
||||||
|
DynamicSupervisor.start_child(__MODULE__, {Sunbeam.User, conn})
|
||||||
|
end
|
||||||
|
end
|
||||||
30
mix.exs
Normal file
30
mix.exs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
defmodule Sunbeam.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :sunbeam,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.18",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:logger, :observer_cli],
|
||||||
|
mod: {Sunbeam.Application, []}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||||
|
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||||
|
{:observer_cli, "~> 1.7"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
4
mix.lock
Normal file
4
mix.lock
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
%{
|
||||||
|
"observer_cli": {:hex, :observer_cli, "1.8.6", "65547365b56532dabb42c91de90af5b55894218a986ae47e1db47ecb8c979d8f", [:mix, :rebar3], [{:recon, "~> 2.5.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "ea743c6477bc4799f215f8ef7cd5eee1536dcdda311fe2bd204e88e7c1e6a11a"},
|
||||||
|
"recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"},
|
||||||
|
}
|
||||||
14
packages/nix/shell.nix
Normal file
14
packages/nix/shell.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{...}: {
|
||||||
|
perSystem = {pkgs, ...}: {
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
beamPackages.elixir
|
||||||
|
beamPackages.erlang
|
||||||
|
rebar3
|
||||||
|
hex
|
||||||
|
openssl
|
||||||
|
git
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
5
packages/nix/systems.nix
Normal file
5
packages/nix/systems.nix
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{...}: {
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
];
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue