Skip to content

Commit

Permalink
- rename Command -> Goal
Browse files Browse the repository at this point in the history
- rename `Commands` -> `Goals`
- support builtin `terraform_workspace` assertion
  • Loading branch information
aaabramov committed Nov 9, 2021
1 parent 01e12b0 commit 6e23067
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 132 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Allows you to create local aliases withing directory/repository with proper asse

**The idea behind is to:**

- simplify executing scoped repetitive commands
- simplify executing scoped repetitive commands
- avoid executing commands on wrong environment (e.g. _kubectl_, _terraform_, _helm_, _etc._)

## Install
Expand Down Expand Up @@ -133,15 +133,16 @@ $ goal tf-apply --on stage
- [X] Pipe STDIN for "yes/no" inputs, etc.
- [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] 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?
- [ ] Recursive dependencies
- [ ] Recursive dependencies
- [ ] Assertions
- [X] ref output
- [X] support multiple assertions
Expand All @@ -151,5 +152,9 @@ $ goal tf-apply --on stage
- [ ] Self-autocompletion via [https://github.com/posener/complete](complete) library
- [ ] Support both goal.yaml & goal.yml
- [ ] Generate simple markdown file from `goal.yaml` (ops-doc)
- [ ] Add predefined assertions like `k8s_cluster`, `terraform_workspace`, `etc.`
- [ ] Add predefined assertions:
- [ ] `k8s_cluster`
- [ ] `terraform_workspace`
- [ ] `goal add GOAL_NAME` -- check if already exists
- [ ] rework `Fatal` with `err`
- [ ] suggest `fix?` when precondition failed with `yes/no` prompt
2 changes: 1 addition & 1 deletion cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var cliCmd = &cobra.Command{
if cmd, exists := commands.GetWithEnv(strings.TrimSpace(goal), env); exists {
lib.Info(cmd.Cli())
} else {
msg := fmt.Sprintf("No such goal: %s", goal)
msg := fmt.Sprintf("No such goal: %s", goal)
if env != "" {
msg += fmt.Sprintf(" on env \"%s\"", env)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ func initGoals(filename string) {
lib.Info("⌛ Generating default %s file", filename)
bytes, err := yaml.Marshal(defaultGoals)
if err != nil {
lib.Fatal("Failed to generate default YAML for goals")
lib.Fatal("Failed to generate default YAML for goals")
}
if err = ioutil.WriteFile(filename, bytes, 0644); err != nil {
lib.Fatal("Failed to create %s", filename)
lib.Fatal("Failed to create %s", filename)
}
lib.Info("✅ Generated default %s file. Try running `goal` to see available goals.", filename)
}
8 changes: 4 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

var goalFile string
var commands *lib.Commands
var commands *lib.Goals

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -44,18 +44,18 @@ func loadGoals() {
if goalFile != "" {
bytes, err := ioutil.ReadFile(goalFile)
if err != nil {
lib.Fatal("Failed to read goals file: %s\n"+
lib.Fatal("Failed to read goals file: %s\n"+
"\t- check if goal.yaml files exists in current directory\n"+
"\t- specify goal.yaml explicitly using -c flag, e.g. 'goal -c ../goal.yaml'\n"+
"\t- run 'goal init' to generate example goal.yaml file in current directory", goalFile)
}
parsed, err := lib.ParseCommands(bytes)
if err != nil {
lib.Fatal("Invalid goals file: %s", goalFile)
lib.Fatal("Invalid goals file: %s", goalFile)
} else {
commands = parsed
}
} else {
lib.Fatal("Goals filename not specified. Either create goal.yaml file or specify location explicitly with -c option")
lib.Fatal("Goals filename not specified. Either create goal.yaml file or specify location explicitly with -c option")
}
}
81 changes: 81 additions & 0 deletions lib/assertions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package lib

import (
"errors"
"fmt"
"strings"
)

type Assertion interface {
describe() string
check(commands Goals) error
}

// RefAssertion calls another goal and compares its trimmed output with Expect
type RefAssertion struct {
Desc string
Ref string
Expect string
Fix string
}

func (a RefAssertion) describe() string {
return a.Desc
}

func (a RefAssertion) check(c Goals) error {
// TODO: env or !env?
ref, exists := c.get(a.Ref)

if exists {
out := strings.TrimSpace(getOutput(ref.Name, ref.Args...))
if out != a.Expect {
msg := fmt.Sprintf(
"Precondition failed: %s\n\tOutput: \"%s\"\n\tExpected: \"%s\"\n\tCLI: %s",
ref.Name,
out,
a.Expect,
ref.Cli(),
)
if a.Fix != "" {
msg += fmt.Sprintf("\n\tFix: %s", a.Fix)
}
return errors.New(msg)
} else {
Info("✅ Precondition: " + a.Desc)
return nil
}
} else {
return errors.New("Unknown assertion ref: " + a.Ref)
}
}

// TerraformWorkspaceAssertion checks current Terraform workspace by executing `terraform workspace show`
// and compares its output with Expect
type TerraformWorkspaceAssertion struct {
Expect string
}

func (a TerraformWorkspaceAssertion) describe() string {
return fmt.Sprintf("Check if on %s workspace", a.Expect)
}

func (a TerraformWorkspaceAssertion) check(c Goals) error {
out := strings.TrimSpace(getOutput("terraform", "workspace", "show"))
if out == a.Expect {
return nil
} else {
return errors.New(
fmt.Sprintf(
"❌ Precondition failed: %s\n"+
"\tExpected terraform workspace to be: \"%s\"\n"+
"\tActual terraform workspace: \"%s\"\n"+
"\tFix: \"terraform workspace select %s\"",
a.describe(),
out,
a.Expect,
a.Expect,
),
)
}
}

0 comments on commit 6e23067

Please sign in to comment.