11defmodule Testcontainers.Util.PropertiesParser do
22 @ moduledoc false
33
4- @ file_path "~/.testcontainers.properties"
4+ @ user_file "~/.testcontainers.properties"
5+ @ project_file ".testcontainers.properties"
6+ @ env_prefix "TESTCONTAINERS_"
57
6- def read_property_file ( file_path \\ @ file_path ) do
8+ def read_property_file ( file_path \\ @ user_file ) do
79 if File . exists? ( Path . expand ( file_path ) ) do
810 with { :ok , content } <- File . read ( Path . expand ( file_path ) ) ,
911 properties <- parse_properties ( content ) do
@@ -18,6 +20,78 @@ defmodule Testcontainers.Util.PropertiesParser do
1820 end
1921 end
2022
23+ @ doc """
24+ Reads properties from all sources with proper precedence.
25+
26+ Configuration is read from three sources with the following precedence
27+ (highest to lowest):
28+
29+ 1. Environment variables (TESTCONTAINERS_* prefix)
30+ 2. User file (~/.testcontainers.properties)
31+ 3. Project file (.testcontainers.properties)
32+
33+ Environment variables are converted from TESTCONTAINERS_PROPERTY_NAME format
34+ to property.name format (uppercase to lowercase, underscores to dots, prefix removed).
35+
36+ ## Options
37+
38+ - `:user_file` - path to user properties file (default: ~/.testcontainers.properties)
39+ - `:project_file` - path to project properties file (default: .testcontainers.properties)
40+ - `:env_prefix` - environment variable prefix (default: TESTCONTAINERS_)
41+
42+ ## Returns
43+
44+ - `{:ok, map}` with merged properties.
45+ """
46+ def read_property_sources ( opts \\ [ ] ) do
47+ user_file = Keyword . get ( opts , :user_file , @ user_file )
48+ project_file = Keyword . get ( opts , :project_file , @ project_file )
49+ env_prefix = Keyword . get ( opts , :env_prefix , @ env_prefix )
50+
51+ project_props = read_file_silent ( project_file )
52+ user_props = read_file_silent ( user_file )
53+ env_props = read_env_vars ( env_prefix )
54+
55+ # Merge in order of lowest to highest precedence
56+ merged =
57+ project_props
58+ |> Map . merge ( user_props )
59+ |> Map . merge ( env_props )
60+
61+ { :ok , merged }
62+ end
63+
64+ defp read_file_silent ( file_path ) do
65+ expanded = Path . expand ( file_path )
66+
67+ if File . exists? ( expanded ) do
68+ case File . read ( expanded ) do
69+ { :ok , content } -> parse_properties ( content )
70+ { :error , _ } -> % { }
71+ end
72+ else
73+ % { }
74+ end
75+ end
76+
77+ defp read_env_vars ( prefix ) do
78+ System . get_env ( )
79+ |> Enum . filter ( fn { key , _value } -> String . starts_with? ( key , prefix ) end )
80+ |> Enum . map ( & env_to_property ( & 1 , prefix ) )
81+ |> Map . new ( )
82+ end
83+
84+ # Converts TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED to ryuk.container.privileged
85+ defp env_to_property ( { key , value } , prefix ) do
86+ property_key =
87+ key
88+ |> String . replace_prefix ( prefix , "" )
89+ |> String . downcase ( )
90+ |> String . replace ( "_" , "." )
91+
92+ { property_key , value }
93+ end
94+
2195 defp parse_properties ( content ) do
2296 content
2397 |> String . split ( "\n " )
0 commit comments