From c0a19b6d9af8eb090133dcabec1c6af7d1aa369c Mon Sep 17 00:00:00 2001 From: kp-cat <52385411+kp-cat@users.noreply.github.com> Date: Thu, 18 Jun 2026 11:10:16 +0200 Subject: [PATCH 01/13] security update: httpoison --- mix.exs | 2 +- mix.lock | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 25899bc..4429034 100644 --- a/mix.exs +++ b/mix.exs @@ -77,7 +77,7 @@ defmodule ConfigCat.MixProject do {:elixir_uuid, "~> 1.2"}, {:ex_doc, "~> 0.31.0", only: :dev, runtime: false}, {:excoveralls, "~> 0.18.0", only: :test}, - {:httpoison, "~> 1.7 or ~> 2.0"}, + {:httpoison, "~> 2.0 or ~> 3.0"}, {:jason, "~> 1.2"}, {:mix_test_interactive, "~> 1.2", only: :dev, runtime: false}, {:mox, "~> 1.1", only: :test}, diff --git a/mix.lock b/mix.lock index 3227b39..e39e081 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, + "certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"}, "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, @@ -9,22 +9,25 @@ "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "excoveralls": {:hex, :excoveralls, "0.18.2", "86efd87a0676a3198ff50b8c77620ea2f445e7d414afa9ec6c4ba84c9f8bdcc2", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230262c418f0de64077626a498bd4fdf1126d5c2559bb0e6b43deac3005225a4"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, - "httpoison": {:hex, :httpoison, "2.2.3", "a599d4b34004cc60678999445da53b5e653630651d4da3d14675fedc9dd34bd6", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "fa0f2e3646d3762fdc73edb532104c8619c7636a6997d20af4003da6cfc53e53"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"}, + "hackney": {:hex, :hackney, "4.4.3", "644c79b2eac605724e55fca651259c0560c4eea44b4c60b64baf5a2258b99376", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.1", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "db6bbd96ff8562e6398c0f468ae63fc516857a0a12ecf232b3cb8e34fd167018"}, + "httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"}, + "idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, + "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, "mix_test_interactive": {:hex, :mix_test_interactive, "1.2.2", "72f72faa7007d6cb9634ee5f6989b25ee5b194c5729e5e45a962e68b2e217374", [:mix], [{:file_system, "~> 0.2", [hex: :file_system, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "f49f2a70d00aee93418506dde4d95387fe56bdba501ef9d2aa06ea07d4823508"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"}, + "quic": {:hex, :quic, "1.6.5", "28b7d49c1732b2de3861701bb03ae7798537240552370ac49f0d139d2ea8f621", [:rebar3], [], "hexpm", "de1a88972c33201a50d1a17c8c4a14528bd1d8f25ef705897d680ae312d0aa78"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "styler": {:hex, :styler, "0.11.9", "2595393b94e660cd6e8b582876337cc50ff047d184ccbed42fdad2bfd5d78af5", [:mix], [], "hexpm", "8b7806ba1fdc94d0a75127c56875f91db89b75117fcc67572661010c13e1f259"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, "tz": {:hex, :tz, "0.26.6", "4d46178dd5bc4d2c1e78c9affcc3fd46764e29cd2a148c06666edb83cb18629f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.6", [hex: :mint, repo: "hexpm", optional: true]}], "hexpm", "9ca97ea48b412f2404740867f6c321ee8ce112602035bb79b0b90c9c03174652"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, + "webtransport": {:hex, :webtransport, "0.4.1", "60ef6cd99282b5964c8172d674519239e31e357d934bde028bec70da34636fe5", [:rebar3], [{:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "006e4e52a8f03b69201d4637c85b424a4ddccc1909f32c5742fed8d495a75174"}, } From 3528ce5e9bcb52203e28ae9889f51eea879d007b Mon Sep 17 00:00:00 2001 From: kp-cat <52385411+kp-cat@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:00:53 +0200 Subject: [PATCH 02/13] drop not outdated elixir versions. Min Elixir version is 1.16 --- .github/workflows/elixir-ci.yml | 6 +++--- .github/workflows/snyk.yml | 2 +- mix.exs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/elixir-ci.yml b/.github/workflows/elixir-ci.yml index 73f29d6..6b8a18a 100644 --- a/.github/workflows/elixir-ci.yml +++ b/.github/workflows/elixir-ci.yml @@ -12,15 +12,15 @@ on: workflow_dispatch: env: - ELIXIR_VERSION: '1.16.x' - OTP_VERSION: '24.x' + ELIXIR_VERSION: '1.18.x' + OTP_VERSION: '27.x' jobs: test: runs-on: ubuntu-latest strategy: matrix: - elixir-version: ['1.12.x', '1.13.x', '1.14.x', '1.15.x', '1.16.x'] + elixir-version: ['1.16.x', '1.17.x', '1.18.x', '1.19.x', '1.20.x'] steps: - uses: actions/checkout@v2 - name: Set up Elixir diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 94072e8..533bf22 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -9,7 +9,7 @@ on: workflow_dispatch: env: - ELIXIR_VERSION: '1.15.x' + ELIXIR_VERSION: '1.16.x' OTP_VERSION: '26.x' jobs: diff --git a/mix.exs b/mix.exs index 4429034..ee94645 100644 --- a/mix.exs +++ b/mix.exs @@ -9,8 +9,8 @@ defmodule ConfigCat.MixProject do name: "ConfigCat", source_url: @source_url, homepage_url: "https://configcat.com/", - version: "4.0.4", - elixir: "~> 1.12", + version: "5.0.0", + elixir: "~> 1.16", description: description(), package: package(), elixirc_options: elixirc_options(Mix.env()), From b7c24bf8749c6c90ce5b40d4eca02af0d4bcc409 Mon Sep 17 00:00:00 2001 From: kp-cat <52385411+kp-cat@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:51:57 +0200 Subject: [PATCH 03/13] dep update --- mix.exs | 2 +- mix.lock | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mix.exs b/mix.exs index ee94645..8b88a6d 100644 --- a/mix.exs +++ b/mix.exs @@ -79,7 +79,7 @@ defmodule ConfigCat.MixProject do {:excoveralls, "~> 0.18.0", only: :test}, {:httpoison, "~> 2.0 or ~> 3.0"}, {:jason, "~> 1.2"}, - {:mix_test_interactive, "~> 1.2", only: :dev, runtime: false}, + {:mix_test_interactive, "~> 5.1", only: :dev, runtime: false}, {:mox, "~> 1.1", only: :test}, {:styler, "~> 0.11", only: [:dev, :test], runtime: false}, {:typed_struct, "~> 0.3.0"}, diff --git a/mix.lock b/mix.lock index e39e081..3625a5e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,28 +1,29 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"}, - "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, + "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "excoveralls": {:hex, :excoveralls, "0.18.2", "86efd87a0676a3198ff50b8c77620ea2f445e7d414afa9ec6c4ba84c9f8bdcc2", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230262c418f0de64077626a498bd4fdf1126d5c2559bb0e6b43deac3005225a4"}, - "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, "h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"}, "hackney": {:hex, :hackney, "4.4.3", "644c79b2eac605724e55fca651259c0560c4eea44b4c60b64baf5a2258b99376", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.1", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "db6bbd96ff8562e6398c0f468ae63fc516857a0a12ecf232b3cb8e34fd167018"}, "httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"}, "idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"}, - "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, - "mix_test_interactive": {:hex, :mix_test_interactive, "1.2.2", "72f72faa7007d6cb9634ee5f6989b25ee5b194c5729e5e45a962e68b2e217374", [:mix], [{:file_system, "~> 0.2", [hex: :file_system, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "f49f2a70d00aee93418506dde4d95387fe56bdba501ef9d2aa06ea07d4823508"}, + "mix_test_interactive": {:hex, :mix_test_interactive, "5.1.0", "a2edd66806f06f6a0dbb92f023126a7e4ecdf02f7d34e3bb16f76886b79bed43", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:process_tree, ">= 0.1.3", [hex: :process_tree, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "0df8ab74c176040acd78939e92a5328775bf9f414a0f0472372cce852ee548cf"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"}, + "process_tree": {:hex, :process_tree, "0.3.0", "0eb58ec68f3d22a4f36040b0374469464c95fae5465c011b55bea01649b0bd70", [:mix], [], "hexpm", "6cb3b7be9c7d74b28a9f6e0f03115d5953e6bbda44be2b6fd9e667020870eb86"}, "quic": {:hex, :quic, "1.6.5", "28b7d49c1732b2de3861701bb03ae7798537240552370ac49f0d139d2ea8f621", [:rebar3], [], "hexpm", "de1a88972c33201a50d1a17c8c4a14528bd1d8f25ef705897d680ae312d0aa78"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "styler": {:hex, :styler, "0.11.9", "2595393b94e660cd6e8b582876337cc50ff047d184ccbed42fdad2bfd5d78af5", [:mix], [], "hexpm", "8b7806ba1fdc94d0a75127c56875f91db89b75117fcc67572661010c13e1f259"}, From 460be6c1dac8ef7552ca1147a369fc467ba24251 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 15:21:25 -0700 Subject: [PATCH 04/13] Use latest Elixir/Erlang versions locally --- .tool-versions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tool-versions b/.tool-versions index 08216bb..a97a008 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.16.0-otp-26 -erlang 26.2 +elixir 1.20.2-otp-29 +erlang 29.0.3 From 3bf922ae36840dac44bf88c9c9fa8d8b0b527a03 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 15:21:45 -0700 Subject: [PATCH 05/13] Only check formatting on latest Elixir version (1.20.*) --- .github/workflows/elixir-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/elixir-ci.yml b/.github/workflows/elixir-ci.yml index 6b8a18a..3437673 100644 --- a/.github/workflows/elixir-ci.yml +++ b/.github/workflows/elixir-ci.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: env: - ELIXIR_VERSION: '1.18.x' + ELIXIR_VERSION: '1.20.x' OTP_VERSION: '27.x' jobs: From 9faf517646f55bfb5596ac5c53a02a6d0c00f192 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 15:22:21 -0700 Subject: [PATCH 06/13] Switch to `preferred_envs` `preferred_cli_env` was deprecated as of Elixir 1.15, and we've now moved past that version. --- mix.exs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 8b88a6d..696617f 100644 --- a/mix.exs +++ b/mix.exs @@ -28,13 +28,7 @@ defmodule ConfigCat.MixProject do logo: "assets/logo.png", main: "readme" ], - test_coverage: [tool: ExCoveralls], - preferred_cli_env: [ - coveralls: :test, - "coveralls.travis": :test, - "coveralls.html": :test, - "coveralls.json": :test - ] + test_coverage: [tool: ExCoveralls] ] end @@ -45,6 +39,17 @@ defmodule ConfigCat.MixProject do ] end + def cli do + [ + preferred_envs: [ + coveralls: :test, + "coveralls.travis": :test, + "coveralls.html": :test, + "coveralls.json": :test + ] + ] + end + defp elixirc_options(:dev) do [ all_warnings: true, From 3bf503c4987c197abac04d411cb2119f0a8756cf Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 15:22:47 -0700 Subject: [PATCH 07/13] Convert unused requires into aliases This fixes a new compiler warning in Elixir 1.20. --- lib/config_cat/cache_policy/lazy.ex | 3 +-- lib/config_cat/cache_policy/manual.ex | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/config_cat/cache_policy/lazy.ex b/lib/config_cat/cache_policy/lazy.ex index 4cbb118..2f191fe 100644 --- a/lib/config_cat/cache_policy/lazy.ex +++ b/lib/config_cat/cache_policy/lazy.ex @@ -7,11 +7,10 @@ defmodule ConfigCat.CachePolicy.Lazy do alias ConfigCat.CachePolicy.Helpers alias ConfigCat.CachePolicy.Helpers.State + alias ConfigCat.ConfigCatLogger alias ConfigCat.ConfigEntry alias ConfigCat.FetchTime - require ConfigCat.ConfigCatLogger, as: ConfigCatLogger - typedstruct enforce: true do field :cache_refresh_interval_ms, non_neg_integer() field :mode, String.t(), default: "l" diff --git a/lib/config_cat/cache_policy/manual.ex b/lib/config_cat/cache_policy/manual.ex index 0bec1af..4ce84f4 100644 --- a/lib/config_cat/cache_policy/manual.ex +++ b/lib/config_cat/cache_policy/manual.ex @@ -7,8 +7,7 @@ defmodule ConfigCat.CachePolicy.Manual do alias ConfigCat.CachePolicy.Helpers alias ConfigCat.CachePolicy.Helpers.State - - require ConfigCat.ConfigCatLogger, as: ConfigCatLogger + alias ConfigCat.ConfigCatLogger typedstruct enforce: true do field :mode, String.t(), default: "m" From 2055bd471a724ff91c239ba5e48b84c8f37b111f Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 15:41:20 -0700 Subject: [PATCH 08/13] Fix type warnings in table-driven tests When we generate tests in a `for` loop, it generates individual tests with values hard-coded into the test body. The Elixir 1.20 type-checker knows the exact type of each value, so gives us warnings when we have conditional code that works for all of the possible values. The workaround is to put the test values into `@tag`s and then extract them from the test context. For consistency, we modify all table-driven tests to use the `@tag`/test context pattern. --- test/config_cat/cache_test.exs | 9 ++- test/config_cat_test.exs | 19 ++++-- test/flag_override_test.exs | 40 ++++++++----- test/integration_test.exs | 12 ++-- test/rollout_test.exs | 104 +++++++++++++++++++++------------ 5 files changed, 120 insertions(+), 64 deletions(-) diff --git a/test/config_cat/cache_test.exs b/test/config_cat/cache_test.exs index ee6770a..37bdcc6 100644 --- a/test/config_cat/cache_test.exs +++ b/test/config_cat/cache_test.exs @@ -18,9 +18,12 @@ defmodule ConfigCat.CacheTest do {"configcat-sdk-1/TEST_KEY-0123456789012/1234567890123456789012", "f83ba5d45bceb4bb704410f51b704fb6dfa19942"}, {"configcat-sdk-1/TEST_KEY2-123456789012/1234567890123456789012", "da7bfd8662209c8ed3f9db96daed4f8d91ba5876"} ] do - test "generates platform-independent cache keys - #{sdk_key}" do - sdk_key = unquote(sdk_key) - expected_cache_key = unquote(expected_cache_key) + @tag expected_cache_key: expected_cache_key, + sdk_key: sdk_key + test "generates platform-independent cache keys - #{sdk_key}", %{ + expected_cache_key: expected_cache_key, + sdk_key: sdk_key + } do assert Cache.generate_key(sdk_key) == expected_cache_key end end diff --git a/test/config_cat_test.exs b/test/config_cat_test.exs index 4503312..a869c85 100644 --- a/test/config_cat_test.exs +++ b/test/config_cat_test.exs @@ -169,13 +169,20 @@ defmodule ConfigCatTest do {"testBoolKey", nil, 0.1, true, true}, {"testBoolKey", nil, "default", true, true} ] do + @tag default_value: default_value, + expected_value: expected_value, + key: key, + user_id: user_id, + warning?: warning? test "default value and setting type mismatch with key: #{key} user_id: #{user_id} default_value: #{default_value}", - %{client: client} do - key = unquote(key) - user_id = unquote(user_id) - default_value = unquote(default_value) - expected_value = unquote(expected_value) - warning? = unquote(warning?) + %{ + client: client, + default_value: default_value, + expected_value: expected_value, + key: key, + user_id: user_id, + warning?: warning? + } do user = if user_id, do: User.new(user_id) logs = diff --git a/test/flag_override_test.exs b/test/flag_override_test.exs index c93affa..bce0c8d 100644 --- a/test/flag_override_test.exs +++ b/test/flag_override_test.exs @@ -176,18 +176,24 @@ defmodule ConfigCat.FlagOverrideTest do {"stringDependsOnInt", "2", "john@notsensitivecompany.com", :local_over_remote, "Dog"}, {"stringDependsOnInt", "2", "john@notsensitivecompany.com", :local_only, nil} ] do - test "prerequisite flag override with key: #{key} user_id: #{user_id} email: #{email} override behaviour: #{inspect(override_behaviour)}" do + @tag email: email, + expected_value: expected_value, + key: key, + override_behaviour: override_behaviour, + user_id: user_id + test "prerequisite flag override with key: #{key} user_id: #{user_id} email: #{email} override behaviour: #{inspect(override_behaviour)}", + %{ + email: email, + expected_value: expected_value, + key: key, + override_behaviour: override_behaviour, + user_id: user_id + } do # The flag override alters the definition of the following flags: # * 'mainStringFlag': to check the case where a prerequisite flag is # overridden (dependent flag: 'stringDependsOnString') # * 'stringDependsOnInt': to check the case where a dependent flag is # overridden (prerequisite flag: 'mainIntFlag') - key = unquote(key) - user_id = unquote(user_id) - email = unquote(email) - override_behaviour = unquote(override_behaviour) - expected_value = unquote(expected_value) - user = User.new(user_id, email: email) overrides = @@ -214,17 +220,23 @@ defmodule ConfigCat.FlagOverrideTest do {"notDeveloperAndNotBetaUserSegment", "2", "kate@example.com", :local_over_remote, true}, {"notDeveloperAndNotBetaUserSegment", "2", "kate@example.com", :local_only, nil} ] do - test "salt/segment override with key: #{key} user_id: #{user_id} email: #{email} override behaviour: #{inspect(override_behaviour)}" do + @tag email: email, + expected_value: expected_value, + key: key, + override_behaviour: override_behaviour, + user_id: user_id + test "salt/segment override with key: #{key} user_id: #{user_id} email: #{email} override behaviour: #{inspect(override_behaviour)}", + %{ + email: email, + expected_value: expected_value, + key: key, + override_behaviour: override_behaviour, + user_id: user_id + } do # The flag override uses a different config json salt than the downloaded one and # overrides the following segments: # * "Beta Users": User.Email IS ONE OF ["jane@example.com"] # * "Developers": User.Email IS ONE OF ["john@example.com"] - key = unquote(key) - user_id = unquote(user_id) - email = unquote(email) - override_behaviour = unquote(override_behaviour) - expected_value = unquote(expected_value) - user = User.new(user_id, email: email) overrides = diff --git a/test/integration_test.exs b/test/integration_test.exs index 44d5d3d..a7124fd 100644 --- a/test/integration_test.exs +++ b/test/integration_test.exs @@ -45,10 +45,14 @@ defmodule ConfigCat.IntegrationTest do {"configcat-proxy/sdk-key-90123456789012", false, false}, {"configcat-proxy/sdk-key-90123456789012", true, true} ] do - test "validates SDK key format - sdk_key: #{sdk_key} | custom_base_url: #{custom_base_url?}" do - sdk_key = unquote(sdk_key) - custom_base_url? = unquote(custom_base_url?) - valid? = unquote(valid?) + @tag custom_base_url?: custom_base_url?, + sdk_key: sdk_key, + valid?: valid? + test "validates SDK key format - sdk_key: #{sdk_key} | custom_base_url: #{custom_base_url?}", %{ + custom_base_url?: custom_base_url?, + sdk_key: sdk_key, + valid?: valid? + } do options = if custom_base_url?, do: [base_url: "https://my-configcat-proxy"], else: [] if valid? do diff --git a/test/rollout_test.exs b/test/rollout_test.exs index 1b6280a..200dabb 100644 --- a/test/rollout_test.exs +++ b/test/rollout_test.exs @@ -180,15 +180,23 @@ defmodule ConfigCat.RolloutTest do {"12345", "b@configcat.com", "", "Falcon", false, true}, {"12345", "b@configcat.com", "US", "Spider", false, true} ] do - test "matched evaluation rule and percentage option with user_id: #{inspect(user_id)} email: #{inspect(email)} percentage_base: #{inspect(percentage_base)}" do + @tag email: email, + expected_matched_percentage_option: expected_matched_percentage_option, + expected_matched_targeting_rule: expected_matched_targeting_rule, + expected_return_value: expected_return_value, + percentage_base: percentage_base, + user_id: user_id + test "matched evaluation rule and percentage option with user_id: #{inspect(user_id)} email: #{inspect(email)} percentage_base: #{inspect(percentage_base)}", + %{ + email: email, + expected_matched_percentage_option: expected_matched_percentage_option, + expected_matched_targeting_rule: expected_matched_targeting_rule, + expected_return_value: expected_return_value, + percentage_base: percentage_base, + user_id: user_id + } do sdk_key = "configcat-sdk-1/JcPbCGl_1E-K9M-fJOyKyQ/P4e3fAz_1ky2-Zg2e4cbkw" key = "stringMatchedTargetingRuleAndOrPercentageOption" - user_id = unquote(user_id) - email = unquote(email) - percentage_base = unquote(percentage_base) - expected_return_value = unquote(expected_return_value) - expected_matched_targeting_rule = unquote(expected_matched_targeting_rule) - expected_matched_percentage_option = unquote(expected_matched_percentage_option) {:ok, client} = start_config_cat(sdk_key) @@ -330,13 +338,18 @@ defmodule ConfigCat.RolloutTest do {"configcat-sdk-1/JcPbCGl_1E-K9M-fJOyKyQ/OfQqcTjfFUGBwMKqtyEOrQ", "stringArrayContainsAnyOfDogDefaultCat", "x, read", "Cat"} ] do - test "attribute value conversion with key: '#{key}' value: '#{inspect(custom_attribute_value)}" do - sdk_key = unquote(sdk_key) - key = unquote(key) + @tag custom_attribute_value: custom_attribute_value, + expected_return_value: expected_return_value, + key: key, + sdk_key: sdk_key + test "attribute value conversion with key: '#{key}' value: '#{inspect(custom_attribute_value)}", %{ + custom_attribute_value: custom_attribute_value, + expected_return_value: expected_return_value, + key: key, + sdk_key: sdk_key + } do user_id = "12345" custom_attribute_name = "Custom1" - custom_attribute_value = unquote(Macro.escape(custom_attribute_value)) - expected_return_value = unquote(expected_return_value) {:ok, client} = start_config_cat(sdk_key) @@ -352,10 +365,12 @@ defmodule ConfigCat.RolloutTest do {"key2", "'key2' -> 'key3' -> 'key2'"}, {"key4", "'key4' -> 'key3' -> 'key2' -> 'key3'"} ] do - test "prerequisite flag circular dependency for key: #{key}" do - key = unquote(key) - dependency_cycle = unquote(dependency_cycle) - + @tag dependency_cycle: dependency_cycle, + key: key + test "prerequisite flag circular dependency for key: #{key}", %{ + dependency_cycle: dependency_cycle, + key: key + } do config = "test_circulardependency_v6.json" |> fixture_file() @@ -403,14 +418,21 @@ defmodule ConfigCat.RolloutTest do {"stringDependsOnDouble", "float()", "mainDoubleFlag", [0.1], nil}, {"stringDependsOnDouble", "float()", "mainDoubleFlag", nil, nil} ] do - test "prerequisite flag value type mismatch with key: #{key} type: #{comparison_value_type} flag_key: #{prerequisite_flag_key} value: #{inspect(prerequisite_flag_value)}" do + @tag comparison_value_type: comparison_value_type, + expected_value: expected_value, + key: key, + prerequisite_flag_key: prerequisite_flag_key, + prerequisite_flag_value: prerequisite_flag_value + test "prerequisite flag value type mismatch with key: #{key} type: #{comparison_value_type} flag_key: #{prerequisite_flag_key} value: #{inspect(prerequisite_flag_value)}", + %{ + comparison_value_type: _comparison_value_type, + expected_value: expected_value, + key: key, + prerequisite_flag_key: prerequisite_flag_key, + prerequisite_flag_value: prerequisite_flag_value + } do sdk_key = "configcat-sdk-1/JcPbCGl_1E-K9M-fJOyKyQ/JoGwdqJZQ0K2xDy7LnbyOg" - key = unquote(key) - flag_key = unquote(prerequisite_flag_key) - flag_value = unquote(prerequisite_flag_value) - expected_value = unquote(expected_value) - - flag_overrides = LocalMapDataSource.new(%{flag_key => flag_value}, :local_over_remote) + flag_overrides = LocalMapDataSource.new(%{prerequisite_flag_key => prerequisite_flag_value}, :local_over_remote) {:ok, client} = start_config_cat(sdk_key, flag_overrides: flag_overrides) @@ -421,7 +443,7 @@ defmodule ConfigCat.RolloutTest do unless expected_value do expected_message = - ~r/Type mismatch between comparison value '[^']+' and prerequisite flag '#{flag_key}'/ + ~r/Type mismatch between comparison value '[^']+' and prerequisite flag '#{prerequisite_flag_key}'/ assert logs =~ expected_message end @@ -446,11 +468,15 @@ defmodule ConfigCat.RolloutTest do {"stringArrayToStringConversionSpecialChars", ["+<>%\"'\\/\t\r\n"], "3"}, {"stringArrayToStringConversionUnicode", ["äöüÄÖÜçéèñışğ⢙✓😀"], "2"} ] do - test "comparison attribute conversion to canonical string representation - key: #{key} | custom_attribute_value: #{custom_attribute_value}" do - key = unquote(key) - custom_attribute_value = unquote(Macro.escape(custom_attribute_value)) - expected_return_value = unquote(expected_return_value) - + @tag custom_attribute_value: custom_attribute_value, + expected_return_value: expected_return_value, + key: key + test "comparison attribute conversion to canonical string representation - key: #{key} | custom_attribute_value: #{custom_attribute_value}", + %{ + custom_attribute_value: custom_attribute_value, + expected_return_value: expected_return_value, + key: key + } do config = "comparison_attribute_conversion.json" |> fixture_file() @@ -506,10 +532,12 @@ defmodule ConfigCat.RolloutTest do {"containsanyof", "no trim"}, {"notcontainsanyof", "no trim"} ] do - test "comparison attribute trimming - key: #{key}" do - key = unquote(key) - expected_return_value = unquote(expected_return_value) - + @tag expected_return_value: expected_return_value, + key: key + test "comparison attribute trimming - key: #{key}", %{ + expected_return_value: expected_return_value, + key: key + } do config = "comparison_attribute_trimming.json" |> fixture_file() @@ -561,10 +589,12 @@ defmodule ConfigCat.RolloutTest do {"semvergreater", "8 trim"}, {"semvergreaterequals", "9 trim"} ] do - test "comparison value trimming - key: #{key}" do - key = unquote(key) - expected_return_value = unquote(expected_return_value) - + @tag expected_return_value: expected_return_value, + key: key + test "comparison value trimming - key: #{key}", %{ + expected_return_value: expected_return_value, + key: key + } do config = "comparison_value_trimming.json" |> fixture_file() From 433b7e9aaf4be487e1dac6c3112c290f40d9964b Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 16:34:22 -0700 Subject: [PATCH 09/13] Fix dialyzer warnings The newer version of dialyzer caught some type errors that it missed before, so we fix those. HTTPoison 3.0 also has some type errors of its own which we ignore until [this upstream PR](https://github.com/edgurgel/httpoison/pull/511) is merged and released (or we migrate away from HTTPoison). --- .dialyzer_ignore.exs | 10 ++++++++++ .formatter.exs | 2 +- lib/config_cat/config/user_comparator.ex | 2 +- lib/config_cat/hooks.ex | 2 +- mix.lock | 4 ++-- 5 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 .dialyzer_ignore.exs diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs new file mode 100644 index 0000000..3acc0fb --- /dev/null +++ b/.dialyzer_ignore.exs @@ -0,0 +1,10 @@ +[ + # The following four ignore rules are due to an upstream type problem in + # HTTPoison 3.0. Once https://github.com/edgurgel/httpoison/pull/511 has been + # merged and released, we should be able to delete these rules. + {"deps/httpoison/lib/httpoison/base.ex", "Type mismatch with behaviour callback to stream_next/1."}, + {"deps/httpoison/lib/httpoison/base.ex", "Type mismatch for @callback stream_next."}, + {"deps/httpoison/lib/httpoison/base.ex", + "The pattern variable _3 can never match the type, because it is covered by previous clauses."}, + {"lib/config_cat/api.ex", "Invalid type specification for function stream_next."} +] diff --git a/.formatter.exs b/.formatter.exs index a2af9da..ec8672d 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,6 +1,6 @@ # Used by "mix format" [ import_deps: [:typed_struct], - inputs: ["{mix,.credo,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], + inputs: ["{mix,.credo,.dialyzer_ignore,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], plugins: [Styler] ] diff --git a/lib/config_cat/config/user_comparator.ex b/lib/config_cat/config/user_comparator.ex index 5661526..3c95b6a 100644 --- a/lib/config_cat/config/user_comparator.ex +++ b/lib/config_cat/config/user_comparator.ex @@ -524,7 +524,7 @@ defmodule ConfigCat.Config.UserComparator do end @spec to_unix_seconds(DateTime.t() | NaiveDateTime.t() | number() | String.t()) :: - {:ok, float()} | {:error, :invalid_float} + {:ok, float()} | {:error, :invalid_datetime | :invalid_float} def to_unix_seconds(%DateTime{} = value) do {:ok, DateTime.to_unix(value, :millisecond) / 1000.0} end diff --git a/lib/config_cat/hooks.ex b/lib/config_cat/hooks.ex index 9f09918..f52efaa 100644 --- a/lib/config_cat/hooks.ex +++ b/lib/config_cat/hooks.ex @@ -87,7 +87,7 @@ defmodule ConfigCat.Hooks do | {:on_error, on_error_callback()} | {:on_flag_evaluated, on_flag_evaluated_callback()} @type start_option :: {:hooks, t()} | {:instance_id, ConfigCat.instance_id()} - @opaque t :: ConfigCat.instance_id() + @type t :: ConfigCat.instance_id() @doc false @spec start_link([start_option()]) :: GenServer.on_start() diff --git a/mix.lock b/mix.lock index 3625a5e..3d06a53 100644 --- a/mix.lock +++ b/mix.lock @@ -2,10 +2,10 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"}, "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, - "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, + "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, + "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "excoveralls": {:hex, :excoveralls, "0.18.2", "86efd87a0676a3198ff50b8c77620ea2f445e7d414afa9ec6c4ba84c9f8bdcc2", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230262c418f0de64077626a498bd4fdf1126d5c2559bb0e6b43deac3005225a4"}, "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, From 129785e9accf259048e2da5aa7fc0c03da2bd6a1 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 16:41:13 -0700 Subject: [PATCH 10/13] Don't report unused dialyzer filters for now Different Elixir versions find different problems, so a filter that's needed in Elixir 1.17 and above may not be needed in Elixir 1.16. --- mix.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 696617f..3144648 100644 --- a/mix.exs +++ b/mix.exs @@ -18,7 +18,10 @@ defmodule ConfigCat.MixProject do start_permanent: Mix.env() == :prod, deps: deps(), dialyzer: [ - list_unused_filters: true, + # When we clean out the dialyzer ignore file, we should uncomment this + # line. It's commented out for now because different Elixir versions + # find different dialyzer issues. + # list_unused_filters: true, plt_local_path: "priv/plts/dialyzer.plt" ], docs: [ From 9802f90d69ef9a55f83d1bc334495274781bd043 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 16:48:39 -0700 Subject: [PATCH 11/13] Explicitly set `list_unused_filters` to false Just commenting out the `true` version didn't appear to be enough. --- mix.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 3144648..980baf9 100644 --- a/mix.exs +++ b/mix.exs @@ -18,10 +18,10 @@ defmodule ConfigCat.MixProject do start_permanent: Mix.env() == :prod, deps: deps(), dialyzer: [ - # When we clean out the dialyzer ignore file, we should uncomment this - # line. It's commented out for now because different Elixir versions - # find different dialyzer issues. - # list_unused_filters: true, + # When we clean out the dialyzer ignore file, we should set + # list_unused_filters back to `true`. It's `false` for now because + # different Elixir versions find different dialyzer issues. + list_unused_filters: false, plt_local_path: "priv/plts/dialyzer.plt" ], docs: [ From 6cc4e14dc39ea69d69eaa141154dec0f812c0adb Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 17:02:28 -0700 Subject: [PATCH 12/13] Use less-strict patterns in dialyzer ignore Some of the message wording has changed from version to version. --- .dialyzer_ignore.exs | 9 ++++----- mix.exs | 5 +---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs index 3acc0fb..e9f5981 100644 --- a/.dialyzer_ignore.exs +++ b/.dialyzer_ignore.exs @@ -2,9 +2,8 @@ # The following four ignore rules are due to an upstream type problem in # HTTPoison 3.0. Once https://github.com/edgurgel/httpoison/pull/511 has been # merged and released, we should be able to delete these rules. - {"deps/httpoison/lib/httpoison/base.ex", "Type mismatch with behaviour callback to stream_next/1."}, - {"deps/httpoison/lib/httpoison/base.ex", "Type mismatch for @callback stream_next."}, - {"deps/httpoison/lib/httpoison/base.ex", - "The pattern variable _3 can never match the type, because it is covered by previous clauses."}, - {"lib/config_cat/api.ex", "Invalid type specification for function stream_next."} + {"deps/httpoison/lib/httpoison/base.ex", :callback_arg_type_mismatch}, + {"deps/httpoison/lib/httpoison/base.ex", :callback_type_mismatch}, + {"deps/httpoison/lib/httpoison/base.ex", :pattern_match_cov}, + {"lib/config_cat/api.ex", :invalid_contract} ] diff --git a/mix.exs b/mix.exs index 980baf9..696617f 100644 --- a/mix.exs +++ b/mix.exs @@ -18,10 +18,7 @@ defmodule ConfigCat.MixProject do start_permanent: Mix.env() == :prod, deps: deps(), dialyzer: [ - # When we clean out the dialyzer ignore file, we should set - # list_unused_filters back to `true`. It's `false` for now because - # different Elixir versions find different dialyzer issues. - list_unused_filters: false, + list_unused_filters: true, plt_local_path: "priv/plts/dialyzer.plt" ], docs: [ From aa81de3abe10cea3ec821081fd842dd8710b5463 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 3 Jul 2026 17:16:10 -0700 Subject: [PATCH 13/13] Turn off fail-fast for matrix builds in CI --- .github/workflows/elixir-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/elixir-ci.yml b/.github/workflows/elixir-ci.yml index 3437673..971959d 100644 --- a/.github/workflows/elixir-ci.yml +++ b/.github/workflows/elixir-ci.yml @@ -19,6 +19,7 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: elixir-version: ['1.16.x', '1.17.x', '1.18.x', '1.19.x', '1.20.x'] steps: