Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add "plan" command #16

Merged
merged 3 commits into from
Sep 28, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelist of gmig releases

## v1.7.0, 2018-09-28

- add "plan" command that logs all commands that would be executed on "up".

## v1.6.0

- add options to new to set the commands for the do,undo,view section directly
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ and use the `view` subcommand.
up Runs the do section of all pending migrations in order, one after the other.
If a migration file is specified then stop after applying that one.
down Runs the undo section of the last applied migration only.
plan Log commands of the do section of all pending migrations in order, one after the other.
status List all migrations with details compared to the current state.
view Runs the view section of all applied migrations to see the current state reported by your infrastructure.
force state | do | undo
Expand Down Expand Up @@ -133,6 +134,11 @@ List all migrations with an indicator (applied,pending) whether is has been appl

Run this command in the directory where all migrations are stored. Use `--migrations` for a different location.

### plan [path] [|migration file] [--migrations folder]

Log commands of the `do` section of all pending migrations in order, one after the other.
If `migration file` is given then stop after applying that one.

### up [path] [|migration file] [--migrations folder]

Executes the `do` section of each pending migration compared to the last applied change to the infrastructure.
Expand Down
60 changes: 43 additions & 17 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
pending = "... pending ..."
execDo = "... do ..."
execUndo = "... undo ..."
execPlan = "... plan ..."
stopped = "... stopped ..."
)

Expand Down Expand Up @@ -60,6 +61,14 @@ func cmdCreateMigration(c *cli.Context) error {
}

func cmdMigrationsUp(c *cli.Context) error {
return runMigrations(c, !true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why !true instead of false?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks nice

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:' )

}

func cmdMigrationsPlan(c *cli.Context) error {
return runMigrations(c, true)
}

func runMigrations(c *cli.Context, isLogOnly bool) error {
mtx, err := getMigrationContext(c)
if err != nil {
printError(err.Error())
Expand All @@ -84,26 +93,38 @@ func cmdMigrationsUp(c *cli.Context) error {
reportError(mtx.stateProvider.Config(), "up until stop", errors.New("No such migration file: "+stopAfter))
return errAbort
}
prettyWidth := largestWithOf(all)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

largestWidthOf
note the D

for _, each := range all {
log.Println(statusSeparator)
log.Println(execDo, pretty(each.Filename))
if err := ExecuteAll(each.DoSection, mtx.config().shellEnv(), c.GlobalBool("v")); err != nil {
reportError(mtx.stateProvider.Config(), "do", err)
return errAbort
leadingTitle := execDo
if isLogOnly {
leadingTitle = execPlan
}
mtx.lastApplied = each.Filename
// save after each succesful migration
if err := mtx.stateProvider.SaveState(mtx.lastApplied); err != nil {
reportError(mtx.stateProvider.Config(), "save state", err)
return errAbort
log.Printf("%s %-"+strconv.Itoa(prettyWidth)+"s (%s)\n", leadingTitle, pretty(each.Filename), each.Filename)
if isLogOnly {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract to a func that accepts 1 param (isLogOnly)

will reduce the size of this function significantly maybe a runAll private function?

log.Println("")
if SimulateAll(each.DoSection, mtx.config().shellEnv(), true); err != nil {
reportError(mtx.stateProvider.Config(), "plan do", err)
return errAbort
}
} else {
if err := ExecuteAll(each.DoSection, mtx.config().shellEnv(), c.GlobalBool("v")); err != nil {
reportError(mtx.stateProvider.Config(), "do", err)
return errAbort
}
mtx.lastApplied = each.Filename
// save after each succesful migration
if err := mtx.stateProvider.SaveState(mtx.lastApplied); err != nil {
reportError(mtx.stateProvider.Config(), "save state", err)
return errAbort
}
}
// if not empty then stop after applying this migration
if stopAfter == each.Filename {
log.Println(stopped)
log.Println(statusSeparator)
break
}
log.Println(statusSeparator)
}
return nil
}
Expand Down Expand Up @@ -139,6 +160,17 @@ func cmdMigrationsDown(c *cli.Context) error {
return nil
}

func largestWithOf(list []Migration) int {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

largestWidthOf, not largestWithOf

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

prettyWidth := 0
for _, each := range list {
pf := pretty(each.Filename)
if len(pf) > prettyWidth {
prettyWidth = len(pf)
}
}
return prettyWidth
}

func cmdMigrationsStatus(c *cli.Context) error {
mtx, err := getMigrationContext(c)
if err != nil {
Expand All @@ -152,13 +184,7 @@ func cmdMigrationsStatus(c *cli.Context) error {
}
log.Println(statusSeparator)
var last string
prettyWidth := 0
for _, each := range all {
pf := pretty(each.Filename)
if len(pf) > prettyWidth {
prettyWidth = len(pf)
}
}
prettyWidth := largestWithOf(all)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to rename this after renaming the function

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactor tooling helps here

for _, each := range all {
status := applied
if each.Filename > mtx.lastApplied {
Expand Down
12 changes: 12 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ func newApp() *cli.App {
ArgsUsage: `[title]
title - what the effect of this migration is on infrastructure.`,
},
{
Name: "plan",
Usage: "Log commands of the do section of all pending migrations in order, one after the other. If a migration file is specified then stop after applying that one.",
Action: func(c *cli.Context) error {
defer started(c, "plan = log commands of pending migrations")()
return cmdMigrationsPlan(c)
},
Flags: []cli.Flag{migrationsFlag},
ArgsUsage: `[path] [stop]
path - name of the folder that contains the configuration of the target project.
stop - (optional) the name of the migration file after which applying migrations will stop.`,
},
{
Name: "up",
Usage: "Runs the do section of all pending migrations in order, one after the other. If a migration file is specified then stop after applying that one.",
Expand Down
32 changes: 32 additions & 0 deletions migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,38 @@ func ExecuteAll(commands []string, envs []string, verbose bool) error {
return nil
}

// SimulateAll logs expanded commands using the environment variables of both the config and the OS.
func SimulateAll(commands []string, envs []string, verbose bool) error {
allEnv := append(os.Environ(), envs...)
envMap := map[string]string{}
for _, each := range allEnv {
kv := strings.Split(each, "=")
envMap[kv[0]] = kv[1]
}
for _, each := range commands {
log.Println(expandVarsIn(envMap, each))
}
return nil
}

// expandVarsIn returns a command with all occurrences of environment variables replaced by known values.
func expandVarsIn(envs map[string]string, command string) string {
// assume no recurse expand
expanded := command
for k, v := range envs {
// if the value itself is a known variable then skip it
if strings.HasPrefix(v, "$") {
if _, ok := envs[v]; ok {
log.Printf("Warning, skipping non-expandable environment var %s=%v\n", k, v)
continue
}
}
varName := "$" + k
expanded = strings.Replace(expanded, varName, v, -1)
}
return expanded
}

func setupShellScript(verbose bool) string {
flag := "-v"
if verbose {
Expand Down