Skip to content

Commit

Permalink
- add goal init command that initialized goal in CWD
Browse files Browse the repository at this point in the history
- add validation for `goal.cmd` & `goal.assert.ref`
- update README
  • Loading branch information
aaabramov committed Nov 9, 2021
1 parent 5c4cec2 commit 2966fbc
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 54 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ $ goal tf-apply --on stage
- [X] Add `assert.fix`. Display when assertion failed, e.g. `terraform workspace select dev`
- [X] Add "environment" management to avoid tf-plan-dev, tf-plan-stage, tf-plan-prod, etc. E.g. `goal tf-apply --on dev` & `goal.env: dev` matches
- [X] Support `-f my-goal.yaml`
- [X] Validate empty goal cmd
- [X] Validate empty assertion ref
- [X] Add `goal init` which simply generated example `goal.yaml`
- [ ] Simpler `brew tap aaabramov/goal`
- [ ] Manual approvals for proceeding like `assert.approval`
- [ ] Add "depends on" other task like switch to dev?
Expand All @@ -146,6 +149,6 @@ $ goal tf-apply --on stage
- [ ] Global aliases in `$HOME` directory?
- [ ] Self-autocompletion via [https://github.com/posener/complete](complete) library
- [ ] Support both goal.yaml & goal.yml
- [ ] Add `goal init` which simply generated example `goal.yaml`
- [ ] Generate simple markdown file from `goal.yaml` (ops-doc)
- [ ] Add predefined assertions like `k8s_cluster`, `terraform_workspace`, `etc.`
- [ ] `goal add GOAL_NAME` -- check if already exists
68 changes: 68 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"github.com/aaabramov/goal/lib"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"io/ioutil"
)

// initCmd represents the init command
var initCmd = &cobra.Command{
Use: "init",
Short: "Create new goal.yaml file in current file",
Long: "Create new goal.yaml file in current file",
Run: func(cmd *cobra.Command, args []string) {
initGoals()
},
}

func init() {
rootCmd.AddCommand(initCmd)
// TODO: support templates
}

func initGoals() {
lib.Info("⌛ Generating default goal.yaml file")
goals := map[string]lib.YamlGoal{
"workspace": {
Cmd: "terraform",
Args: []string{"apply", "-var-file", "vars/dev.tfvars"},
Assert: nil,
},
"tf-apply": {
Envs: &map[string]lib.YamlEnvGoal{
"dev": {
Desc: "Terraform apply on dev",
Cmd: "terraform",
Args: []string{"apply", "-var-file", "vars/dev.tfvars"},
Assert: &lib.Assert{
Desc: "Check if on dev workspace",
Ref: "workspace",
Expect: "dev",
Fix: "terraform workspace select dev",
},
},
"stage": {
Desc: "Terraform apply on stage",
Cmd: "terraform",
Args: []string{"apply", "-var-file", "vars/stage.tfvars"},
Assert: &lib.Assert{
Desc: "Check if on stage workspace",
Ref: "workspace",
Expect: "stage",
Fix: "terraform workspace select stage",
},
},
},
},
}
bytes, err := yaml.Marshal(goals)
if err != nil {
lib.Fatal("Failed to generate default YAML for goals")
}
if err = ioutil.WriteFile("goal.yaml", bytes, 0644); err != nil {
lib.Fatal("Failed to create goal.yaml")
}
lib.Info("✅ Generated default goal.yaml file. Try running `goal` to see available goals.")
}
68 changes: 22 additions & 46 deletions goal.yaml
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
workspace:
desc: Current terraform workspace
cmd: terraform
args:
- workspace
- show

tf-plan:
tf-apply:
envs:
dev:
desc: Terraform plan on dev
assert:
desc: Check if on dev workspace
ref: workspace # References goal above
expect: dev # Checks whether trimmed output from 'ref' goal is equal to "dev"
cmd: terraform
args:
- plan
- -var-file
- vars/dev.tfvars
stage:
desc: Terraform plan on stage
assert:
desc: Check if on stage workspace
ref: workspace # References goal above
expect: stage # Checks whether trimmed output from 'ref' goal is equal to "stage"
cmd: terraform
args:
- plan
- -var-file
- vars/stage.tfvars

tf-apply:
envs:
dev:
desc: Terraform apply on dev
- apply
- -var-file
- vars/dev.tfvars
assert:
desc: Check if on dev workspace
ref: workspace # References goal above
expect: dev # Checks whether trimmed output from 'ref' goal is equal to "dev"
ref: workspace
expect: dev
fix: terraform workspace select dev
desc: Terraform apply on dev
stage:
cmd: terraform
args:
- apply
- -var-file
- vars/dev.tfvars
stage:
desc: Terraform apply on stage
- apply
- -var-file
- vars/stage.tfvars
assert:
desc: Check if on stage workspace
ref: workspace # References goal above
expect: stage # Checks whether trimmed output from 'ref' goal is equal to "stage"
cmd: terraform
args:
- apply
- -var-file
- vars/stage.tfvars
ref: workspace
expect: stage
fix: terraform workspace select stage
desc: Terraform apply on stage
workspace:
cmd: terraform
args:
- apply
- -var-file
- vars/dev.tfvars
32 changes: 25 additions & 7 deletions lib/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ func (a Assert) String() string {

type YamlEnvGoal struct {
Cmd string `yaml:"cmd"`
Args []string `yaml:"args"`
Assert *Assert `yaml:"assert"`
Args []string `yaml:"args,omitempty"`
Assert *Assert `yaml:"assert,omitempty"`
Desc string `yaml:"desc"`
}

type YamlGoal struct {
Envs *map[string]YamlEnvGoal `yaml:"envs"`
Cmd string `yaml:"cmd"`
Args []string `yaml:"args"`
Assert *Assert `yaml:"assert"`
Desc string `yaml:"desc"`
Envs *map[string]YamlEnvGoal `yaml:"envs,omitempty"`
Cmd string `yaml:"cmd,omitempty"`
Args []string `yaml:"args,omitempty"`
Assert *Assert `yaml:"assert,omitempty"`
Desc string `yaml:"desc,omitempty"`
}

type Command struct {
Expand Down Expand Up @@ -200,10 +200,27 @@ func normalizeArgs(args []string) []string {
}
}

func validateAssert(name string, env string, assert *Assert) {
if assert == nil {
return
}
if assert.Ref == "" {
if env == "" {
Fatal("Malformed goals. %s.assert.ref could not be empty", name)
} else {
Fatal("Malformed goals. %s.%s.assert.ref could not be empty", name, env)
}
}
}

func parseEnvCommands(name string, envs map[string]YamlEnvGoal) []Command {
var commands []Command
for env, envCommand := range envs {
args := normalizeArgs(envCommand.Args)
if envCommand.Cmd == "" {
Fatal("Malformed goals. %s.%s.cmd could not be empty", name, env)
}
validateAssert(name, env, envCommand.Assert)
commands = append(commands, Command{
Name: name,
Cmd: envCommand.Cmd,
Expand All @@ -228,6 +245,7 @@ func ParseCommands(bytes []byte) (*Commands, error) {
if command.Envs != nil {
res = append(res, parseEnvCommands(name, *command.Envs)...)
} else {
validateAssert(name, "", command.Assert)
args := normalizeArgs(command.Args)
res = append(res, Command{
Name: name,
Expand Down

0 comments on commit 2966fbc

Please sign in to comment.