Compile_env vs get_env and 1.14

Hi,

We’re in the process of upgrading from 1.13 to 1.14.

We had various calls to compile_env to access env variables, and from what the warnings are telling me these should be get_env.

I’ve replaced these all, going from

@access_key Application.compile_env(:app, key)

to

defp access_key, do: Application.get_env(:app, :key)

This seems to have worked fine and cleared 99% of the errors, however there is one issue with Guardian that I can’t solve:

defmodule App.Auth.Bla do
  use Guardian,
    otp_app: :app,
    allowed_algos: ["HS512"],
    issuer: "app",
    secret_key: Application.compile_env(:app, __MODULE__)[:secret_key]
end

Because this call happens outside of a function, I cannot replace it with get_env. But leaving it at compile_env throws an error. I also can’t move it into a function as the function is not yet defined when the above code is invoked.

How can I access an environment variable and feed it into Guardian as the secret key without calling compile_env? Or is there another approach I’m missing?

Thanks

1 Like

Can you post the error you get? You should be using compile_env for compile time set config.

With compile_env you want to put the complete path into the function call:
Application.compile_env(:app, [__MODULE__, :secret_key])
That allows elixir to track dependencies better.

3 Likes

Sure, it’s:

== Compilation error in file lib/app/auth/bla.ex ==
** (RuntimeError) Application.compile_env/3 cannot be called inside functions, only in the module body
    (elixir 1.14.0) lib/application.ex:532: Application."MACRO-compile_env"/4
    (elixir 1.14.0) expanding macro: Application.compile_env/2
    lib/app/auth/bla.ex:12: App.Auth.Bla.config/0
    (elixir 1.14.0) expanding macro: Kernel.|>/2
    lib/app/auth/bla.ex:8: App.Auth.Bla.config/

It mentions line 8 and line 12 of the file, which are use Guardian and secret_key: Application.compile_env(:app, __MODULE__)[:secret_key] respectively

Seems like guardian is putting the AST of the call in a function. I’d probably just put assign the secret to a module attribute and give that to guardian, unless you actually need that to be runtime configurable.

Annoyingly I can’t do that

@secret_key Application.compile_env(:app, [__MODULE__, :secret_key])

Throws an error as this variable is different at compile time vs run time, and I can’t call get_env here as it’s outside of a function.

I also can’t do

defp secret_key, do: Application.get_env(:app, [__MODULE__, :secret_key])

as this function isn’t defined when Guardian has its secret key assigned

I might be off here, but if guardian indeed puts the AST for fetching the secret in a function for runtime use and also tries to access it at compile time then I think that’s something to fix on guardian’s end. It should be doing either/or, but not both.

You can keep configuration to the minimum in implementation module of the Guardian - move the rest in config files.

# bla.ex
defmodule App.Auth.Bla do
  use Guardian, otp_app: :app
end
# sample.config/dev.exs 
config :app, App.Auth.Bla,
  allowed_algos: ["HS512"],
  issuer: "app",
  secret_key: "AppBlaSecretKey"

You can read more in config section of Guardian docs

This will be regular configuration loading from environment, once the implementation module is kept minimal.

3 Likes

This actually works too thank you so much

2 Likes