Re-enable Credo ModuleDoc check

Closes #108
This commit is contained in:
Claudio Ortolina
2026-03-13 10:52:01 +00:00
parent 4db9213129
commit 9e1dbfd530
48 changed files with 175 additions and 4 deletions
+27 -4
View File
@@ -4,11 +4,34 @@
name: "default", name: "default",
checks: %{ checks: %{
extra: [ extra: [
{Credo.Check.Refactor.Nesting, max_nesting: 3} {Credo.Check.Refactor.Nesting, max_nesting: 3},
{Credo.Check.Readability.ModuleDoc,
ignore_names: [
# Credo defaults
~r/(\.\w+Controller|\.Endpoint|\.\w+Live(\.\w+)?|\.Repo|\.Router|\.\w+Socket|\.\w+View|\.\w+HTML|\.\w+JSON|\.Telemetry|\.Layouts|\.Mailer)$/,
# Custom: external API internals follow a three-module pattern
# where only the facade needs documentation
~r/\.API(\..*)?$/,
~r/\.Config$/,
# Custom: test support modules
~r/Fixtures/,
# Custom: mix tasks
~r/^Mix\.Tasks\./
], ],
disabled: [ ignore_modules_using: [
{Credo.Check.Readability.ModuleDoc, []} # Credo defaults
] Credo.Check,
Ecto.Schema,
Phoenix.LiveView,
~r/\.Web$/,
# Custom: LiveComponents and Oban workers are self-documenting
Oban.Worker,
# Matches `use MusicLibraryWeb, :live_component` etc.
MusicLibraryWeb
]},
{Credo.Check.Refactor.CyclomaticComplexity, max_complexity: 12}
],
disabled: []
} }
# files etc. # files etc.
} }
+4
View File
@@ -1,4 +1,8 @@
defmodule BraveSearch do defmodule BraveSearch do
@moduledoc """
Brave Search API facade for cover art and artist image search.
"""
alias BraveSearch.API alias BraveSearch.API
@spec search_images(String.t(), keyword()) :: {:ok, [map()]} | {:error, term()} @spec search_images(String.t(), keyword()) :: {:ok, [map()]} | {:error, term()}
+4
View File
@@ -1,4 +1,8 @@
defmodule Discogs do defmodule Discogs do
@moduledoc """
Discogs API facade for artist profiles and images.
"""
alias Discogs.API alias Discogs.API
@spec get_artist(integer() | String.t()) :: {:ok, map()} | {:error, term()} @spec get_artist(integer() | String.t()) :: {:ok, map()} | {:error, term()}
+4
View File
@@ -1,4 +1,8 @@
defmodule LastFm do defmodule LastFm do
@moduledoc """
Last.fm API facade for scrobbling and listening history.
"""
alias LastFm.{API, Feed, Refresh, Scrobble, Track, Worker} alias LastFm.{API, Feed, Refresh, Scrobble, Track, Worker}
alias MusicLibrary.{BackgroundRepo, Repo} alias MusicLibrary.{BackgroundRepo, Repo}
+2
View File
@@ -1,4 +1,6 @@
defmodule LastFm.Import do defmodule LastFm.Import do
@moduledoc false
@spec batch(keyword()) :: {:ok, non_neg_integer()} | {:error, term()} @spec batch(keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
def batch(opts) do def batch(opts) do
with {:ok, tracks} <- LastFm.get_tracks(opts) do with {:ok, tracks} <- LastFm.get_tracks(opts) do
+2
View File
@@ -1,4 +1,6 @@
defmodule LastFm.Scrobble do defmodule LastFm.Scrobble do
@moduledoc false
defstruct [:track, :artist, :timestamp, :album, :album_artist, :mbid] defstruct [:track, :artist, :timestamp, :album, :album_artist, :mbid]
@type t :: %__MODULE__{ @type t :: %__MODULE__{
+2
View File
@@ -1,4 +1,6 @@
defmodule LastFm.Session do defmodule LastFm.Session do
@moduledoc false
require Record require Record
defstruct [:name, :key, :pro] defstruct [:name, :key, :pro]
+2
View File
@@ -1,4 +1,6 @@
defmodule LastFm.Supervisor do defmodule LastFm.Supervisor do
@moduledoc false
use Supervisor use Supervisor
def start_link(config) do def start_link(config) do
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicBrainz do defmodule MusicBrainz do
@moduledoc """
MusicBrainz API facade for release and artist metadata search.
"""
alias MusicBrainz.API alias MusicBrainz.API
@type search_opts :: [limit: non_neg_integer(), offset: non_neg_integer()] @type search_opts :: [limit: non_neg_integer(), offset: non_neg_integer()]
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicBrainz.Artist do defmodule MusicBrainz.Artist do
@moduledoc false
@enforce_keys [:id, :name, :sort_name] @enforce_keys [:id, :name, :sort_name]
defstruct [:id, :name, :sort_name, :country, :relations, :musicbrainz_data] defstruct [:id, :name, :sort_name, :country, :relations, :musicbrainz_data]
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicBrainz.ExternalLink do defmodule MusicBrainz.ExternalLink do
@moduledoc false
defstruct [:name, :url] defstruct [:name, :url]
@type t :: %__MODULE__{ @type t :: %__MODULE__{
+8
View File
@@ -1,4 +1,6 @@
defmodule MusicBrainz.Release do defmodule MusicBrainz.Release do
@moduledoc false
@enforce_keys [ @enforce_keys [
:id, :id,
:title, :title,
@@ -38,6 +40,8 @@ defmodule MusicBrainz.Release do
} }
defmodule Artist do defmodule Artist do
@moduledoc false
@enforce_keys [:id, :name, :sort_name] @enforce_keys [:id, :name, :sort_name]
defstruct [:id, :name, :sort_name] defstruct [:id, :name, :sort_name]
@@ -49,6 +53,8 @@ defmodule MusicBrainz.Release do
end end
defmodule Medium do defmodule Medium do
@moduledoc false
@enforce_keys [:title, :format, :number, :track_count, :tracks] @enforce_keys [:title, :format, :number, :track_count, :tracks]
defstruct [:title, :format, :number, :track_count, :tracks] defstruct [:title, :format, :number, :track_count, :tracks]
@@ -62,6 +68,8 @@ defmodule MusicBrainz.Release do
end end
defmodule Track do defmodule Track do
@moduledoc false
@enforce_keys [:id, :title, :artists, :length, :number, :position] @enforce_keys [:id, :title, :artists, :length, :number, :position]
defstruct [:id, :title, :artists, :length, :number, :position] defstruct [:id, :title, :artists, :length, :number, :position]
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicBrainz.ReleaseGroup do defmodule MusicBrainz.ReleaseGroup do
@moduledoc false
alias MusicBrainz.ReleaseGroupSearchResult alias MusicBrainz.ReleaseGroupSearchResult
@spec included_release_groups(map()) :: [ReleaseGroupSearchResult.t()] @spec included_release_groups(map()) :: [ReleaseGroupSearchResult.t()]
@@ -1,4 +1,6 @@
defmodule MusicBrainz.ReleaseGroupSearchResult do defmodule MusicBrainz.ReleaseGroupSearchResult do
@moduledoc false
alias MusicBrainz.ReleaseGroup alias MusicBrainz.ReleaseGroup
@enforce_keys [:id, :type, :title, :artists, :release_date] @enforce_keys [:id, :type, :title, :artists, :release_date]
@@ -1,4 +1,6 @@
defmodule MusicBrainz.ReleaseSearchResult do defmodule MusicBrainz.ReleaseSearchResult do
@moduledoc false
alias MusicBrainz.ReleaseGroup alias MusicBrainz.ReleaseGroup
@enforce_keys [:id, :title, :release_group, :artists, :date, :barcode, :media] @enforce_keys [:id, :title, :release_group, :artists, :date, :barcode, :media]
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.ArtistChat do defmodule MusicLibrary.ArtistChat do
@moduledoc """
Chat implementation for artists using OpenAI streaming with Wikipedia context.
"""
@behaviour MusicLibrary.Chat @behaviour MusicLibrary.Chat
alias MusicLibrary.Artists.ArtistInfo alias MusicLibrary.Artists.ArtistInfo
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Artists do defmodule MusicLibrary.Artists do
@moduledoc """
Artist metadata management from MusicBrainz, Discogs, Wikipedia, and Last.fm.
"""
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias MusicLibrary.Artists.ArtistInfo alias MusicLibrary.Artists.ArtistInfo
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Artists.Batch do defmodule MusicLibrary.Artists.Batch do
@moduledoc """
Batch operations for artists: refresh MusicBrainz, Discogs, Wikipedia, and Last.fm data.
"""
import Ecto.Query import Ecto.Query
alias MusicLibrary.Artists alias MusicLibrary.Artists
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Assets do defmodule MusicLibrary.Assets do
@moduledoc """
Binary asset storage and retrieval for covers and artist images.
"""
alias MusicLibrary.Assets.{Asset, Cache} alias MusicLibrary.Assets.{Asset, Cache}
alias MusicLibrary.Repo alias MusicLibrary.Repo
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Assets.Cache do defmodule MusicLibrary.Assets.Cache do
@moduledoc """
ETS-based asset cache with TTL for serving frequently accessed images.
"""
@spec new() :: :ets.table() @spec new() :: :ets.table()
def new do def new do
:ets.new(__MODULE__, [:named_table, :public, :compressed, read_concurrency: true]) :ets.new(__MODULE__, [:named_table, :public, :compressed, read_concurrency: true])
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Assets.Image do defmodule MusicLibrary.Assets.Image do
@moduledoc """
Image processing via Vix (libvips) for covers and artist images.
"""
alias Vix.Vips.{Image, Operation} alias Vix.Vips.{Image, Operation}
fallback_path = Application.app_dir(:music_library, ["priv", "image-not-found.jpg"]) fallback_path = Application.app_dir(:music_library, ["priv", "image-not-found.jpg"])
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Assets.Transform do defmodule MusicLibrary.Assets.Transform do
@moduledoc """
Represents an image transformation (hash + target width) for asset serving.
"""
@derive JSON.Encoder @derive JSON.Encoder
defstruct [:hash, :width] defstruct [:hash, :width]
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicLibrary.BackgroundRepo do defmodule MusicLibrary.BackgroundRepo do
@moduledoc false
use Ecto.Repo, use Ecto.Repo,
otp_app: :music_library, otp_app: :music_library,
adapter: Ecto.Adapters.SQLite3 adapter: Ecto.Adapters.SQLite3
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.BarcodeScan do defmodule MusicLibrary.BarcodeScan do
@moduledoc """
Barcode-to-MusicBrainz lookup workflow.
"""
alias MusicLibrary.BarcodeScan.Result alias MusicLibrary.BarcodeScan.Result
alias MusicLibrary.Records alias MusicLibrary.Records
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicLibrary.BarcodeScan.Result do defmodule MusicLibrary.BarcodeScan.Result do
@moduledoc false
defstruct [:status, :number, :record_id, :release] defstruct [:status, :number, :record_id, :release]
@type t :: %__MODULE__{ @type t :: %__MODULE__{
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Batch do defmodule MusicLibrary.Batch do
@moduledoc """
Generic batch runner: streams records through a transaction with error accumulation.
"""
alias MusicLibrary.Repo alias MusicLibrary.Repo
require Logger require Logger
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Chat do defmodule MusicLibrary.Chat do
@moduledoc """
Behaviour for streaming AI chat with entity-specific context.
"""
@callback stream_response( @callback stream_response(
messages :: list(map()), messages :: list(map()),
context :: term(), context :: term(),
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Collection do defmodule MusicLibrary.Collection do
@moduledoc """
Queries for collected records (where `purchased_at` is set).
"""
import Ecto.Query, warn: false import Ecto.Query, warn: false
import MusicLibrary.Records, only: [order_alphabetically: 0] import MusicLibrary.Records, only: [order_alphabetically: 0]
+1
View File
@@ -1,3 +1,4 @@
defmodule MusicLibrary.Encrypted.Binary do defmodule MusicLibrary.Encrypted.Binary do
@moduledoc false
use Cloak.Ecto.Binary, vault: MusicLibrary.Vault use Cloak.Ecto.Binary, vault: MusicLibrary.Vault
end end
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Notes do defmodule MusicLibrary.Notes do
@moduledoc """
Free-text notes attached to records and artists.
"""
alias MusicLibrary.Notes.Note alias MusicLibrary.Notes.Note
alias MusicLibrary.Repo alias MusicLibrary.Repo
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.RecordChat do defmodule MusicLibrary.RecordChat do
@moduledoc """
Chat implementation for records using OpenAI streaming with web search.
"""
@behaviour MusicLibrary.Chat @behaviour MusicLibrary.Chat
alias MusicLibrary.Records.Record alias MusicLibrary.Records.Record
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.RecordSets do defmodule MusicLibrary.RecordSets do
@moduledoc """
User-curated record groupings with ordered items.
"""
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias MusicLibrary.RecordSets.{RecordSet, RecordSetItem} alias MusicLibrary.RecordSets.{RecordSet, RecordSetItem}
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Records.Batch do defmodule MusicLibrary.Records.Batch do
@moduledoc """
Batch operations for records: refresh MusicBrainz data and generate embeddings.
"""
import Ecto.Query import Ecto.Query
alias MusicLibrary.Batch alias MusicLibrary.Batch
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Records.TracklistPdf do defmodule MusicLibrary.Records.TracklistPdf do
@moduledoc """
Generates 120mm x 120mm PDF tracklists from record and release data via Typst.
"""
alias MusicBrainz.Release alias MusicBrainz.Release
alias MusicLibraryWeb.Duration alias MusicLibraryWeb.Duration
alias Typst.Format alias Typst.Format
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.ScrobbleActivity do defmodule MusicLibrary.ScrobbleActivity do
@moduledoc """
Scrobbling releases, media, and tracks to Last.fm.
"""
alias LastFm.Scrobble alias LastFm.Scrobble
alias MusicBrainz.Release alias MusicBrainz.Release
alias MusicLibrary.Secrets alias MusicLibrary.Secrets
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Secrets do defmodule MusicLibrary.Secrets do
@moduledoc """
Encrypted key-value storage for API keys and credentials.
"""
alias MusicLibrary.Repo alias MusicLibrary.Repo
alias MusicLibrary.Secrets.Secret alias MusicLibrary.Secrets.Secret
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicLibrary.TelemetryRepo do defmodule MusicLibrary.TelemetryRepo do
@moduledoc false
use Ecto.Repo, use Ecto.Repo,
otp_app: :music_library, otp_app: :music_library,
adapter: Ecto.Adapters.SQLite3 adapter: Ecto.Adapters.SQLite3
+1
View File
@@ -1,3 +1,4 @@
defmodule MusicLibrary.Vault do defmodule MusicLibrary.Vault do
@moduledoc false
use Cloak.Vault, otp_app: :music_library use Cloak.Vault, otp_app: :music_library
end end
+4
View File
@@ -1,4 +1,8 @@
defmodule MusicLibrary.Wishlist do defmodule MusicLibrary.Wishlist do
@moduledoc """
Queries for wishlisted records (where `purchased_at` is nil).
"""
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias MusicLibrary.Records alias MusicLibrary.Records
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicLibraryWeb.Auth do defmodule MusicLibraryWeb.Auth do
@moduledoc false
use Gettext, backend: MusicLibraryWeb.Gettext use Gettext, backend: MusicLibraryWeb.Gettext
import LiveToast, only: [put_toast: 3] import LiveToast, only: [put_toast: 3]
@@ -1,4 +1,6 @@
defmodule MusicLibraryWeb.Hooks.ShowToast do defmodule MusicLibraryWeb.Hooks.ShowToast do
@moduledoc false
import Phoenix.LiveView import Phoenix.LiveView
import LiveToast, only: [put_toast: 3] import LiveToast, only: [put_toast: 3]
@@ -1,4 +1,6 @@
defmodule MusicLibraryWeb.LiveHelpers.Params do defmodule MusicLibraryWeb.LiveHelpers.Params do
@moduledoc false
@pagination Application.compile_env!(:music_library, :pagination) @pagination Application.compile_env!(:music_library, :pagination)
@spec parse_page(String.t() | nil | term()) :: pos_integer() @spec parse_page(String.t() | nil | term()) :: pos_integer()
@@ -1,4 +1,6 @@
defmodule MusicLibraryWeb.Telemetry.Storage do defmodule MusicLibraryWeb.Telemetry.Storage do
@moduledoc false
use GenServer use GenServer
@buffer_size Application.compile_env( @buffer_size Application.compile_env(
+4
View File
@@ -1,4 +1,8 @@
defmodule OpenAI do defmodule OpenAI do
@moduledoc """
OpenAI API facade for text embeddings and streaming chat.
"""
alias OpenAI.API alias OpenAI.API
@type chat_stream_opts :: [ @type chat_stream_opts :: [
+2
View File
@@ -1,4 +1,6 @@
defmodule OpenAI.Completion do defmodule OpenAI.Completion do
@moduledoc false
@enforce_keys [:content] @enforce_keys [:content]
defstruct content: "", defstruct content: "",
temperature: 0.2, temperature: 0.2,
+4
View File
@@ -1,4 +1,8 @@
defmodule Wikipedia do defmodule Wikipedia do
@moduledoc """
Wikipedia API facade for artist biographies.
"""
alias Wikipedia.API alias Wikipedia.API
@spec get_artist_summary(String.t()) :: {:ok, map()} | {:error, :no_english_wikipedia | term()} @spec get_artist_summary(String.t()) :: {:ok, map()} | {:error, :no_english_wikipedia | term()}
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicLibrary.ColorHelpers do defmodule MusicLibrary.ColorHelpers do
@moduledoc false
@doc """ @doc """
Returns true if the given string is a valid lowercase hex color code. Returns true if the given string is a valid lowercase hex color code.
+2
View File
@@ -1,4 +1,6 @@
defmodule MusicLibraryWeb.LiveTestHelpers do defmodule MusicLibraryWeb.LiveTestHelpers do
@moduledoc false
def escape(string) do def escape(string) do
LazyHTML.html_escape(string) LazyHTML.html_escape(string)
end end