github.com/goliatone/go-envset@v0.7.0/README.md (about) 1 # envset 2 3 `envset` run commands in an environment defined using a [ini][ini] configuration file. 4 5 --- 6 7 <!-- vscode-markdown-toc --> 8 * [Environment level configuration](#environment-level-configuration) 9 * [Similar Tools](#similar-tools) 10 * [Examples](#examples) 11 * [Executing A Command](#executing-a-command) 12 * [Restart Command](#restart-command) 13 * [Variable Substitution](#variable-substitution) 14 * [Inherit Environment](#inherit-environment) 15 * [Overwriting Variables At Runtime](#overwriting-variables-at-runtime) 16 * [Load Env File To Current Shell Session](#load-env-file-to-current-shell-session) 17 * [Required Environment Variables](#required-environment-variables) 18 * [Generating An Example Template](#generating-an-example-template) 19 * [Support For .env Files](#support-for-envfiles) 20 * [Metadata](#metadata) 21 * [Metadata Compare](#metadata-compara) 22 * [Ignore Variables](#ignore-variables) 23 * [Installation](#installation) 24 * [macOS](#macos) 25 * [Ubuntu/Debian x86_64 - amd64](#ubuntu-debianx86-64-amd64) 26 * [CentOS/Redhat x86_64 - amd64](#centos-redhat-x86-64-amd64) 27 * [Manual Install x86_64 - amd64](#manual-install-x86-64-amd64) 28 * [Documentation](#documentation) 29 * [Commands](#commands) 30 * [Variable Expansion](#variable-expansion) 31 * [Commands](#commands-1) 32 * [.envset File](#envset-file) 33 * [.envsetrc](#envsetrc) 34 * [Configuration](#configuration) 35 * [Configuration Syntax](#configuration-syntax) 36 * [Ignored And Required Sections](#ignored-and-required-sections) 37 * [License](#license) 38 39 <!-- vscode-markdown-toc-config 40 numbering=false 41 autoSave=true 42 /vscode-markdown-toc-config --> 43 <!-- /vscode-markdown-toc --> 44 45 46 --- 47 48 ## <a name='environment-level-configuration'></a>Environment level configuration 49 50 Application configuration is (usually) specific to an environment and will change between different build environments- e.g. app secrets for a staging environment are different than your production secrets. 51 52 The [12 factor app][12factor] guidelines suggest you store your application's configuration in the environment. 53 54 Environment variables enable us to manage application configuration outside of our application code. 55 56 Application configuration usually are small and sensitive data such as API keys or tokens, database credentials, etc. However not all environment configuration have to be secrets, there might be build distribution specific values such as the application's base URL to build OAuth callbacks, a dependent service endpoint or anything that changes between development and production environments. 57 58 `envset` helps you manage environment variables for multiple build environments. 59 60 The following command will run a Node.js application with a `development` environment: 61 62 ```console 63 $ envset development -- node server.js 64 ``` 65 66 `envset` will load the variables defined in the `[development]` section of a local `.envset` file and execute the command after the `--`. 67 68 See the [examples](#examples) section for more details. 69 70 ## <a name='similar-tools'></a>Similar Tools 71 72 Inspired by [daemontools][dtools]' tool [envdir][envdir] and tools such as [dotenv](https://github.com/bkeepers/dotenv). 73 74 * Distributed as a single binary 75 * No dependencies in your codebase 76 * e.g. `dotenv-rails` and `dotenv`<sup>[1](#node-dotenv)</sup> for Node.js require you to use a library 77 * Support multiple environments in a single file 78 * Generates an example file with your current env vars to keep documentation updated. 79 * Interpolation of variable using POSIX variable expansion. 80 * Command expansion 81 * Define required variables and exit with error if not set 82 * By default the shell environment is not loaded in the context 83 84 Instead of having an `.env` file per environment you can have one single `.envset` file with one section per environment. 85 86 <a name="node-dotenv">1</a>: You an actually require the library outside of your project with the `node -r` flag. 87 88 ## <a name='examples'></a>Examples 89 90 ### <a name='executing-a-command'></a>Executing A Command 91 92 An **.envset** file could look like this: 93 94 ```ini 95 [development] 96 APP_SECRET_TOKEN=4c038a0b-4ed9-44e6-8682-9c82d5b831fd 97 APP_REMOTE_SERVICE_KEY=07ed716a-078a-4327-8847-b86394f14575 98 99 [production] 100 APP_SECRET_TOKEN=796bca0f-2692-495b-a994-e8ce82cad55f 101 APP_REMOTE_SERVICE_KEY=5655969e-af9f-4ac5-b186-184f43468443 102 ``` 103 104 To use it, simply prefix the call to your program with `envset` and the name of the environment section. The node `app.js` will be running with the environment variables specified in the **development** section of the **.envset** file. 105 106 ```console 107 $ envset development -- node app.js 108 ``` 109 110 #### <a name='restart-command'></a>Restart Command 111 112 `envset` will optionally restart your command if it exits with an error code. 113 There are three flags you can use to manage the restart behavior: 114 * `--restart`: Restart command on exit error, default to `true`. 115 * `--max-restarts [int]`: Max times to restart command, defaults to `3`. 116 * `--forever`: If present restart the command for as long as `envset` is running. 117 118 All these can be configured using an `.envsetrc` [file](#envsetrc). 119 120 This will restart the node app for a maximum of `--max-restarts`. 121 122 ```console 123 $ envset development --restart --max-restarts 10 -- node app.js 124 ``` 125 126 #### <a name='variable-substitution'></a>Variable Substitution 127 128 You can execute commands that use environment variables in the command arguments. 129 130 Is important to note that you need to scape the variable so that it is not replaced in the shell as you call `envset`. You can do so by using single quotes `'` or the scape char `\$`. 131 132 ```console 133 $ envset development -- say '${APP_ENV}' 134 $ envset development -- say \${APP_ENV} 135 ``` 136 137 #### <a name='inherit-environment'></a>Inherit Environment 138 139 Sometimes the command you want to run will assume that has access to predefined environment variables: 140 141 ```console 142 $ envset development -- spd-say '${APP_ENV}' 143 Failed to connect to Speech Dispatcher: 144 Error: Can't connect to unix socket ~/.cache/speech-dispatcher/speechd.sock: No such file or directory. Autospawn: Autospawn failed. Speech Dispatcher refused to start with error code, stating this as a reason: 145 exit status 1 146 ``` 147 148 By default the process in which `envset` runs `spd-say` in an isolated mode has no access to your shell. 149 150 You can control environment inheritance using two flags: 151 152 - `--isolated`: Inherit all parent environment variables 153 - `--inherit`: Inherit specific parent environment variables 154 155 If you need the executed command to inherit the host's environment wholesale use the `--isolated=false` flag. 156 157 ```console 158 $ envset development --isolated=false -- spd-say '${APP_ENV}' 159 ``` 160 161 Some commands might rely on a known environment variable set on your shell, for instance if you want to `go run`: 162 163 ```console 164 $ envset development -- go run cmd/app/server.go 165 missing $GOPATH 166 ``` 167 168 You will get an error saying that `$GOPATH` is not available. The `--inherit` flag lets you specify a list of environment variable keys that will be inherited from the parent environment: 169 170 ```console 171 $ envset development -I=GOPATH -I=HOME -- go run cmd/app/server.go 172 ``` 173 174 #### <a name='overwriting-variables-at-runtime'></a>Overwriting Variables At Runtime 175 176 You can overwrite environment variables without editing your `.envset` file. 177 178 ```console 179 APP_NAME="New Name" envset development --isolated=false -- spd-say '${APP_NAME}' 180 ``` 181 182 #### <a name='load-env-file-to-current-shell-session'></a>Load Env File To Current Shell Session 183 184 If you want to make the variables defined in a env file to your running shell session use something like the following snippet. 185 186 187 ```sh 188 $ eval $(envset development) 189 ``` 190 191 #### <a name='required-environment-variables'></a>Required Environment Variables 192 193 You can specify a list of required environment variables for your command using the `--required` flag or its `-R` alias. 194 195 Given the following env file: 196 197 ```ini 198 [development] 199 APP_SECRET_TOKEN={{APP_SECRET_TOKEN}} 200 APP_REMOTE_SERVICE_KEY={{APP_REMOTE_SERVICE_KEY}} 201 ``` 202 203 If you run the following command: 204 205 ```console 206 $ envset development --required=BOOM -R BOOM2 -- node index.js 207 ``` 208 209 `envset` will exit with an error and a message with the missing variables: 210 211 ```console 212 missing required keys: BOOM,BOOM2 213 ``` 214 215 ### <a name='generating-an-example-template'></a>Generating An Example Template 216 217 If we run the `envset template` command with the previous **.envset** file we generate a **envset.example** file: 218 219 ```ini 220 [development] 221 APP_SECRET_TOKEN={{APP_SECRET_TOKEN}} 222 APP_REMOTE_SERVICE_KEY={{APP_REMOTE_SERVICE_KEY}} 223 224 [production] 225 APP_SECRET_TOKEN={{APP_SECRET_TOKEN}} 226 APP_REMOTE_SERVICE_KEY={{APP_REMOTE_SERVICE_KEY}} 227 ``` 228 229 230 ### <a name='support-for-envfiles'></a>Support For .env Files 231 232 You can load other environment files like `.env` files: 233 234 ```console 235 $ envset --env-file=.env -- node index.js 236 ``` 237 238 ### <a name='metadata'></a>Metadata 239 240 The `metadata` command will generate a JSON file capturing the values of the provided env file. 241 242 ### <a name='metadata-compara'></a>Metadata Compare 243 244 Note that `envset metadata compare` will output to **stderr** in the case that both files do not match. 245 246 ```console 247 $ envset metadata compare --section=development .meta/data.json .meta/prod.data.json 248 ``` 249 250 You can omit the path to the local source metadata file and only pass the remote file you want to compare against, it will use the configured path: 251 252 ``` 253 $ envset metadata compare --section=development .meta/prod.data.json 254 ``` 255 256 Pretty output 257 258 ```console 259 • source: .meta/data.json 260 STATUS ENV KEY HASH 261 👻 Missing MY_APP_NAME 6d22b97ab7dd... 262 263 264 • target: .meta/env.staging.json 265 👍 target has no extra environment variables 266 267 • different values 268 STATUS ENV KEY HASH 269 ❓ Different APP_ENV 2e9975854897... 270 ❓ Different NEW_THING 8896f09440c1... 271 272 273 👻 Missing in source (1) | 🌱 Missing in target (1) 274 275 ❓ Different values (2) | 🤷 Ignored Keys (0) 276 ``` 277 278 To have JSON output you can pass the `--json` flag: 279 280 ```console 281 $ envset metadata compare --json -s development .meta/data.json .meta/prod.json 282 { 283 "name": "development", 284 "values": [ 285 { 286 "key": "MY_APP_SECRET", 287 "hash": "aca50d5cf2f285a5a5c5469c8fe9df2540b9bea6905a23461b", 288 "comment": "different hash value" 289 }, 290 { 291 "key": "MY_APP_NAME", 292 "hash": "6d22b97ab7dd929f1b30099dcacd3a8f883373cefbe4a59a05", 293 "comment": "missing in source" 294 } 295 ] 296 } 297 ``` 298 299 #### <a name='ignore-variables'></a>Ignore Variables 300 301 When comparing metadata files you can optionally ignore some variables that you know will be different or will be missing. You can do pass `--ignore` or `-I` flag with the variable name: 302 303 ```console 304 $ envset metadata compare --section=development -I IGNORED_VAR .meta/prod.data.json 305 ``` 306 307 ## <a name='installation'></a>Installation 308 309 ### <a name='macos'></a>macOS 310 <!-- 311 TODO: List how to install in all different platforms 312 --> 313 314 Add tap to brew: 315 316 ```console 317 $ brew tap goliatone/homebrew-tap 318 ``` 319 320 Install `envset`: 321 322 ```console 323 $ brew install envset 324 ``` 325 326 327 ### <a name='ubuntu-debianx86-64-amd64'></a>Ubuntu/Debian x86_64 - amd64 328 329 ```console 330 $ export tag=<version> 331 $ cd /tmp 332 $ wget https://github.com/goliatone/go-envset/releases/download/v${tag}/envset_${tag}_linux_x86_64.deb 333 $ sudo dpkg -i envset_${tag}_linux_x86_64.deb 334 ``` 335 336 ### <a name='centos-redhat-x86-64-amd64'></a>CentOS/Redhat x86_64 - amd64 337 338 ```console 339 $ yum localinstall https://github.com/goliatone/go-envset/releases/download/v<version>/envset_<version>_linux_x86_64.rpm 340 ``` 341 342 ### <a name='manual-install-x86-64-amd64'></a>Manual Install x86_64 - amd64 343 344 ```console 345 $ wget https://github.com/goliatone/go-envset/releases/download/v<version>/envset_<version>_linux_x86_64.tar.gz 346 $ tar -C /usr/bin/ -xzvf envset_<version>_linux_x86_64.tar.gz envset 347 $ chmod +x /usr/bin/envset 348 ``` 349 350 ## <a name='documentation'></a>Documentation 351 352 `envset` will look for a file defining different environments and make them available as commands. 353 354 ```ini 355 [development] 356 APP_BASE_URL=https://localhost:3003 357 358 [production] 359 APP_BASE_URL=https://envset.sh 360 ``` 361 362 Your `.envset` files can have global variables that you are not part of any section but can be used to do string interpolation in section's variable values. 363 364 The syntax to interpolate is `%(KEY)s`. 365 366 ```ini 367 VERSION=v0.6.2 368 369 [development] 370 APP_BASE_URL=https://localhost:3003 371 DOWNLOAD_URL=%(APP_BASE_URL)s/%(VERSION)s 372 373 [production] 374 APP_BASE_URL=https://envset.sh 375 DOWNLOAD_URL=%(APP_BASE_URL)s/%(VERSION)s 376 ``` 377 378 You can have a special section with comments and the content will not generate syntax errors. 379 380 381 ### <a name='Commands'></a>Commands 382 383 The following is a list of the available commands: 384 385 * metadata 386 * compare 387 * template 388 389 ### <a name='VariableExpansion'></a>Variable Expansion 390 391 `envset` can interpolate variables using POSIX variable expansion in both the loaded environment file and the running command arguments. 392 393 ```ini 394 [development] 395 CLIENT_NAME=$(whoami -f) 396 CLIENT_ID=${CLIENT_NAME}.devices.local 397 ``` 398 399 ``` 400 $ envset development -- node cli.js --user '${USER}' 401 ``` 402 403 ### <a name='Commands-1'></a>Commands 404 405 If you type `envset` without arguments it will display help and a list of supported environment names. 406 407 ## <a name='envset-file'></a>.envset File 408 409 410 ## <a name='envsetrc'></a>.envsetrc 411 You can create an `.envsetrc` file with configuration options for `envset`. 412 413 The default `.envsetrc` looks like this: 414 415 ```ini 416 # Default configuration 417 filename=.envset 418 expand=true 419 isolated=true 420 export_environment=APP_ENV 421 restart=true 422 max_restarts=3 423 restart_forever=false 424 425 [metadata] 426 dir=.meta 427 file=data.json 428 print=true 429 json=false 430 431 [template] 432 dir=. 433 file=envset.example 434 435 [environments] 436 name=test 437 name=staging 438 name=production 439 name=development 440 441 [comments] 442 key=COMMENTS 443 key=DOCUMENTATION 444 ``` 445 446 ### <a name='Configuration'></a>Configuration 447 448 Follows `rc` [standards][rcstand]. 449 450 451 ### <a name='ConfigurationSyntax'></a>Configuration Syntax 452 453 The loaded files need to be valid `ini` syntax. 454 455 ```ini 456 [development] 457 APP_BASE_URL=https://localhost:3003 458 459 [production] 460 APP_BASE_URL=https://envset.sh 461 ``` 462 463 ### <a name='IgnoredAndRequiredSections'></a>Ignored And Required Sections 464 465 You can add a `[required]` or `[ignored]` section in your `.envsetrc`: 466 467 ```ini 468 [ignored] 469 development=MY_APP_NAME 470 development=MY_APP_SECRET 471 staging=MY_IGNORED_STAGING 472 473 [required] 474 development=MY_REQUIRED_APP_NAME 475 staging=MY_REQUIRED_VAR_STAGING 476 ``` 477 478 ## <a name='license'></a>License 479 Copyright (c) 2015 goliatone 480 Licensed under the MIT license. 481 482 483 484 [ini]: https://en.wikipedia.org/wiki/INI_file 485 [dtools]: http://cr.yp.to/daemontools.html 486 [envdir]: http://cr.yp.to/daemontools/envdir.html 487 [rcstand]: https://github.com/dominictarr/rc#standards 488 [12factor]: http://12factor.net/config 489 [vcn]: https://github.com/goliatone/vcn 490 [npm-fix-perm]:https://docs.npmjs.com/getting-started/fixing-npm-permissions 491 492 493 494 <!-- 495 Add self-update 496 * [go-update](https://github.com/tj/go-update) 497 * [go-update](https://github.com/inconshreveable/go-update) 498 * [s3update](https://github.com/heetch/s3update): Related article [here](https://medium.com/inside-heetch/self-updating-tools-in-go-lang-9c07291d6285) 499 500 documentation extension for urfav/cli/v2 501 https://github.com/clok/cdocs 502 -->