Skip to content

Commit

Permalink
Feature: apply local command
Browse files Browse the repository at this point in the history
- Modified ApplyXxx functions to accept an io.Writer. Responsibility of creating the correct type of writer (e.g. stdout or file) is now within applyCmds.
- Failed test assertions now display the real location when available
  • Loading branch information
buddhike committed Jan 23, 2018
1 parent 257071c commit 29ca1c4
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 18 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ mbt build diff --from <commit-sha> --to <commit-sha>

```
> Currently, all `mbt` commands interrogates the git repository for
> source files required by the operation. Therefore, side effects of
> un-committed changes will not be visible.
## Dependencies
### Module Dependencies
Sometimes a change to a module could require the build of the modules that
Expand Down Expand Up @@ -188,6 +184,9 @@ mbt apply branch <branch-name> --to <path to the template>

# Apply the state as of a particular commit
mbt apply commit <git-commit-sha> --to <path to the template>

# Apply the state of local working directory (i.e. uncommitted work)
mbt apply local --to <path to the template>
```
Output of above commands written to `stdout` by default but can be directed to a
Expand Down
46 changes: 44 additions & 2 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cmd

import (
"errors"
"io"
"os"

"github.com/mbtproject/mbt/lib"
"github.com/spf13/cobra"
Expand All @@ -16,6 +18,7 @@ func init() {
applyCmd.PersistentFlags().StringVar(&out, "out", "", "output path")
applyCmd.AddCommand(applyBranchCmd)
applyCmd.AddCommand(applyCommitCmd)
applyCmd.AddCommand(applyLocal)
RootCmd.AddCommand(applyCmd)
}

Expand Down Expand Up @@ -51,7 +54,12 @@ Calculated manifest and the template is based on the tip of the specified branch
return errors.New("requires the path to template")
}

return handle(lib.ApplyBranch(in, to, branch, out))
output, err := getOutput(out)
if err != nil {
return handle(err)
}

return handle(lib.ApplyBranch(in, to, branch, output))
},
}

Expand All @@ -71,6 +79,40 @@ Commit SHA must be the complete 40 character SHA1 string.

commit := args[0]

return handle(lib.ApplyCommit(in, commit, to, out))
output, err := getOutput(out)
if err != nil {
return handle(err)
}

return handle(lib.ApplyCommit(in, commit, to, output))
},
}

var applyLocal = &cobra.Command{
Use: "local",
Short: "Applies the manifest of local directory state over a template",
Long: `Applies the manifest of local directory state over a template
Calculated manifest and the template is based on the content of local directory.
This command is useful for testing pending changes in workspace.
`,
RunE: func(cmd *cobra.Command, args []string) error {
if to == "" {
return errors.New("requires the path to template")
}

output, err := getOutput(out)
if err != nil {
return handle(err)
}

return handle(lib.ApplyLocal(in, to, output))
},
}

func getOutput(out string) (io.Writer, error) {
if out == "" {
return os.Stdout, nil
}
return os.Create(out)
}
50 changes: 38 additions & 12 deletions lib/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package lib

import (
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"text/template"

Expand All @@ -18,7 +21,7 @@ type TemplateData struct {
}

// ApplyBranch applies the repository manifest to specified template.
func ApplyBranch(dir, templatePath, branch, output string) error {
func ApplyBranch(dir, templatePath, branch string, output io.Writer) error {
repo, err := git.OpenRepository(dir)
if err != nil {
return wrap(err)
Expand All @@ -33,7 +36,7 @@ func ApplyBranch(dir, templatePath, branch, output string) error {
}

// ApplyCommit applies the repository manifest to specified template.
func ApplyCommit(dir, sha, templatePath, output string) error {
func ApplyCommit(dir, sha, templatePath string, output io.Writer) error {
repo, err := git.OpenRepository(dir)
if err != nil {
return wrap(err)
Expand All @@ -52,7 +55,38 @@ func ApplyCommit(dir, sha, templatePath, output string) error {
return applyCore(repo, commit, dir, templatePath, output)
}

func applyCore(repo *git.Repository, commit *git.Commit, dir, templatePath, output string) error {
// ApplyLocal applies local directory manifest over an specified template
func ApplyLocal(dir, templatePath string, output io.Writer) error {
absDir, err := filepath.Abs(dir)
if err != nil {
return wrap(err)
}

c, err := ioutil.ReadFile(path.Join(absDir, templatePath))
if err != nil {
return wrap(err)
}

temp, err := template.New("template").Parse(string(c))
if err != nil {
return wrap(err)
}

m, err := ManifestByLocalDir(absDir, true)
if err != nil {
return err
}

data := &TemplateData{
Sha: m.Sha,
Env: getEnvMap(),
Modules: m.indexByName(),
}

return temp.Execute(output, data)
}

func applyCore(repo *git.Repository, commit *git.Commit, dir, templatePath string, output io.Writer) error {
tree, err := commit.Tree()
if err != nil {
return wrap(err)
Expand All @@ -73,14 +107,6 @@ func applyCore(repo *git.Repository, commit *git.Commit, dir, templatePath, outp
return wrap(err)
}

var writer io.Writer = os.Stdout
if output != "" {
writer, err = os.Create(output)
if err != nil {
return wrap(err)
}
}

m, err := fromCommit(repo, dir, commit)
if err != nil {
return err
Expand All @@ -92,7 +118,7 @@ func applyCore(repo *git.Repository, commit *git.Commit, dir, templatePath, outp
Modules: m.indexByName(),
}

return temp.Execute(writer, data)
return temp.Execute(output, data)
}

func getEnvMap() map[string]string {
Expand Down
131 changes: 131 additions & 0 deletions lib/apply_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package lib

import (
"bytes"
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestApplyBranch(t *testing.T) {
clean()
repo, err := createTestRepository(".tmp/repo")
check(t, err)

check(t, repo.InitModule("app-a"))
check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
check(t, repo.Commit("first"))

check(t, repo.SwitchToBranch("feature"))
check(t, repo.InitModule("app-b"))
check(t, repo.Commit("second"))

output := new(bytes.Buffer)
check(t, ApplyBranch(".tmp/repo", "template.tmpl", "feature", output))

assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyCommit(t *testing.T) {
clean()
repo, err := createTestRepository(".tmp/repo")
check(t, err)

check(t, repo.InitModule("app-a"))
check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
check(t, repo.Commit("first"))

check(t, repo.InitModule("app-b"))
check(t, repo.Commit("second"))

output := new(bytes.Buffer)
check(t, ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output))

assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestApplyLocal(t *testing.T) {
clean()
repo, err := createTestRepository(".tmp/repo")
check(t, err)

check(t, repo.InitModule("app-a"))
check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
check(t, repo.Commit("first"))

check(t, repo.InitModule("app-b"))
check(t, repo.Commit("second"))

output := new(bytes.Buffer)
check(t, ApplyLocal(".tmp/repo", "template.tmpl", output))

assert.Equal(t, "app-a,app-b,\n", output.String())
}

func TestIncorrectTemplatePath(t *testing.T) {
clean()
repo, err := createTestRepository(".tmp/repo")
check(t, err)

check(t, repo.InitModule("app-a"))
check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
{{- $mod.Name }},
{{- end}}
`))
check(t, repo.Commit("first"))

output := new(bytes.Buffer)
err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "foo/template.tmpl", output)

assert.EqualError(t, err, "mbt: the path 'foo' does not exist in the given tree")
assert.Equal(t, "", output.String())
}

func TestBadTemplate(t *testing.T) {
clean()
repo, err := createTestRepository(".tmp/repo")
check(t, err)

check(t, repo.InitModule("app-a"))
check(t, repo.WriteContent("template.tmpl", `
{{- range $i, $mod := .Modules}}
`))
check(t, repo.Commit("first"))

output := new(bytes.Buffer)
err = ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output)

assert.EqualError(t, err, "mbt: template: template:2: unexpected EOF")
assert.Equal(t, "", output.String())
}

func TestEnvironmentVariables(t *testing.T) {
clean()
repo, err := createTestRepository(".tmp/repo")
check(t, err)

check(t, repo.InitModule("app-a"))
check(t, repo.WriteContent("template.tmpl", "{{.Env.EXTERNAL_VALUE}}"))
check(t, repo.Commit("first"))

os.Setenv("EXTERNAL_VALUE", "FOO")

output := new(bytes.Buffer)
check(t, ApplyCommit(".tmp/repo", repo.LastCommit.String(), "template.tmpl", output))

assert.Equal(t, "FOO", output.String())
}
4 changes: 4 additions & 0 deletions lib/mbt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ func clean() {

func check(t *testing.T, err error) {
if err != nil {
mbtError := err.(*MbtError)
if mbtError != nil {
err = mbtError.WithLocation()
}
t.Fatal(err)
}
}

0 comments on commit 29ca1c4

Please sign in to comment.