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:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
|
|
||||||
|
- name: Install just
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y just
|
||||||
|
|
||||||
# Cache deps/ directory — keyed on mix.lock
|
# Cache deps/ directory — keyed on mix.lock
|
||||||
- name: Cache Mix deps
|
- name: Cache Mix deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@@ -115,7 +120,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Node Sync E2E tests
|
- name: Run Node Sync E2E tests
|
||||||
if: ${{ matrix.main }}
|
if: ${{ matrix.main }}
|
||||||
run: mix test.node_sync_e2e
|
run: just e2e node-sync
|
||||||
|
|
||||||
- name: Run Marmot E2E tests
|
- 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:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
|
|
||||||
|
- name: Install just
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y just
|
||||||
|
|
||||||
- name: Cache Mix deps
|
- name: Cache Mix deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
id: deps-cache
|
id: deps-cache
|
||||||
@@ -113,10 +118,10 @@ jobs:
|
|||||||
run: mix test --color
|
run: mix test --color
|
||||||
|
|
||||||
- name: Run Node Sync E2E
|
- name: Run Node Sync E2E
|
||||||
run: mix test.node_sync_e2e
|
run: just e2e node-sync
|
||||||
|
|
||||||
- name: Run Marmot E2E
|
- name: Run Marmot E2E
|
||||||
run: mix test.marmot_e2e
|
run: just e2e marmot
|
||||||
|
|
||||||
- name: Check for unused locked deps
|
- name: Check for unused locked deps
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -4,12 +4,14 @@
|
|||||||
|
|
||||||
Parrhesia is a Nostr relay server written in Elixir/OTP.
|
Parrhesia is a Nostr relay server written in Elixir/OTP.
|
||||||
|
|
||||||
|
**BETA CONDITION – BREAKING CHANGES MAY STILL HAPPEN!**
|
||||||
|
|
||||||
Supported storage backends:
|
Supported storage backends:
|
||||||
|
|
||||||
- PostgreSQL, which is the primary and production-oriented backend
|
- PostgreSQL, which is the primary and production-oriented backend
|
||||||
- in-memory storage, which is useful for tests, local experiments, and benchmarks
|
- 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).
|
- 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.
|
- 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 shell := ["bash", "-euo", "pipefail", "-c"]
|
||||||
set script-interpreter := ["bash", "-euo", "pipefail"]
|
|
||||||
|
|
||||||
repo_root := justfile_directory()
|
repo_root := justfile_directory()
|
||||||
|
|
||||||
@@ -12,8 +11,9 @@ help topic="":
|
|||||||
@cd "{{repo_root}}" && ./scripts/just_help.sh "{{topic}}"
|
@cd "{{repo_root}}" && ./scripts/just_help.sh "{{topic}}"
|
||||||
|
|
||||||
# Raw e2e harness commands.
|
# Raw e2e harness commands.
|
||||||
[script]
|
e2e subcommand *args:
|
||||||
e2e subcommand="help" *args:
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
cd "{{repo_root}}"
|
cd "{{repo_root}}"
|
||||||
subcommand="{{subcommand}}"
|
subcommand="{{subcommand}}"
|
||||||
|
|
||||||
@@ -40,8 +40,9 @@ e2e subcommand="help" *args:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Benchmark flows (local/cloud/history + direct relay targets).
|
# Benchmark flows (local/cloud/history + direct relay targets).
|
||||||
[script]
|
bench subcommand *args:
|
||||||
bench subcommand="help" *args:
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
cd "{{repo_root}}"
|
cd "{{repo_root}}"
|
||||||
subcommand="{{subcommand}}"
|
subcommand="{{subcommand}}"
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,17 @@ defmodule Parrhesia.Storage.Adapters.Memory.Store do
|
|||||||
audit_logs: []
|
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
|
def put_event(event_id, event) when is_binary(event_id) and is_map(event) do
|
||||||
:ok = ensure_started()
|
:ok = ensure_started()
|
||||||
@@ -159,28 +169,72 @@ defmodule Parrhesia.Storage.Adapters.Memory.Store do
|
|||||||
defp normalize_reduce_result(next_acc), do: next_acc
|
defp normalize_reduce_result(next_acc), do: next_acc
|
||||||
|
|
||||||
def get(fun) do
|
def get(fun) do
|
||||||
:ok = ensure_started()
|
with_store(fn pid -> Agent.get(pid, fun) end)
|
||||||
Agent.get(@name, fun)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(fun) do
|
def update(fun) do
|
||||||
:ok = ensure_started()
|
with_store(fn pid -> Agent.update(pid, fun) end)
|
||||||
Agent.update(@name, fun)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_and_update(fun) do
|
def get_and_update(fun) do
|
||||||
:ok = ensure_started()
|
with_store(fn pid -> Agent.get_and_update(pid, fun) end)
|
||||||
Agent.get_and_update(@name, fun)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp start_store do
|
defp start_store do
|
||||||
case Agent.start_link(&init_state/0, name: @name) do
|
case start_link() do
|
||||||
{:ok, _pid} -> :ok
|
{:ok, _pid} ->
|
||||||
{:error, {:already_started, _pid}} -> :ok
|
:ok
|
||||||
{:error, reason} -> {:error, reason}
|
|
||||||
|
{:error, {:already_started, pid}} ->
|
||||||
|
if Process.alive?(pid) do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
wait_for_store_exit(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:error, reason}
|
||||||
end
|
end
|
||||||
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
|
defp init_state do
|
||||||
ensure_tables_started()
|
ensure_tables_started()
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,19 @@ defmodule Parrhesia.Storage.Supervisor do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(_init_arg) do
|
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)
|
Supervisor.init(children, strategy: :one_for_one)
|
||||||
end
|
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
|
defp moderation_cache_children do
|
||||||
if PostgresRepos.postgres_enabled?() and
|
if PostgresRepos.postgres_enabled?() and
|
||||||
Application.get_env(:parrhesia, :moderation_cache_enabled, true) do
|
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.Events
|
||||||
alias Parrhesia.Storage.Adapters.Memory.Groups
|
alias Parrhesia.Storage.Adapters.Memory.Groups
|
||||||
alias Parrhesia.Storage.Adapters.Memory.Moderation
|
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
|
test "memory adapter supports basic behavior contract operations" do
|
||||||
event_id = String.duplicate("a", 64)
|
event_id = String.duplicate("a", 64)
|
||||||
|
|||||||
Reference in New Issue
Block a user