From f5dc8c6ff6cf8bfaad87f62b76ce9a1817b61561 Mon Sep 17 00:00:00 2001 From: Paul Iannazzo Date: Mon, 5 Oct 2026 17:21:46 +0800 Subject: [PATCH] fix: resolve Elixir 1.18 #Reference macro escaping bugs This commit also includes a changeset. --- .../changesets/fix-elixir-1-18-macro-bug.md | 5 + lib/posthog/config.ex | 176 +----------------- lib/posthog/config_schema.ex | 167 +++++++++++++++++ 3 files changed, 179 insertions(+), 169 deletions(-) create mode 100644 .sampo/changesets/fix-elixir-1-18-macro-bug.md create mode 100644 lib/posthog/config_schema.ex diff --git a/.sampo/changesets/fix-elixir-1-18-macro-bug.md b/.sampo/changesets/fix-elixir-1-18-macro-bug.md new file mode 100644 index 0000000..d998ed6 --- /dev/null +++ b/.sampo/changesets/fix-elixir-1-18-macro-bug.md @@ -0,0 +1,5 @@ +--- +"hex/posthog": patch +--- + +Refactor regex schemas out of module attributes into private functions to fix Elixir 1.18 `#Reference` macro compilation crash. diff --git a/lib/posthog/config.ex b/lib/posthog/config.ex index 56bdcf0..f82062d 100644 --- a/lib/posthog/config.ex +++ b/lib/posthog/config.ex @@ -1,5 +1,6 @@ defmodule PostHog.Config do require Logger + alias PostHog.Config.Schema defmodule Secret do @moduledoc false @@ -25,171 +26,8 @@ defmodule PostHog.Config do @default_api_host "https://us.i.posthog.com" - @shared_schema [ - test_mode: [ - type: :boolean, - default: false, - doc: - "Test mode keeps captured events in memory for assertions instead of sending them to PostHog." - ] - ] - - @configuration_schema [ - api_host: [ - type: :string, - default: @default_api_host, - doc: - "`https://us.i.posthog.com` for US cloud or `https://eu.i.posthog.com` for EU cloud" - ], - api_key: [ - type: :string, - default: "", - doc: """ - Your PostHog Project API key. Find it in your project's settings under the Project ID section. - If omitted or empty after trimming whitespace, PostHog starts in disabled/no-op mode. - """ - ], - api_client_module: [ - type: :atom, - default: PostHog.API.Client, - doc: "API client to use" - ], - feature_flags_request_max_retries: [ - type: :non_neg_integer, - default: 1, - doc: - "Number of retries for /flags requests after network, transport, or timeout failures. Set to 0 to disable retries." - ], - secret_key: [ - type: {:or, [:string, nil]}, - default: nil, - doc: """ - A privileged project secret (`phs_`) or appropriately scoped personal API key (`phx_`) - used only to load definitions for local feature flag evaluation. Local evaluation is inert - and starts no poller when this value is absent or blank. - """ - ], - enable_local_evaluation: [ - type: :boolean, - default: true, - doc: - "Enable local feature flag evaluation when a non-empty `secret_key` is configured." - ], - feature_flags_poll_interval_ms: [ - type: :pos_integer, - default: 30_000, - doc: - "Interval in milliseconds between local feature flag definition refreshes." - ], - flag_definition_cache_provider: [ - type: {:or, [{:tuple, [:atom, :any]}, nil]}, - default: nil, - doc: - "Optional `{module, state}` implementing `PostHog.FeatureFlags.FlagDefinitionCacheProvider`." - ], - flag_definition_cache_provider_timeout_ms: [ - type: :pos_integer, - default: 5_000, - doc: - "Maximum time in milliseconds allowed for each definition cache provider callback." - ], - flag_definition_request_timeout_ms: [ - type: :pos_integer, - default: 10_000, - doc: - "Maximum time in milliseconds allowed for a local feature flag definition request." - ], - supervisor_name: [ - type: :atom, - default: PostHog, - doc: "Name of the supervisor process running PostHog" - ], - metadata: [ - type: {:or, [{:list, :atom}, {:in, [:all]}]}, - default: [], - doc: - "List of Logger metadata keys to include in event properties. Set to `:all` to include all metadata. This only affects Error Tracking events." - ], - capture_level: [ - type: {:or, [{:in, Logger.levels()}, nil]}, - default: :error, - doc: - "Minimum level for logs that should be captured as errors. Errors with `crash_reason` are always captured." - ], - global_properties: [ - type: :map, - default: %{}, - doc: "Map of properties that should be added to all events" - ], - before_send: [ - type: {:or, [{:fun, 1}, nil]}, - default: nil, - doc: - "Callback invoked with the fully enriched event before it is queued. Return the event to send a modified version, or nil to drop it." - ], - is_server: [ - type: :boolean, - default: true, - doc: - "Whether this SDK runs as a server. When `true` (the default), a `$is_server: true` property is added to all events so PostHog attributes them as server-side. Set to `false` when using posthog-elixir as a client/CLI so the device OS is attributed normally." - ], - in_app_otp_apps: [ - type: {:list, :atom}, - default: [], - doc: - "List of OTP app names of your applications. Stacktrace entries that belong to these apps will be marked as \"in_app\"." - ], - enable_source_code_context: [ - type: :boolean, - default: false, - doc: - "Enable source code context in error tracking stack frames. Requires source code to be available at runtime or packaged via `mix posthog.package_source_code`." - ], - root_source_code_paths: [ - type: {:list, :string}, - default: [], - doc: - "List of root paths to scan for source files. Used by the source context feature and `mix posthog.package_source_code`." - ], - source_code_path_pattern: [ - type: :string, - default: "**/*.ex", - doc: "Glob pattern for source files to include in source context." - ], - source_code_exclude_patterns: [ - type: {:list, {:struct, Regex}}, - default: [~r"^_build/", ~r"^priv/", ~r"^test/"], - doc: - ~s(List of regex patterns to exclude from source context. Defaults to excluding `_build/`, `priv/`, and `test/` directories.) - ], - context_lines: [ - type: :non_neg_integer, - default: 5, - doc: - "Number of source lines to include before and after the error line in stack frames." - ], - source_code_map_path: [ - type: :string, - doc: - "Custom path to a packaged source map file. Defaults to `priv/posthog_source.map` in the `:posthog` application directory." - ] - ] ++ @shared_schema - - @convenience_schema [ - enable: [ - type: :boolean, - default: true, - doc: "Automatically start PostHog?" - ], - enable_error_tracking: [ - type: :boolean, - default: true, - doc: "Automatically start the logger handler for error tracking?" - ] - ] ++ @shared_schema - - @compiled_configuration_schema NimbleOptions.new!(@configuration_schema) - @compiled_convenience_schema NimbleOptions.new!(@convenience_schema) + @compiled_configuration_schema NimbleOptions.new!(Schema.configuration()) + @compiled_convenience_schema NimbleOptions.new!(Schema.convenience()) @system_global_properties %{ "$lib": PostHog.Lib.name(), @@ -236,14 +74,14 @@ defmodule PostHog.Config do def read!() do configuration_options = Application.get_all_env(:posthog) - |> Keyword.take(Keyword.keys(@configuration_schema)) + |> Keyword.take(Keyword.keys(Schema.configuration())) convenience_options = Application.get_all_env(:posthog) - |> Keyword.take(Keyword.keys(@convenience_schema)) + |> Keyword.take(Keyword.keys(Schema.convenience())) convenience_options - |> NimbleOptions.validate!(@compiled_convenience_schema) + |> NimbleOptions.validate!(Schema.convenience()) |> Map.new() |> case do %{enable: true} = conv -> @@ -289,7 +127,7 @@ defmodule PostHog.Config do normalized_options = normalize_options(options) with {:ok, validated} <- - NimbleOptions.validate(normalized_options, @compiled_configuration_schema) do + NimbleOptions.validate(normalized_options, Schema.configuration()) do api_key_blank? = blank_api_key?(validated) log_blank_api_key(validated) diff --git a/lib/posthog/config_schema.ex b/lib/posthog/config_schema.ex new file mode 100644 index 0000000..b602c63 --- /dev/null +++ b/lib/posthog/config_schema.ex @@ -0,0 +1,167 @@ +defmodule PostHog.Config.Schema do + @moduledoc false + @default_api_host "https://us.i.posthog.com" + + def shared do + [ + test_mode: [ + type: :boolean, + default: false, + doc: + "Test mode keeps captured events in memory for assertions instead of sending them to PostHog." + ] + ] + end + + def configuration do + [ + api_host: [ + type: :string, + default: @default_api_host, + doc: "`https://us.i.posthog.com` for US cloud or `https://eu.i.posthog.com` for EU cloud" + ], + api_key: [ + type: :string, + default: "", + doc: """ + Your PostHog Project API key. Find it in your project's settings under the Project ID section. + If omitted or empty after trimming whitespace, PostHog starts in disabled/no-op mode. + """ + ], + api_client_module: [ + type: :atom, + default: PostHog.API.Client, + doc: "API client to use" + ], + feature_flags_request_max_retries: [ + type: :non_neg_integer, + default: 1, + doc: + "Number of retries for /flags requests after network, transport, or timeout failures. Set to 0 to disable retries." + ], + secret_key: [ + type: {:or, [:string, nil]}, + default: nil, + doc: """ + A privileged project secret (`phs_`) or appropriately scoped personal API key (`phx_`) + used only to load definitions for local feature flag evaluation. Local evaluation is inert + and starts no poller when this value is absent or blank. + """ + ], + enable_local_evaluation: [ + type: :boolean, + default: true, + doc: "Enable local feature flag evaluation when a non-empty `secret_key` is configured." + ], + feature_flags_poll_interval_ms: [ + type: :pos_integer, + default: 30_000, + doc: "Interval in milliseconds between local feature flag definition refreshes." + ], + flag_definition_cache_provider: [ + type: {:or, [{:tuple, [:atom, :any]}, nil]}, + default: nil, + doc: + "Optional `{module, state}` implementing `PostHog.FeatureFlags.FlagDefinitionCacheProvider`." + ], + flag_definition_cache_provider_timeout_ms: [ + type: :pos_integer, + default: 5_000, + doc: "Maximum time in milliseconds allowed for each definition cache provider callback." + ], + flag_definition_request_timeout_ms: [ + type: :pos_integer, + default: 10_000, + doc: "Maximum time in milliseconds allowed for a local feature flag definition request." + ], + supervisor_name: [ + type: :atom, + default: PostHog, + doc: "Name of the supervisor process running PostHog" + ], + metadata: [ + type: {:or, [{:list, :atom}, {:in, [:all]}]}, + default: [], + doc: + "List of Logger metadata keys to include in event properties. Set to `:all` to include all metadata. This only affects Error Tracking events." + ], + capture_level: [ + type: {:or, [{:in, Logger.levels()}, nil]}, + default: :error, + doc: + "Minimum level for logs that should be captured as errors. Errors with `crash_reason` are always captured." + ], + global_properties: [ + type: :map, + default: %{}, + doc: "Map of properties that should be added to all events" + ], + before_send: [ + type: {:or, [{:fun, 1}, nil]}, + default: nil, + doc: + "Callback invoked with the fully enriched event before it is queued. Return the event to send a modified version, or nil to drop it." + ], + is_server: [ + type: :boolean, + default: true, + doc: + "Whether this SDK runs as a server. When `true` (the default), a `$is_server: true` property is added to all events so PostHog attributes them as server-side. Set to `false` when using posthog-elixir as a client/CLI so the device OS is attributed normally." + ], + in_app_otp_apps: [ + type: {:list, :atom}, + default: [], + doc: + "List of OTP app names of your applications. Stacktrace entries that belong to these apps will be marked as \"in_app\"." + ], + enable_source_code_context: [ + type: :boolean, + default: false, + doc: + "Enable source code context in error tracking stack frames. Requires source code to be available at runtime or packaged via `mix posthog.package_source_code`." + ], + root_source_code_paths: [ + type: {:list, :string}, + default: [], + doc: + "List of root paths to scan for source files. Used by the source context feature and `mix posthog.package_source_code`." + ], + source_code_path_pattern: [ + type: :string, + default: "**/*.ex", + doc: "Glob pattern for source files to include in source context." + ], + source_code_exclude_patterns: [ + type: {:list, {:struct, Regex}}, + default: [~r"^_build/", ~r"^priv/", ~r"^test/"], + doc: + ~s(List of regex patterns to exclude from source context. Defaults to excluding `_build/`, `priv/`, and `test/` directories.) + ], + context_lines: [ + type: :non_neg_integer, + default: 5, + doc: "Number of source lines to include before and after the error line in stack frames." + ], + source_code_map_path: [ + type: :string, + doc: + "Custom path to a packaged source map file. Defaults to `priv/posthog_source.map` in the `:posthog` application directory." + ] + ] ++ shared() + end + + def convenience do + [ + enable: [ + type: :boolean, + default: true, + doc: "Automatically start PostHog?" + ], + enable_error_tracking: [ + type: :boolean, + default: true, + doc: "Automatically start the logger handler for error tracking?" + ] + ] ++ shared() + end +end