power-config v0.22.0
power-config
A tool for generating a configuration file.
Features
- Command-line tool that can prompt user for input
- Values can be hard-coded or entered by user
- Supports using both JSON and YAML
- Supports multiple environments, such as dev, test, and prod
- Makes tracking configuration file changes over time easier
Background
Applications and software tools often need run-time specific information. Rather than hard-coding this information into the application, the information is separated out into either environment variables and/or some kind of "configuration file". A "configuration file" is simply a set of key/value pairs, such as port = 8080. In some cases, a "configuration file" includes structure by nesting key/value pairs within a namespace.
Sometimes the amount of run-time configuration information is very simple or small. In those cases, power-config is probably overkill and environment variables or a simple hand-written configuration file is good enough.
In other cases, an application requires significant amounts of run-time configuration. Again, you could get by with a hand-written configuration file but experience has shown this presents some challenges.
- As your application code changes over time and moves through the stages of local development, integration testing, and live in production, the content and structure of your configuration file needs to reflect those changes.
- Maintaining multiple configuration files for development, test, and production is not DRY and can be challenging to keep synchronized.
- Documentation for configuration files is often lacking. A best practice is to include an example configuration file in your source code. However, the place holders for the key/value pairs don't necessarily include context and instructions for new developers on how or where to get the actual values. Some formats, such as JSON, don't even support comments.
Introducing power-config
The power-config tool attempts to address the challenges of maintaining a significant configuration file.
It starts with the best practice of an example configuration file that is part of your source code. But rather than simply having a key/value pair and a placeholder for its value, it uses the value to describe the value itself. This supports documentation for new developers. The description can either be a hard-coded value or a prompt for the user to enter a value. The prompt can also include step-by-step instructions for how and where to get the value.
power-config will read the example configuration file and generate the configuration file. In cases where user input is required, power-config will prompt the user, capture the input, and save it to another file, the "input file". The "input file", unlike the "example file", should probably not be part of your source code as it might contain sensitive data.
As your code moves from local development, to integration testing, to live in production, you re-run power-config with the "input file". If the "input file" already contains a value, the user will not prompted and the value will be passed through to the "configuration file". If the value does not exist, then the use will be prompted and the "input file" will be updated.
The intent of power-config is have one "example file" and one "input file" that can generate a "configuration file" for development, test, and production. This will keep things DRY. The key/value pairs in the "example file" can be associated with different environments.
A Tale of Three Files
example + input = output
The Example File
The "example file" is written by developers, in either JSON or YAML, and describes the data fields and structure of the configuration file you wish to generate. It is intended to be checked into source control so that changes to your configuration file can be tracked over time. Since it is intended for source control, it should never contain any sensitive data.
When power-config is run, it reads the "example file" and prompts the user for any necessary inputs.
The Input File
The "input file" is automatically read and updated by power-config, in either JSON or YAML, and captures all the input values entered by the user. It is NOT intended for this file to ever be checked into source control. This file is where sensitive data is captured and saved. The "input file", or a copy of it, should be stored in some kind of virtual safety deposit box, like cloud storage, that is reliably backed up, protected from unauthorized access, and not publicly discoverable.
When power-config is run, it reads the "input file" along with the "example file". If the "example file" contains any data fields not included in the "input file", the user will be prompted for input. The user input will be captured and the "input file" will be updated.
The Output File
The "output file", also known as the "configuration file", is automatically generated by power-confg, in either JSON or YAML. This is the file that your application will use at run-time. It is NOT intended for this file to ever be checked into source control since it can contain sensitive data. The file does not need to be backed up either. The combination of the two backed up files, "example file" and "input file", will allow you to re-create the "output file" at any time.
Installation
Installing power-config is just like any other npm package. I would recommend using --save-dev to include it in the devDependencies. You could install it globally with the -g option, but I personally like installing everything locally. This way everything is self-contained within the project, I don't run into any permissions problems, and I can control the package versions on a per project basis.
npm install --save-dev power-configOnce installed, you can add some scripts to package.json to make power-config easier to work with locally.
vim package.jsonAdd whatever scripts you want. Here are some suggestions.
"scripts": {
"power-config": "power-config",
"pc": "power-config"
}With the scripts in place, you can use npm run to run power-config. You will have to include -- on the command-line to pass thru the arguments.
npm run power-config -- --help
npm run pc -- --helpAPI Examples
The "example file" can be written in either JSON or YAML. The API examples shown here are all in YAML.
The simplest case
Start by creating an "example file".
vim examples/yaml/simple.example.yml
In the simplest case, we can define a key with a hard-coded value requiring no input from the user.
port: 8080Use power-config to read the "example file" and generate an "output file".
npm run power-config -- -x examples/yaml/simple.example.yml
The output file is automatically created since there is no input is needed from the user,
cat examples/yaml/sample.yml
port: 8080While this scenario is simple, it does not showcase all the features of power-config. The following sections describe the features of power-config.
The value field
The value field simply hard-codes the data value without any input from the user. If a value field is not specified, the user will be prompted to enter a value.
vim examples/yaml/value.yml
port:
value: 8080npm run power-config -- -x examples/yaml/value.example.yml
Since no input is needed from the user, the output file is automatically created.
cat examples/yaml/value.yml
port: 8080The type field
The type field defines how the user input will be stored. The value can be one of: string, integer, boolean, arrayOfString, or arrayOfInteger. This is an optional field and will default to string. For array types, use a comma to enter multiple values.
vim examples/yaml/type.yml
hostname:
type: string
port:
type: integer
public:
type: booleannpm run power-config -- -x examples/yaml/type.example.yml
The user will be prompted for input. For example,
TYPE: string
hostname : wakanda
----------
TYPE: integer
port : 8080
----------
TYPE: boolean
public : nocat examples/yaml/type.yml
hostname: wakanda
port: 8080,
public: falseThe default field
The default field defines the value when the user does not provide any input. This is an optional field. If provided, the default value will be displayed in the prompt inside brackets.
vim examples/yaml/default.example.yml
port:
default: 80npm run power-config -- -x examples/yaml/default.example.yml
The user will be prompted for input. For example,
TYPE: string
port [ 80 ] :If the user clicks the enter/return key without any input, then the default value will be used.
cat examples/yaml/default.yml
port: 80The include field
The include field is an alternative to using the value field as a means of providing hard-coded input. It specifies the path to a file that will be read and parsed based on the file type.
vim examples/yaml/inclue.yml
avengers:
include: examples/yaml/avengers.json
jla:
include: examples/yaml/jla.ymlThe include file does not have to be the same file type as the example file. You can include a JSON file.
cat examples/yaml/avengers.json
["blackwidow", "captain", "hawkeye", "hulk", "ironman", "thor"]Or you can include a YAML file.
cat examples/yaml/jla.yml
- batman
- flash
- aquaman
- wonderwoman
- cyborgnpm run power-config -- -x examples/yaml/include.example.yml
The two files are read and parsed based on their file extensions.
cat examples/yaml/include.yml
avengers:
- blackwidow
- captain
- hawkeye
- hulk
- ironman
- thor
jla:
- batman
- flash
- aquaman
- wonderwoman
- cyborgThe description field
The description field provides the user with some context for the given data field. The value can be either a string or an array.
vim examples/yaml/description.yml
hostname:
description: The hostname
port:
description:
- The port
- usually a number
type: integernpm run power-config -- -x examples/yaml/description.example.yml
The user will be prompted for input. For example,
DESCRIPTION: The hostname
TYPE: string
hostname : wakanda
----------
DESCRIPTION:
- The port
- usually a number
TYPE: string
port : 80cat examples/yaml/description.yml
hostname: wakanda
port: 80The steps field
The steps field enhances the description field by providing a list of step-by-step instructions on how the user can find the information needed for input.
vim examples/yaml/steps.example.yml
username:
description: The AWS username
steps:
- go to AWS Console
- - select IAM
- - click on Users
- - find your usernamenpm run power-config -- -x examples/yaml/steps.example.yml
The user will be prompted for input. For example,
DESCRIPTION: The AWS username
- go to AWS Console
- select IAM
- click on Users
TYPE: string
username : sallycat examples/yaml/steps.yml
username: sallyUsing reserved words as data fields
You might want to define a data field that is the same name as one of the field names used in the power-config API. For example, you might want to define a field called type. To do that, you must wrap the field name with a leading and a trailing underscore.
vim examples/yaml/reserved.example.yml
_type_:
description: The typenpm run power-config -- -x examples/yaml/reserved.example.yml
The user will be prompted for input. For example,
DESCRIPTION: The type
TYPE: string
_type_ : EC2 Instancecat examples/yaml/reserved.yml
type: EC2 InstanceAdding nested structure
The structure of your configuration file does not have to be flat. You can create a namespace by nesting values within values.
vim examples/yaml/nested.example.yml
server:
database:
hostname:
description: The hostname
port:
description: The port
type: integer
proxy:
hostname:
description: The hostname
port:
description: The port
type: integernpm run power-config -- -x examples/yaml/nested.example.yml
The user will be prompted for input. For example,
DESCRIPTION: The hostname
TYPE: string
serve.database.hostname : blue
----------
DESCRIPTION: The port
TYPE: integer
server.database.port : 3000
----------
DESCRIPTION: The hostname
TYPE: string
server.proxy.hostname : green
----------
DESCRIPTION: The port
TYPE: integer
server.proxy.port : 8080Each prompt shows the entire nested namespace.
cat examples/yaml/nested.yml
server:
database:
hostname: blue
port: 3000
proxy:
hostname: green
port: 8080Working with multiple environments
It is not uncommon to have multiple environments, such as development, test, and production. And as soon as you have multiple environments, your configuration will need to be different in each of those environments.
power-config supports multiple working environments in a variety of ways so that you can create your configuration file exactly how you want it. By default, power-config is aware of the following environments: local, dev, test, and prod. (See the CLI examples on how you can change this default.)
In your example file, you can use the environment just like any other nested namespace.
vim examples/yaml/environments1.example.yml
dev:
port:
description: The port
type: integer
test:
port:
description: The port
type: integer
prod:
port:
description: The port
type: integerThe ability to change the structure of the output configuration file rests in the command-line options specified. The first output structure includes all the environments and is created by simply specifying the example file.
npm run power-config -- -x examples/yaml/environments1.example.yml
The user will be prompted for input. For example,
DESCRIPTION: The port
TYPE: integer
dev.port : 8080
----------
DESCRIPTION: The port
TYPE: integer
test.port : 8080
----------
DESCRIPTION: The port
TYPE: integer
prod.port : 80Notice you were prompted for all three environments and the output included all three environments.
cat examples/yaml/environments1.example.yml
dev:
port: 8080
test:
port: 8080
prod:
port: 80The second output structure limits the scope to just one environment by specifying the environment using -e.
npm run power-config -- -x examples/yaml/environments2.example.yml -e test
The user will be prompted for input. For example,
DESCRIPTION: The port
TYPE: integer
test.port : 8080Notice you were only prompted for the one environment and the output only included the one environment.
cat examples/yaml/environments2.example.yml
test:
port: 8080Finally, the third output structure not only limits the scope to just one environment but also flattens the output using -f and removes the environment namespace.
npm run power-config -- -x examples/yaml/environments3.example.yml -e test -f
The user will be prompted for input. For example,
DESCRIPTION: The port
TYPE: integer
test.port : 8080Again, you were only prompted for the one environment but now the output does not include the environment itself.
cat examples/yaml/environments3.example.yml
port: 8080Of course, using the environment as a namespace is not limited to the top level. The environments can also be used at any level of the namespace. You can change the structure of the output, just like the previous three examples, by specifying command-line options.
vim examples/yaml/environments4.example.yml
port:
dev:
value: 8080
test:
description: The port
type: integer
prod:
description: The port
type: integerFirst, simply specify the example file.
npm run power-config -- -x examples/yaml/environments4.example.yml
The user will be prompted for input. For example,
DESCRIPTION: The port
TYPE: integer
port.test : 8080
----------
DESCRIPTION: The port
TYPE: integer
port.prod : 80cat examples/yaml/environments4.yml
port:
dev: 8080
test: 8080
prod: 80Second, limit the scope to just one environment.
npm run power-config -- -x examples/yaml/environments5.example.yml -e test
The user will be prompted for input. For example,
DESCRIPTION: The port
TYPE: integer
port.test : 8080cat examples/yaml/environments5.yml
port:
test: 8080And third, limit the scope to just one environment and flatten.
npm run power-config -- -x examples/yaml/environments6.example.yml -e test -f
The user will be prompted for input. For example,
DESCRIPTION: The port
TYPE: integer
port.test : 8080cat examples/yaml/environments6.yml
port: 8080The environment field
The environment field is an alternative to the environments namespace as a means to limit the scope of the data field to one or more environments. You can set the value as a single string or an array of strings.
vim examples/yaml/environment1.example.yml
hostname:
description: The hostname
type: string
port:
description: The port
type: integer
environment: [test, prod]
domain:
description: The domain
type": string
environment: test
ip:
description: The IP address
type: string
environment: prodWhen a data field has no environment field defined, then the data field applies to all environments. Running with -e dev will prompt the user for only one input.
npm run power-config -- -x examples/yaml/environment1.example.yml -e dev
The user will be prompted for input. For example,
DESCRIPTION: The hostname
TYPE: string
hostname : wakandaThe output only includes the one value.
cat examples/yaml/environment1.yml
hostname: wakandaRunning with -e test will prompt the user for three inputs.
npm run power-config -- -x examples/yaml/environment2.example.yml -e test
The user will be prompted for input. For example,
DESCRIPTION: The hostname
TYPE: string
hostname : wakanda
----------
DESCRIPTION: The port
TYPE: integer
port : 8080
----------
DESCRIPTION: The domain
TYPE: string
domain : mcuThe output only includes three values .
cat examples/yaml/environment2.yml
hostname: wakanda
port:
test: 8080
domain:
test: mcuRunning with -e prod will prompt the user for a different set of three inputs.
npm run power-config -- -x examples/yaml/environment3.example.yml -e prod
The user will be prompted for input. For example,
DESCRIPTION: The hostname
TYPE: string
hostname : wakanda
----------
DESCRIPTION: The port
TYPE: integer
port : 80
----------
DESCRIPTION: The IP address
TYPE: string
ip : 127.0.0.0The output only includes three values.
cat examples/yaml/environment3.yml
hostname: wakanda
port:
prod: 80
ip:
prod: 127.0.0.0CLI Examples
The power-config command-line tool supports a number of options.
npm run power-config -- --help
Usage: power-config [options]
Options:
-V, --version output the version number
-i, --input <input> input configuration file
-o, --output <output> output configuration file
-x, --example <example> example configuration file. Default is environment.example.json
-e, --environment <environment> environment such as dev, test, or prod
-f, --flatten flatten nested environment
-C, --clear <clear> path(s) to input value(s) to clear and/or set
-U, --no-user-inputs exit with failure if user input is required
-R, --review review all user inputs
-h, --help output usage informationRunning with no options
If you run power-config with no options, it uses the following defaults:
- the "example file" is
environment.example.yml - the "input file is"
environment.input.yml - the "output file" is
environment.yml
The --example option
Use the --example option to specify the "example file". The file can be written in either JSON or YAML.
npm run power-config -- -x my_system_config.json
npm run power-config -- -x my_system_config.yml
If this option is not specified, then the default will be environment.example.json
The --output option
Use the --output option to specify the "output file". The file can be written in either JSON or YAML.
npm run power-config -- -o configuration.json
npm run power-config -- -o configuration.yml
If this option is not specified, then the default will be to use the name of the "example file" with the words "example" and "sample" removed. The output format will default to the same type as the "example file".
npm run power-config -- -x environment.example.json # (output file is environment.json)
npm run power-config -- -x environment.sample.json # (output file is environment.json)
The --input option
Use the --input option to specify the "input file". The file can be written in either JSON or YAML.
npm run power-config -- -i my_configuration.json
npm run power-config -- -i my_configuration.yml
If this option is not specified, then the default will be to use the name of the "ouput file" with a dot added in front of the file name. The dot will make the file hidden on Linux. The input format will default to the same type as the "output file".
npm run power-config -- -o environment.json # (input file is .environment.json)
npm run power-config -- -o path/to/environment.yml # (input file is path/to/.environment.yml)
The --environment option
Use the --environment option to limit the scope to a specific environment. The default set of environments is:
local, dev, test,prod. You can change the set of environments using the .power-config file.
npm run power-config -- -e test
If this option is not specified, then all environments will be processed.
The --flatten option
Use the --flatten option in conjunction with the --environment option to compress the structure of the output file by eliminating the environment from the namespace.
npm run power-config -- -e test -f
Running with different formats
power-config fully supports both JSON and YAML. The example, input, and output files do not all have to be the same format. You can mix and match the formats by specifying the names with the appropriate extensions using -x, -o, -i options.
npm run power-config -- -x example.yml -o environment.json -i input.yml
The --clear option
Use the --clear option to clear and/or set a specific value(s) in the input file before running through the example. To clear a value, specify its path. To set a value, specify its path followed by = followed by its value. To clear/set more than one path, use a comma.
npm run power-config
npm run power-config -- -clear port.test
npm run power-config -- -clear port.test=8080
npm run power-config -- -clear hostname.test,port.test=8080
The .power-config rc file
You can customize power-config using an rc file written in either JSON or YAML. power-config will search for the rc file in the following order:
./.power-config.json # project directory
./.power-config.yml # project directory
~/.power-config.json # HOME directory
~/.power-config.yml # HOME directoryIn the rc file, you can change the default set of environments by specifying an array of values.
environments:
- development
- integration
- live6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago