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
21 changes: 14 additions & 7 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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

Expand All @@ -48,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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/') &&
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/web-build-test-analyze.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 29.0.5
elixir 1.20.3-otp-29
erlang 29.1.1
elixir 1.20.4-otp-29
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -83,7 +83,7 @@ mix mac.dmg
```

Output lands in `dist/` (e.g. `ElixirTorrent Web.app`,
`ElixirTorrent Web-0.4.0-macos-arm64.dmg` on Apple Silicon, or `…-macos-x64.dmg`
`ElixirTorrent Web-0.4.1-macos-arm64.dmg` on Apple Silicon, or `…-macos-x64.dmg`
on Intel).

The **Liquid Glass** icon (`Assets.car`, macOS 26+) requires **Xcode 26+**
Expand Down
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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:@=.),
Expand Down
3 changes: 3 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
1 change: 1 addition & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
53 changes: 53 additions & 0 deletions lib/elixir_torrent_web_ui/log_files.ex
Original file line number Diff line number Diff line change
@@ -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), &regular?/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
38 changes: 38 additions & 0 deletions lib/elixir_torrent_web_ui_web/live/torrents_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do
Engine,
IssueReport,
Locale,
LogFiles,
MagnetIngest,
StatsStore,
TorrentIngest,
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()}
/>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1419,6 +1437,26 @@ defmodule ElixirTorrentWebUIWeb.TorrentsLive do
</button>
</div>

<div
:if={@debug_tools}
id="settings-debug"
class="mt-6 rounded-lg border border-base-300 bg-base-200/40 px-3 py-3"
>
<p class="text-sm font-medium text-base-content">{gettext("Debug")}</p>
<p class="mt-1 text-xs text-base-content/60">
{gettext("Remove the log files written while ElixirTorrent runs.")}
</p>
<button
type="button"
id="settings-delete-log-files"
phx-click="delete_log_files"
data-confirm={gettext("Delete all log files?")}
class="mt-3 inline-flex cursor-pointer items-center rounded-md border border-error/50 bg-base-100 px-4 py-2 text-sm font-semibold text-error transition hover:bg-error hover:text-error-content"
>
{gettext("Delete log files")}
</button>
</div>

<div class="mt-6 flex flex-wrap justify-end gap-3">
<button
type="button"
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ElixirTorrentWebUI.MixProject do
def project do
[
app: :elixir_torrent_web_ui,
version: "0.4.0",
version: "0.4.1",
elixir: "~> 1.20",
elixirc_paths: elixirc_paths(Mix.env()),
elixirc_options: elixirc_options(),
Expand Down Expand Up @@ -86,7 +86,7 @@ defmodule ElixirTorrentWebUI.MixProject do
# 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.6"}
nil -> {:elixir_torrent, "~> 0.6.8"}
path -> {:elixir_torrent, path: path}
end
end
Expand Down
Loading