Platform upgrade: elixir 1.19.4 and otp 27.3.4.6 (#5920)

* Platform upgrade: elixir 1.19.4 and otp 27.3.4.6

* !fixup

* credo

* credo

* Bump cache

* fix docker image tag

* hum

* hum

* Match docker images

* Define ALPINE_VERSION once

* fixup
This commit is contained in:
Adam Rutkowski 2025-12-01 13:50:49 +01:00 committed by GitHub
parent 8a7d681c43
commit b64a2355a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 124 additions and 60 deletions

View File

@ -11,7 +11,7 @@ concurrency:
cancel-in-progress: true
env:
CACHE_VERSION: v16
CACHE_VERSION: v17
PERSISTENT_CACHE_DIR: cached
jobs:

View File

@ -1,3 +1,3 @@
erlang 27.3.1
elixir 1.18.3-otp-27
erlang 27.3.4.6
elixir 1.19.4-otp-27
nodejs 23.2.0

View File

@ -1,8 +1,10 @@
# 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.18.3-erlang-27.3.1-alpine-3.21.3 AS buildcontainer
FROM hexpm/elixir:1.19.4-erlang-27.3.4.6-alpine-${ALPINE_VERSION} AS buildcontainer
ARG MIX_ENV=ce
@ -20,7 +22,7 @@ RUN mkdir /app
WORKDIR /app
# install build dependencies
RUN apk add --no-cache git "nodejs-current=23.2.0-r1" yarn npm python3 ca-certificates wget gnupg make gcc libc-dev brotli
RUN apk add --no-cache git "nodejs-current=23.11.1-r0" yarn npm python3 ca-certificates wget gnupg make gcc libc-dev brotli
COPY mix.exs ./
COPY mix.lock ./
@ -54,7 +56,7 @@ COPY rel rel
RUN mix release plausible
# Main Docker Image
FROM alpine:3.21.3
FROM alpine:${ALPINE_VERSION}
LABEL maintainer="plausible.io <hello@plausible.io>"
ARG BUILD_METADATA={}
@ -84,3 +86,4 @@ EXPOSE 8000
ENV DEFAULT_DATA_DIR=/var/lib/plausible
VOLUME /var/lib/plausible
CMD ["run"]

View File

@ -1,15 +1,3 @@
defimpl Bamboo.Formatter, for: Plausible.Auth.User do
def format_email_address(user, _opts) do
{user.name, user.email}
end
end
defimpl FunWithFlags.Actor, for: Plausible.Auth.User do
def id(%{id: id}) do
"user:#{id}"
end
end
defmodule Plausible.Auth.User do
use Plausible
use Ecto.Schema
@ -284,3 +272,15 @@ defmodule Plausible.Auth.User do
end
end
end
defimpl Bamboo.Formatter, for: Plausible.Auth.User do
def format_email_address(user, _opts) do
{user.name, user.email}
end
end
defimpl FunWithFlags.Actor, for: Plausible.Auth.User do
def id(%{id: id}) do
"user:#{id}"
end
end

View File

@ -202,7 +202,7 @@ defmodule Plausible.Google.GA4.API do
end
end
defp prepare_request(report_request, date_range, property, access_token) do
defp prepare_request(%GA4.ReportRequest{} = report_request, date_range, property, access_token) do
%GA4.ReportRequest{
report_request
| date_range: date_range,

View File

@ -44,7 +44,7 @@ defmodule Plausible.Shield.CountryRuleCache do
|> where([rule, site], rule.country_code == ^country_code and site.domain == ^domain)
case Plausible.Repo.one(query) do
{_, _, rule} -> %CountryRule{rule | from_cache?: false}
{_, _, rule = %CountryRule{}} -> %CountryRule{rule | from_cache?: false}
_any -> nil
end
end

View File

@ -45,7 +45,7 @@ defmodule Plausible.Shield.HostnameRuleCache do
case Plausible.Repo.all(query) do
[_ | _] = results ->
Enum.map(results, fn {_, _, rule} ->
Enum.map(results, fn {_, _, rule = %HostnameRule{}} ->
%HostnameRule{rule | from_cache?: false}
end)

View File

@ -44,7 +44,7 @@ defmodule Plausible.Shield.IPRuleCache do
|> where([rule, site], rule.inet == ^address and site.domain == ^domain)
case Plausible.Repo.one(query) do
{_, _, rule} -> %IPRule{rule | from_cache?: false}
{_, _, rule = %IPRule{}} -> %IPRule{rule | from_cache?: false}
_any -> nil
end
end

View File

@ -44,7 +44,7 @@ defmodule Plausible.Shield.PageRuleCache do
|> where([..., site], site.domain == ^domain)
case Plausible.Repo.one(query) do
{_, _, rule} -> %PageRule{rule | from_cache?: false}
{_, _, rule = %PageRule{}} -> %PageRule{rule | from_cache?: false}
_any -> nil
end
end

View File

@ -63,7 +63,7 @@ defmodule Plausible.Site.Cache do
query = from s in base_db_query(), where: s.domain == ^domain
case Plausible.Repo.one(query) do
{_, _, site} -> %Site{site | from_cache?: false}
{_, _, site = %Site{}} -> %Site{site | from_cache?: false}
_any -> nil
end
end

View File

@ -100,7 +100,7 @@ defmodule Plausible.Stats.QueryOptimizer do
end
end
defp update_time_in_order_by(query) do
defp update_time_in_order_by(%Query{} = query) do
order_by =
query.order_by
|> Enum.map(fn
@ -126,7 +126,7 @@ defmodule Plausible.Stats.QueryOptimizer do
# To avoid showing referrers across hostnames when event:hostname
# filter is present for breakdowns, add entry/exit page hostname
# filters
defp extend_hostname_filters_to_visit(query) do
defp extend_hostname_filters_to_visit(%Query{} = query) do
# Note: Only works since event:hostname is only allowed as a top level filter
hostname_filters =
query.filters

View File

@ -31,7 +31,7 @@ defmodule PlausibleWeb.AvatarController do
end
@forwarded_headers ["content-type", "cache-control", "expires"]
defp forward_headers(conn, headers) do
defp forward_headers(%Plug.Conn{} = conn, headers) do
headers_to_forward = Enum.filter(headers, fn {k, _} -> k in @forwarded_headers end)
%Plug.Conn{conn | resp_headers: headers_to_forward}
end

View File

@ -130,7 +130,7 @@ defmodule PlausibleWeb.Favicon do
end
@forwarded_headers ["content-type", "cache-control", "expires"]
defp forward_headers(conn, headers) do
defp forward_headers(%Plug.Conn{} = conn, headers) do
headers_to_forward = Enum.filter(headers, fn {k, _} -> k in @forwarded_headers end)
%Plug.Conn{conn | resp_headers: headers_to_forward}
end

View File

@ -1,4 +1,4 @@
defmodule Plausible.CondolidatedView.CacheTestSync do
defmodule Plausible.CondolidatedView.CacheSyncTest do
use Plausible.DataCase, async: false
on_ee do
@ -27,10 +27,10 @@ defmodule Plausible.CondolidatedView.CacheTestSync do
}
] = Sentry.Test.pop_sentry_reports()
end
end
defp start_test_cache(cache_name) do
%{start: {m, f, a}} = Cache.child_spec(cache_name: cache_name)
apply(m, f, a)
end
end
end

View File

@ -1,9 +1,10 @@
defmodule Plausible.Segments.SegmentTest do
use ExUnit.Case
doctest Plausible.Segments.Segment, import: true
alias Plausible.Segments.Segment
doctest Segment, import: true
setup do
segment = %Plausible.Segments.Segment{
segment = %Segment{
name: "any name",
type: :personal,
segment_data: %{"filters" => ["is", "visit:page", ["/blog"]]},
@ -15,7 +16,7 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset has required fields" do
assert Plausible.Segments.Segment.changeset(%Plausible.Segments.Segment{}, %{}).errors == [
assert Segment.changeset(%Segment{}, %{}).errors == [
segment_data: {"property \"filters\" must be an array with at least one member", []},
name: {"can't be blank", [validation: :required]},
segment_data: {"can't be blank", [validation: :required]},
@ -27,7 +28,7 @@ defmodule Plausible.Segments.SegmentTest do
test "changeset does not allow setting owner_id to nil (setting to nil happens with database triggers)",
%{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
assert Segment.changeset(
valid_segment,
%{
owner_id: nil
@ -38,7 +39,7 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset forbids too long name", %{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
assert Segment.changeset(
valid_segment,
%{
name: String.duplicate("a", 256)
@ -51,7 +52,7 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset forbids too large segment_data", %{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
assert Segment.changeset(
valid_segment,
%{
segment_data:
@ -65,9 +66,9 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset allows setting nil owner_id to a user id (to be able to recover dangling site segments)",
%{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
%Plausible.Segments.Segment{
%{segment: valid_segment = %Segment{}} do
assert Segment.changeset(
%Segment{
valid_segment
| owner_id: nil
},
@ -78,7 +79,7 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset requires segment_data to be structured as expected", %{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
assert Segment.changeset(
valid_segment,
%{
segment_data: %{"filters" => 1, "labels" => true, "other" => []}
@ -93,7 +94,7 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset forbids empty filters list", %{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
assert Segment.changeset(
valid_segment,
%{
segment_data: %{
@ -107,7 +108,7 @@ defmodule Plausible.Segments.SegmentTest do
end
test "changeset permits well-structured segment data", %{segment: valid_segment} do
assert Plausible.Segments.Segment.changeset(
assert Segment.changeset(
valid_segment,
%{
segment_data: %{

View File

@ -224,7 +224,12 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QuerySpecialMetricsTest do
build(:pageview, user_id: 1, pathname: "/two", timestamp: ~N[2021-01-01 00:10:00]),
build(:pageview, user_id: 3, pathname: "/one", timestamp: ~N[2021-01-01 00:00:00]),
build(:pageview, user_id: 3, pathname: "/never-exit", timestamp: ~N[2021-01-01 00:00:00]),
build(:event, user_id: 3, name: "a", pathname: "/one", timestamp: ~N[2021-01-01 00:00:00]),
build(:event,
user_id: 3,
name: "a",
pathname: "/one",
timestamp: ~N[2021-01-01 00:00:00]
),
build(:pageview, user_id: 3, pathname: "/one", timestamp: ~N[2021-01-01 00:10:00])
])
@ -323,8 +328,16 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QuerySpecialMetricsTest do
test "in visit:exit_page breakdown filtered by visit:country", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, pathname: "/one", country_code: "EE", timestamp: ~N[2021-01-01 00:00:00]),
build(:pageview, pathname: "/one", country_code: "US", timestamp: ~N[2021-01-01 00:00:00]),
build(:pageview,
pathname: "/one",
country_code: "EE",
timestamp: ~N[2021-01-01 00:00:00]
),
build(:pageview,
pathname: "/one",
country_code: "US",
timestamp: ~N[2021-01-01 00:00:00]
),
build(:pageview,
user_id: 1,
pathname: "/one",

View File

@ -615,7 +615,11 @@ defmodule PlausibleWeb.Api.ExternalStatsController.TimeseriesTest do
user_id: @user_id,
timestamp: ~N[2021-01-01 00:25:00]
),
build(:pageview, pathname: "/blog", user_id: @user_id, timestamp: ~N[2021-01-01 00:25:00]),
build(:pageview,
pathname: "/blog",
user_id: @user_id,
timestamp: ~N[2021-01-01 00:25:00]
),
build(:pageview, pathname: "/", timestamp: ~N[2021-01-01 00:25:00])
])

View File

@ -1166,8 +1166,18 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do
populate_stats(site, site_import.id, [
build(:imported_pages, page: "/", visitors: 1, pageviews: 1, date: ~D[2021-01-01]),
build(:imported_pages, page: "/blog/one", visitors: 2, pageviews: 2, date: ~D[2021-01-01]),
build(:imported_pages, page: "/blog/two", visitors: 3, pageviews: 3, date: ~D[2021-01-01]),
build(:imported_pages,
page: "/blog/one",
visitors: 2,
pageviews: 2,
date: ~D[2021-01-01]
),
build(:imported_pages,
page: "/blog/two",
visitors: 3,
pageviews: 3,
date: ~D[2021-01-01]
),
build(:imported_pages,
page: "/blog/three",
visitors: 4,

View File

@ -849,7 +849,11 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
site: site
} do
populate_stats(site, [
build(:pageview, user_id: @user_id, pathname: "/blog", timestamp: ~N[2020-01-01 00:00:00]),
build(:pageview,
user_id: @user_id,
pathname: "/blog",
timestamp: ~N[2020-01-01 00:00:00]
),
build(:engagement,
user_id: @user_id,
pathname: "/blog",

View File

@ -21,8 +21,16 @@ defmodule PlausibleWeb.Api.StatsController.ScreenSizesTest do
test "returns bounce_rate and visit_duration when detailed=true", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, user_id: 123, timestamp: ~N[2021-01-01 12:00:00], screen_size: "Desktop"),
build(:pageview, user_id: 123, timestamp: ~N[2021-01-01 12:10:00], screen_size: "Desktop"),
build(:pageview,
user_id: 123,
timestamp: ~N[2021-01-01 12:00:00],
screen_size: "Desktop"
),
build(:pageview,
user_id: 123,
timestamp: ~N[2021-01-01 12:10:00],
screen_size: "Desktop"
),
build(:pageview, timestamp: ~N[2021-01-01 12:00:00], screen_size: "Desktop"),
build(:pageview, timestamp: ~N[2021-01-01 12:00:00], screen_size: "Laptop")
])

View File

@ -1279,7 +1279,11 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
populate_stats(site, [
build(:pageview, pathname: "/index", hostname: "example.com"),
build(:pageview, pathname: "/index", hostname: "example.com", user_id: @user_id),
build(:pageview, pathname: "/blog/post1", hostname: "blog.example.com", user_id: @user_id),
build(:pageview,
pathname: "/blog/post1",
hostname: "blog.example.com",
user_id: @user_id
),
build(:pageview, pathname: "/blog/post2", hostname: "blog.example.com")
])
@ -1306,8 +1310,16 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
populate_stats(site, [
build(:pageview, pathname: "/index", hostname: "example.com"),
build(:pageview, pathname: "/index", hostname: "example.com", user_id: @user_id),
build(:pageview, pathname: "/blog/post1", hostname: "blog.example.com", user_id: @user_id),
build(:pageview, pathname: "/blog/post2", hostname: "blog.example.com", user_id: @user_id),
build(:pageview,
pathname: "/blog/post1",
hostname: "blog.example.com",
user_id: @user_id
),
build(:pageview,
pathname: "/blog/post2",
hostname: "blog.example.com",
user_id: @user_id
),
build(:pageview, pathname: "/blog/post2", hostname: "blog.example.com"),
build(:pageview, pathname: "/blog/post2", hostname: "about.example.com")
])
@ -1336,8 +1348,16 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
populate_stats(site, [
build(:pageview, pathname: "/index", hostname: "example.com"),
build(:pageview, pathname: "/index", hostname: "example.com", user_id: @user_id),
build(:pageview, pathname: "/blog/post1", hostname: "blog.example.com", user_id: @user_id),
build(:pageview, pathname: "/blog/post2", hostname: "blog.example.com", user_id: @user_id),
build(:pageview,
pathname: "/blog/post1",
hostname: "blog.example.com",
user_id: @user_id
),
build(:pageview,
pathname: "/blog/post2",
hostname: "blog.example.com",
user_id: @user_id
),
build(:pageview, pathname: "/blog/post3", hostname: "blog.example.com"),
build(:pageview,
pathname: "/blog/post2",

View File

@ -37,7 +37,8 @@ defmodule ObanErrorReporterTest do
)
end)
assert log =~ "[error] ** (BadMapError) expected a map, got: :bad_job"
assert log =~ "(BadMapError)"
assert log =~ ":bad_job"
end
end
end