Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
135f6cd
Bump elixir version in asdf config
zoldar Jun 8, 2026
fa231dc
Remove unused module requires
zoldar Jun 8, 2026
fd015dc
Avoid generating function body inside `defprotocol`
zoldar Jun 8, 2026
4abbe0e
unused require fixup
zoldar Jun 8, 2026
ee4f079
Don't branch on always-true expression
zoldar Jun 8, 2026
53dacf2
Fix usages of :erlang.phash2 dialyzer trick
zoldar Jun 8, 2026
30cd6e6
Don't check for expression which is always true
zoldar Jun 8, 2026
ce1d00a
Fix faulty `repo.config` -> `repo.config()`
zoldar Jun 8, 2026
6a6f61f
Avoid using `Phoenix.Component` twice
zoldar Jun 8, 2026
166711b
Bump ExMoney
zoldar Jun 8, 2026
180be38
Add `logger_backends` dependency
zoldar Jun 8, 2026
eae765f
Use `LoggerBackends.add` instead of `Logger.add_backend`
zoldar Jun 8, 2026
9e57008
Remove test assertions already caught by type checker
zoldar Jun 8, 2026
eb9459b
Don't pattern match on regex
zoldar Jun 8, 2026
4634523
Avoid passing literal for subscription in `Status.in?` test calls
zoldar Jun 8, 2026
6162bb7
Fix a type issue in `Audit.Encoder` reported by dialyzer
zoldar Jun 9, 2026
fef47a3
Fix `phash2` workaround in `Plausible`
zoldar Jun 9, 2026
c235056
Bump OTP to 29.0.1 in asdf config
zoldar Jun 9, 2026
e37a389
Bump patch versions of `x509` and `credo` dependencies
zoldar Jun 9, 2026
a5ba6ec
Remove `Bamboo.SMTPAdapter` as it's no longer maintained and broken o…
zoldar Jun 9, 2026
85e322d
Make user agent test string larger to trigger ua timeout telemetry event
zoldar Jun 9, 2026
3bf562d
Fix regex comparison as internal reference is changing now
zoldar Jun 9, 2026
aa0119e
Add FIXME comments for surfaced cache bugs
zoldar Jun 9, 2026
4a536ae
Downgrade OTP to more proven 28.5.0.1
zoldar Jun 9, 2026
eb52e6d
Use exported regex pattern for stored regex to avoid cache issues
zoldar Jun 9, 2026
aef435b
Bump elixir in asdf to 1.20.1
zoldar Jun 10, 2026
2f73f2e
Add dialyzer-ignore file to suppress Ecto.Multi opaque errors
zoldar Jun 10, 2026
45e4700
Update builder image in Dockerfile
zoldar Jun 10, 2026
43cc84b
Bust CI cache
zoldar Jun 10, 2026
4a4e573
Fix type errors in CE
zoldar Jun 10, 2026
c82174c
Make user agent test string even longer to increase chances of timeout
zoldar Jun 10, 2026
321b06e
Address credo issues in touched files
zoldar Jun 10, 2026
43a2531
Fix another dialyzer regression
zoldar Jun 10, 2026
dfcdf22
Increase credo's max nesting threshold from 2 to 3 because this is bs
zoldar Jun 10, 2026
cd4f8ba
Allow operators in if condition assignment expressions in credo
zoldar Jun 10, 2026
f9f4082
Ignore a bogus dialyzer error
zoldar Jun 10, 2026
5792d5e
Re-add missing Logger require
zoldar Jun 10, 2026
75c72eb
Add changelog entry
zoldar Jun 10, 2026
623b817
Apply suggestions from code review
zoldar Jul 7, 2026
90df892
Bump elixir to 1.20.2 and OTP to 28.5.0.3
zoldar Jul 7, 2026
d91cc54
Update packages according to security advisories from mix deps.get
zoldar Jul 7, 2026
1684c43
Update code and tests to account for changed Finch error format
zoldar Jul 7, 2026
34f95d8
Stub DNS lookup in live domain change tests instead of expecting a si…
zoldar Jul 7, 2026
a45499b
Avoid logging error in live tests due to premature close of DB conn
zoldar Jul 7, 2026
4d589d5
Remove skipped empty test
zoldar Jul 7, 2026
ca5baa2
Remove TODO and adjust typespec for readability at least
zoldar Jul 7, 2026
8d1e924
Make credo happy about missing moduledocs for internal stuff
zoldar Jul 7, 2026
e163112
Bump phoenix in response to recent security advisory
zoldar Jul 7, 2026
6e1bc52
Update code comment
zoldar Jul 8, 2026
e4856e1
Returned informative error when `Bamboo.SMTPAdapter` is setup
zoldar Jul 8, 2026
758bf75
Use implicit nils in typcheck via macro workaround
zoldar Jul 8, 2026
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
5 changes: 2 additions & 3 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,15 @@
{Credo.Check.Refactor.CyclomaticComplexity, false},
{Credo.Check.Refactor.FilterCount, []},
{Credo.Check.Refactor.FilterFilter, []},
{Credo.Check.Refactor.MatchInCondition, []},
{Credo.Check.Refactor.MatchInCondition, [allow_operators: true]},
{Credo.Check.Refactor.RedundantWithClauseResult, []},
{Credo.Check.Refactor.RejectReject, []},
{Credo.Check.Refactor.FunctionArity, []},
{Credo.Check.Refactor.LongQuoteBlocks, []},
{Credo.Check.Refactor.MatchInCondition, []},
{Credo.Check.Refactor.MapJoin, []},
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
{Credo.Check.Refactor.NegatedConditionsWithElse, false},
{Credo.Check.Refactor.Nesting, []},
{Credo.Check.Refactor.Nesting, [max_nesting: 3]},
{Credo.Check.Refactor.UnlessWithElse, []},
{Credo.Check.Refactor.WithClauses, []},
{Credo.Check.Refactor.FilterFilter, []},
Expand Down
6 changes: 6 additions & 0 deletions .dialyzer_ignore.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{"lib/plausible/goals/goals.ex", :call_without_opaque},
{"lib/plausible/sites.ex", :call_without_opaque},
{"lib/plausible/teams/invitations.ex", :call_without_opaque},
{"lib/plausible_web/live/components/modal.ex", :pattern_match, 1}
]
2 changes: 1 addition & 1 deletion .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ concurrency:
cancel-in-progress: true

env:
CACHE_VERSION: v17
CACHE_VERSION: v18
PERSISTENT_CACHE_DIR: cached

jobs:
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
erlang 27.3.4.6
elixir 1.19.4-otp-27
erlang 28.5.0.3
elixir 1.20.2-otp-28
nodejs 23.2.0
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file.
### Removed

- Removed the standalone team switcher page; team switching is now done from the topbar dropdown only
- Removed `Bamboo.SMTPAdapter` from supported e-mail adapters; the library is no longer in active developments and does not compile under Elixir 1.20+

### Changed

Expand Down
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# we can not use the pre-built tar because the distribution is
# platform specific, it makes sense to build it in the docker

ARG ALPINE_VERSION=3.22.2

#### Builder
FROM hexpm/elixir:1.19.4-erlang-27.3.4.6-alpine-${ALPINE_VERSION} AS buildcontainer
FROM hexpm/elixir:1.20.2-erlang-28.5.0.3-alpine-3.22.5@sha256:8875407828c1b789485f9fcc6006e1ace39bb073b0467a95df3a3905d8d86d30 AS buildcontainer

ARG MIX_ENV=ce

Expand Down Expand Up @@ -55,7 +54,7 @@ COPY rel rel
RUN mix release plausible

# Main Docker Image
FROM alpine:${ALPINE_VERSION}
FROM alpine:3.22.5@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce
LABEL maintainer="plausible.io <hello@plausible.io>"

ARG BUILD_METADATA={}
Expand Down
16 changes: 4 additions & 12 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -743,18 +743,10 @@ case mailer_adapter do
api_key: get_var_from_path_or_env(config_dir, "SENDGRID_API_KEY")

"Bamboo.SMTPAdapter" ->
config :plausible, Plausible.Mailer,
adapter: Bamboo.SMTPAdapter,
server: get_var_from_path_or_env(config_dir, "SMTP_HOST_ADDR", "mail"),
hostname: base_url.host,
port: get_var_from_path_or_env(config_dir, "SMTP_HOST_PORT", "25"),
username: get_var_from_path_or_env(config_dir, "SMTP_USER_NAME"),
password: get_var_from_path_or_env(config_dir, "SMTP_USER_PWD"),
tls: :if_available,
allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
ssl: get_bool_from_path_or_env(config_dir, "SMTP_HOST_SSL_ENABLED", false),
retries: get_var_from_path_or_env(config_dir, "SMTP_RETRIES") || 2,
no_mx_lookups: get_bool_from_path_or_env(config_dir, "SMTP_MX_LOOKUPS_ENABLED", true)
raise ArgumentError, """
Bamboo.SMTPAdapter is no longer supported as the adapter is no longer maintained.
Please switch to Bamboo.Mua instead.
"""

"Bamboo.Mua" ->
config :plausible, Plausible.Mailer, adapter: Bamboo.Mua
Expand Down
4 changes: 2 additions & 2 deletions extra/lib/license.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ defmodule Plausible.License do
at hello@plausible.io to discuss obtaining a license.
"""

require Logger

if Mix.env() == :prod do
require Logger

def ensure_valid_license do
if has_valid_license?() do
:ok
Expand Down
19 changes: 12 additions & 7 deletions extra/lib/plausible/audit/encoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ defmodule Plausible.Audit.EncoderError do
end

defprotocol Plausible.Audit.Encoder do
def encode(x, opts \\ [])
def encode(x)

def encode(x, opts)
end

defimpl Plausible.Audit.Encoder, for: Ecto.Changeset do
def encode(changeset, opts) do
def encode(changeset, opts \\ []) do
changes =
Enum.reduce(changeset.changes, %{}, fn {k, v}, acc ->
Map.put(acc, k, Plausible.Audit.Encoder.encode(v, opts))
Expand All @@ -32,7 +34,7 @@ defimpl Plausible.Audit.Encoder, for: Ecto.Changeset do
end

defimpl Plausible.Audit.Encoder, for: Map do
def encode(x, opts) do
def encode(x, opts \\ []) do
{allow_not_loaded, data} = Map.pop(x, :__allow_not_loaded__)
raise_on_not_loaded? = Keyword.get(opts, :raise_on_not_loaded?, true)

Expand All @@ -53,22 +55,24 @@ defimpl Plausible.Audit.Encoder, for: Map do
end

defimpl Plausible.Audit.Encoder, for: [Integer, BitString, Float] do
def encode(x, _opts), do: x
def encode(x, _opts \\ []), do: x
end

defimpl Plausible.Audit.Encoder, for: [DateTime, Date, NaiveDateTime, Time] do
def encode(x, _opts), do: to_string(x)
def encode(x, _opts \\ []), do: to_string(x)
end

defimpl Plausible.Audit.Encoder, for: Atom do
def encode(x, opts \\ [])

def encode(nil, _opts), do: nil
def encode(true, _opts), do: true
def encode(false, _opts), do: false
def encode(x, _opts), do: Atom.to_string(x)
end

defimpl Plausible.Audit.Encoder, for: List do
def encode(x, opts) do
def encode(x, opts \\ []) do
Enum.map(x, &Plausible.Audit.Encoder.encode(&1, opts))
end
end
Expand Down Expand Up @@ -116,12 +120,13 @@ defimpl Plausible.Audit.Encoder, for: Any do

quote do
defimpl Plausible.Audit.Encoder, for: unquote(module) do
def encode(struct, opts) do
def encode(struct, opts \\ []) do
Plausible.Audit.Encoder.encode(unquote(extractor), opts)
end
end
end
end

def encode(_), do: raise("Implement me")
def encode(_, _), do: raise("Implement me")
end
1 change: 0 additions & 1 deletion extra/lib/plausible/auth/sso/domain/verification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ defmodule Plausible.Auth.SSO.Domain.Verification do
"""

alias Plausible.Auth.SSO.Domain
require Domain

@prefix "plausible-sso-verification"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ defmodule Plausible.InstallationSupport.Detection.Diagnostics do
@moduledoc """
Module responsible for translating diagnostics to user-friendly errors and recommendations.
"""
require Logger

# in this struct, nil means indeterminate
defstruct v1_detected: nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ defmodule Plausible.InstallationSupport.Verification.Diagnostics do
@moduledoc """
Module responsible for translating diagnostics to user-friendly errors and recommendations.
"""
require Logger

# In this struct
# - the default nil value for each field means that the value is indeterminate (e.g. we didn't even get to the part where response_status is set)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.Billing do
import Ecto.Query, only: [from: 2]
import PlausibleWeb.Components.Generic

require Plausible.Billing.Subscription.Status

def update(%{team: team}, socket) do
usage = Teams.Billing.quota_usage(team, with_features: true)

Expand Down
4 changes: 3 additions & 1 deletion lib/mix/tasks/create_free_subscription.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
defmodule Mix.Tasks.CreateFreeSubscription do
@moduledoc false

use Mix.Task
use Plausible.Repo
require Logger

alias Plausible.Billing.Subscription

# coveralls-ignore-start
Expand Down
3 changes: 2 additions & 1 deletion lib/mix/tasks/generate_referrer_favicons.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
defmodule Mix.Tasks.GenerateReferrerFavicons do
@moduledoc false

use Mix.Task
use Plausible.Repo
require Logger

@dialyzer {:nowarn_function, run: 1}
# coveralls-ignore-start
Expand Down
1 change: 0 additions & 1 deletion lib/mix/tasks/send_pageview.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ defmodule Mix.Tasks.SendPageview do
"""

use Mix.Task
require Logger

@default_host "http://localhost:8000"
@default_ip_address "127.0.0.1"
Expand Down
10 changes: 8 additions & 2 deletions lib/plausible.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ defmodule Plausible do

# :erlang.phash2(1, 1) == 0 tricks dialyzer as per:
# https://github.com/elixir-lang/elixir/blob/v1.12.3/lib/elixir/lib/gen_server.ex#L771-L778
# and also tricks elixir 1.18 type checker
# and also tricks elixir >1.18 type checker

defmacro always(term) do
quote do
:erlang.phash2(1, 1) == 0 && unquote(term)
case :erlang.phash2(1, 1) do
0 ->
unquote(term)

1 ->
not unquote(term)
end
end
end

Expand Down
4 changes: 1 addition & 3 deletions lib/plausible/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ defmodule Plausible.Application do
use Application
use Plausible

require Logger

def start(_type, _args) do
on_ee(do: Plausible.License.ensure_valid_license())
on_ce(do: :inet_db.set_tcp_module(:happy_tcp))
Expand Down Expand Up @@ -360,7 +358,7 @@ defmodule Plausible.Application do
end

def setup_sentry() do
Logger.add_backend(Sentry.LoggerBackend)
LoggerBackends.add(Sentry.LoggerBackend)

:telemetry.attach_many(
"oban-errors",
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible/billing/billing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Plausible.Billing do
"""
use Plausible
use Plausible.Repo
require Plausible.Billing.Subscription.Status

alias Plausible.Auth
alias Plausible.Billing.Subscription
alias Plausible.Teams
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ defmodule Plausible.DataMigration.PopulateEventSessionColumns do
"""
use Plausible.DataMigration, dir: "PopulateEventSessionColumns", repo: Plausible.IngestRepo

require Logger

# See https://clickhouse.com/docs/en/sql-reference/dictionaries#cache for meaning of these defaults
@default_dictionary_config %{
lifetime: 600_000,
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible/ecto/types/compiled_regex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ defmodule Plausible.Ecto.Types.CompiledRegex do
def cast(val) when is_binary(val), do: {:ok, val}
def cast(_), do: :error

def load(val), do: {:ok, Regex.compile!(val)}
def load(val), do: {:ok, Regex.compile!(val, "E")}
def dump(val), do: {:ok, val}
end
2 changes: 1 addition & 1 deletion lib/plausible/exports.ex
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ defmodule Plausible.Exports do
{:error, {:http_error, 404, _response}} ->
nil

{:error, %Mint.TransportError{} = e} ->
{:error, %Finch.TransportError{} = e} ->
raise e
end
end
Expand Down
2 changes: 0 additions & 2 deletions lib/plausible/google/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ defmodule Plausible.Google.API do
alias Plausible.Google.SearchConsole
alias Plausible.Stats.Query

require Logger

@search_console_scope URI.encode_www_form(
"email https://www.googleapis.com/auth/webmasters.readonly"
)
Expand Down
12 changes: 8 additions & 4 deletions lib/plausible/http_client.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule Plausible.HTTPClient.Non200Error do
@moduledoc false

defstruct reason: nil

@type t :: %__MODULE__{reason: Finch.Response.t()}
Expand All @@ -11,13 +13,17 @@ defmodule Plausible.HTTPClient.Non200Error do
end

defmodule Plausible.HTTPClient.Interface do
@moduledoc false

@type t() :: module()

@type finch_request_opts() :: Keyword.t()
@type url() :: Finch.Request.url()
@type headers() :: Finch.Request.headers()
@type params() :: Finch.Request.body() | map()
@type response() ::
{:ok, Finch.Response.t()}
| {:error, Mint.Types.error() | Finch.Error.t() | Plausible.HTTPClient.Non200Error.t()}
| {:error, Finch.error() | Plausible.HTTPClient.Non200Error.t()}

@callback get(url(), headers(), params()) :: response()
@callback get(url(), headers()) :: response()
Expand Down Expand Up @@ -58,9 +64,7 @@ defmodule Plausible.HTTPClient do
call(:get, url, headers, params)
end

# TODO: Is it possible to tell the type checker that we're returning a module that conforms to the
# Plausible.HTTPClient.Interface behaviour?
@spec impl() :: Plausible.HTTPClient
@spec impl() :: Plausible.HTTPClient.Interface.t()
def impl() do
Application.get_env(:plausible, :http_impl, __MODULE__)
end
Expand Down
2 changes: 0 additions & 2 deletions lib/plausible/ingestion/counters.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ defmodule Plausible.Ingestion.Counters do

@behaviour :gen_cycle

require Logger

alias Plausible.Ingestion.Counters.Buffer
alias Plausible.Ingestion.Counters.Record
alias Plausible.Ingestion.Counters.TelemetryHandler
Expand Down
2 changes: 0 additions & 2 deletions lib/plausible/ingestion/persistor/embedded.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ defmodule Plausible.Ingestion.Persistor.Embedded do

alias Plausible.ClickhouseEventV2

require Logger

def persist_event(ingest_event, previous_user_id, opts) do
event = ingest_event.clickhouse_event
session_attrs = ingest_event.clickhouse_session_attrs
Expand Down
4 changes: 2 additions & 2 deletions lib/plausible/open_telemetry.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule Plausible.OpenTelemetry do
@moduledoc false

require OpenTelemetry.Tracer, as: Tracer
alias OpenTelemetry.Tracer

def current_trace_id do
case OpenTelemetry.Tracer.current_span_ctx() do
case Tracer.current_span_ctx() do
:undefined ->
nil

Expand Down
2 changes: 0 additions & 2 deletions lib/plausible/open_telemetry/logger.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ defmodule Plausible.OpenTelemetry.Logger do
to the Logger metadata when Phoenix router dispatch starts.
"""

require Logger

@doc """
Attaches telemetry handlers to set trace_id in Logger metadata.
Expand Down
Loading
Loading