https://github.com/atty303/mise-dotenv-sops
Load .env values from YAML/JSON/dotenv with optional SOPS decryption, variable expansion and command substitution — directly from Mise
https://github.com/atty303/mise-dotenv-sops
dotenv dotenvx mise mise-plugin sops
Last synced: 9 months ago
JSON representation
Load .env values from YAML/JSON/dotenv with optional SOPS decryption, variable expansion and command substitution — directly from Mise
- Host: GitHub
- URL: https://github.com/atty303/mise-dotenv-sops
- Owner: atty303
- License: mit
- Created: 2025-09-12T00:25:01.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-09-15T12:50:22.000Z (9 months ago)
- Last Synced: 2025-09-19T18:59:59.191Z (9 months ago)
- Topics: dotenv, dotenvx, mise, mise-plugin, sops
- Language: Lua
- Homepage:
- Size: 148 KB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# mise-dotenv-sops
[](https://github.com/atty303/mise-dotenv-sops/actions/workflows/build.yaml)
[](https://github.com/atty303/mise-dotenv-sops/blob/main/LICENSE)
Load `.env` values from YAML/JSON/dotenv with
optional [SOPS](https://getsops.io/)
decryption, variable expansion (`$VAR`) and command substitution (`$(cmd)`) —
directly from [Mise](https://mise.jdx.dev/).
- Cleanly loads environment variables from `.env`, `.env.yaml`, and `.env.json`
files in your
project.
- Decrypts SOPS-encrypted files when needed.
- Supports `$VAR` expansion and `$(command)` substitution.
- Respects ordered `MISE_ENV` like `stg,prod` with last-one-wins merging.
## Quickstart
```bash
# install the plugin (once per machine)
❯ mise plugin install dotenv-sops https://github.com/atty303/mise-dotenv-sops.git
# in your project
❯ mise set "_.dotenv-sops="
❯ echo '{"URL":"http://localhost:3000"}' > .env.json
# show effective environment
❯ mise env --dotenv
URL=http://localhost:3000
```
A full example is available in [example/complete](./example/complete).
## Features
- Load dotenv, YAML, and JSON `.env` files
- `MISE_ENV` supports comma-separated ordered environments (e.g. `stg,prod`)
- Variable expansion: `$VAR`
- Command substitution: `$(command)`
- Flatten nested keys using a configurable separator
- SOPS-aware: auto-decrypt if sops metadata is present
## Requirements
- [`mise`](https://mise.jdx.dev/)
- `mise use sops` (only when decrypting encrypted files)
- `mise use yq` (reading plain YAML when `yq = true`)
## Usage
Minimal `mise.toml` (see example/complete for a full version):
```toml
[env._.dotenv-sops]
# log level: "debug" | "info" | "warn" | "error" | "off" (default: "off")
log = "off"
# allow $(...) command substitution (default: false)
command = false
# allow $VAR expansion (default: true)
env = true
# separator for flattening nested keys (default: "_")
sep = "_"
# stop on failure; when false, continue and log errors (default: true)
strict = true
# use slower but complete yq parser instead of lua-tinyyaml (default: false)
yq = false
```
### SOPS decryption
When decrypting a file with the corresponding identity, SOPS will look for a
text file name `keys.txt` located in a `sops` subdirectory of your user
configuration directory:
- On Linux, this would be `$XDG_CONFIG_HOME/sops/age/keys.txt`.
- On macOS, this would be `$HOME/Library/Application Support/sops/age/keys.txt`.
- On Windows, this would be `%AppData%\sops\age\keys.txt`.
You can specify the location of this file manually by setting the environment
variable `SOPS_AGE_KEY_FILE` or `MISE_SOPS_AGE_KEY_FILE`.
Alternatively, you can provide the key(s) directly by setting the `SOPS_AGE_KEY`
or `MISE_SOPS_AGE_KEY` environment variable.
`MISE_SOPS_AGE_KEY` and `MISE_SOPS_AGE_KEY_FILE` are prioritized over
`SOPS_AGE_KEY` and `SOPS_AGE_KEY_FILE`.
And it can share
with [Mise's secrets feature](https://mise.jdx.dev/environments/secrets.html).
### SOPS encryption
Nothing special. Just use `sops` CLI.
See [SOPS documentation](https://getsops.io/docs/).
### File loading order
Files are loaded from the current directory only. When `MISE_ENV=ENV1,ENV2`,
existing files are read in this order and merged; later entries win.
- `.env`, `.env.json`, `.env.yaml`
- `.env.local`, `.env.local.json`, `.env.local.yaml`
- `.env.`, `.env..json`, `.env..yaml`
- `.env..local`, `.env..local.json`, `.env..local.yaml`
- `.env.`, `.env..json`, `.env..yaml`
- `.env..local`, `.env..local.json`, `.env..local.yaml`
## Tips & FAQ
- Nothing loaded?
- Turn on debug logs to see what’s happening: set `log = "debug"` in your
`mise.toml`, then run `mise env` to view file discovery, decryption, and
resolving steps.
- Can I reference outer environment variables in expansions?
- Yes. Later values in the same file can reference earlier ones, too.
- Is command substitution risky?
- It is disabled by default. Enable only in trusted projects.
- `mise env --env NAME` doesn’t load variables from `.env.NAME.yaml`
- The `--env` global option only affects `mise.toml` loading; it doesn’t
affect plugins. Use `MISE_ENV=NAME mise env`.
- My YAML file is not parsed correctly
- Use `yq = true` to use `yq` instead of `lua-tinyyaml`.
## Security notes
- Keep `command = false` unless you fully trust the repository.
- Review `.env.*` files for unintended secrets before committing.
## Development
Local link plugin for development:
```bash
mise plugin link dotenv-sops . --force
```
Run tests:
```bash
mise run test
```
How tests work:
- Compare `mise env -J` output with each case’s `expected.yaml`.
- SOPS keys are set temporarily according to `expected.yaml` using
`MISE_SOPS_AGE_KEY` / `MISE_SOPS_AGE_KEY_FILE`.
## Roadmap
- [ ] Import another dotenv file from dotenv files
- [ ] Use cache for speed optimization
- [ ] Support TOML and INI formats