feat: Add .envrc support

This commit is contained in:
Adriano Caloiaro 2024-01-19 17:23:56 -07:00
parent 3497414a7c
commit 09381ed0fe
No known key found for this signature in database
GPG key ID: C2BC56DE73CE3F75
9 changed files with 331 additions and 51 deletions

13
.envrc Normal file
View file

@ -0,0 +1,13 @@
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
fi
watch_file flake.nix
watch_file flake.lock
watch_file go.lock
if ! use flake . --impure
then
echo "devenv could not be built. The devenv environment was not loaded." >&2
fi

3
.gitignore vendored
View file

@ -1 +1,4 @@
/gomod2nix-template
.direnv
.devenv
.pre-commit-config.yaml

View file

@ -6,14 +6,45 @@ Automatically keep `.env` files in sync with `env.sample`
---
`ess` checks whether the local repository contains an `.env` file (configurable), scrubs it of secrets/
values, and makes the scrubbed version available as `env.sample` (configurable).
`ess` transforms environment files containing secrets (e.g. `.env`, `.envrc`) into sample environment files (e.g.
`env.sample`) that may be safely checked into git.
This process can be run manually, or automatically as a git-hook, ensuring that all application environment variables
are safely and automatically documented without leaking secrets.
`ess` may be run manually by running the cli executable, or automatically by installing the pre-commit hook with `ess install`
in any git repository. Installing `ess` as a git hook ensures that project environment files are automatically and safely
revision controlled when they change.
Crucially, `ess` allows comments in `env` files, which are carried over to `env.sample`. This lets
developers add thorough environment variable documentation to source control.
Doing so allows environment configurations to be shared across teams without leaking secrets.
## How it works
By default, `ess` checks the local directory for environment files named `.env`. The env file name is controlled by
the `--env-file` switch. Next, the environment file is parsed for environment variables. Environment variables
may be of the following forms:
```
# Standard environment variables
FOO=bar baz
FOO="bar baz"
FOO='bar baz'
# Envrc environment variable
export FOO=bar baz
export FOO="bar baz"
export FOO='bar baz'
```
By default, variable values are replaced with innert values named after the variable, e.g. `FOO=bar` is replaced by `FOO=<FOO>`.
Example values may be provided with the `--example` switch, e.g. `--exmaple=FOO="enter your foo here"` will set `FOO`'s
value as follows `FOO="enter your foo here"` in the sample file.
Finally, when all variables are replaced, the sample file is written with the sanitized variable values, along with all
non-variable strings from the file. By default the sample file is named `env.sample`, which is controlled by the `--env-sample`
switch.
Because `ess` permits non-variable strings in environment files, it means that both comments and script code (in the case
of `.envrc` files) is included in environment sample files. This allows environment files to not only be checked into git, but
documented with comments.
# Installation & Usage
@ -212,7 +243,7 @@ repos:
args: [--example=FOO="Provide your foo here", --example=BAR="You can fetch bars from https://example.com/bars"]
```
Example env file
Example environment file
`.env`
```
FOO=the_value_of_my_secret_foo

View file

@ -1,15 +1,50 @@
{
"nodes": {
"flake-utils": {
"devenv": {
"inputs": {
"systems": "systems"
"flake-compat": "flake-compat",
"nix": "nix",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"lastModified": 1688058187,
"narHash": "sha256-ipDcc7qrucpJ0+0eYNlwnE+ISTcq4m03qW+CWUshRXI=",
"owner": "cachix",
"repo": "devenv",
"rev": "c8778e3dc30eb9043e218aaa3861d42d4992de77",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "v0.6.3",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
@ -18,11 +53,49 @@
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"gomod2nix": {
"inputs": {
"flake-utils": [
"flake-utils"
],
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
]
@ -41,27 +114,144 @@
"type": "github"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1676545802,
"narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
"owner": "domenkozar",
"repo": "nix",
"rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "relaxed-flakes",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1705133751,
"narHash": "sha256-rCIsyE80jgiOU78gCWN3A0wE0tR2GI5nH6MlS+HaaSQ=",
"lastModified": 1706487304,
"narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9b19f5e77dd906cb52dade0b7bd280339d2a1f3d",
"rev": "90f456026d284c22b3e3497be980b2e47d0b28ac",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1678872516,
"narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1706487304,
"narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "90f456026d284c22b3e3497be980b2e47d0b28ac",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1686050334,
"narHash": "sha256-R0mczWjDzBpIvM3XXhO908X5e2CQqjyh/gFbwZk/7/Q=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "6881eb2ae5d8a3516e34714e7a90d9d95914c4dc",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"devenv": "devenv",
"gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs_2",
"systems": "systems_2"
}
},
"systems": {
@ -78,6 +268,21 @@
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View file

@ -1,29 +1,54 @@
{
description = "Automatically keep .env files in sync with env.sample";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
systems.url = "github:nix-systems/default";
devenv.url = "github:cachix/devenv/v0.6.3";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.gomod2nix.url = "github:nix-community/gomod2nix";
inputs.gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
inputs.gomod2nix.inputs.flake-utils.follows = "flake-utils";
gomod2nix = {
url = "github:nix-community/gomod2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, flake-utils, gomod2nix }:
(flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = nixpkgs.legacyPackages.${system};
outputs = {
self,
nixpkgs,
devenv,
systems,
gomod2nix,
...
} @ inputs: let
forEachSystem = nixpkgs.lib.genAttrs (import systems);
in {
devShells = forEachSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
default = devenv.lib.mkShell {
inherit inputs pkgs;
modules = [
{
packages = with pkgs; [
automake
go_1_21
gomod2nix.legacyPackages.${system}.gomod2nix
gotools
golangci-lint
go-tools
gopls
pre-commit
flyctl
];
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
# This has no effect on other platforms.
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
in
{
packages.default = callPackage ./. {
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
};
devShells.default = callPackage ./shell.nix {
inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;
};
})
);
pre-commit.hooks.gomod2nix = {
enable = true;
pass_filenames = false;
name = "gomod2nix";
description = "Run gomod2nix before commit";
entry = "${gomod2nix.legacyPackages.${system}.gomod2nix}/bin/gomod2nix";
};
}
];
};
});
};
}

2
go.mod
View file

@ -2,4 +2,6 @@ module github.com/acaloiaro/ess
go 1.21
replace github.com/hashicorp/go-envparse v0.1.0 => github.com/acaloiaro/go-envparse v0.3.0
require github.com/hashicorp/go-envparse v0.1.0

4
go.sum
View file

@ -1,2 +1,2 @@
github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY=
github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=
github.com/acaloiaro/go-envparse v0.3.0 h1:TGHMQ6VK8ABhqalxaRTU2gj75dCDorQq6YTxGVI5X+4=
github.com/acaloiaro/go-envparse v0.3.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=

View file

@ -2,5 +2,6 @@ schema = 3
[mod]
[mod."github.com/hashicorp/go-envparse"]
version = "v0.1.0"
hash = "sha256-MkHaxEZDHVz84IImaC8+s4lAgNLhsaAMSkVmt55JC3M="
version = "v0.3.0"
hash = "sha256-pbffSgC8BDtvkQAyI6ILkrMleI8m2JtedV0PL64Ok3I="
replaced = "github.com/acaloiaro/go-envparse"

View file

@ -125,9 +125,9 @@ func sync(dir string) {
os.Exit(0)
}
envFile, err := envparse.Parse(envFileReader)
envFile, err := envparse.ParsePermissive(envFileReader)
if err != nil {
fmt.Println("unable to parse env file:", err)
fmt.Fprintf(os.Stderr, "unable to parse env file (%s): %v", envFilePath, err)
os.Exit(1)
}