GithubHelp home page GithubHelp logo

dwyl / envar Goto Github PK

View Code? Open in Web Editor NEW
30.0 11.0 3.0 95 KB

๐Ÿ“‚ envar helps check and get environment variables in Elixir

License: GNU General Public License v2.0

Elixir 100.00%
elixir environment variables environment-variables

envar's Introduction

envar rainbow logo

A collection of functions for checking/getting/setting environment variables in Elixir.

GitHub Workflow Status codecov.io Hex.pm Dependencies: None contributions welcome HitCount


Why?

We needed a way of checking that environment variables were defined and improving the developer experience when they are not.

What?

envar is our solution to a very specific problem: the developer experience when a required environment variable is undefined.


How?

Installation

Install the package by adding envar to your list of dependencies in your mix.exs file:

def deps do
  [
    {:envar, "~> 1.1.0"}
  ]
end

Usage

Load an .env file containing a line-separated list of environment variables on your localhost:

Envar.load(".env")

That will locate the .env file in your project and set each environment variable in it.

If the .env must exist e.g. in a :test context, invoke:

Envar.require_env_file(".env")

That will log an Error if the file does not exist (or can't be found):

09:10:54.103 [error] Required .env file does not exist at path: /Alex/awesome/project/.env

When you need to retrieve a specific environment variable, use the following:

DATABASE_URL = Envar.get("DATABASE_URL")

If you want to define a default/fallback value for the environment variable when it's not set, simply add it as the second argument:

DATABASE_URL = Envar.get("DATABASE_URL", "postgres://uname:pass@host:5432/dbname"")

If you need to check that a variable is set, use is_set?/1

if Envar.is_set?("Heroku") do
  # do stuff on Heroku
end

To check if any of the variables in a List are set, invoke Envar.is_set_any?/1:

if Envar.is_set_any?(~w/HEROKU FLYIO/) do
  # Do something on non-prod environment
end

Conversely, to confirm that all the environment variables in a list are set, invoke Envar.is_set_all?/1:

if Envar.is_set_all?(~w/ADMIN_EMAIL AUTH_API_KEY/) do
  # Do something with the required environment variables
end

We needed a couple more convenience functions, so we wrote them!

If you need to read an .env file and get the keys & values as a Map, invoke Envar.read/1:

iex> Envar.read(".env")
%{
  "ADMIN_EMAIL" => "[email protected]",
  "EVERYTHING" => "awesome!",
  "SECRET" => "master plan"
}

If you need just the keys of the list of environment variables in an .env file, invoke: Envar.keys/1:

iex> Envar.keys(".env")
["ADMIN_EMAIL", "EVERYTHING", "SECRET"]

And if you need just the values of the environment variables in an .env file, invoke: Envar.values/1:

iex> Envar.values(".env")
["[email protected]", "awesome!", "master plan"]

For more detail, please see docs: https://hexdocs.pm/envar/Envar.html#functions



Happy Elixir Coding! ๐Ÿš€



Context

We created this module after doing a search of the Elixir (Hex.pm) ecosystem,
see: dwyl/learn-environment-variables/issues/18

There are several modules available, we wanted something very basic/specific to our needs.
We don't expect anyone else to use this; it's Open Source because that's just what we do:
/manifesto.md#open-source-always

envar's People

Contributors

arhell avatar dependabot[bot] avatar luchoturtle avatar nelsonic avatar simonlab avatar tomkra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

envar's Issues

Use `Logger.alert` instead of `IO.puts` for Logging

Instead of having IO.inspect in the code, we should do it the "right" way using the Logger module: https://hexdocs.pm/logger/Logger.html

If you attempt to invoke any Logger function without requiring it, you will see the following:

** (CompileError) lib/envar.ex:25: you must require Logger before invoking the macro Logger.alert/1

Simply add the following line near the top of the file:

require Logger

Ref: https://timber.io/blog/the-ultimate-guide-to-logging-in-elixir/

`Envar.keys/1` should return the list of environment variable keys for an `.env` file

Recently, I found myself manually writing out a list of environment variable names
that are already described in an .env file ... ๐Ÿ™„

This is a horrible duplication and to be avoided!

So I figure we could just read the .env file and extract the keys.
Similar to Object.keys in JS land or Map.keys/1 here in Elixir world.

Todo โœ…

  • Create tests for what we expect to receive when parsing a sample .env file e.g: .env_sample ๐Ÿ“
  • Implement Envar.keys/1 (and any helper) function(s) ๐Ÿง‘โ€๐Ÿ’ป
  • Publish new version of package to Hex.pm ๐Ÿ“ฆ

I expect we can borrow some code from load/1 and simply return the keys. ๐Ÿ’ญ
(or maybe refactor into a reusable helper function ...)
(so it should only take a few mins to implement this ... ๐Ÿคž)

Chore: warning: Application.get_env/2 discouraged in module body, use Application.compile_env/3

Getting the following warnings when compiling the MVP project:

warning: Application.get_env/3 is discouraged in the module body, use Application.compile_env/3 instead
  lib/plug/conn/status.ex:6: Plug.Conn.Status

warning: Application.get_env/2 is discouraged in the module body, use Application.compile_env/3 instead
  lib/plug/mime.ex:4: Plug.MIME

warning: Application.get_env/2 is discouraged in the module body, use Application.compile_env/3 instead
  lib/auth_plug.ex:22: AuthPlug

The last one is related to AuthPlug which we "control". So we should update our code.
This means we have an opportunity to create an Envar.compile_env/3 function
that proxies Application.compile_env/3 but enhances it for usability.

Load an `.env` file of environment variables

  • Load an .env file before attempting to run an App.

We have a rudimentary version of this in:

https://github.com/dwyl/auth/blob/6466f8b212a40229fbec6e7398f33f2ecfa22a8c/priv/repo/seeds.exs#L93-L110

  # load the .env file
  def load_env() do
    path = File.cwd!() <> "/.env"
    IO.inspect(path, label: ".env file path")
    {:ok, data} = File.read(path)

    data
    |> String.trim()
    |> String.split("\n")
    |> Enum.each(fn line ->
      with line <- String.replace(line, ["export ", "'"], ""),
           [key | rest] <- String.split(line, "="),
           value <- Enum.join(rest, "=") do
        System.put_env(key, value)
      end
    end)
  end

It already does what we need so going to add some tests and see how far we can get.

Bug: function `Envar.get/1` is undefined

== Compilation error in file lib/protect/github.ex ==
** (UndefinedFunctionError) function Envar.get/1 is undefined (module Envar is not available)
    Envar.get("GITHUB_ACCESS_TOKEN")

The default should not be required.

Chore: Log a `Error` if the `.env` file does not exist on `localhost`

Currently in the Development section of auth_plug: https://github.com/dwyl/auth_plug#development
We recommend the manual steps:

Create a .env file:

cp .env_sample .env
source .env

We can eliminate these steps completely by load/1-ing the .env_sample file.
But what about a project where we want there to be an .env file present on localhost and it's not there? ๐Ÿคท

Todo

  • Create a new function e.g: require_env_file/1 that throws a Logger.warn if the .env file is not present.

Intro Logo?

At present the repo looks a bit bare/boring:

image

That's fine for a "utility" module like this one. But it would be nice to have a more attractive intro logo ...

Chore: Respect Logging Pref of Parent/Supervisor App

As noted by @vans163 in #36 (comment) our current approach to logging
is a little crude and could result in a lot of noise and potentially even perf issues on a high traffic app. ๐Ÿ’ญ

As highlighted in https://github.com/dwyl/envar/pull/36/files#diff-c24eb47ea99dc64a5109913f247d10fd83fa57e93cd9b4609cb703552215c901 there are 4 instances of Logger calls:

envar/lib/envar.ex

Lines 31 to 33 in 03b035d

if is_nil(val) do
Logger.error("ERROR: #{varname} Environment Variable is not set")
end

envar/lib/envar.ex

Lines 55 to 58 in 03b035d

case System.get_env(varname) do
nil ->
Logger.debug("#{varname} Environment Variable is not set")
false

envar/lib/envar.ex

Lines 165 to 170 in 03b035d

if File.exists?(path) do
load(filename)
else
Logger.error("Required .env file does not exist at path: #{path}")
:error
end

envar/lib/envar.ex

Lines 199 to 202 in 03b035d

def read(filename) do
path = Path.join(File.cwd!(), filename)
Logger.debug(".env file path: #{path}")

Most of these are conditional, i.e. they won't log if the variable is set or file exists.
But this last one ... I have to agree with @vans163 is sloppy. ๐Ÿคฆโ€โ™‚๏ธ

Todo

  • Re-introduce Logs into the project but they should:
    • Be OFF by default i.e. not noisy! [they can be enabled on :dev or :test]
    • Respect the Logging level of the "parent" (e.g. Phoenix) App.

Side Quest:

What features do we need?

We need to:

  • check for the presence of an environment variable e.g: `Getenv.
  • give the developer a helpful/friendly error/warning message when the variable is not set

Add Dependabot to Update Dependencies

Just spotted another Elixir project that doesn't have auto-dependency updates.
Not really a problem in the case of this repo where we have no actual depenencies only :dev and :test ...

envar/mix.exs

Lines 33 to 44 in 5d5291d

defp deps do
[
# track test coverage: https://github.com/parroty/excoveralls
{:excoveralls, "~> 0.13.2", only: [:test, :dev]},
# Create Documentation Hex.docs: https://hex.pm/packages/ex_doc
{:ex_doc, "~> 0.22.6", only: :dev},
# Keep Code Tidy: https://github.com/rrrene/credo
{:credo, "~> 1.6.2", only: [:dev, :test], runtime: false},
]
end

But just for completeness and to delegate updating to ๐Ÿค– ...

Todo:

  • Create a file with the following path: .github/dependabot.yml

With the following contents:

version: 2
updates:
- package-ecosystem: mix
  directory: "/"
  schedule:
    interval: daily
    time: "07:00"
    timezone: Europe/London

Docs: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot

see: https://github.com/dwyl/fields/tree/main/.github

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.