From d106c1e62cd39177e75f37652e4502c5ff8c01ec Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Sat, 17 Oct 2026 11:15:16 +0300 Subject: [PATCH 1/5] build: use the sibling engine checkout by default, with its own lock mix.exs now resolves the engine as ELIXIR_TORRENT_PATH, then Hex when ELIXIR_TORRENT_HEX=1 or CI is set, then ../ElixirTorrent when it exists, then Hex. A machine with both repos builds the local engine with no setup, and CI keeps building the published package. A path dependency resolves the engine's own requirements, which run ahead of the committed lock as soon as the engine bumps one (hackney 4.8.4 to 4.8.5 broke mac.dmg's deps.get --check-locked). A local build therefore uses a gitignored mix.local.lock seeded from mix.lock, and the build script enforces --check-locked only for the Hex source. The script's mix config probes gained --no-deps-check, so the DMG is no longer named 0.1.0 when deps are not fetched or locked yet. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/build-windows.yml | 4 +-- .gitignore | 3 ++ CONTRIBUTING.md | 6 +--- README.md | 24 ++++++++++----- mix.exs | 43 ++++++++++++++++++++++++++- priv/scripts/macos/build-macos-dmg.sh | 19 ++++++++++-- 6 files changed, 81 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 8dbf42a..35e398c 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -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) diff --git a/.gitignore b/.gitignore index 401340c..ddab27b 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e02ac4..c63e310 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/README.md b/README.md index 12b4762..091307e 100644 --- a/README.md +++ b/README.md @@ -107,17 +107,26 @@ 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=`: 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. @@ -125,7 +134,6 @@ 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 diff --git a/mix.exs b/mix.exs index 9fdaf34..4658e93 100644 --- a/mix.exs +++ b/mix.exs @@ -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] @@ -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 + case engine_path() do nil -> {:elixir_torrent, "~> 0.6.8"} 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: # diff --git a/priv/scripts/macos/build-macos-dmg.sh b/priv/scripts/macos/build-macos-dmg.sh index cfb5f2e..7acf7a5 100755 --- a/priv/scripts/macos/build-macos-dmg.sh +++ b/priv/scripts/macos/build-macos-dmg.sh @@ -6,7 +6,16 @@ cd "$ROOT" APP_DISPLAY_NAME="ElixirTorrent Web" EXECUTABLE_NAME="ElixirTorrentWebUI" -VERSION="$(mix run -e 'IO.write(Mix.Project.config()[:version])' --no-start --no-compile 2>/dev/null || echo 0.1.0)" +# --no-deps-check: these probes only read mix.exs, and must not fail (and fall +# back to a stale default) just because deps are not fetched or locked yet. +mix_config() { + mix run -e "IO.write(Mix.Project.config()[:$1])" --no-start --no-compile --no-deps-check 2>/dev/null +} +VERSION="$(mix_config version || echo 0.1.0)" +# mix.exs builds against the sibling ../ElixirTorrent checkout when it exists +# (or ELIXIR_TORRENT_PATH), with its own lock; only the Hex build is held to the +# committed mix.lock. +LOCKFILE="$(mix_config lockfile || echo mix.lock)" DIST="$ROOT/dist" APP="$DIST/${APP_DISPLAY_NAME}.app" STAGING="$DIST/dmg-staging" @@ -38,7 +47,13 @@ echo "==> Building release (prod, desktop)…" export ELIXIR_TORRENT_DESKTOP_BUILD=1 export MIX_ENV=prod -mix deps.get --only prod --check-locked +if [[ "$LOCKFILE" == "mix.lock" ]]; then + echo "==> Engine: elixir_torrent from Hex (mix.lock)" + mix deps.get --only prod --check-locked +else + echo "==> Engine: local checkout (${LOCKFILE})" + mix deps.get --only prod +fi MIX_ENV=prod mix compile mix phx.digest.clean --all mix assets.deploy From 64159f5fc77ccb88b9fda33a7079a481480e18bc Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Sat, 17 Oct 2026 11:15:16 +0300 Subject: [PATCH 2/5] deps: take bandit 1.13.1 1.12.5 carries EEF-CVE-2026-105659 (HTTP/2 send deadlines reset on every WINDOW_UPDATE, so a trickling stream can pin a process) and EEF-CVE-2026-105660 (unvalidated HTTP/1 request-target authority). Co-Authored-By: Claude Opus 5.5 (1M context) --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 5a23c51..3e665fd 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "bandit": {:hex, :bandit, "1.12.5", "af205a8e550f304caae09a97d29fd3c79a7f337526ea7cd772d2ff11d2f7c800", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.5", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "c5684ca062fa407cac115aec3256383f3e2ec9fdced7904d59cf5a7bb7ed6181"}, + "bandit": {:hex, :bandit, "1.13.1", "1ce25a7beb750ed3fa2e905962f1f9a679cb585f1da0ae45637d06e385ce2eae", [:mix], [{:hpax, "~> 1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.6", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "39028f0b5a21404661410a6c19275060a9822c9dcca70b7c3e4e4fa50b11da58"}, "bento": {:hex, :bento, "1.0.0", "5097e6f02e4980b72d08bf0270026f6f5c9bf5d8ca606b43d41321b549d49de8", [:mix], [], "hexpm", "b921b335a555f7570adfac0cc41864c76377f6852617cf1f25fbb88578b993c8"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, @@ -46,7 +46,7 @@ "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.2.0", "7632c19c01d88d8aaca5da1a0e8912f5af39b79e7c08a2c253aeb3c14c2c957e", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "71dde12fc29b58b9c77ec17ec319109e5ca848d010fc1965ed4463bba1837c07"}, "telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"}, - "thousand_island": {:hex, :thousand_island, "1.5.0", "f50a213cac97262b6d5ebb85745aa2c00fec1413191e6e66834788d45425cecb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "708923d40523e43cf99041ab37a0d4b0ec426ac6438fa3716ab23d919eaeb412"}, + "thousand_island": {:hex, :thousand_island, "1.6.0", "d6ad7c2220c96a270cc8ca1586ec4d8bde648a4a3a53f003ce093f41beec0288", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4d4720470f091fc21a1311777c2ac66916c43621bffc068590c9f9bb3cd057a3"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.6.0", "73db5ab8aaefd1a876a97ce3e6afc96562625de69ef17a4e04426e034849d0b8", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "50021a85bce8f203b086705d9e0c5415e2c7eb05d319111b0428fe71f9934617"}, "webtransport": {:hex, :webtransport, "0.4.7", "8e0abd5875daab05c7020b8fc0c3b318fe93aa9329302fe5ecda2a0f953cf688", [:rebar3], [{:h2, "~> 0.12", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 2.0", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "9d2fcbfc561a172cf6f2eef00b89fc89774c039ce850414356964c147707f85e"}, From 9bcb6d010f7a01962ec8400cf94a1a57269165e2 Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Sat, 17 Oct 2026 11:45:13 +0300 Subject: [PATCH 3/5] fix(logging): stop the debug build from dropping log lines The debug build writes ~500-900 lines/s, above the default handler's overload protection (burst_limit_max_count 500 per second, drop_mode_qlen 200). Every line past the cap was discarded, info and warning included: a debug log read 501 lines in its median second and was missing whole events such as a magnet's metadata_ok. The debug build now disables the burst limit and lets the queue grow to 5000 before it blocks and 20000 before it drops. Production builds are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- config/prod.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/config/prod.exs b/config/prod.exs index da8d861..0e51f8a 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -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, From dd55749606e28ec50ce06184396dcb459266432f Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Sun, 18 Oct 2026 20:13:15 +0300 Subject: [PATCH 4/5] deps: take elixir_torrent 0.7.0 and refresh the rest elixir_torrent 0.7.0 is published on Hex (magnets keep their peers, per- tracker min interval, no orphaned uTP connections). The lock also moves phoenix_pubsub to 2.4.1 and the hackney chain to 4.8.5; every other dependency is already on its latest release. Co-Authored-By: Claude Opus 5.5 (1M context) --- mix.exs | 2 +- mix.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 4658e93..e5b29fd 100644 --- a/mix.exs +++ b/mix.exs @@ -87,7 +87,7 @@ defmodule ElixirTorrentWebUI.MixProject do # ELIXIR_TORRENT_PATH=../ElixirTorrent mix phx.server defp elixir_torrent_dep do case engine_path() do - nil -> {:elixir_torrent, "~> 0.6.8"} + nil -> {:elixir_torrent, "~> 0.7.0"} path -> {:elixir_torrent, path: path} end end diff --git a/mix.lock b/mix.lock index 3e665fd..c279ec0 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "dialyxir": {:hex, :dialyxir, "1.4.8", "7ef671a8aff9948b091d8c30f09467fbb16e77305cda451bce48109a0f5e021c", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cbd5a851571e5dfeb32aaf2e840bfa98b7864cb3071bf2ef5d95d1276b12e072"}, "dns_cluster": {:hex, :dns_cluster, "0.3.1", "b4e707e4ddaf0d3f13580ee7dc4ab0e685f7f7f5bbe90e84535524bc12d1cf3e", [:mix], [], "hexpm", "afa60bbbd84463e977eeeecc0c564d8c5ddea260beccdb8d5f45cbb860846627"}, "elixir_make": {:hex, :elixir_make, "0.10.0", "16577e2583a79bb79237bbff349619ef5d80afffc07eac6e4faf0d00e2ddaf7d", [:mix], [], "hexpm", "dc1f09fb7fa68866b886abd5f0f3c83553b1a19a52359a899e92af1bb3b31982"}, - "elixir_torrent": {:hex, :elixir_torrent, "0.6.8", "c54a92d7c7bb898c564de48e0c821378bf473fd68a923ac9d460db1afe7ba955", [:mix], [{:bento, "~> 1.0.0", [hex: :bento, repo: "hexpm", optional: false]}, {:httpoison, "~> 3.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:logger_backends, "~> 1.0", [hex: :logger_backends, repo: "hexpm", optional: false]}, {:logger_file_backend, "~> 0.1.1", [hex: :logger_file_backend, repo: "hexpm", optional: false]}, {:recon, "~> 2.5.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "a5ca789924b3cb02613c1467e89e2fdf4318177f665d6880045cee7f25570cfc"}, + "elixir_torrent": {:hex, :elixir_torrent, "0.7.0", "8fd9566757c3c1cbce393e3f147106d95f00b14ee60697fe964a86b9f5f3bc59", [:mix], [{:bento, "~> 1.0.0", [hex: :bento, repo: "hexpm", optional: false]}, {:httpoison, "~> 3.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:logger_backends, "~> 1.0", [hex: :logger_backends, repo: "hexpm", optional: false]}, {:logger_file_backend, "~> 0.1.1", [hex: :logger_file_backend, repo: "hexpm", optional: false]}, {:recon, "~> 2.5.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "a544b80fd4d0651e6b269f9a6dbb89671a1864c294919f2f0cec452842552672"}, "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, "esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"}, "excoveralls": {:hex, :excoveralls, "0.18.5", "e229d0a65982613332ec30f07940038fe451a2e5b29bce2a5022165f0c9b157e", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "523fe8a15603f86d64852aab2abe8ddbd78e68579c8525ae765facc5eae01562"}, @@ -16,8 +16,8 @@ "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, "fine": {:hex, :fine, "0.1.6", "4bf7151493443c454aac9f2fa2f34f5fefd0346a83fb5586a016c4a135c63247", [:mix], [], "hexpm", "5638eb4495488e885ebec167fa57973e5c35e1a50c344eb7666c90ec1c4e3b12"}, "gettext": {:hex, :gettext, "1.0.2", "5457e1fd3f4abe47b0e13ff85086aabae760497a3497909b8473e0acee57673b", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "eab805501886802071ad290714515c8c4a17196ea76e5afc9d06ca85fb1bfeb3"}, - "h2": {:hex, :h2, "0.12.3", "20e3fd0e384ec6f586e4736acd409a57ea87b4a56002d8dcf1132514b3d7600a", [:rebar3], [], "hexpm", "996af98698f7dc68bcc7688d70d97384b53ddd0286ba07e6d4a9ac54f1970d32"}, - "hackney": {:hex, :hackney, "4.8.4", "73d1c4389a7a41b90c169e8af2253d307213506b4f199e8c216265a563b49b55", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.12.3", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.5", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 2.0", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.7", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "4d3f3575c1d9a50a66e0469e303477fe72acffe98b108cd164621b75c22bb583"}, + "h2": {:hex, :h2, "0.12.4", "f8cb769ccc17369d185717135353bee1d50f2cd193a9e8cd998d88ce6cafc51d", [:rebar3], [], "hexpm", "238553579d2599e823f1f7b1cacb763362ee6e2df8e8f3b0c8fc67da982c0675"}, + "hackney": {:hex, :hackney, "4.8.5", "214a58c2e77115b0e2c171f01e59b7fbcb257e07471fc0ae1814b4654f8e2fa4", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.12.4", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.5", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 2.1.1", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.8", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "d1c18324ce5eaa5e45a245f31e68d212c4f3f24ee0a27e4eac09c5959831c138"}, "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "0435d4ca364a608cc75e2f8683d374e55abbae26", [tag: "v2.2.0", sparse: "optimized", depth: 1]}, "hpax": {:hex, :hpax, "1.1.0", "782931867cc23217c68fb5f68fe1a11f5e7544c7fda82c8a7019a5df5a4a1cdf", [:mix], [], "hexpm", "0b8d0f05832f55571d65ac720f79bf8994138ffbb133209dc4685eae0ad456a8"}, "httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"}, @@ -34,11 +34,11 @@ "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.9.1", "d4786b24bbe1eacc431049dbbb8d377f5359fce57b9d7cdd773bba4ac0af2ef6", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "de25e1a28cfb64413d8bdaf3ba5b64da1a19e0ed44a02320119523cc45da051d"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.7.0", "fb1e429f6d8778ce3a6962debdc5e555428a05a6e7b058d6dbad13d281a2c31f", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "dc9f44271aa6fc4ab7797f2aa374ba096ef2c87520586280eb095626b7387a68"}, "phoenix_live_view": {:hex, :phoenix_live_view, "1.2.12", "35848150bbab579e9d0aff79525269d7c0801a4f61232b84f635c11222cee88f", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "656810d716e3369545dd63981196a5d68b77fdb253afe02ef0c6fa14cfd8dc2b"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.3.0", "03916bfbc31a5121945b3cfffe5aec647a5c97fe1dc172a319b94428562359c9", [:mix], [], "hexpm", "eec7be6e9cf02e2551d389b558402d6c637cd3973796326e7ba4bb03c6b2e91d"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.4.1", "2ff3e7ba8b432120989870997b9adaefb76e1f63805554ab0b9e948987981f4b", [:mix], [], "hexpm", "352fba7a87bf61395b6e97da66944ad9b1bfe5459e5981039f60d68270bd9700"}, "phoenix_template": {:hex, :phoenix_template, "1.1.0", "b329582281b1e00e4dc664afd60ef5b489cbb356923caa3b1e00abe8651b9a18", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "eba70070de79b2c3501ef205a74a69f98ab352f3785aa15da9ed161f9fe0fd5d"}, "plug": {:hex, :plug, "1.20.3", "56c480c633ec2ce10140e236e15233bf576e1d323887d7c96711bd02ab5160db", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be266aee1b8536ef6409d58cf39a3121319f0ec47cfa1b24024485aa0e76ad76"}, "plug_crypto": {:hex, :plug_crypto, "2.2.0", "144014737daaf485407f5ed77daeaad74d651b216a28c87543f8cc7043f8efc8", [:mix], [], "hexpm", "83a95744ab1c75876542b6fab135fcc176280e0f301a111c1f757fddcec95d2c"}, - "quic": {:hex, :quic, "2.1.0", "abfea35b8abd108510ccd693b78fcb0844b8fb75ed09d227dc1fa72477d4baac", [:rebar3], [], "hexpm", "58b250ae21045bd3838a448de0592e2cd61b73586307bd6cdcbab1bf246f52ed"}, + "quic": {:hex, :quic, "2.1.1", "e5099110061f5f68fcd57ece30dfb419a2360cbaff2f58a40517b08f831b630f", [:rebar3], [], "hexpm", "305d78a857298d940f6621a9acd7391c02980555d5e7fffef364eccd330fd64d"}, "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "sobelow": {:hex, :sobelow, "0.16.0", "13e3f761bc7fcc88e81ae23ddef9140617a12a3efb26601e5516453290539398", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "00a0260edf9aeac35d9116366f55dc9451d11e0e696e28ec5570c6a56bd41f1e"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, @@ -49,5 +49,5 @@ "thousand_island": {:hex, :thousand_island, "1.6.0", "d6ad7c2220c96a270cc8ca1586ec4d8bde648a4a3a53f003ce093f41beec0288", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4d4720470f091fc21a1311777c2ac66916c43621bffc068590c9f9bb3cd057a3"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.6.0", "73db5ab8aaefd1a876a97ce3e6afc96562625de69ef17a4e04426e034849d0b8", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "50021a85bce8f203b086705d9e0c5415e2c7eb05d319111b0428fe71f9934617"}, - "webtransport": {:hex, :webtransport, "0.4.7", "8e0abd5875daab05c7020b8fc0c3b318fe93aa9329302fe5ecda2a0f953cf688", [:rebar3], [{:h2, "~> 0.12", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 2.0", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "9d2fcbfc561a172cf6f2eef00b89fc89774c039ce850414356964c147707f85e"}, + "webtransport": {:hex, :webtransport, "0.4.8", "3c340d7ea381e3f639ece90378d85fb46b1da4eece17bba3223051e833166501", [:rebar3], [{:h2, "~> 0.12", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 2.0", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "08477a2195ccbf944da0b78f85dc4e6ee4498790e3dd7fe84ee7a245938e1b26"}, } From 070aea7182df5611703b4de69d1289e6e65e96fc Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Sun, 18 Oct 2026 20:13:15 +0300 Subject: [PATCH 5/5] chore(toolchain): bump Erlang/OTP to 29.1.2 Matches the engine, which moved to the latest OTP 29 patch for 0.7.0. Co-Authored-By: Claude Opus 5.5 (1M context) --- .tool-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index 5617120..8dfeb9e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 29.1.1 +erlang 29.1.2 elixir 1.20.4-otp-29