-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ab59589
Showing
8 changed files
with
533 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.DS_Store | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# envgen | ||
|
||
_envgen_ is CLI tool that generates .env files for subpackages in your project based on a configuration file. | ||
|
||
The purpose of this tool is to: | ||
- avoid using a bash script: harder to maintain, test, lacks the safety | ||
of a statically typed and compiled language like Go; | ||
- provide flexibility and ease of use/reuse: additional features can be | ||
easily added and made available to projects already using the tool. | ||
|
||
### Usage | ||
|
||
`$ envgen config.yaml` | ||
|
||
Here's the structure of the configuration file: | ||
|
||
```yaml | ||
branchVarName: CIRCLE_BRANCH | ||
branchVarDefault: develop | ||
|
||
branches: | ||
- name: develop | ||
suffix: _DEV | ||
- name: staging | ||
suffix: _STG | ||
|
||
packages: | ||
- package: awesome-crawler | ||
variables: | ||
- AC_THRESHOLD | ||
- AC_TITLE | ||
- package: web-server | ||
variables: | ||
- WS_PORT | ||
- WS_ADDRESS | ||
|
||
globals: | ||
- V_DATABASE | ||
``` | ||
Configuration details: | ||
| Key | Description | | ||
| ----------------- |:-------------:| | ||
| branchVarName | name of the env var that contains the CI branch (CIRCLE_BRANCH), for CircleCI | | ||
| branchVarDefault | default value of `branchVarName` | | ||
| branches | list of branches and their suffixes | | ||
| packages | list of subpackages | | ||
| packages.package | subpackage path to where the .env file will be written | | ||
| packages.variables| subpackage environment variables to generate | | ||
| globals | list of variables that are environment independent and global to all subpackages | | ||
|
||
For each entry in the `packages` array, a `.env` file will be created in the path `package`, | ||
with the variables defined in `variables` and `globals`. | ||
|
||
#### Example: | ||
|
||
Project structure: | ||
``` | ||
├── bla.go | ||
├── awesome-crawler | ||
│ └── index.js | ||
└── web-server | ||
└── index.html | ||
``` | ||
|
||
Loaded env vars: | ||
``` | ||
CIRCLE_BRANCH=develop | ||
V_DATABASE=somedburl | ||
WS_ADDRESS_DEV=www.sample.web | ||
WS_PORT_DEV=1234 | ||
AC_TITLE_DEV=sometitle | ||
AC_THRESHOLD_DEV=50 | ||
``` | ||
|
||
Resulting structure: | ||
``` | ||
├── bla.go | ||
├── awesome-crawler | ||
│ ├── .env | ||
│ └── index.js | ||
└── web-server | ||
├── .env | ||
└── index.html | ||
``` | ||
|
||
Content of awesome-crawler/.env: | ||
``` | ||
V_DATABASE=somedburl | ||
AC_TITLE=sometitle | ||
AC_THRESHOLD=50 | ||
``` | ||
|
||
Content of web-server/.env: | ||
``` | ||
V_DATABASE=somedburl | ||
WS_ADDRESS=www.sample.web | ||
WS_PORT=1234 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/spf13/cobra" | ||
"io/ioutil" | ||
"os" | ||
|
||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
type ConfBranches struct { | ||
Name string `yaml:"name"` | ||
Suffix string `yaml:"suffix"` | ||
} | ||
|
||
type ConfPackages struct { | ||
Package string `yaml:"package"` | ||
Variables []string `yaml:"variables"` | ||
} | ||
|
||
type GeneratorConfig struct { | ||
BranchVarName string `yaml:"branchVarName"` | ||
BranchVarDefault string `yaml:"branchVarDefault"` | ||
Branches []ConfBranches `yaml:"branches"` | ||
Packages []ConfPackages `yaml:"packages"` | ||
Globals []string `yaml:"globals"` | ||
} | ||
|
||
type Generator struct { | ||
conf GeneratorConfig | ||
branchSuffix string | ||
} | ||
|
||
var rootCmd = &cobra.Command{ | ||
Version: "1.0.0", | ||
SilenceErrors: true, | ||
Use: "envgen <configFilePath>", | ||
Short: "envgen generates env files for sub packages", | ||
Long: "envgen is CLI tool that generates .env files for subpackages in your project based on a configuration file", | ||
Args: cobra.ExactArgs(1), | ||
RunE: generateEnvFiles, | ||
} | ||
|
||
func Execute() { | ||
rootCmd.SetErr(errorWriter{}) | ||
if err := rootCmd.Execute(); err != nil { | ||
rootCmd.PrintErr(err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func generateEnvFiles(cmd *cobra.Command, args []string) error { | ||
gen := &Generator{} | ||
err := gen.loadConfig(args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
logInfo("Starting env files generation") | ||
fmt.Println() | ||
|
||
globals, err := getVariablesValues(gen.conf.Globals, "") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, g := range gen.conf.Packages { | ||
pkg := g.Package | ||
logInfo("> Loading variables for " + pkg) | ||
|
||
// generate package specific vars | ||
packageVars, err := getVariablesValues(g.Variables, gen.branchSuffix) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// append globals | ||
packageVars = append(packageVars, globals...) | ||
|
||
logInfo("> Writing env file for " + pkg) | ||
err = writeFile(fmt.Sprintf("%s/.env", pkg), packageVars) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
logInfo("> Done generating env file for " + pkg) | ||
fmt.Println() | ||
} | ||
|
||
logInfo("Finished env files generation!") | ||
return nil | ||
} | ||
|
||
func getVariablesValues(envVars []string, suffix string) ([]string, error) { | ||
vars := []string{} | ||
for _, v := range envVars { | ||
val, ok := os.LookupEnv(v + suffix) | ||
if !ok { | ||
err := fmt.Errorf("missing variable %s", v) | ||
return nil, err | ||
} | ||
|
||
vars = append(vars, fmt.Sprintf("%s=%s", v, val)) | ||
} | ||
|
||
return vars, nil | ||
} | ||
|
||
// loadConfig loads the configuration from the provided yaml file | ||
// into the instance of the Generator. It also determines the branch suffix property. | ||
func (g *Generator) loadConfig(filepath string) error { | ||
config := &GeneratorConfig{} | ||
file, err := ioutil.ReadFile(filepath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = yaml.Unmarshal(file, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
g.conf = *config | ||
|
||
g.branchSuffix, err = g.findBranchSuffix() | ||
if err != nil { | ||
return fmt.Errorf("missing branch suffix") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// findBranchSuffix determines the branch suffix to use, depending on the current CI branch | ||
func (g *Generator) findBranchSuffix() (string, error) { | ||
branch := getEnv(g.conf.BranchVarName, "", g.conf.BranchVarDefault) | ||
|
||
for _, b := range g.conf.Branches { | ||
if b.Name == branch { | ||
return b.Suffix, nil | ||
} | ||
} | ||
|
||
return "", fmt.Errorf("could not find branch suffix") | ||
} | ||
|
||
// getEnv looks up for a loaded environment variable. | ||
// An optional suffix may be passed, as well as a default value to return if the env var is not loaded. | ||
func getEnv(key string, suffix string, defaultVal string) string { | ||
if value, exists := os.LookupEnv(key + suffix); exists { | ||
return value | ||
} | ||
|
||
return defaultVal | ||
} | ||
|
||
// writeFile writes a slice of strings into a file, separated by new lines | ||
func writeFile(path string, vars []string) error { | ||
file, err := os.Create(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer file.Close() | ||
|
||
sep := "\n" | ||
for _, line := range vars { | ||
if _, err = file.WriteString(line + sep); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
const ( | ||
InfoColor = "\033[1;34m%s\033[0m\n" | ||
ErrorColor = "\033[1;31m%s\033[0m\n" | ||
) | ||
|
||
func printLog(color string, msg interface{}) { | ||
fmt.Printf(color, msg) | ||
} | ||
|
||
func logInfo(msg interface{}) { | ||
printLog(InfoColor, msg) | ||
} | ||
|
||
type errorWriter struct {} | ||
func (w errorWriter) Write(p []byte) (n int, err error) { | ||
return fmt.Fprintf(os.Stdout, ErrorColor, p) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
module envgen | ||
|
||
go 1.14 | ||
|
||
require ( | ||
github.com/fsnotify/fsnotify v1.4.9 // indirect | ||
github.com/mitchellh/mapstructure v1.2.2 // indirect | ||
github.com/pelletier/go-toml v1.7.0 // indirect | ||
github.com/spf13/afero v1.2.2 // indirect | ||
github.com/spf13/cast v1.3.1 // indirect | ||
github.com/spf13/cobra v1.0.0 | ||
github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
github.com/spf13/viper v1.6.3 // indirect | ||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect | ||
golang.org/x/text v0.3.2 // indirect | ||
gopkg.in/ini.v1 v1.55.0 // indirect | ||
gopkg.in/yaml.v2 v2.2.8 | ||
) |
Oops, something went wrong.