From d9908d4566539bab4f60cc18d6dfb455c81c058d Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Fri, 2 Oct 2026 14:31:23 +0300 Subject: [PATCH 1/8] chore(deps): bump phoenix, esbuild and the transport chain Update phoenix to 1.8.15, dns_cluster, lazy_html, sobelow and the esbuild binary to 0.28.2, plus the transitive h2, hackney, hpax, phoenix_template and webtransport updates (quic moves 1.8.2 to 2.1.0). logger_file_backend stays at 0.0.14 until the engine release that allows 0.1.x is published. Co-Authored-By: Claude Sonnet 5.5 --- config/config.exs | 2 +- mix.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config/config.exs b/config/config.exs index 5c4113c..6322fa8 100644 --- a/config/config.exs +++ b/config/config.exs @@ -37,7 +37,7 @@ config :elixir_torrent_web_ui, ElixirTorrentWebUIWeb.Endpoint, # Configure esbuild (the version is required) config :esbuild, - version: "0.28.1", + version: "0.28.2", elixir_torrent_web_ui: [ args: ~w(js/app.js --bundle --target=es2022 --outdir=../priv/static/assets/js --external:/fonts/* --external:/images/* --alias:@=.), diff --git a/mix.lock b/mix.lock index 5687508..4aeae40 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"}, "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, "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.0", "5064e20cbde3b9a974671d85d996227d6dcc9e570b7a80ed464a2b01fc06a513", [:mix], [], "hexpm", "088428d2d128f8b278ad057d9167fcad221885a5129d0b246c42ed1ae7efc752"}, + "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.7", "a60d7c9054de7e94dd4fac513148a9a89f6c1b3fc41f769371483a88345979f7", [: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.0.14", [hex: :logger_file_backend, repo: "hexpm", optional: false]}, {:recon, "~> 2.5.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "3d1d3eae2f54baed8008621bf3de98ee06329875848ae9cf66ad186bd90c88f9"}, "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, @@ -16,31 +16,31 @@ "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.0", "f393539ee2728f8118fb2024b6d5f3e2c45e40ceb31b18b4e9bf5e50d028f80f", [:rebar3], [], "hexpm", "beaafc93c54cdc5d623247334d3970cdf4bc66b6b8b296b74ba1d7c7513c3dfc"}, - "hackney": {:hex, :hackney, "4.7.4", "8fe2ddaa3ca27de99d68e682d72b66d07d2331da680f77c8000580a0122c69e6", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.12.0", [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]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.5", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "d07d7e1358353ab6cc75132f058c155287f3e013d43f709fbb79d79eeab98195"}, + "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"}, "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "0435d4ca364a608cc75e2f8683d374e55abbae26", [tag: "v2.2.0", sparse: "optimized", depth: 1]}, - "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, + "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"}, "idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, - "lazy_html": {:hex, :lazy_html, "0.1.12", "31a55ee622918fce988c94b06232227b42daa64e4eab14ac32081d0f3fd8db6f", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "8a0da594776caee58782c6f93b2abaa5bdb809daf8d43351a561f7de9dc2e2a8"}, + "lazy_html": {:hex, :lazy_html, "0.1.13", "860ea816f5bc7936d1ab5ccebc388f9d1031ba9c35ed81a4db9a1c3a50737704", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "9a8405d6785fe6f8423b86e0ec5f21806ef79941fe853eac3d14fbbb173c34e9"}, "logger_backends": {:hex, :logger_backends, "1.0.1", "08a9bd09eed271eac41ff35dd7603b6abf9e0f8b6ffe40359846cb0bfd37704f", [:mix], [], "hexpm", "d7903244fe8d84eeb3d3d42f8d7de080fd0409b613040df54c78b3591672d652"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, "parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"}, - "phoenix": {:hex, :phoenix, "1.8.14", "9279cbbcd755ac8d5f42d206915e7df13b9ab49096b98114f1c99ff908e5b42e", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, 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.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 2.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "2782ff375824b2b5e41561fbae4764ee7b875af6898483bca49f24a9d1e37816"}, + "phoenix": {:hex, :phoenix, "1.8.15", "dcdb304113660ec97e1579458824d3c34b5d2eed07a0fdf25a836a9515b63146", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, 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.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 2.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "7b83ed6b3d544f24a29277eab7f051be38b76f390bb511bb6ddb7ec6e8e05b95"}, "phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"}, "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_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, + "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, "1.8.2", "c315176d2c4fad0725e52f2a8033b96d01c4fecfc7e6a7333615ff10041142a5", [:rebar3], [], "hexpm", "274d2f41ee9c00d8d6415248df9fb5637381fe69b5387771dc37e91038a65479"}, + "quic": {:hex, :quic, "2.1.0", "abfea35b8abd108510ccd693b78fcb0844b8fb75ed09d227dc1fa72477d4baac", [:rebar3], [], "hexpm", "58b250ae21045bd3838a448de0592e2cd61b73586307bd6cdcbab1bf246f52ed"}, "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, - "sobelow": {:hex, :sobelow, "0.15.0", "b067d7f8522a9d758fa89cb2bfcbab7ad72c45a0993cb958c989c6fd956fdd56", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "24a800e2d7fa8c3bd21561b6ad8ad4745ed726a09fd606598981d9048708da98"}, + "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"}, "tailwind": {:hex, :tailwind, "0.5.1", "35435b13158c90d37da11e1cfc808755fca1d7b6c5ab87b1b19c5de87e2f0a10", [:mix], [], "hexpm", "c4e26302a59fec72abc5610ecb6ad2116d9aa31f31aab2d4b8eb6e95d25a689c"}, "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, @@ -49,5 +49,5 @@ "thousand_island": {:hex, :thousand_island, "1.5.0", "f50a213cac97262b6d5ebb85745aa2c00fec1413191e6e66834788d45425cecb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "708923d40523e43cf99041ab37a0d4b0ec426ac6438fa3716ab23d919eaeb412"}, "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.5", "0e387202bbe707389fe81373ef8c56faa9d5aa321bb4800fa4765ee7c1399785", [:rebar3], [{:h2, "~> 0.12", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 1.8.0", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "bcb512239e48e551d5bd5c667312a9a7de4f29b89d84b1d33c5af44e1f3f730d"}, + "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 3c4b498094fe9417d30f9a0497fb011e3b82972d Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Fri, 2 Oct 2026 14:36:47 +0300 Subject: [PATCH 2/8] build(macos): move the Swift launcher toolchain to Swift 6.4 / Xcode 27 The launcher builds warning-free (also with -warnings-as-errors) under Swift 6.4 in Swift 6 language mode, which is still the newest mode the compiler accepts, and SwiftLint --strict reports no violations. - CI: run the macOS job on the xcode-27 runner image (Xcode 27, Swift 6.4) and log the toolchain version at the start of the job - README: Swift badge 6.3 -> 6.4 - CONTRIBUTING: require Xcode 27 / Swift 6.4 for the native launcher Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/build-macos.yml | 19 +++++++++++++------ CONTRIBUTING.md | 2 +- README.md | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 9ae42ca..144b6ed 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -6,11 +6,13 @@ name: Build macOS # Runs the full ExUnit suite on macOS (arm64) and produces the signed DMG # artifact that release.yml uploads to the GitHub Release. # -# macos-26 = Apple Silicon (M-series), latest Xcode preinstalled (26.6 as of -# this writing — priv/macos/src/*.swift needs its toolchain for Swift 6 language mode -# / strict concurrency checking). GitHub bills macOS runners 10x vs Linux, so -# this workflow deliberately does not run on every PR — only on tags or -# on-demand dispatch. +# xcode-27 = Apple Silicon (M-series) macOS 27 image with Xcode 27 (Swift 6.4) +# as the default toolchain — still a public-preview label, so move back to +# the plain `macos-27` label once GitHub promotes it to GA. priv/macos/src/*.swift +# is built in Swift 6 language mode (strict concurrency checking); the 6.4 +# compiler still accepts nothing newer than `-swift-version 6`. GitHub bills +# macOS runners 10x vs Linux, so this workflow deliberately does not run on +# every PR — only on tags or on-demand dispatch. on: workflow_call: @@ -26,7 +28,7 @@ concurrency: jobs: build: name: Test and package (macOS arm64) - runs-on: macos-26 + runs-on: xcode-27 timeout-minutes: 45 env: @@ -36,6 +38,11 @@ jobs: - name: Check out WebUI uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Show Swift toolchain + run: | + xcodebuild -version + swift --version + - name: Install SwiftLint run: brew install swiftlint diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1bbfa1c..4e02ac4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,7 +28,7 @@ Requires Elixir 1.20+ on OTP 29 (see `.tool-versions` for the exact toolchain CI ELIXIR_TORRENT_PATH=../ElixirTorrent mix deps.get ``` -The native launchers are optional for most work and only build on their own platform: the Swift shell (`priv/macos/src/`) needs Xcode with the Swift 6 toolchain, and the C# WinUI 3 shell (`priv/windows/Launcher/`) needs the .NET 10 SDK on Windows 11 x64. +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. ## Before opening a pull request diff --git a/README.md b/README.md index d9a5da3..adafee8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![GitHub release](https://img.shields.io/github/v/release/daniboybye/ElixirTorrentWebUI?label=release&logo=github&color=181717)](https://github.com/daniboybye/ElixirTorrentWebUI/releases/latest) [![GitHub](https://img.shields.io/badge/source-ElixirTorrentWebUI-181717?logo=github)](https://github.com/daniboybye/ElixirTorrentWebUI) [![CI](https://img.shields.io/github/actions/workflow/status/daniboybye/ElixirTorrentWebUI/web-build-test-analyze.yml?branch=main&label=CI&logo=github)](https://github.com/daniboybye/ElixirTorrentWebUI/actions/workflows/web-build-test-analyze.yml) [![codecov](https://codecov.io/gh/daniboybye/ElixirTorrentWebUI/branch/main/graph/badge.svg)](https://codecov.io/gh/daniboybye/ElixirTorrentWebUI) [![Last commit](https://img.shields.io/github/last-commit/daniboybye/ElixirTorrentWebUI/main)](https://github.com/daniboybye/ElixirTorrentWebUI/commits/main) [![Engine](https://img.shields.io/badge/Engine-ElixirTorrent-181717?logo=github)](https://github.com/daniboybye/ElixirTorrent) [![Platforms](https://img.shields.io/badge/platforms-macOS%20%7C%20Windows-lightgrey)](https://github.com/daniboybye/ElixirTorrentWebUI/releases/latest) [![License](https://img.shields.io/github/license/daniboybye/ElixirTorrentWebUI?label=license&color=blue)](LICENSE) -[![Elixir](https://img.shields.io/badge/elixir-%7E%3E%201.20-4B275F?logo=elixir)](https://elixir-lang.org) [![OTP](https://img.shields.io/badge/OTP-29-A90533?logo=erlang)](https://www.erlang.org) [![Swift](https://img.shields.io/badge/swift-6.3-F05138?logo=swift&logoColor=white)](https://www.swift.org) [![.NET](https://img.shields.io/badge/10-512BD4?logo=dotnet&logoColor=white)](https://dotnet.microsoft.com) +[![Elixir](https://img.shields.io/badge/elixir-%7E%3E%201.20-4B275F?logo=elixir)](https://elixir-lang.org) [![OTP](https://img.shields.io/badge/OTP-29-A90533?logo=erlang)](https://www.erlang.org) [![Swift](https://img.shields.io/badge/swift-6.4-F05138?logo=swift&logoColor=white)](https://www.swift.org) [![.NET](https://img.shields.io/badge/10-512BD4?logo=dotnet&logoColor=white)](https://dotnet.microsoft.com) Phoenix (LiveView) Web UI and desktop shell for the [`elixir_torrent`](https://hex.pm/packages/elixir_torrent) BitTorrent engine. From 735bdd0aa491617bfb840d5f9eb631f5ca464ffb Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Fri, 2 Oct 2026 20:25:57 +0300 Subject: [PATCH 3/8] build(macos): compile the launcher with strict memory safety, swap hdiutil - Build the Swift launcher with -strict-memory-safety (script and integration test) and clear its seven diagnostics: the Dock speed text uses FormatStyle (same output as String(format:), without C varargs), and the Unmanaged / IOKit out-parameter calls are marked `unsafe` with the retain rule they rely on - Create the DMG with `diskutil image create from` instead of the deprecated `hdiutil create` (macOS 15+, same UDZO format) Co-Authored-By: Claude Sonnet 5.5 --- priv/macos/src/AppDelegate+DockMenu.swift | 17 ++++++++++++++--- priv/macos/src/DefaultHandlerCoordinator.swift | 4 +++- priv/macos/src/SleepPreventer.swift | 8 +++++--- priv/scripts/macos/build-macos-dmg.sh | 13 ++++++++----- test/macos/launcher_integration_test.exs | 1 + 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/priv/macos/src/AppDelegate+DockMenu.swift b/priv/macos/src/AppDelegate+DockMenu.swift index 6013de5..640546f 100644 --- a/priv/macos/src/AppDelegate+DockMenu.swift +++ b/priv/macos/src/AppDelegate+DockMenu.swift @@ -31,6 +31,17 @@ extension AppDelegate { } } + /// Fixed `.`-separated, ungrouped digits, as `String(format:)` produced, + /// without the C-varargs call strict memory safety rejects. + private static func formatSpeedValue(_ value: Double, fractionDigits: Int) -> String { + value.formatted( + .number + .precision(.fractionLength(fractionDigits)) + .grouping(.never) + .locale(Locale(identifier: "en_US_POSIX")) + ) + } + func applicationDockMenu(_: NSApplication) -> NSMenu? { guard !dockTorrents.isEmpty else { return nil } @@ -106,11 +117,11 @@ extension AppDelegate { let bytesPerSecond = kbps * 1024 if bytesPerSecond >= 1024 * 1024 { - return String(format: "%.1f MB/s", bytesPerSecond / (1024 * 1024)) + return "\(Self.formatSpeedValue(bytesPerSecond / (1024 * 1024), fractionDigits: 1)) MB/s" } if bytesPerSecond >= 1024 { - return String(format: "%.1f KB/s", bytesPerSecond / 1024) + return "\(Self.formatSpeedValue(bytesPerSecond / 1024, fractionDigits: 1)) KB/s" } - return String(format: "%.0f B/s", bytesPerSecond) + return "\(Self.formatSpeedValue(bytesPerSecond, fractionDigits: 0)) B/s" } } diff --git a/priv/macos/src/DefaultHandlerCoordinator.swift b/priv/macos/src/DefaultHandlerCoordinator.swift index 92c63f6..b63d0e3 100644 --- a/priv/macos/src/DefaultHandlerCoordinator.swift +++ b/priv/macos/src/DefaultHandlerCoordinator.swift @@ -137,7 +137,9 @@ enum DefaultHandlerCoordinator: Sendable { } private static func handlerForContentType(_ uti: String) -> String? { - LSCopyDefaultRoleHandlerForContentType(uti as CFString, .viewer)? + // Copy rule: the returned handler is +1 retained, which is exactly + // what `takeRetainedValue()` consumes. + unsafe LSCopyDefaultRoleHandlerForContentType(uti as CFString, .viewer)? .takeRetainedValue() as String? } diff --git a/priv/macos/src/SleepPreventer.swift b/priv/macos/src/SleepPreventer.swift index ba3f9a6..c9b46f6 100644 --- a/priv/macos/src/SleepPreventer.swift +++ b/priv/macos/src/SleepPreventer.swift @@ -48,8 +48,10 @@ final class SleepPreventer { /// versus a download queue that silently never finishes, which is the bug /// this class exists to fix. private static func isOnACPower() -> Bool { - guard let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue(), - let source = IOPSGetProvidingPowerSourceType(snapshot)?.takeUnretainedValue() + // IOPSCopy… is +1 retained (retained value); IOPSGet… follows the get + // rule and is borrowed from `snapshot` (unretained value). + guard let snapshot = unsafe IOPSCopyPowerSourcesInfo()?.takeRetainedValue(), + let source = unsafe IOPSGetProvidingPowerSourceType(snapshot)?.takeUnretainedValue() else { return true } @@ -78,7 +80,7 @@ final class SleepPreventer { guard !isHeld else { return } var id: IOPMAssertionID = 0 - let result = IOPMAssertionCreateWithName( + let result = unsafe IOPMAssertionCreateWithName( kIOPMAssertPreventUserIdleSystemSleep as CFString, IOPMAssertionLevel(kIOPMAssertionLevelOn), "ElixirTorrent is downloading" as CFString, diff --git a/priv/scripts/macos/build-macos-dmg.sh b/priv/scripts/macos/build-macos-dmg.sh index e771818..cfb5f2e 100755 --- a/priv/scripts/macos/build-macos-dmg.sh +++ b/priv/scripts/macos/build-macos-dmg.sh @@ -62,6 +62,7 @@ swiftc "$ROOT"/priv/macos/src/*.swift \ -framework AppKit \ -framework IOKit \ -swift-version 6 \ + -strict-memory-safety \ -O chmod +x "$APP/Contents/MacOS/${EXECUTABLE_NAME}" @@ -89,11 +90,13 @@ mkdir -p "$STAGING" cp -R "$APP" "$STAGING/" ln -s /Applications "$STAGING/Applications" -hdiutil create \ - -volname "$APP_DISPLAY_NAME" \ - -srcfolder "$STAGING" \ - -ov \ - -format UDZO \ +# `diskutil image` (macOS 15+) replaces the deprecated `hdiutil create`; it +# overwrites an existing destination like `-ov` did, but prints percentage +# progress on stdout, hence the redirect. +diskutil image create from \ + --format UDZO \ + --volumeName "$APP_DISPLAY_NAME" \ + "$STAGING" \ "$DMG" >/dev/null rm -rf "$STAGING" diff --git a/test/macos/launcher_integration_test.exs b/test/macos/launcher_integration_test.exs index 7205a2e..7747f8c 100644 --- a/test/macos/launcher_integration_test.exs +++ b/test/macos/launcher_integration_test.exs @@ -83,6 +83,7 @@ defmodule ElixirTorrentWebUI.MacOS.LauncherIntegrationTest do "IOKit", "-swift-version", "6", + "-strict-memory-safety", "-O" ], env: CommandEnvironment.scrubbed() From 2583746f971256b263f2ac4079974dfa6c6d2837 Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Fri, 2 Oct 2026 20:25:57 +0300 Subject: [PATCH 4/8] chore(toolchain): bump Erlang/OTP to 29.1.1 and Elixir to 1.20.4 Co-Authored-By: Claude Sonnet 5.5 --- .tool-versions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tool-versions b/.tool-versions index 4c96bef..5617120 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 29.0.5 -elixir 1.20.3-otp-29 +erlang 29.1.1 +elixir 1.20.4-otp-29 From 4d4459d702e7ddae583740851b811b3427a9c120 Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Fri, 2 Oct 2026 20:33:25 +0300 Subject: [PATCH 5/8] ci: run Linux jobs on ubuntu-26.04 and pin setup-beam comments to v1.24.1 Every action was already at its latest release; the Linux runner image was the one stale tool. Hex publishes OTP 29.1.1 for ubuntu-26.04, so setup-beam's strict .tool-versions install still resolves. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/web-build-test-analyze.yml | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 144b6ed..03510e9 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -55,7 +55,7 @@ jobs: run: swiftlint lint --strict - name: Install Erlang and Elixir - uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1 + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 with: version-file: .tool-versions version-type: strict diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 1cdf5f6..a49c867 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -63,7 +63,7 @@ jobs: --configuration Release --runtime win-x64 -warnaserror - name: Install Erlang and Elixir - uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1 + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 with: version-file: .tool-versions version-type: strict diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c408b6..6bd1e79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: publish: name: Publish GitHub Release needs: [macos, windows] - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 timeout-minutes: 10 if: >- startsWith(github.ref, 'refs/tags/') && diff --git a/.github/workflows/web-build-test-analyze.yml b/.github/workflows/web-build-test-analyze.yml index 595d62e..96c25a9 100644 --- a/.github/workflows/web-build-test-analyze.yml +++ b/.github/workflows/web-build-test-analyze.yml @@ -1,7 +1,7 @@ name: Build WebUI, Test and Analyze # Runs on every PR and every push to main. Kept intentionally on the cheapest -# GitHub-hosted image (ubuntu-24.04, 1x billing) — no macOS/Windows packaging +# GitHub-hosted image (ubuntu-26.04, 1x billing) — no macOS/Windows packaging # happens here. Platform artifacts are produced by .github/workflows/release.yml. on: @@ -21,7 +21,7 @@ concurrency: jobs: quality: name: Quality and tests - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 timeout-minutes: 20 env: @@ -40,7 +40,7 @@ jobs: version: latest - name: Install Erlang and Elixir - uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1 + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 with: version-file: .tool-versions version-type: strict From a2b0b6e047b2436f32c27aa2f9853373e6bce75d Mon Sep 17 00:00:00 2001 From: Daniel Urumov Date: Fri, 2 Oct 2026 20:44:03 +0300 Subject: [PATCH 6/8] feat(settings): add Debug-only "Delete log files" button Settings shows a Debug section only when the :debug_tools flag is set (dev, and ELIXIR_TORRENT_DEBUG_BUILD=1 builds). The button truncates active *.log files in the data directory and removes rotated *.log.N copies. server.log is truncated rather than unlinked because the launcher keeps it open; the macOS launcher now opens it with O_APPEND so the server writes at the new end instead of leaving a NUL gap. Strings are translated in all 64 locale catalogs. Co-Authored-By: Claude Sonnet 5.5 --- config/dev.exs | 3 + config/prod.exs | 1 + lib/elixir_torrent_web_ui/log_files.ex | 53 ++++ .../live/torrents_live.ex | 38 +++ priv/gettext/af/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/am/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ar/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/be/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/bg/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/bn/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/bs/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ca/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/cs/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/cy/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/da/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/de/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/default.pot | 292 ++++++++++-------- priv/gettext/el/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/en/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/es/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/et/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/eu/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/fa/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/fi/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/fr/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ga/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/gl/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/gu/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/he/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/hi/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/hr/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/hu/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/id/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/is/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/it/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ja/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/km/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ko/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/lb/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/lt/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/lv/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/mk/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/mr/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ms/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/mt/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/my/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ne/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/nl/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/no/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/pa/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/pl/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/pt/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ro/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ru/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/sk/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/sl/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/sq/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/sr/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/sv/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/sw/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ta/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/te/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/th/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/tl/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/tr/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/uk/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/ur/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/vi/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/gettext/zh/LC_MESSAGES/default.po | 292 ++++++++++-------- priv/macos/src/ServerLifecycle.swift | 4 + test/elixir_torrent_web_ui/log_files_test.exs | 59 ++++ .../live/torrents_live_components_test.exs | 15 + .../live/torrents_live_test.exs | 47 +++ 73 files changed, 10685 insertions(+), 8515 deletions(-) create mode 100644 lib/elixir_torrent_web_ui/log_files.ex create mode 100644 test/elixir_torrent_web_ui/log_files_test.exs diff --git a/config/dev.exs b/config/dev.exs index 92e6480..c13c37f 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -65,6 +65,9 @@ config :elixir_torrent_web_ui, ElixirTorrentWebUIWeb.Endpoint, # Enable dev routes for dashboard and mailbox config :elixir_torrent_web_ui, dev_routes: true +# Show Debug-only tools (e.g. "Delete log files") in Settings. +config :elixir_torrent_web_ui, debug_tools: true + # Do not include metadata nor timestamps in development logs config :logger, :default_formatter, format: "[$level] $message\n" diff --git a/config/prod.exs b/config/prod.exs index e5b8f9f..da8d861 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -25,6 +25,7 @@ end # level can still be raised back to `:info` over RPC without a rebuild. if System.get_env("ELIXIR_TORRENT_DEBUG_BUILD") == "1" do config :logger, level: :debug + config :elixir_torrent_web_ui, :debug_tools, true else config :logger, level: :info, diff --git a/lib/elixir_torrent_web_ui/log_files.ex b/lib/elixir_torrent_web_ui/log_files.ex new file mode 100644 index 0000000..c0d34e1 --- /dev/null +++ b/lib/elixir_torrent_web_ui/log_files.ex @@ -0,0 +1,53 @@ +defmodule ElixirTorrentWebUI.LogFiles do + @moduledoc false + + # Logs written while ElixirTorrent runs live directly in the data directory: + # `server.log` (release stdout/stderr, including Engine output), `debug.log` + # and the rotated `*.log.N` siblings. + # + # The desktop launcher keeps `server.log` open for the life of the server, so + # deleting it would leave the release writing into an unlinked file. Active + # logs are therefore truncated in place and only rotated copies are removed. + + @spec enabled?() :: boolean() + def enabled?, do: Application.get_env(:elixir_torrent_web_ui, :debug_tools, false) + + @spec delete_all(Path.t()) :: {:ok, non_neg_integer()} | {:error, [{Path.t(), term()}]} + def delete_all(root \\ ElixirTorrentWebUI.DataDir.root()) do + results = + Enum.map(active_logs(root), &truncate/1) ++ Enum.map(rotated_logs(root), &remove/1) + + case for({:error, _} = error <- results, do: error) do + [] -> {:ok, Enum.count(results, &(&1 == :ok))} + errors -> {:error, Enum.map(errors, fn {:error, failure} -> failure end)} + end + end + + @spec active_logs(Path.t()) :: [Path.t()] + defp active_logs(root), do: wildcard_files(Path.join(root, "*.log")) + + @spec rotated_logs(Path.t()) :: [Path.t()] + defp rotated_logs(root), do: wildcard_files(Path.join(root, "*.log.[0-9]*")) + + @spec wildcard_files(Path.t()) :: [Path.t()] + defp wildcard_files(pattern), do: Enum.filter(Path.wildcard(pattern), ®ular?/1) + + @spec regular?(Path.t()) :: boolean() + defp regular?(path), do: match?({:ok, %File.Stat{type: :regular}}, File.stat(path)) + + @spec truncate(Path.t()) :: :ok | {:error, {Path.t(), term()}} + defp truncate(path) do + case File.write(path, "") do + :ok -> :ok + {:error, reason} -> {:error, {path, reason}} + end + end + + @spec remove(Path.t()) :: :ok | {:error, {Path.t(), term()}} + defp remove(path) do + case File.rm(path) do + :ok -> :ok + {:error, reason} -> {:error, {path, reason}} + end + end +end diff --git a/lib/elixir_torrent_web_ui_web/live/torrents_live.ex b/lib/elixir_torrent_web_ui_web/live/torrents_live.ex index 7cd8dfc..e670e9d 100644 --- a/lib/elixir_torrent_web_ui_web/live/torrents_live.ex +++ b/lib/elixir_torrent_web_ui_web/live/torrents_live.ex @@ -6,6 +6,7 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do Engine, IssueReport, Locale, + LogFiles, MagnetIngest, StatsStore, TorrentIngest, @@ -32,6 +33,7 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do |> assign(:settings_locale, locale) |> assign(:settings_download_folder, download_folder) |> assign(:download_folder, download_folder) + |> assign(:debug_tools?, LogFiles.enabled?()) |> assign(:default_handler, DefaultHandler.status()) |> assign(:default_prompt_dismissed, false) |> assign(:report_open, false) @@ -339,6 +341,20 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do |> assign_torrents(Engine.list_torrents(socket.assigns.expanded))} end + @impl Phoenix.LiveView + def handle_event("delete_log_files", _params, %{assigns: %{debug_tools?: true}} = socket) do + case LogFiles.delete_all() do + {:ok, _count} -> + {:noreply, put_flash(socket, :info, gettext("Log files deleted"))} + + {:error, failures} -> + Logger.warning("TorrentsLive: delete_log_files failed reasons=#{inspect(failures)}") + {:noreply, put_flash(socket, :error, gettext("Could not delete log files"))} + end + end + + def handle_event("delete_log_files", _params, socket), do: {:noreply, socket} + @impl Phoenix.LiveView def handle_event("request_default_handler", _params, socket) do case DefaultHandler.request_default() do @@ -689,6 +705,7 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do open={@settings_open} locale={@settings_locale} download_folder={@settings_download_folder} + debug_tools={@debug_tools?} default_handler={@default_handler} languages={ElixirTorrentWebUI.Languages.list()} /> @@ -1286,6 +1303,7 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do attr :download_folder, :string, required: true attr :default_handler, :map, required: true attr :languages, :list, required: true + attr :debug_tools, :boolean, default: false @doc false def settings_dialog(assigns) do @@ -1419,6 +1437,26 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do +
+

{gettext("Debug")}

+

+ {gettext("Remove the log files written while ElixirTorrent runs.")} +

+ +
+