Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

elixir: config...

+403 -373
+356
apps/sower/lib/sower/config.ex
··· 1 + defmodule Sower.Config do 2 + import Config 3 + require Logger 4 + 5 + @schema %{ 6 + "type" => "object", 7 + "required" => ["auth", "database"], 8 + "properties" => %{ 9 + "auth" => %{ 10 + "type" => "object", 11 + "required" => ["oidc_base_url", "oidc_client_id"], 12 + "properties" => %{ 13 + "oidc_base_url" => %{ 14 + "type" => "string" 15 + }, 16 + "oidc_client_id" => %{ 17 + "type" => "string" 18 + }, 19 + "oidc_client_secret_file" => %{ 20 + "type" => "string" 21 + }, 22 + "oidc_redirect_uri" => %{ 23 + "type" => "string" 24 + } 25 + } 26 + }, 27 + "clients" => %{ 28 + "type" => "object", 29 + "properties" => %{ 30 + "x86_64-linux" => %{ 31 + "type" => "object", 32 + "properties" => %{ 33 + "path" => %{ 34 + "type" => "string" 35 + } 36 + } 37 + }, 38 + "aarch64-linux" => %{ 39 + "type" => "object", 40 + "properties" => %{ 41 + "path" => %{ 42 + "type" => "string" 43 + } 44 + } 45 + }, 46 + "x86_64-darwin" => %{ 47 + "type" => "object", 48 + "properties" => %{ 49 + "path" => %{ 50 + "type" => "string" 51 + } 52 + } 53 + }, 54 + "aarch64-darwin" => %{ 55 + "type" => "object", 56 + "properties" => %{ 57 + "path" => %{ 58 + "type" => "string" 59 + } 60 + } 61 + } 62 + } 63 + }, 64 + "database" => %{ 65 + "type" => "object", 66 + "properties" => %{ 67 + "host" => %{ 68 + "type" => "string" 69 + }, 70 + "database" => %{ 71 + "type" => "string" 72 + }, 73 + "port" => %{ 74 + "type" => "integer", 75 + "minimum" => 80, 76 + "maximum" => 65535 77 + }, 78 + "socket" => %{ 79 + "type" => "string" 80 + }, 81 + "user" => %{ 82 + "type" => "string" 83 + }, 84 + "password_file" => %{ 85 + "type" => "string" 86 + }, 87 + "encryption_key_file" => %{ 88 + "type" => "string", 89 + "description" => "base64 encoded secret key used for encrypted database items" 90 + } 91 + } 92 + }, 93 + "listen_address" => %{ 94 + "oneOf " => [ 95 + %{"type" => "string", "format" => "ipv4"}, 96 + %{"type" => "string", "format" => "ipv6"} 97 + ] 98 + }, 99 + "listen_port" => %{ 100 + "default" => 4000, 101 + "type" => "integer", 102 + "minimum" => 80, 103 + "maximum" => 65535 104 + }, 105 + "organization" => %{ 106 + "type" => "object", 107 + "properties" => %{ 108 + "mode" => %{ 109 + "type" => "string", 110 + "enum" => ["single", "multi"], 111 + "default" => "single", 112 + "description" => 113 + "Whether to run in single or multiple organization mode. Will create all new resources in a default organization if set to single." 114 + }, 115 + "name" => %{ 116 + "type" => "string", 117 + "default" => "default organization", 118 + "description" => "Name of the default organization in single org mode" 119 + } 120 + } 121 + }, 122 + "public_url" => %{ 123 + "type" => "string", 124 + "format" => "uri" 125 + }, 126 + "secret_key_base_file" => %{ 127 + "type" => "string" 128 + } 129 + } 130 + } 131 + 132 + def load() do 133 + json_config = 134 + case load_config_file() do 135 + {:ok, json_config} -> 136 + json_config |> set_logger_config() 137 + 138 + {:error, err} -> 139 + Logger.error("Failed to load configuration: #{inspect(err)}") 140 + Kernel.exit(1) 141 + end 142 + 143 + Logger.debug("Loaded configuration") 144 + Logger.debug(json_config) 145 + 146 + # compute urls 147 + public_url = json_config |> Keyword.fetch!(:public_url) 148 + 149 + json_config = 150 + json_config 151 + |> Keyword.put( 152 + :auth, 153 + json_config 154 + |> Keyword.fetch!(:auth) 155 + |> Keyword.put(:oidc_redirect_uri, ~s"#{public_url}/auth") 156 + ) 157 + 158 + secret_key_base = 159 + with {:ok, secret_key_base_file} <- json_config |> Keyword.fetch(:secret_key_base_file), 160 + {:ok, secret_key_base} <- read_credential(secret_key_base_file) do 161 + secret_key_base 162 + else 163 + :error -> 164 + Logger.warning("Configuration is missing `secret_key_base_file`.") 165 + Kernel.exit(1) 166 + 167 + {:error, err} -> 168 + Logger.warning("Failed to load secret_key_base from secret file, #{err}.") 169 + Kernel.exit(1) 170 + end 171 + 172 + # database password file 173 + json_config = 174 + with {:ok, database} <- json_config |> Keyword.fetch(:database), 175 + {:ok, password_file} <- database |> Keyword.fetch(:password_file), 176 + {:ok, password} <- read_credential(password_file) do 177 + json_config |> Keyword.put(:database, database |> Keyword.put(:password, password)) 178 + else 179 + # assume missing password_file is intentional 180 + :error -> 181 + Logger.debug("Configuration does not have `database.password_file` to read. Skipping.") 182 + json_config 183 + 184 + {:error, err} -> 185 + Logger.warning("Failed to load database password from file, #{err}.") 186 + json_config 187 + end 188 + 189 + # database encryption key 190 + json_config = 191 + with {:ok, database} <- json_config |> Keyword.fetch(:database), 192 + {:ok, encryption_key_file} <- database |> Keyword.fetch(:encryption_key_file), 193 + {:ok, encryption_key} <- read_credential(encryption_key_file), 194 + {:ok, encryption_key} <- Base.decode64(encryption_key) do 195 + json_config 196 + |> Keyword.put(:database, database |> Keyword.put(:encryption_key, encryption_key)) 197 + else 198 + :error -> 199 + Logger.warning("Failed to load database.encryption_key from secret file.") 200 + Kernel.exit(1) 201 + 202 + {:error, err} -> 203 + Logger.warning("Failed to load database.encryption_key from file, #{err}.") 204 + Kernel.exit(1) 205 + end 206 + 207 + # oidc client secret file 208 + json_config = 209 + with {:ok, auth} <- json_config |> Keyword.fetch(:auth), 210 + {:ok, oidc_client_secret_file} <- auth |> Keyword.fetch(:oidc_client_secret_file), 211 + {:ok, oidc_client_secret} <- read_credential(oidc_client_secret_file) do 212 + json_config 213 + |> Keyword.put(:auth, auth |> Keyword.put(:oidc_client_secret, oidc_client_secret)) 214 + else 215 + {:error, err} -> 216 + Logger.warning("Failed to load oidc_client_secret from secret file, #{err}.") 217 + Kernel.exit(1) 218 + 219 + :error -> 220 + Logger.warning("Configuration is missing `auth.oidc_client_secret_file`.") 221 + Kernel.exit(1) 222 + end 223 + 224 + Logger.debug("Final configuration:") 225 + Logger.debug(json_config) 226 + 227 + json_config |> Enum.map(fn {k, v} -> put_config(k, v) end) 228 + 229 + # load some non-app namespaced configs 230 + %URI{scheme: scheme, host: host, port: port} = URI.parse(public_url) 231 + 232 + put_config(SowerWeb.Endpoint, 233 + server: true, 234 + url: [host: host, port: port, scheme: scheme], 235 + http: [ 236 + ip: ip_to_inet(json_config |> Keyword.fetch!(:listen_address)), 237 + port: json_config |> Keyword.fetch!(:listen_port) 238 + ], 239 + secret_key_base: secret_key_base, 240 + persistent: true 241 + ) 242 + 243 + config :sower, Sower.Accounts.UserAuthentication, 244 + issuer: "oidcc", 245 + secret_key: secret_key_base 246 + 247 + config :ueberauth_oidcc, :issuers, [ 248 + %{ 249 + name: :oidcc_issuer, 250 + issuer: json_config |> Keyword.fetch!(:auth) |> Keyword.fetch!(:oidc_base_url) 251 + } 252 + ] 253 + 254 + config :ueberauth, Ueberauth, 255 + providers: [ 256 + oidcc: { 257 + Ueberauth.Strategy.Oidcc, 258 + client_id: json_config |> Keyword.fetch!(:auth) |> Keyword.fetch!(:oidc_client_id), 259 + client_secret: 260 + json_config |> Keyword.fetch!(:auth) |> Keyword.fetch!(:oidc_client_secret), 261 + issuer: :oidcc_issuer, 262 + scopes: ["openid", "profile", "email"], 263 + require_pkce: true 264 + } 265 + ] 266 + 267 + Logger.info("Finished loading configuration.") 268 + end 269 + 270 + def load_config_file() do 271 + {:ok, _} = Application.ensure_all_started(:jason) 272 + {:ok, _} = Application.ensure_all_started(:logger) 273 + Logger.debug("Loading configuration") 274 + 275 + config_file = System.get_env("SOWER_SERVER_CONFIG_FILE", "/etc/sower/server.json") 276 + 277 + defaults = %{ 278 + "listen_address" => "127.0.0.1", 279 + "listen_port" => 4000 280 + } 281 + 282 + with {:ok, contents} <- File.read(config_file), 283 + {:ok, json} <- Jason.decode(contents), 284 + :ok <- ExJsonSchema.Validator.validate(ExJsonSchema.Schema.resolve(@schema), json) do 285 + {:ok, defaults |> Map.merge(json) |> atomize()} 286 + else 287 + {:error, err} -> 288 + Logger.error(~s"Failed to read configuration file #{config_file}") 289 + Logger.error(err) 290 + Kernel.exit(1) 291 + end 292 + end 293 + 294 + def set_logger_config(json_config) do 295 + # set log level to atom and remove from config 296 + if Keyword.has_key?(json_config, :log_level) do 297 + level = Keyword.get(json_config, :log_level) |> String.to_existing_atom() 298 + Logger.info(~s"Overriding log level from config to #{level}") 299 + 300 + config :logger, :console, level: level 301 + end 302 + 303 + json_config |> Keyword.delete(:log_level) 304 + end 305 + 306 + defp atomize([head | rest]) do 307 + [atomize(head)] ++ atomize(rest) 308 + end 309 + 310 + defp atomize(map = %{}) do 311 + map 312 + |> Enum.map(fn {k, v} -> {String.to_atom(k), atomize(v)} end) 313 + end 314 + 315 + defp atomize(nil), do: nil 316 + defp atomize(other), do: other 317 + 318 + defp read_credential(path) when is_binary(path) do 319 + case path |> File.read() do 320 + {:ok, content} -> 321 + {:ok, content |> String.trim()} 322 + 323 + other -> 324 + other 325 + end 326 + end 327 + 328 + defp read_credential(nil), do: {:error, :is_nil} 329 + 330 + defp put_config(config_atom, new_values) when is_atom(config_atom) and is_list(new_values) do 331 + config = 332 + case Application.fetch_env(:sower, config_atom) do 333 + {:ok, previous_values} -> Keyword.merge(previous_values, new_values) 334 + :error -> new_values 335 + end 336 + 337 + config(:sower, config_atom, config) 338 + end 339 + 340 + defp put_config(config_atom, new_value) when is_atom(config_atom) do 341 + config(:sower, config_atom, new_value) 342 + end 343 + 344 + defp ip_to_inet(ip) do 345 + case ip 346 + |> to_charlist() 347 + |> :inet.parse_address() do 348 + {:ok, ip} -> 349 + ip 350 + 351 + {:error, _err} -> 352 + Logger.error(~s"Failed to parse ip #{ip}") 353 + Kernel.exit(1) 354 + end 355 + end 356 + end
+26 -3
apps/sower_agent/lib/sower_agent/config.ex
··· 5 5 field :access_token, String.t() 6 6 end 7 7 8 - def load() do 8 + # Could you define a behavior with different runtime/compiletime callbacks 9 + # with a simple Mod.func call in config which would be an entrypoint 10 + 11 + @app :sower_agent 12 + @env_prefix "SOWER_AGENT" 13 + 14 + def load(:prod) do 15 + %__MODULE__{ 16 + access_token: read_env_file!("access_token_file") 17 + } 18 + end 19 + 20 + def load(:dev) do 9 21 %__MODULE__{ 10 - access_token: 11 - System.fetch_env!("SOWER_AGENT_ACCESS_TOKEN_FILE") |> File.read!() |> String.trim() 22 + access_token: Application.fetch_env!(@app, :access_token_file) |> read_file() 12 23 } 24 + end 25 + 26 + defp fetch_env!(name) do 27 + (@env_prefix <> name) |> String.upcase() 28 + end 29 + 30 + defp read_env_file!(name) do 31 + name |> fetch_env!() |> read_file() 32 + end 33 + 34 + defp read_file(path) do 35 + path |> File.read!() |> String.trim() 13 36 end 14 37 end
+3 -1
config/dev.exs
··· 78 78 uri: "ws://localhost:7150/agent/websocket", 79 79 reconnect_after_msec: [200, 500, 1_000, 2_000] 80 80 81 - config :sower_agent, SowerAgent.Storage, file: "./data/storage.etf" 81 + config :sower_agent, SowerAgent.Storage, file: Path.expand("./data/storage.etf", __DIR__) 82 + config :sower_agent, file: Path.expand("../data/storage.etf", __DIR__) 83 + config :sower_agent, access_token_file: Path.expand("../.dev-api-token", __DIR__) 82 84 83 85 config :exsync, 84 86 reload_callback: {SowerAgent.SocketClient, :restart, []}
+1 -361
config/runtime.exs
··· 1 1 import Config 2 2 3 - if System.get_env("PHX_SERVER") do 4 - config :sower, SowerWeb.Endpoint, server: true 5 - end 6 - 7 - defmodule Sower.Config do 8 - require Logger 9 - 10 - @schema %{ 11 - "type" => "object", 12 - "required" => ["auth", "database"], 13 - "properties" => %{ 14 - "auth" => %{ 15 - "type" => "object", 16 - "required" => ["oidc_base_url", "oidc_client_id"], 17 - "properties" => %{ 18 - "oidc_base_url" => %{ 19 - "type" => "string" 20 - }, 21 - "oidc_client_id" => %{ 22 - "type" => "string" 23 - }, 24 - "oidc_client_secret_file" => %{ 25 - "type" => "string" 26 - }, 27 - "oidc_redirect_uri" => %{ 28 - "type" => "string" 29 - } 30 - } 31 - }, 32 - "clients" => %{ 33 - "type" => "object", 34 - "properties" => %{ 35 - "x86_64-linux" => %{ 36 - "type" => "object", 37 - "properties" => %{ 38 - "path" => %{ 39 - "type" => "string" 40 - } 41 - } 42 - }, 43 - "aarch64-linux" => %{ 44 - "type" => "object", 45 - "properties" => %{ 46 - "path" => %{ 47 - "type" => "string" 48 - } 49 - } 50 - }, 51 - "x86_64-darwin" => %{ 52 - "type" => "object", 53 - "properties" => %{ 54 - "path" => %{ 55 - "type" => "string" 56 - } 57 - } 58 - }, 59 - "aarch64-darwin" => %{ 60 - "type" => "object", 61 - "properties" => %{ 62 - "path" => %{ 63 - "type" => "string" 64 - } 65 - } 66 - } 67 - } 68 - }, 69 - "database" => %{ 70 - "type" => "object", 71 - "properties" => %{ 72 - "host" => %{ 73 - "type" => "string" 74 - }, 75 - "database" => %{ 76 - "type" => "string" 77 - }, 78 - "port" => %{ 79 - "type" => "integer", 80 - "minimum" => 80, 81 - "maximum" => 65535 82 - }, 83 - "socket" => %{ 84 - "type" => "string" 85 - }, 86 - "user" => %{ 87 - "type" => "string" 88 - }, 89 - "password_file" => %{ 90 - "type" => "string" 91 - }, 92 - "encryption_key_file" => %{ 93 - "type" => "string", 94 - "description" => "base64 encoded secret key used for encrypted database items" 95 - } 96 - } 97 - }, 98 - "listen_address" => %{ 99 - "oneOf " => [ 100 - %{"type" => "string", "format" => "ipv4"}, 101 - %{"type" => "string", "format" => "ipv6"} 102 - ] 103 - }, 104 - "listen_port" => %{ 105 - "default" => 4000, 106 - "type" => "integer", 107 - "minimum" => 80, 108 - "maximum" => 65535 109 - }, 110 - "organization" => %{ 111 - "type" => "object", 112 - "properties" => %{ 113 - "mode" => %{ 114 - "type" => "string", 115 - "enum" => ["single", "multi"], 116 - "default" => "single", 117 - "description" => 118 - "Whether to run in single or multiple organization mode. Will create all new resources in a default organization if set to single." 119 - }, 120 - "name" => %{ 121 - "type" => "string", 122 - "default" => "default organization", 123 - "description" => "Name of the default organization in single org mode" 124 - } 125 - } 126 - }, 127 - "public_url" => %{ 128 - "type" => "string", 129 - "format" => "uri" 130 - }, 131 - "secret_key_base_file" => %{ 132 - "type" => "string" 133 - } 134 - } 135 - } 136 - 137 - def load() do 138 - json_config = 139 - case load_config_file() do 140 - {:ok, json_config} -> 141 - json_config |> set_logger_config() 142 - 143 - {:error, err} -> 144 - Logger.error("Failed to load configuration: #{inspect(err)}") 145 - Kernel.exit(1) 146 - end 147 - 148 - Logger.debug("Loaded configuration") 149 - Logger.debug(json_config) 150 - 151 - # compute urls 152 - public_url = json_config |> Keyword.fetch!(:public_url) 153 - 154 - json_config = 155 - json_config 156 - |> Keyword.put( 157 - :auth, 158 - json_config 159 - |> Keyword.fetch!(:auth) 160 - |> Keyword.put(:oidc_redirect_uri, ~s"#{public_url}/auth") 161 - ) 162 - 163 - secret_key_base = 164 - with {:ok, secret_key_base_file} <- json_config |> Keyword.fetch(:secret_key_base_file), 165 - {:ok, secret_key_base} <- read_credential(secret_key_base_file) do 166 - secret_key_base 167 - else 168 - :error -> 169 - Logger.warning("Configuration is missing `secret_key_base_file`.") 170 - Kernel.exit(1) 171 - 172 - {:error, err} -> 173 - Logger.warning("Failed to load secret_key_base from secret file, #{err}.") 174 - Kernel.exit(1) 175 - end 176 - 177 - # database password file 178 - json_config = 179 - with {:ok, database} <- json_config |> Keyword.fetch(:database), 180 - {:ok, password_file} <- database |> Keyword.fetch(:password_file), 181 - {:ok, password} <- read_credential(password_file) do 182 - json_config |> Keyword.put(:database, database |> Keyword.put(:password, password)) 183 - else 184 - # assume missing password_file is intentional 185 - :error -> 186 - Logger.debug("Configuration does not have `database.password_file` to read. Skipping.") 187 - json_config 188 - 189 - {:error, err} -> 190 - Logger.warning("Failed to load database password from file, #{err}.") 191 - json_config 192 - end 193 - 194 - # database encryption key 195 - json_config = 196 - with {:ok, database} <- json_config |> Keyword.fetch(:database), 197 - {:ok, encryption_key_file} <- database |> Keyword.fetch(:encryption_key_file), 198 - {:ok, encryption_key} <- read_credential(encryption_key_file), 199 - {:ok, encryption_key} <- Base.decode64(encryption_key) do 200 - json_config 201 - |> Keyword.put(:database, database |> Keyword.put(:encryption_key, encryption_key)) 202 - else 203 - :error -> 204 - Logger.warning("Failed to load database.encryption_key from secret file.") 205 - Kernel.exit(1) 206 - 207 - {:error, err} -> 208 - Logger.warning("Failed to load database.encryption_key from file, #{err}.") 209 - Kernel.exit(1) 210 - end 211 - 212 - # oidc client secret file 213 - json_config = 214 - with {:ok, auth} <- json_config |> Keyword.fetch(:auth), 215 - {:ok, oidc_client_secret_file} <- auth |> Keyword.fetch(:oidc_client_secret_file), 216 - {:ok, oidc_client_secret} <- read_credential(oidc_client_secret_file) do 217 - json_config 218 - |> Keyword.put(:auth, auth |> Keyword.put(:oidc_client_secret, oidc_client_secret)) 219 - else 220 - {:error, err} -> 221 - Logger.warning("Failed to load oidc_client_secret from secret file, #{err}.") 222 - Kernel.exit(1) 223 - 224 - :error -> 225 - Logger.warning("Configuration is missing `auth.oidc_client_secret_file`.") 226 - Kernel.exit(1) 227 - end 228 - 229 - Logger.debug("Final configuration:") 230 - Logger.debug(json_config) 231 - 232 - json_config |> Enum.map(fn {k, v} -> put_config(k, v) end) 233 - 234 - # load some non-app namespaced configs 235 - %URI{scheme: scheme, host: host, port: port} = URI.parse(public_url) 236 - 237 - put_config(SowerWeb.Endpoint, 238 - url: [host: host, port: port, scheme: scheme], 239 - http: [ 240 - ip: ip_to_inet(json_config |> Keyword.fetch!(:listen_address)), 241 - port: json_config |> Keyword.fetch!(:listen_port) 242 - ], 243 - secret_key_base: secret_key_base, 244 - persistent: true 245 - ) 246 - 247 - config :sower, Sower.Accounts.UserAuthentication, 248 - issuer: "oidcc", 249 - secret_key: secret_key_base 250 - 251 - config :ueberauth_oidcc, :issuers, [ 252 - %{ 253 - name: :oidcc_issuer, 254 - issuer: json_config |> Keyword.fetch!(:auth) |> Keyword.fetch!(:oidc_base_url) 255 - } 256 - ] 257 - 258 - config :ueberauth, Ueberauth, 259 - providers: [ 260 - oidcc: { 261 - Ueberauth.Strategy.Oidcc, 262 - client_id: json_config |> Keyword.fetch!(:auth) |> Keyword.fetch!(:oidc_client_id), 263 - client_secret: 264 - json_config |> Keyword.fetch!(:auth) |> Keyword.fetch!(:oidc_client_secret), 265 - issuer: :oidcc_issuer, 266 - scopes: ["openid", "profile", "email"], 267 - require_pkce: true 268 - } 269 - ] 270 - 271 - Logger.info("Finished loading configuration.") 272 - end 273 - 274 - def load_config_file() do 275 - {:ok, _} = Application.ensure_all_started(:jason) 276 - {:ok, _} = Application.ensure_all_started(:logger) 277 - Logger.debug("Loading configuration") 278 - 279 - config_file = System.get_env("SOWER_SERVER_CONFIG_FILE", "/etc/sower/server.json") 280 - 281 - defaults = %{ 282 - "listen_address" => "127.0.0.1", 283 - "listen_port" => 4000 284 - } 285 - 286 - with {:ok, contents} <- File.read(config_file), 287 - {:ok, json} <- Jason.decode(contents), 288 - :ok <- ExJsonSchema.Validator.validate(ExJsonSchema.Schema.resolve(@schema), json) do 289 - {:ok, defaults |> Map.merge(json) |> atomize()} 290 - else 291 - {:error, err} -> 292 - Logger.error(~s"Failed to read configuration file #{config_file}") 293 - Logger.error(err) 294 - Kernel.exit(1) 295 - end 296 - end 297 - 298 - def set_logger_config(json_config) do 299 - # set log level to atom and remove from config 300 - if Keyword.has_key?(json_config, :log_level) do 301 - level = Keyword.get(json_config, :log_level) |> String.to_existing_atom() 302 - Logger.info(~s"Overriding log level from config to #{level}") 303 - 304 - config :logger, :console, level: level 305 - end 306 - 307 - json_config |> Keyword.delete(:log_level) 308 - end 309 - 310 - defp atomize([head | rest]) do 311 - [atomize(head)] ++ atomize(rest) 312 - end 313 - 314 - defp atomize(map = %{}) do 315 - map 316 - |> Enum.map(fn {k, v} -> {String.to_atom(k), atomize(v)} end) 317 - end 318 - 319 - defp atomize(nil), do: nil 320 - defp atomize(other), do: other 321 - 322 - defp read_credential(path) when is_binary(path) do 323 - case path |> File.read() do 324 - {:ok, content} -> 325 - {:ok, content |> String.trim()} 326 - 327 - other -> 328 - other 329 - end 330 - end 331 - 332 - defp read_credential(nil), do: {:error, :is_nil} 333 - 334 - defp put_config(config_atom, new_values) when is_atom(config_atom) and is_list(new_values) do 335 - config = 336 - case Application.fetch_env(:sower, config_atom) do 337 - {:ok, previous_values} -> Keyword.merge(previous_values, new_values) 338 - :error -> new_values 339 - end 340 - 341 - config(:sower, config_atom, config) 342 - end 343 - 344 - defp put_config(config_atom, new_value) when is_atom(config_atom) do 345 - config(:sower, config_atom, new_value) 346 - end 347 - 348 - defp ip_to_inet(ip) do 349 - case ip 350 - |> to_charlist() 351 - |> :inet.parse_address() do 352 - {:ok, ip} -> 353 - ip 354 - 355 - {:error, _err} -> 356 - Logger.error(~s"Failed to parse ip #{ip}") 357 - Kernel.exit(1) 358 - end 359 - end 360 - end 361 - 362 3 if config_env() != :test do 4 + config :sower_agent, config: SowerAgent.Config.load(config_env()) 363 5 Sower.Config.load() 364 6 end 365 - 366 - config :sower_agent, :config, SowerAgent.Config.load()
+1 -1
config/runtime_agent.exs
··· 1 1 import Config 2 2 3 - config :sower_agent, :config, SowerAgent.Config.load() 3 + config :sower_agent, :config, SowerAgent.Config.load(config_env())
+3
config/runtime_server.exs
··· 1 + import Config 2 + 3 + Sower.Config.load()
+4 -2
mix.exs
··· 11 11 agent: [ 12 12 version: version, 13 13 applications: [sower_agent: :permanent], 14 - runtime_config_path: "config/runtime_agent.exs" 14 + runtime_config_path: "config/runtime_agent.exs", 15 + include_executables_for: [:unix] 15 16 ], 16 17 server: [ 17 18 version: version, 18 19 applications: [sower: :permanent], 19 - runtime_config_path: "config/runtime.exs" 20 + runtime_config_path: "config/runtime_server.exs", 21 + include_executables_for: [:unix] 20 22 ] 21 23 ], 22 24 start_permanent: Mix.env() == :prod,
+4 -4
nix/nixos/server.nix
··· 106 106 RELEASE_COOKIE=$(cat ${cfg.environment.RELEASE_COOKIE_FILE}) 107 107 export RELEASE_COOKIE 108 108 109 - ${cfg.package}/bin/sower eval Sower.Release.migrate 110 - ${lib.optionalString cfg.e2eTest "${cfg.package}/bin/sower eval Sower.Repo.Seeds.Preseed.for_e2e"} 111 - exec ${cfg.package}/bin/sower start 109 + ${lib.getExe cfg.package} eval Sower.Release.migrate 110 + ${lib.optionalString cfg.e2eTest "${lib.getExe cfg.package} eval Sower.Repo.Seeds.Preseed.for_e2e"} 111 + exec ${lib.getExe cfg.package} start 112 112 ''; 113 - ExecStop = "${cfg.package}/bin/sower stop"; 113 + ExecStop = "${lib.getExe cfg.package} stop"; 114 114 }; 115 115 116 116 environment = {
+5 -1
nix/packages/server.nix
··· 61 61 mix do deps.loadpaths --no-deps-check, assets.deploy --no-deps-check 62 62 ''; 63 63 64 + postInstall = '' 65 + mv $out/bin/server $out/bin/sower-server 66 + ''; 67 + 64 68 # disabled because requires test deps to work 65 69 # doCheck = true; 66 70 # nativeCheckInputs = [ ··· 83 87 inherit mixNixDeps; 84 88 }; 85 89 86 - meta.mainProgram = "sower"; 90 + meta.mainProgram = "sower-server"; 87 91 }