Skip to content

Commit

Permalink
add defer step
Browse files Browse the repository at this point in the history
  • Loading branch information
sergey-koba-mobidev committed Mar 29, 2018
1 parent 2808e3e commit fdb9af2
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 11 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,48 @@
# tci
CI in your terminal
Deploys in your terminal. Describe deployment steps in YAML file and run them from terminal using `tci deploy` command.

Features:
- describe deployment steps in YAML file
- enjoy colored output :)

Step types:
- terminal command
- cycles with conditions, retries and delays
- defer commands, which will run in the end of deploy even if it failed

## Install


## Usage
- Create `tci.yml` file. Example:
```yml
steps:
# run `ls -a`
- mode: cmd
command: ls -a

# default mode is `cmd` so you can omit it
- command: ls

# the defer step will be executed in the end of deploy no matter what
- mode: defer
command: echo q

# Run `date +%s` maximum 5 times with 1s delay between retries until output contains `99`
- mode: until
command: date +%s
contains: 99
retries: 5 # default value is 3
delay: 1000 # default value is 100
```
- Run `tci deploy` or `tci d` to run `tci.yml` file
- To run another file use `--file` or `-f` flag. Example: `tci -f deploy.yml d`

## Real Example

## Help
- Run `tci` or `tci help`

## Roadmap
- add `run: always`
- add readme
- add real example to readme
- release
2 changes: 1 addition & 1 deletion commands/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func Deploy(filename string) error {
return err
}

utils.LogInfo("Found %d step(s).", len(conf.Steps))
utils.LogSuccess(" ﹂Found %d step(s).", len(conf.Steps))
err = steps.RunSteps(conf)
if err != nil {
return err
Expand Down
38 changes: 38 additions & 0 deletions steps/defer_step.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package steps

import (
"strings"
"os/exec"
"github.com/sergey-koba-mobidev/tci/utils"
"errors"
)

type DeferStep struct {
step utils.Step
}

func (c DeferStep) run() ([]byte, error) {
if c.step.Command == "" {
return nil, errors.New("`command` cannot be blank for defer step")
}

parts := strings.Fields(c.step.Command)
head := parts[0]
parts = parts[1:len(parts)]

out, err := exec.Command(head, parts...).Output()
if err != nil {
return nil, err
}

return out, nil
}

func (c DeferStep) name() string {
return c.step.Command
}

func (c DeferStep) setStep(s utils.Step) StepInterface {
c.step = s
return c
}
48 changes: 42 additions & 6 deletions steps/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,49 @@ import (
"fmt"
"reflect"
"errors"
"strings"
)

var stepRegistry = make(map[string]reflect.Type)
var runSteps []utils.Step
var deferSteps []utils.Step

func initRegistry() {
stepRegistry["cmd"] = reflect.TypeOf(CmdStep{})
stepRegistry["until"] = reflect.TypeOf(UntilStep{})
stepRegistry["defer"] = reflect.TypeOf(DeferStep{})
}

func RunSteps(conf *utils.Conf) error {
var err error = nil
initRegistry()

for i := 0; i < len(conf.Steps); i++ {
err := runStep(conf.Steps[i])
if conf.Steps[i].Mode == "defer" {
deferSteps = append(deferSteps, conf.Steps[i])
} else {
runSteps = append(runSteps, conf.Steps[i])
}
}

// Run steps
for i := 0; i < len(runSteps); i++ {
err = runStep(runSteps[i])
if err != nil {
return err
utils.LogError(" ﹂Step failed with error %s", err.Error())
break
}
}
return nil

// Run defer steps
for i := 0; i < len(deferSteps); i++ {
deferErr := runStep(deferSteps[i])
if deferErr != nil {
utils.LogError(" ﹂Step failed with error %s", deferErr.Error())
}
}

return err
}

func runStep(step utils.Step) error {
Expand All @@ -36,15 +61,26 @@ func runStep(step utils.Step) error {

s := reflect.New(stepRegistry[step.Mode]).Elem().Interface().(StepInterface)
s = s.setStep(step)
utils.LogInfo("Running step %s", s.name())
utils.LogInfo("\r\nRunning step %s", s.name())

out, err := s.run()
if err != nil {
return err
}

fmt.Printf("%s", out)
utils.LogSuccess("Finished step %s", s.name())
printStepOutput(out)
utils.LogSuccess("\rFinished step %s", s.name())

return nil
}

func printStepOutput(out []byte) {
var lines []string = strings.Split(string(out), "\n")
for index, line := range lines {
line = " " + line
if index != len(lines)-1 {
line += "\n"
}
fmt.Printf("%s", line)
}
}
2 changes: 1 addition & 1 deletion steps/until_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (c UntilStep) run() ([]byte, error) {
}
out = retryOut
if !strings.Contains(string(out), c.step.Contains) {
utils.LogInfo("Waiting %d milliseconds before next retry %d", c.step.Delay, retries)
utils.LogInfo("Waiting %d milliseconds before next retry %d", c.step.Delay, retries)
time.Sleep(time.Duration(c.step.Delay) * time.Millisecond)
}
retries++
Expand Down

0 comments on commit fdb9af2

Please sign in to comment.