Merge remote-tracking branch 'public/master' (GH actions, test stability)
This commit is contained in:
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
@@ -69,6 +69,11 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Install just
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y just
|
||||
|
||||
# Cache deps/ directory — keyed on mix.lock
|
||||
- name: Cache Mix deps
|
||||
uses: actions/cache@v4
|
||||
@@ -115,7 +120,8 @@ jobs:
|
||||
|
||||
- name: Run Node Sync E2E tests
|
||||
if: ${{ matrix.main }}
|
||||
run: mix test.node_sync_e2e
|
||||
run: just e2e node-sync
|
||||
|
||||
- name: Run Marmot E2E tests
|
||||
run: mix test.marmot_e2e
|
||||
if: ${{ matrix.main }}
|
||||
run: just e2e marmot
|
||||
|
||||
9
.github/workflows/release.yaml
vendored
9
.github/workflows/release.yaml
vendored
@@ -68,6 +68,11 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Install just
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y just
|
||||
|
||||
- name: Cache Mix deps
|
||||
uses: actions/cache@v4
|
||||
id: deps-cache
|
||||
@@ -113,10 +118,10 @@ jobs:
|
||||
run: mix test --color
|
||||
|
||||
- name: Run Node Sync E2E
|
||||
run: mix test.node_sync_e2e
|
||||
run: just e2e node-sync
|
||||
|
||||
- name: Run Marmot E2E
|
||||
run: mix test.marmot_e2e
|
||||
run: just e2e marmot
|
||||
|
||||
- name: Check for unused locked deps
|
||||
run: |
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
Parrhesia is a Nostr relay server written in Elixir/OTP.
|
||||
|
||||
**BETA CONDITION – BREAKING CHANGES MAY STILL HAPPEN!**
|
||||
|
||||
Supported storage backends:
|
||||
|
||||
- PostgreSQL, which is the primary and production-oriented backend
|
||||
- in-memory storage, which is useful for tests, local experiments, and benchmarks
|
||||
|
||||
**BETA CONDITION – BREAKING CHANGES MAY STILL HAPPEN!**
|
||||
Advanced Nostr features:
|
||||
|
||||
- Advanced Querying: Full-text search (NIP-50) and COUNT queries (NIP-45).
|
||||
- Secure Messaging: First-class support for Marmot MLS-encrypted groups and NIP-17/44/59 gift-wrapped DMs.
|
||||
|
||||
11
justfile
11
justfile
@@ -1,5 +1,4 @@
|
||||
set shell := ["bash", "-euo", "pipefail", "-c"]
|
||||
set script-interpreter := ["bash", "-euo", "pipefail"]
|
||||
|
||||
repo_root := justfile_directory()
|
||||
|
||||
@@ -12,8 +11,9 @@ help topic="":
|
||||
@cd "{{repo_root}}" && ./scripts/just_help.sh "{{topic}}"
|
||||
|
||||
# Raw e2e harness commands.
|
||||
[script]
|
||||
e2e subcommand="help" *args:
|
||||
e2e subcommand *args:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "{{repo_root}}"
|
||||
subcommand="{{subcommand}}"
|
||||
|
||||
@@ -40,8 +40,9 @@ e2e subcommand="help" *args:
|
||||
fi
|
||||
|
||||
# Benchmark flows (local/cloud/history + direct relay targets).
|
||||
[script]
|
||||
bench subcommand="help" *args:
|
||||
bench subcommand *args:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "{{repo_root}}"
|
||||
subcommand="{{subcommand}}"
|
||||
|
||||
|
||||
@@ -22,7 +22,17 @@ defmodule Parrhesia.Storage.Adapters.Memory.Store do
|
||||
audit_logs: []
|
||||
}
|
||||
|
||||
def ensure_started, do: start_store()
|
||||
def start_link(opts \\ []) do
|
||||
name = Keyword.get(opts, :name, @name)
|
||||
Agent.start_link(&init_state/0, name: name)
|
||||
end
|
||||
|
||||
def ensure_started do
|
||||
case Process.whereis(@name) do
|
||||
pid when is_pid(pid) -> :ok
|
||||
nil -> start_store()
|
||||
end
|
||||
end
|
||||
|
||||
def put_event(event_id, event) when is_binary(event_id) and is_map(event) do
|
||||
:ok = ensure_started()
|
||||
@@ -159,28 +169,72 @@ defmodule Parrhesia.Storage.Adapters.Memory.Store do
|
||||
defp normalize_reduce_result(next_acc), do: next_acc
|
||||
|
||||
def get(fun) do
|
||||
:ok = ensure_started()
|
||||
Agent.get(@name, fun)
|
||||
with_store(fn pid -> Agent.get(pid, fun) end)
|
||||
end
|
||||
|
||||
def update(fun) do
|
||||
:ok = ensure_started()
|
||||
Agent.update(@name, fun)
|
||||
with_store(fn pid -> Agent.update(pid, fun) end)
|
||||
end
|
||||
|
||||
def get_and_update(fun) do
|
||||
:ok = ensure_started()
|
||||
Agent.get_and_update(@name, fun)
|
||||
with_store(fn pid -> Agent.get_and_update(pid, fun) end)
|
||||
end
|
||||
|
||||
defp start_store do
|
||||
case Agent.start_link(&init_state/0, name: @name) do
|
||||
{:ok, _pid} -> :ok
|
||||
{:error, {:already_started, _pid}} -> :ok
|
||||
{:error, reason} -> {:error, reason}
|
||||
case start_link() do
|
||||
{:ok, _pid} ->
|
||||
:ok
|
||||
|
||||
{:error, {:already_started, pid}} ->
|
||||
if Process.alive?(pid) do
|
||||
:ok
|
||||
else
|
||||
wait_for_store_exit(pid)
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp with_store(fun, attempts \\ 2)
|
||||
|
||||
defp with_store(fun, attempts) when attempts > 0 do
|
||||
:ok = ensure_started()
|
||||
|
||||
case Process.whereis(@name) do
|
||||
pid when is_pid(pid) ->
|
||||
try do
|
||||
fun.(pid)
|
||||
catch
|
||||
:exit, reason ->
|
||||
if noproc_exit?(reason) and attempts > 1 do
|
||||
with_store(fun, attempts - 1)
|
||||
else
|
||||
exit(reason)
|
||||
end
|
||||
end
|
||||
|
||||
nil ->
|
||||
with_store(fun, attempts - 1)
|
||||
end
|
||||
end
|
||||
|
||||
defp with_store(_fun, 0), do: exit(:noproc)
|
||||
|
||||
defp wait_for_store_exit(pid) do
|
||||
ref = Process.monitor(pid)
|
||||
|
||||
receive do
|
||||
{:DOWN, ^ref, :process, ^pid, _reason} -> start_store()
|
||||
after
|
||||
100 -> start_store()
|
||||
end
|
||||
end
|
||||
|
||||
defp noproc_exit?({:noproc, _details}), do: true
|
||||
defp noproc_exit?(_reason), do: false
|
||||
|
||||
defp init_state do
|
||||
ensure_tables_started()
|
||||
|
||||
|
||||
@@ -13,11 +13,19 @@ defmodule Parrhesia.Storage.Supervisor do
|
||||
|
||||
@impl true
|
||||
def init(_init_arg) do
|
||||
children = moderation_cache_children() ++ PostgresRepos.started_repos()
|
||||
children =
|
||||
memory_store_children() ++ moderation_cache_children() ++ PostgresRepos.started_repos()
|
||||
|
||||
Supervisor.init(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
defp memory_store_children do
|
||||
case Application.get_env(:parrhesia, :storage, [])[:backend] do
|
||||
:memory -> [Parrhesia.Storage.Adapters.Memory.Store]
|
||||
_other -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp moderation_cache_children do
|
||||
if PostgresRepos.postgres_enabled?() and
|
||||
Application.get_env(:parrhesia, :moderation_cache_enabled, true) do
|
||||
|
||||
@@ -6,6 +6,12 @@ defmodule Parrhesia.Storage.Adapters.Memory.AdapterTest do
|
||||
alias Parrhesia.Storage.Adapters.Memory.Events
|
||||
alias Parrhesia.Storage.Adapters.Memory.Groups
|
||||
alias Parrhesia.Storage.Adapters.Memory.Moderation
|
||||
alias Parrhesia.Storage.Adapters.Memory.Store
|
||||
|
||||
setup do
|
||||
start_supervised!(Store)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "memory adapter supports basic behavior contract operations" do
|
||||
event_id = String.duplicate("a", 64)
|
||||
|
||||
Reference in New Issue
Block a user