ML-154: add early platform detection for SQLite extensions
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: ML-154
|
||||
title: Add early platform detection in runtime.exs for SQLite extensions
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-04-30 10:48'
|
||||
updated_date: '2026-04-30 12:37'
|
||||
labels:
|
||||
- robustness
|
||||
- sqlite
|
||||
dependencies: []
|
||||
references:
|
||||
- lib/music_library/repo.ex
|
||||
- config/runtime.exs
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`MusicLibrary.Repo.extension_path/1` raises `"Unsupported OS or platform"` when run on an unrecognised OS/architecture combination. This is called at startup via `config/runtime.exs` to resolve paths for `unicode` and `vec0` SQLite extensions.
|
||||
|
||||
Instead of silently degrading (which would cause confusing downstream failures), add early detection:
|
||||
|
||||
1. Add `MusicLibrary.Repo.supported_platform?/0` that returns a boolean
|
||||
2. In `config/runtime.exs`, check this BEFORE the `load_extensions` config block
|
||||
3. If unsupported, raise with a helpful message listing supported platforms and the detected OS/arch
|
||||
|
||||
The existing `raise` in `extension_path/1` stays as a defensive fallback (should not be reached if runtime.exs catches it first).
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 #1 `Repo.supported_platform?/0` returns true/false based on OS/arch
|
||||
- [x] #2 #2 `config/runtime.exs` checks `supported_platform?/0` before loading extensions and raises with a helpful message on unsupported platforms
|
||||
- [x] #3 #3 Existing supported platforms (darwin-amd64, darwin-arm64, linux-amd64, linux-arm64) continue to load extensions correctly
|
||||
- [x] #4 #4 `extension_path/1` raise stays as a defensive fallback
|
||||
<!-- AC:END -->
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
---
|
||||
id: ML-154
|
||||
title: >-
|
||||
Replace raise with Logger.warning in Repo.extension_path for unsupported
|
||||
platforms
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-04-30 10:48'
|
||||
labels:
|
||||
- robustness
|
||||
- sqlite
|
||||
dependencies: []
|
||||
references:
|
||||
- lib/music_library/repo.ex
|
||||
- config/runtime.exs
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`MusicLibrary.Repo.extension_path/1` (`lib/music_library/repo.ex`) derives the platform from `:erlang.system_info(:system_architecture)` and raises `"Unsupported OS or platform"` if the combination isn't recognised.
|
||||
|
||||
This is called at startup in `config/runtime.exs` to load SQLite extensions (`unicode`, `vec0`). If deployed on an unexpected architecture, the app crashes before serving any traffic.
|
||||
|
||||
Change the function to:
|
||||
1. Log a warning with the detected OS/arch details
|
||||
2. Return `nil` (or an `{:error, reason}` tuple)
|
||||
3. Update `config/runtime.exs` and any other callers to handle the nil/error case gracefully — skipping extension loading but allowing the app to start
|
||||
|
||||
Features that depend on the extensions (FTS5 unicode support, vector similarity search) will degrade but the app remains available.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 `Repo.extension_path/1` logs a warning and returns nil instead of raising for unsupported platforms
|
||||
- [ ] #2 `config/runtime.exs` handles a nil return by skipping extension loading for that extension
|
||||
- [ ] #3 App can start on an unsupported platform without crashing
|
||||
- [ ] #4 Existing supported platforms (darwin-amd64, darwin-arm64, linux-amd64, linux-arm64) continue to load extensions correctly
|
||||
<!-- AC:END -->
|
||||
@@ -36,6 +36,8 @@ if default_timezone = System.get_env("DEFAULT_TIMEZONE") do
|
||||
config :music_library, :default_timezone, default_timezone
|
||||
end
|
||||
|
||||
MusicLibrary.Repo.ensure_supported_platform!()
|
||||
|
||||
config :music_library, MusicLibrary.Repo,
|
||||
auto_vacuum: :incremental,
|
||||
load_extensions: [
|
||||
|
||||
+73
-14
@@ -1,25 +1,41 @@
|
||||
defmodule MusicLibrary.Repo do
|
||||
@moduledoc """
|
||||
Main application repository using SQLite3 with FTS5 unicode and vec0 extensions.
|
||||
"""
|
||||
|
||||
use Ecto.Repo,
|
||||
otp_app: :music_library,
|
||||
adapter: Ecto.Adapters.SQLite3
|
||||
|
||||
@doc """
|
||||
Returns `:ok` if the current OS/architecture has precompiled SQLite extensions
|
||||
available (unicode, vec0). Raises `RuntimeError` with a diagnostic message
|
||||
including the detected platform and supported alternatives.
|
||||
"""
|
||||
def ensure_supported_platform! do
|
||||
case platform_string() do
|
||||
{:ok, _platform} -> :ok
|
||||
{:error, os, arch} -> raise_unsupported_platform(os, arch)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns `true` if the current OS/architecture has precompiled SQLite extensions
|
||||
available (unicode, vec0).
|
||||
"""
|
||||
def supported_platform? do
|
||||
match?({:ok, _}, platform_string())
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the filesystem path for a named SQLite extension.
|
||||
|
||||
Only macOS (Intel/Apple Silicon) and Linux (amd64/arm64) are supported.
|
||||
"""
|
||||
def extension_path(name) do
|
||||
[arch | _rest] =
|
||||
:erlang.system_info(:system_architecture)
|
||||
|> List.to_string()
|
||||
|> String.split("-")
|
||||
|
||||
platform = platform_string!()
|
||||
{_, os} = :os.type()
|
||||
|
||||
platform =
|
||||
case {os, arch} do
|
||||
{:darwin, "x86_64"} -> "darwin-amd64"
|
||||
{:darwin, "aarch64"} -> "darwin-arm64"
|
||||
{:linux, "x86_64"} -> "linux-amd64"
|
||||
{:linux, "aarch64"} -> "linux-arm64"
|
||||
_other -> raise "Unsupported OS or platform"
|
||||
end
|
||||
|
||||
extension =
|
||||
case os do
|
||||
:darwin -> "dylib"
|
||||
@@ -34,6 +50,49 @@ defmodule MusicLibrary.Repo do
|
||||
])
|
||||
end
|
||||
|
||||
defp raise_unsupported_platform(os, arch) do
|
||||
raise """
|
||||
Unsupported platform: #{os} / #{arch}
|
||||
|
||||
This application ships precompiled SQLite extensions (unicode, vec0) for:
|
||||
• darwin-amd64 (macOS Intel)
|
||||
• darwin-arm64 (macOS Apple Silicon)
|
||||
• linux-amd64 (Linux x86_64)
|
||||
• linux-arm64 (Linux aarch64)
|
||||
|
||||
Your platform (#{inspect(os)} / #{arch}) is not supported. The SQLite extensions
|
||||
required for full-text search (unicode) and vector similarity search (vec0)
|
||||
cannot be loaded on this architecture.
|
||||
|
||||
To run on this platform, provide compatible builds of these extensions in
|
||||
priv/sqlite_extensions/#{os}-#{arch}/.
|
||||
"""
|
||||
end
|
||||
|
||||
defp platform_string! do
|
||||
case platform_string() do
|
||||
{:ok, platform} -> platform
|
||||
{:error, _os, _arch} -> raise "Unsupported OS or platform"
|
||||
end
|
||||
end
|
||||
|
||||
defp platform_string do
|
||||
[arch | _rest] =
|
||||
:erlang.system_info(:system_architecture)
|
||||
|> List.to_string()
|
||||
|> String.split("-")
|
||||
|
||||
{_, os} = :os.type()
|
||||
|
||||
case {os, arch} do
|
||||
{:darwin, "x86_64"} -> {:ok, "darwin-amd64"}
|
||||
{:darwin, "aarch64"} -> {:ok, "darwin-arm64"}
|
||||
{:linux, "x86_64"} -> {:ok, "linux-amd64"}
|
||||
{:linux, "aarch64"} -> {:ok, "linux-arm64"}
|
||||
_ -> {:error, os, arch}
|
||||
end
|
||||
end
|
||||
|
||||
def vacuum, do: query("VACUUM")
|
||||
|
||||
def optimize, do: query("PRAGMA optimize")
|
||||
|
||||
Reference in New Issue
Block a user