Codebase list golang-github-gobuffalo-packr / 8820c3a
Initial Commit Mark Bates 7 years ago
31 changed file(s) with 760 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 ---
1 engines:
2 golint:
3 enabled: true
4 checks:
5 GoLint/Naming/MixedCaps:
6 enabled: false
7 govet:
8 enabled: true
9 gofmt:
10 enabled: true
11 fixme:
12 enabled: true
13 ratings:
14 paths:
15 - "**.go"
16 exclude_paths:
17 - "**/*_test.go"
18 - "*_test.go"
19 - "fixtures/"
0 *.log
1 .DS_Store
2 doc
3 tmp
4 pkg
5 *.gem
6 *.pid
7 coverage
8 coverage.data
9 build/*
10 *.pbxuser
11 *.mode1v3
12 .svn
13 profile
14 .console_history
15 .sass-cache/*
16 .rake_tasks~
17 *.log.lck
18 solr/
19 .jhw-cache/
20 jhw.*
21 *.sublime*
22 node_modules/
23 dist/
24 generated/
25 .vendor/
26 bin/*
27 gin-bin
0 language: go
1
2 sudo: false
3
4 go:
5 - 1.7
6 - 1.8
7 - tip
8
9 matrix:
10 allow_failures:
11 - go: 'tip'
0 The MIT License (MIT)
1 Copyright (c) 2016 Mark Bates
2
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0 # packr
0 package packr
1
2 import (
3 "io/ioutil"
4 "path/filepath"
5 "runtime"
6 )
7
8 // NewBox returns a Box that can be used to
9 // retrieve files from either disk or the embedded
10 // binary.
11 func NewBox(path string) Box {
12 _, filename, _, _ := runtime.Caller(1)
13 return Box{
14 Path: path,
15 callingDir: filepath.Dir(filename),
16 }
17 }
18
19 // Box represent a folder on a disk you want to
20 // have access to in the built Go binary.
21 type Box struct {
22 Path string
23 callingDir string
24 data map[string][]byte
25 }
26
27 // String of the file asked for or an empty string.
28 func (b Box) String(name string) string {
29 return string(b.Bytes(name))
30 }
31
32 // MustString returns either the string of the requested
33 // file or an error if it can not be found.
34 func (b Box) MustString(name string) (string, error) {
35 bb, err := b.MustBytes(name)
36 return string(bb), err
37 }
38
39 // Bytes of the file asked for or an empty byte slice.
40 func (b Box) Bytes(name string) []byte {
41 bb, _ := b.MustBytes(name)
42 return bb
43 }
44
45 // MustBytes returns either the byte slice of the requested
46 // file or an error if it can not be found.
47 func (b Box) MustBytes(name string) ([]byte, error) {
48 bb, err := find(b.Path, name)
49 if err == nil {
50 return bb, err
51 }
52 p := filepath.Join(b.callingDir, b.Path, name)
53 return ioutil.ReadFile(p)
54 }
0 package packr
1
2 import (
3 "testing"
4
5 "github.com/stretchr/testify/require"
6 )
7
8 func Test_Box_String(t *testing.T) {
9 r := require.New(t)
10 s := testBox.String("hello.txt")
11 r.Equal("hello world!\n", s)
12 }
13
14 func Test_Box_MustString(t *testing.T) {
15 r := require.New(t)
16 _, err := testBox.MustString("idontexist.txt")
17 r.Error(err)
18 }
19
20 func Test_Box_Bytes(t *testing.T) {
21 r := require.New(t)
22 s := testBox.Bytes("hello.txt")
23 r.Equal([]byte("hello world!\n"), s)
24 }
25
26 func Test_Box_MustBytes(t *testing.T) {
27 r := require.New(t)
28 _, err := testBox.MustBytes("idontexist.txt")
29 r.Error(err)
30 }
0 package builder
1
2 import (
3 "encoding/json"
4 "io/ioutil"
5 "os"
6 "path/filepath"
7 "strings"
8
9 "github.com/pkg/errors"
10 )
11
12 type box struct {
13 Name string
14 Files []file
15 }
16
17 func (b *box) Walk(root string) error {
18 return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
19 if info == nil || info.IsDir() {
20 return nil
21 }
22 f := file{
23 Name: strings.Replace(path, root, "", 1),
24 }
25
26 bb, err := ioutil.ReadFile(path)
27 if err != nil {
28 return errors.WithStack(err)
29 }
30 bb, err = json.Marshal(bb)
31 if err != nil {
32 return errors.WithStack(err)
33 }
34 f.Contents = strings.Replace(string(bb), "\"", "\\\"", -1)
35
36 b.Files = append(b.Files, f)
37 return nil
38 })
39 }
0 package builder
1
2 import (
3 "context"
4 "fmt"
5 "io/ioutil"
6 "os"
7 "path/filepath"
8 "regexp"
9 "text/template"
10
11 "github.com/pkg/errors"
12 )
13
14 var boxPattern = regexp.MustCompile(`packr.NewBox\(["` + "`" + `](.+)["` + "`" + `]\)`)
15 var packagePattern = regexp.MustCompile(`package\s+(.+)`)
16 var invalidFilePattern = regexp.MustCompile(`(_test|-packr).go$`)
17
18 // Builder scans folders/files looking for `packr.NewBox` and then compiling
19 // the required static files into `<package-name>-packr.go` files so they can
20 // be built into Go binaries.
21 type Builder struct {
22 context.Context
23 RootPath string
24 pkgs map[string]pkg
25 }
26
27 // Run the builder.
28 func (b *Builder) Run() error {
29 err := filepath.Walk(b.RootPath, func(path string, info os.FileInfo, err error) error {
30 base := filepath.Base(path)
31 if base == ".git" || base == "vendor" || base == "node_modules" {
32 return filepath.SkipDir
33 }
34
35 if !info.IsDir() {
36 return b.process(path)
37 }
38 return nil
39 })
40 if err != nil {
41 return errors.WithStack(err)
42 }
43 return b.dump()
44 }
45
46 func (b *Builder) dump() error {
47 for _, p := range b.pkgs {
48 name := filepath.Join(p.Dir, p.Name+"-packr.go")
49 fmt.Printf("--> packing %s\n", name)
50 f, err := os.Create(name)
51 if err != nil {
52 return errors.WithStack(err)
53 }
54 t, err := template.New("").Parse(tmpl)
55
56 if err != nil {
57 return errors.WithStack(err)
58 }
59 err = t.Execute(f, p)
60 if err != nil {
61 return errors.WithStack(err)
62 }
63 }
64 return nil
65 }
66
67 func (b *Builder) process(path string) error {
68 ext := filepath.Ext(path)
69 if ext != ".go" || invalidFilePattern.MatchString(path) {
70 return nil
71 }
72
73 bb, err := ioutil.ReadFile(path)
74 if err != nil {
75 return errors.WithStack(err)
76 }
77 fb := string(bb)
78
79 matches := boxPattern.FindAllStringSubmatch(fb, -1)
80 if len(matches) == 0 {
81 return nil
82 }
83
84 pk := pkg{
85 Dir: filepath.Dir(path),
86 Boxes: []box{},
87 }
88 pname := packagePattern.FindStringSubmatch(fb)
89 pk.Name = pname[1]
90
91 for _, m := range matches {
92 bx := &box{
93 Name: m[1],
94 Files: []file{},
95 }
96 err = bx.Walk(filepath.Join(pk.Dir, bx.Name))
97 if err != nil {
98 return errors.WithStack(err)
99 }
100 if len(bx.Files) > 0 {
101 pk.Boxes = append(pk.Boxes, *bx)
102 }
103 }
104
105 if len(pk.Boxes) > 0 {
106 b.addPkg(pk)
107 }
108 return nil
109 }
110
111 func (b *Builder) addPkg(p pkg) {
112 if _, ok := b.pkgs[p.Name]; !ok {
113 b.pkgs[p.Name] = p
114 return
115 }
116 pp := b.pkgs[p.Name]
117 pp.Boxes = append(pp.Boxes, p.Boxes...)
118 b.pkgs[p.Name] = pp
119 }
120
121 // New Builder with a given context and path
122 func New(ctx context.Context, path string) *Builder {
123 return &Builder{
124 Context: ctx,
125 RootPath: path,
126 pkgs: map[string]pkg{},
127 }
128 }
0 package builder
1
2 import (
3 "bytes"
4 "context"
5 "io/ioutil"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "testing"
10
11 "github.com/stretchr/testify/require"
12 )
13
14 func Test_Builder_Run(t *testing.T) {
15 r := require.New(t)
16
17 root := "../example"
18 defer Clean(root)
19
20 exPackr := filepath.Join(root, "example-packr.go")
21 r.False(fileExists(exPackr))
22
23 fooPackr := filepath.Join(root, "foo", "foo-packr.go")
24 r.False(fileExists(fooPackr))
25
26 b := New(context.Background(), root)
27 err := b.Run()
28 r.NoError(err)
29
30 r.True(fileExists(exPackr))
31 r.True(fileExists(fooPackr))
32
33 bb, err := ioutil.ReadFile(exPackr)
34 r.NoError(err)
35 r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("./assets", "/app.css", "\"Ym9keSB7CiAgYmFja2dyb3VuZDogcmVkOwp9Cg==\"")`)))
36 r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("./assets", "/app.js", "\"YWxlcnQoImhlbGxvISIpOwo=\"")`)))
37
38 bb, err = ioutil.ReadFile(fooPackr)
39 r.NoError(err)
40 r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../assets", "/app.css", "\"Ym9keSB7CiAgYmFja2dyb3VuZDogcmVkOwp9Cg==\"")`)))
41 r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../assets", "/app.js", "\"YWxlcnQoImhlbGxvISIpOwo=\"")`)))
42 r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../templates", "/index.html", "\"PCFET0NUWVBFIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiIC8+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoIiAvPgogICAgPHRpdGxlPklOREVYPC90aXRsZT4KICAgIGxpbmsKICA8L2hlYWQ+CiAgPGJvZHk+CiAgICBib2R5CiAgPC9ib2R5Pgo8L2h0bWw+Cg==\"")`)))
43 }
44
45 func Test_Binary_Builds(t *testing.T) {
46 r := require.New(t)
47 pwd, _ := os.Getwd()
48 defer os.Chdir(pwd)
49
50 root := "../example"
51 defer Clean(root)
52 defer os.RemoveAll(filepath.Join(root, "bin"))
53
54 b := New(context.Background(), root)
55 err := b.Run()
56 r.NoError(err)
57
58 os.Chdir(root)
59 cmd := exec.Command("go", "build", "-v", "-o", "bin/example")
60 err = cmd.Run()
61 r.NoError(err)
62
63 r.True(fileExists("bin/example"))
64 }
65
66 func fileExists(path string) bool {
67 _, err := os.Stat(path)
68 return err == nil
69 }
0 package builder
1
2 import (
3 "fmt"
4 "os"
5 "path/filepath"
6 "strings"
7 )
8
9 // Clean up an *-packr.go files
10 func Clean(root string) {
11 filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
12 base := filepath.Base(path)
13 if base == ".git" || base == "vendor" || base == "node_modules" {
14 return filepath.SkipDir
15 }
16 if info.IsDir() {
17 return nil
18 }
19 if strings.Contains(base, "-packr.go") {
20 fmt.Printf("----> cleaning up %s\n", path)
21 os.Remove(path)
22 }
23 return nil
24 })
25 }
0 package builder
1
2 type file struct {
3 Name string
4 Contents string
5 }
0 package builder
1
2 type pkg struct {
3 Name string
4 Dir string
5 Boxes []box
6 }
0 package builder
1
2 var tmpl = `package {{.Name}}
3
4 import "github.com/gobuffalo/packr"
5
6 // !!! GENERATE FILE !!!
7 // Do NOT hand edit this file!!
8 // It is recommended that you do not check into this file into SCM.
9 // We STRONGLY recommend you delete this file after you have built your
10 // Go binary. You can use the "packr clean" command to clean up this,
11 // and any other packr generated files.
12 func init() {
13 {{ range $box := .Boxes -}}
14 {{range .Files -}}
15 packr.PackJSONBytes("{{$box.Name}}", "{{.Name}}", "{{.Contents}}")
16 {{end -}}
17 {{end -}}
18 }
19 `
0 body {
1 background: red;
2 }
0 alert("hello!");
0 package example
1
2 import "github.com/gobuffalo/packr"
3
4 func init() {
5 packr.NewBox("./assets")
6 }
0 package foo
1
2 import "github.com/gobuffalo/packr"
3
4 func init() {
5 packr.NewBox("../assets")
6 }
0 package foo
1
2 import "github.com/gobuffalo/packr"
3
4 func init() {
5 packr.NewBox("../templates")
6 }
0 <!DOCTYPE html>
1 <html>
2 <head>
3 <meta charset="utf-8" />
4 <meta name="viewport" content="width=device-width" />
5 <title>INDEX</title>
6 link
7 </head>
8 <body>
9 body
10 </body>
11 </html>
0 package packr
1
2 import (
3 "bytes"
4 "os"
5 )
6
7 type file struct {
8 *bytes.Buffer
9 Name string
10 info fileInfo
11 }
12
13 func (f file) Close() error {
14 return nil
15 }
16
17 func (f file) Seek(offset int64, whence int) (int64, error) {
18 return 0, nil
19 }
20
21 func (f file) Readdir(count int) ([]os.FileInfo, error) {
22 return []os.FileInfo{f.info}, nil
23 }
24
25 func (f file) Stat() (os.FileInfo, error) {
26 return f.info, nil
27 }
0 package packr
1
2 import (
3 "os"
4 "time"
5 )
6
7 type fileInfo struct {
8 Path string
9 Contents []byte
10 size int64
11 modTime time.Time
12 }
13
14 func (f fileInfo) Name() string {
15 return f.Path
16 }
17
18 func (f fileInfo) Size() int64 {
19 return f.size
20 }
21
22 func (f fileInfo) Mode() os.FileMode {
23 return 0444
24 }
25
26 func (f fileInfo) ModTime() time.Time {
27 return f.modTime
28 }
29
30 func (f fileInfo) IsDir() bool {
31 return false
32 }
33
34 func (f fileInfo) Sys() interface{} {
35 return nil
36 }
0 goodbye cruel world!
0 hello world!
0 package packr
1
2 import (
3 "bytes"
4 "net/http"
5 "time"
6 )
7
8 // HTTPBox implements http.FileSystem which allows the use of Box with a http.FileServer.
9 // e.g.: http.Handle("/", http.FileServer(packr.NewBox("http-files").HTTPBox()))
10 type HTTPBox struct {
11 Box
12 }
13
14 // HTTPBox creates a new HTTPBox from an existing Box
15 func (b Box) HTTPBox() HTTPBox {
16 return HTTPBox{
17 Box: b,
18 }
19 }
20
21 // Open returns a File using the http.File interface
22 func (hb HTTPBox) Open(name string) (http.File, error) {
23 bb := &bytes.Buffer{}
24 b, err := hb.MustBytes(name)
25 if err != nil {
26 return nil, err
27 }
28 bb.Write(b)
29 f := file{
30 Buffer: bb,
31 Name: name,
32 info: fileInfo{
33 Path: name,
34 Contents: b,
35 size: int64(len(b)),
36 modTime: time.Now(),
37 },
38 }
39 return f, nil
40 }
0 package packr
1
2 import (
3 "net/http"
4 "net/http/httptest"
5 "testing"
6
7 "github.com/stretchr/testify/require"
8 )
9
10 func Test_HTTPBox(t *testing.T) {
11 r := require.New(t)
12
13 mux := http.NewServeMux()
14 mux.Handle("/", http.FileServer(testBox.HTTPBox()))
15
16 req, err := http.NewRequest("GET", "/hello.txt", nil)
17 r.NoError(err)
18
19 res := httptest.NewRecorder()
20
21 mux.ServeHTTP(res, req)
22
23 r.Equal(200, res.Code)
24 r.Equal("hello world!\n", res.Body.String())
25 }
0 package cmd
1
2 import (
3 "github.com/gobuffalo/packr/builder"
4 "github.com/spf13/cobra"
5 )
6
7 var cleanCmd = &cobra.Command{
8 Use: "clean",
9 Short: "removes any *-packr.go files",
10 Run: func(cmd *cobra.Command, args []string) {
11 builder.Clean(input)
12 },
13 }
14
15 func init() {
16 rootCmd.AddCommand(cleanCmd)
17 }
0 package cmd
1
2 import (
3 "context"
4 "os"
5
6 "github.com/gobuffalo/packr/builder"
7 "github.com/spf13/cobra"
8 )
9
10 var input string
11
12 var rootCmd = &cobra.Command{
13 Use: "packr",
14 Short: "compiles static files into Go files",
15 RunE: func(cmd *cobra.Command, args []string) error {
16 b := builder.New(context.Background(), input)
17 return b.Run()
18 },
19 }
20
21 func init() {
22 rootCmd.Flags().StringVarP(&input, "input", "i", ".", "path to scan for packr Boxes")
23 }
24
25 // Execute the commands
26 func Execute() {
27 if err := rootCmd.Execute(); err != nil {
28 os.Exit(-1)
29 }
30 }
0 // Copyright © 2017 NAME HERE <EMAIL ADDRESS>
1 //
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package main
15
16 import "github.com/gobuffalo/packr/packr/cmd"
17
18 func main() {
19 cmd.Execute()
20 }
0 package packr
1
2 import (
3 "encoding/json"
4 "path/filepath"
5 "sync"
6
7 "github.com/pkg/errors"
8 )
9
10 var gil = &sync.Mutex{}
11 var data = map[string][]byte{}
12
13 // PackBytes packs bytes for a file into a box.
14 func PackBytes(box string, name string, bb []byte) {
15 gil.Lock()
16 defer gil.Unlock()
17 data[filepath.Join(box, name)] = bb
18 }
19
20 // PackJSONBytes packs JSON encoded bytes for a file into a box.
21 func PackJSONBytes(box string, name string, jbb string) error {
22 bb := []byte{}
23 err := json.Unmarshal([]byte(jbb), &bb)
24 if err != nil {
25 return err
26 }
27 PackBytes(box, name, bb)
28 return nil
29 }
30
31 func find(box string, name string) ([]byte, error) {
32 p := filepath.Join(box, name)
33 if b, ok := data[p]; ok {
34 return b, nil
35 }
36 return []byte{}, errors.Errorf("%s not found", p)
37 }
0 package packr
1
2 import (
3 "encoding/json"
4 "testing"
5
6 "github.com/stretchr/testify/require"
7 )
8
9 var testBox = NewBox("./fixtures")
10
11 func Test_PackBytes(t *testing.T) {
12 r := require.New(t)
13 PackBytes(testBox.Path, "foo", []byte("bar"))
14 s := testBox.String("foo")
15 r.Equal("bar", s)
16 }
17
18 func Test_PackJSONBytes(t *testing.T) {
19 r := require.New(t)
20 b, err := json.Marshal([]byte("json bytes"))
21 r.NoError(err)
22 err = PackJSONBytes(testBox.Path, "the bytes", string(b))
23 r.NoError(err)
24 s, err := testBox.MustBytes("the bytes")
25 r.NoError(err)
26 r.Equal([]byte("json bytes"), s)
27 }