Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ jobs:
mix local.rebar --force

- name: Fetch dependencies
# No ELIXIR_TORRENT_PATH on CI: mix.exs falls back to the published
# :elixir_torrent package, never a local sibling checkout.
# CI is set on GitHub runners, so mix.exs takes the published
# :elixir_torrent package pinned by mix.lock, never a local checkout.
run: mix deps.get

- name: Compile (dev)
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Dependency lock of a build against a local engine checkout (see mix.exs).
/mix.local.lock

# Temporary files, for example, from tests.
/tmp/

Expand Down
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 29.1.1
erlang 29.1.2
elixir 1.20.4-otp-29
6 changes: 1 addition & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ mix setup
mix phx.server
```

Requires Elixir 1.20+ on OTP 29 (see `.tool-versions` for the exact toolchain CI pins). By default the engine is pulled from Hex. To develop against a local engine checkout instead, point `ELIXIR_TORRENT_PATH` at it — but never commit the resulting `mix.lock` change:

```bash
ELIXIR_TORRENT_PATH=../ElixirTorrent mix deps.get
```
Requires Elixir 1.20+ on OTP 29 (see `.tool-versions` for the exact toolchain CI pins). With the engine checked out next to this repository as `../ElixirTorrent`, it is used automatically; `ELIXIR_TORRENT_PATH` points at a checkout elsewhere, and `ELIXIR_TORRENT_HEX=1` (or CI) takes the published Hex package. A local engine build keeps its own gitignored `mix.local.lock`, so `mix.lock` only ever pins the Hex release. See the README's "Engine dependency" section.

The native launchers are optional for most work and only build on their own platform: the Swift shell (`priv/macos/src/`) needs Xcode 27 with the Swift 6.4 toolchain, and the C# WinUI 3 shell (`priv/windows/Launcher/`) needs the .NET 10 SDK on Windows 11 x64.

Expand Down
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,25 +107,33 @@ handler registration.

## Engine dependency

The committed dependency remains the published Hex package. While both projects
evolve together, validate the WebUI against a local Engine checkout by setting:
`mix.exs` picks the engine source in this order:

1. `ELIXIR_TORRENT_PATH=<dir>`: that local Engine checkout.
2. `ELIXIR_TORRENT_HEX=1`, or any CI run (`CI` set): the published Hex package
pinned by `mix.lock`.
3. Otherwise the sibling `../ElixirTorrent` checkout when it exists, and Hex
when it does not.

So with both repositories checked out side by side, `mix deps.get`, `mix test`
and `mix mac.dmg` build against the local Engine with no extra setup.

A local Engine resolves its own dependencies, which drift ahead of the committed
lock whenever the Engine bumps one. Local builds therefore use their own
`mix.local.lock` (gitignored, seeded from `mix.lock` on first use), and
`mix.lock` changes only when the Hex dependency itself is updated:

```bash
export ELIXIR_TORRENT_PATH=../ElixirTorrent
mix deps.get
ELIXIR_TORRENT_HEX=1 mix deps.update elixir_torrent
```

The local path and Engine revision are development inputs, not committed pins.
Do not commit lock-file changes caused only by switching dependency sources.

## Tests and quality

The deterministic ExUnit/ConnTest/LiveViewTest suite has a 60-second ceiling.
ExCoveralls currently enforces the first 55% project gate (57.2% at introduction);
the roadmap raises this to 70% and then above 80%.

```bash
export ELIXIR_TORRENT_PATH=../ElixirTorrent
mix format --check-formatted
mix quality
perl -e 'alarm shift; exec @ARGV' 60 mix coveralls
Expand Down
17 changes: 17 additions & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,25 @@ end
# A diagnostic build (`ELIXIR_TORRENT_DEBUG_BUILD=1 mix mac.dmg`) keeps the
# `Logger.debug` call sites instead of purging them at compile time, so the
# level can still be raised back to `:info` over RPC without a rebuild.
#
# Its volume (~500-900 lines/s with a dozen torrents) is above the default
# handler's overload protection: `burst_limit_max_count: 500` per second and
# `drop_mode_qlen: 200` silently discarded every line past the cap, info and
# warning included, so a debug log read 501 lines in a median second and was
# missing whole events. A diagnostic log that drops lines is no diagnostic, so
# the debug build turns the burst limit off and lets the queue grow before it
# blocks or drops.
if System.get_env("ELIXIR_TORRENT_DEBUG_BUILD") == "1" do
config :logger, level: :debug

config :logger, :default_handler,
config: [
burst_limit_enable: false,
sync_mode_qlen: 5_000,
drop_mode_qlen: 20_000,
flush_qlen: 50_000
]

config :elixir_torrent_web_ui, :debug_tools, true
else
config :logger,
Expand Down
45 changes: 43 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule ElixirTorrentWebUI.MixProject do
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps(),
lockfile: lockfile(),
test_coverage: [tool: ExCoveralls],
compilers: [:phoenix_live_view] ++ Mix.compilers(),
listeners: [Phoenix.CodeReloader]
Expand Down Expand Up @@ -85,12 +86,52 @@ defmodule ElixirTorrentWebUI.MixProject do
# ELIXIR_TORRENT_PATH=../ElixirTorrent mix deps.get
# ELIXIR_TORRENT_PATH=../ElixirTorrent mix phx.server
defp elixir_torrent_dep do
case System.get_env("ELIXIR_TORRENT_PATH") do
nil -> {:elixir_torrent, "~> 0.6.8"}
case engine_path() do
nil -> {:elixir_torrent, "~> 0.7.0"}
path -> {:elixir_torrent, path: path}
end
end

# Where the engine comes from:
# * `ELIXIR_TORRENT_PATH` names a local checkout explicitly;
# * `ELIXIR_TORRENT_HEX=1`, or any CI run, takes the published package;
# * otherwise the sibling `../ElixirTorrent` checkout, when it exists.
# So a developer machine with both repos builds against the local engine by
# default, and CI keeps building the release users get from Hex.
defp engine_path do
explicit = System.get_env("ELIXIR_TORRENT_PATH")
sibling = Path.expand("../ElixirTorrent", __DIR__)

cond do
explicit not in [nil, ""] -> explicit
System.get_env("ELIXIR_TORRENT_HEX") == "1" or System.get_env("CI") != nil -> nil
File.regular?(Path.join(sibling, "mix.exs")) -> sibling
true -> nil
end
end

# The committed `mix.lock` pins the Hex engine and the versions it brings. A
# local engine resolves its own dependencies, which drift ahead of that lock as
# soon as the engine bumps one, and `deps.get --check-locked` then fails. A local
# build therefore keeps its own lock, seeded from the committed one, so switching
# sources never touches `mix.lock`.
@local_lockfile "mix.local.lock"

defp lockfile do
if engine_path() do
local = Path.join(__DIR__, @local_lockfile)
committed = Path.join(__DIR__, "mix.lock")

if not File.exists?(local) and File.exists?(committed) do
File.cp!(committed, local)
end

@local_lockfile
else
"mix.lock"
end
end

# Aliases are shortcuts or tasks specific to the current project.
# For example, to install project dependencies and perform other setup tasks, run:
#
Expand Down
Loading