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

Support static files #45

Merged
merged 6 commits into from
Oct 13, 2017
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 16 additions & 0 deletions cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,20 @@ type Config struct {
// PluginDirs a set of absolute/relative paths that will be used for
// plugin lookup.
PluginDirs []string

// OutputDir is the directory relative to the project root (where the
// gnorm.toml file is located) in which all the generated files are written
Copy link
Member

Choose a reason for hiding this comment

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

mention this defaults to "."

// to.
//
// This defaults to the current working directory i.e the directory in which
// gnorm.toml is found.
OutputDir string

// StaticDir is the directory relative to the project root (where the
// gnorm.toml file is located) in which all static files , which are
// intended to be copied to the OutputDir are found.
//
// The directory structure is preserved when copying the files to the
// OutputDir
StaticDir string
}
5 changes: 5 additions & 0 deletions cli/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func parse(env environ.Values, r io.Reader) (*run.Config, error) {
if len(c.ExcludeTables) > 0 && len(c.IncludeTables) > 0 {
return nil, errors.New("both include tables and exclude tables")
}
if c.OutputDir == "" {
c.OutputDir = "."
}

include, err := parseTables(c.IncludeTables, c.Schemas)
if err != nil {
Expand All @@ -70,6 +73,8 @@ func parse(env environ.Values, r io.Reader) (*run.Config, error) {
ExcludeTables: exclude,
IncludeTables: include,
},
OutputDir: c.OutputDir,
StaticDir: c.StaticDir,
}
d, err := getDriver(strings.ToLower(c.DBType))
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions run/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ type Config struct {
// for different situations. The values in this field will be available in
// the .Params value for all templates.
Params map[string]interface{}

// OutputDir is the directory relative to the project root (where the
// gnorm.toml file is located) in which all the generated files are written
// to.
OutputDir string

// StaticDir is the directory relative to the project root (where the
// gnorm.toml file is located) in which all static files , which are
// intended to be copied to the OutputDir are found.
//
// The directory structure is preserved when copying the files to the
// OutputDir
StaticDir string
}

// OutputTarget contains a template that generates a filename to write to, and
Expand Down
66 changes: 65 additions & 1 deletion run/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package run // import "gnorm.org/gnorm/run"
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
Expand Down Expand Up @@ -47,7 +49,7 @@ func Generate(env environ.Values, cfg *Config) error {
return err
}
}
return nil
return copyStaticFiles(env, cfg.StaticDir, cfg.OutputDir)
}

func generateSchemas(env environ.Values, cfg *Config, db *data.DBData) error {
Expand Down Expand Up @@ -162,3 +164,65 @@ func doPostRun(env environ.Values, file string, postrun []string) error {
}
return nil
}

// copyStaticFiles copies files recursively from src directory to dest directory
// while preserving the directory structure
func copyStaticFiles(env environ.Values, src string, dest string) error {
if src == "" || dest == "" {
return nil
}
stat, err := os.Stat(src)
if err != nil {
return err
}
if !stat.IsDir() {
return fmt.Errorf("Outputdir specifies a directory path that already exists as file %s", dest)
}
var dstat os.FileInfo
dstat, err = os.Stat(dest)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(dest, stat.Mode())
if err != nil {
return err
}
dstat = stat
} else {
return err
}
}
if !dstat.IsDir() {
return fmt.Errorf("%s is not a directory", dest)
Copy link
Member

Choose a reason for hiding this comment

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

can you make this "Outputdir specifies a directory path that already exists as file %q" . or something like that? it'll make the error more clear to the user.

}
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
base := filepath.Dir(path)
rel, err := filepath.Rel(src, base)
if err != nil {
return err
}
o := filepath.Join(dest, rel)
err = os.MkdirAll(o, stat.Mode())
if err != nil {
return err
}
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()

t, err := os.OpenFile(filepath.Join(o, filepath.Base(path)), os.O_RDWR|os.O_TRUNC|os.O_CREATE, info.Mode())
if err != nil {
return err
}
defer t.Close()
_, err = io.Copy(t, f)
return err
})
}
48 changes: 48 additions & 0 deletions run/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"io/ioutil"
"log"
"os"
"path/filepath"
"reflect"
"sort"
"testing"
"text/template"

Expand Down Expand Up @@ -41,3 +44,48 @@ func TestAtomicGenerate(t *testing.T) {
t.Fatalf("Expected file to be unchanged, but was different. Expected: %q, got: %q", original, b)
}
}

func TestCopyStaticFiles(t *testing.T) {
originPaths := []string{
"base/base.md",
"base/level_one/level_one.md",
"base/level_two/level_two.md",
}
source := "testdata"
dest := "static_asset"

err := copyStaticFiles(environ.Values{}, source, dest)
if err != nil {
t.Fatal(err)
}
defer func() {
err := os.RemoveAll(dest)
if err != nil {
t.Fatal(err)
}
}()

// make sure the structure is preserved
var newPaths []string
filepath.Walk(dest, func(path string, info os.FileInfo, err error) error {
Copy link
Member

Choose a reason for hiding this comment

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

since you have a well-known list of files you're looking for, you can probably use ioutil.ReadDir and the code will be easier to follow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are checking the order of files in nested directories. ReadDir reads the content of one directories, I think walk here is a better fit rather than calling ReadDil a couple of times which we will also be walking the directory tree.

if err != nil {
return err
}
if info.IsDir() {
return nil
}
r, err := filepath.Rel(dest, path)
if err != nil {
return err
}
newPaths = append(newPaths, r)
return nil
})

sort.Strings(originPaths)
sort.Strings(newPaths)

if !reflect.DeepEqual(originPaths, newPaths) {
t.Errorf("expected %v to equal %v", newPaths, originPaths)
}
}
Empty file added run/testdata/base/base.md
Empty file.
Empty file.
Empty file.