├── .codeclimate.yml ├── .github ├── CODEOWNERS ├── FUNDING.yml └── workflows │ └── tests.yml ├── .gitignore ├── .gometalinter.json ├── .goreleaser.yml ├── .goreleaser.yml.plush ├── LICENSE.txt ├── Makefile ├── README.md ├── SHOULDERS.md ├── _fixtures ├── http_test │ ├── css │ │ └── main.css │ ├── footer.html │ ├── index.html │ └── sub │ │ └── index.html ├── import_pkg │ ├── import_pkg.go │ ├── import_pkg_test.go │ └── pkg_test │ │ ├── 1.txt │ │ └── 2.txt ├── list_test │ ├── a.txt │ ├── b │ │ ├── b.txt │ │ └── b2.txt │ └── c │ │ └── c.txt └── templates │ └── foo.txt ├── box.go ├── box_import_test.go ├── box_map.go ├── box_map_test.go ├── box_test.go ├── deprecated.go ├── deprecated_test.go ├── dirs_map.go ├── file ├── file.go ├── info.go └── resolver │ ├── _fixtures │ └── templates │ │ └── foo.txt │ ├── disk.go │ ├── disk_test.go │ ├── encoding │ └── hex │ │ └── hex.go │ ├── hex_gzip.go │ ├── hex_gzip_test.go │ ├── ident.go │ ├── ident_test.go │ ├── in_memory.go │ ├── in_memory_test.go │ ├── packable.go │ ├── resolver.go │ └── resolver_test.go ├── go.mod ├── go.sum ├── helpers.go ├── http_box_test.go ├── jam ├── pack.go ├── parser │ ├── _fixtures │ │ └── new_from_roots │ │ │ ├── _r │ │ │ └── r.go │ │ │ ├── e │ │ │ └── e.go │ │ │ ├── q.go │ │ │ └── w │ │ │ └── w.go │ ├── args.go │ ├── box.go │ ├── file.go │ ├── finder.go │ ├── gogen.go │ ├── parser.go │ ├── parser_test.go │ ├── prospect.go │ ├── prospect_test.go │ ├── roots.go │ ├── visitor.go │ └── visitor_test.go └── store │ ├── _fixtures │ ├── disk-pack │ │ ├── a │ │ │ └── a.go │ │ ├── b │ │ │ └── b.go │ │ ├── c │ │ │ ├── d.txt │ │ │ ├── e.txt │ │ │ └── f.txt │ │ ├── go.mod │ │ └── go.sum │ └── disk │ │ ├── _r │ │ └── r.go │ │ ├── e │ │ ├── e.go │ │ ├── heartbreakers │ │ │ └── refugee.txt │ │ └── petty │ │ │ └── fallin.txt │ │ ├── franklin │ │ ├── aretha.txt │ │ └── think.txt │ │ ├── q.go │ │ └── w │ │ └── w.go │ ├── clean.go │ ├── disk.go │ ├── disk_packed_test.go │ ├── disk_test.go │ ├── disk_tmpl.go │ ├── env.go │ ├── fn.go │ ├── legacy.go │ ├── legacy_test.go │ ├── store.go │ └── store_test.go ├── packr.go ├── packr2 ├── LICENSE ├── cmd │ ├── build.go │ ├── clean.go │ ├── fix.go │ ├── fix │ │ ├── fix.go │ │ ├── imports.go │ │ └── runner.go │ ├── gocmd.go │ ├── install.go │ ├── pack.go │ ├── root.go │ └── version.go └── main.go ├── packr_test.go ├── plog └── plog.go ├── pointer.go ├── pointer_test.go ├── resolvers_map.go ├── version.go ├── walk.go └── walk_test.go /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | golint: 4 | enabled: true 5 | checks: 6 | GoLint/Naming/MixedCaps: 7 | enabled: false 8 | govet: 9 | enabled: true 10 | gofmt: 11 | enabled: true 12 | fixme: 13 | enabled: true 14 | ratings: 15 | paths: 16 | - "**.go" 17 | exclude_paths: 18 | - "**/*_test.go" 19 | - "*_test.go" 20 | - "fixtures/" 21 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default owner 2 | * @gobuffalo/core-managers -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: markbates 4 | patreon: buffalo -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | jobs: 4 | tests: 5 | name: ${{matrix.go-version}} ${{matrix.os}} 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | go-version: [1.16.x, 1.17.x] 10 | os: [macos-latest, windows-latest, ubuntu-latest] 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 1 16 | - name: Setup Go ${{ matrix.go-version }} 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: ${{ matrix.go-version }} 20 | - name: Test 21 | run: | 22 | go test -race -cover -v ./... 23 | go install -v ./packr2 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | ./packr2 3 | .DS_Store 4 | doc 5 | tmp 6 | pkg 7 | *.gem 8 | *.pid 9 | coverage 10 | coverage.data 11 | build/* 12 | *.pbxuser 13 | *.mode1v3 14 | .svn 15 | profile 16 | .console_history 17 | .sass-cache/* 18 | .rake_tasks~ 19 | *.log.lck 20 | solr/ 21 | .jhw-cache/ 22 | jhw.* 23 | *.sublime* 24 | node_modules/ 25 | dist/ 26 | generated/ 27 | .vendor/ 28 | bin/* 29 | gin-bin 30 | /packr_darwin_amd64 31 | /packr_linux_amd64 32 | .vscode/ 33 | debug.test 34 | .grifter/ 35 | *-packr.go 36 | .idea/ 37 | 38 | -------------------------------------------------------------------------------- /.gometalinter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Enable": ["vet", "golint", "goimports", "deadcode", "gotype", "ineffassign", "misspell", "nakedret", "unconvert", "megacheck", "varcheck"] 3 | } 4 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Code generated by github.com/gobuffalo/release. DO NOT EDIT. 2 | # Edit .goreleaser.yml.plush instead 3 | 4 | builds: 5 | - 6 | goos: 7 | - darwin 8 | - linux 9 | - windows 10 | goarch: 11 | - ppc64le 12 | - 386 13 | - amd64 14 | env: 15 | - CGO_ENABLED=0 16 | ignore: 17 | - goos: darwin 18 | goarch: ppc64le 19 | - goos: windows 20 | goarch: ppc64le 21 | main: ./packr2/main.go 22 | binary: packr2 23 | 24 | checksum: 25 | name_template: 'checksums.txt' 26 | 27 | snapshot: 28 | name_template: "{{ .Tag }}-next" 29 | 30 | changelog: 31 | sort: asc 32 | filters: 33 | exclude: 34 | - '^docs:' 35 | - '^test:' 36 | 37 | brews: 38 | - 39 | github: 40 | owner: gobuffalo 41 | name: homebrew-tap 42 | 43 | -------------------------------------------------------------------------------- /.goreleaser.yml.plush: -------------------------------------------------------------------------------- 1 | builds: 2 | - 3 | goos: 4 | - darwin 5 | - linux 6 | - windows 7 | goarch: 8 | - ppc64le 9 | - 386 10 | - amd64 11 | env: 12 | - CGO_ENABLED=0 13 | ignore: 14 | - goos: darwin 15 | goarch: ppc64le 16 | - goos: windows 17 | goarch: ppc64le 18 | main: ./packr2/main.go 19 | binary: packr2 20 | 21 | checksum: 22 | name_template: 'checksums.txt' 23 | 24 | snapshot: 25 | name_template: "{{ .Tag }}-next" 26 | 27 | changelog: 28 | sort: asc 29 | filters: 30 | exclude: 31 | - '^docs:' 32 | - '^test:' 33 | <%= if (brew) { %> 34 | brews: 35 | - 36 | github: 37 | owner: gobuffalo 38 | name: homebrew-tap 39 | <% } %> 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Mark Bates 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TAGS ?= "sqlite" 2 | GO_BIN ?= go 3 | 4 | install: deps 5 | echo "installing packr v2" 6 | packr2 7 | $(GO_BIN) install -v ./packr2 8 | 9 | tidy: 10 | ifeq ($(GO111MODULE),on) 11 | $(GO_BIN) mod tidy 12 | else 13 | echo skipping go mod tidy 14 | endif 15 | 16 | deps: 17 | $(GO_BIN) get -tags ${TAGS} -t ./... 18 | $(GO_BIN) install -v ./packr2 19 | make tidy 20 | 21 | build: deps 22 | packr2 23 | $(GO_BIN) build -v ./packr2 24 | make tidy 25 | 26 | test: 27 | packr2 28 | $(GO_BIN) test -tags ${TAGS} ./... 29 | make tidy 30 | 31 | lint: 32 | gometalinter --vendor ./... --deadline=1m --skip=internal 33 | 34 | update: 35 | $(GO_BIN) get -u -tags ${TAGS} ./... 36 | make tidy 37 | make install 38 | make test 39 | make tidy 40 | 41 | release-test: 42 | $(GO_BIN) test -tags ${TAGS} -race ./... 43 | 44 | release: 45 | release -y -f version.go 46 | make tidy 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **NOTICE: Please consider migrating your projects to 2 | [embed](https://pkg.go.dev/embed) which is native file embedding feature of Go, 3 | or github.com/markbates/pkger. It has an idiomatic API, minimal dependencies, a stronger test suite (tested directly against the std lib counterparts), transparent tooling, and more.** 4 | 5 | https://blog.gobuffalo.io/introducing-pkger-static-file-embedding-in-go-1ce76dc79c65 6 | 7 | # Packr (v2) 8 | 9 | [![GoDoc](https://godoc.org/github.com/gobuffalo/packr/v2?status.svg)](https://godoc.org/github.com/gobuffalo/packr/v2) 10 | [![Actions Status](https://github.com/gobuffalo/packr/workflows/Tests/badge.svg)](https://github.com/gobuffalo/packr/actions) 11 | 12 | Packr is a simple solution for bundling static assets inside of Go binaries. Most importantly it does it in a way that is friendly to developers while they are developing. 13 | 14 | At this moment, supported go versions are: 15 | 16 | * 1.16.x 17 | * 1.17.x 18 | 19 | even though it may (or may not) working well with older versions. 20 | 21 | ## Intro Video 22 | 23 | To get an idea of the what and why of Packr, please enjoy this short video: [https://vimeo.com/219863271](https://vimeo.com/219863271). 24 | 25 | ## Library Installation 26 | 27 | ### Go 1.16 and above 28 | 29 | ```console 30 | $ go install github.com/gobuffalo/packr/v2@v2.8.3 31 | ``` 32 | 33 | or 34 | 35 | ```console 36 | $ go install github.com/gobuffalo/packr/v2@latest 37 | ``` 38 | 39 | ### Go 1.15 and below 40 | 41 | ```console 42 | $ go get -u github.com/gobuffalo/packr/... 43 | ``` 44 | 45 | ## Binary Installation 46 | 47 | ### Go 1.16 and above 48 | 49 | ```console 50 | $ go install github.com/gobuffalo/packr/v2/packr2@v2.8.3 51 | ``` 52 | 53 | or 54 | 55 | ```console 56 | $ go install github.com/gobuffalo/packr/v2/packr2@latest 57 | ``` 58 | 59 | ### Go 1.15 and below 60 | 61 | ```console 62 | $ go get -u github.com/gobuffalo/packr/packr2 63 | ``` 64 | 65 | ## New File Format FAQs 66 | 67 | In version `v2.0.0` the file format changed and is not backward compatible with the `packr-v1.x` library. 68 | 69 | #### Can `packr-v1.x` read the new format? 70 | 71 | No, it can not. Because of the way the new file format works porting it to `packr-v1.x` would be difficult. PRs are welcome though. :) 72 | 73 | #### Can `packr-v2.x` read `packr-v1.x` files? 74 | 75 | Yes it can, but that ability will eventually be phased out. Because of that we recommend moving to the new format. 76 | 77 | #### Can `packr-v2.x` generate `packr-v1.x` files? 78 | 79 | Yes it can, but that ability will eventually be phased out. Because of that we recommend moving to the new format. 80 | 81 | The `--legacy` command is available on all commands that generate `-packr.go` files. 82 | 83 | ```bash 84 | $ packr2 --legacy 85 | ``` 86 | 87 | ## Usage 88 | 89 | ### In Code 90 | 91 | The first step in using Packr is to create a new box. A box represents a folder on disk. Once you have a box you can get `string` or `[]byte` representations of the file. 92 | 93 | ```go 94 | // set up a new box by giving it a name and an optional (relative) path to a folder on disk: 95 | box := packr.New("My Box", "./templates") 96 | 97 | // Get the string representation of a file, or an error if it doesn't exist: 98 | html, err := box.FindString("index.html") 99 | 100 | // Get the []byte representation of a file, or an error if it doesn't exist: 101 | html, err := box.Find("index.html") 102 | ``` 103 | 104 | ### What is a Box? 105 | 106 | A box represents a folder, and any sub-folders, on disk that you want to have access to in your binary. When compiling a binary using the `packr2` CLI the contents of the folder will be converted into Go files that can be compiled inside of a "standard" go binary. Inside of the compiled binary the files will be read from memory. When working locally the files will be read directly off of disk. This is a seamless switch that doesn't require any special attention on your part. 107 | 108 | #### Example 109 | 110 | Assume the follow directory structure: 111 | 112 | ``` 113 | ├── main.go 114 | └── templates 115 | ├── admin 116 | │   └── index.html 117 | └── index.html 118 | ``` 119 | 120 | The following program will read the `./templates/admin/index.html` file and print it out. 121 | 122 | ```go 123 | package main 124 | 125 | import ( 126 | "fmt" 127 | 128 | "github.com/gobuffalo/packr/v2" 129 | ) 130 | 131 | func main() { 132 | box := packr.New("myBox", "./templates") 133 | 134 | s, err := box.FindString("admin/index.html") 135 | if err != nil { 136 | log.Fatal(err) 137 | } 138 | fmt.Println(s) 139 | } 140 | ``` 141 | 142 | ### Development Made Easy 143 | 144 | In order to get static files into a Go binary, those files must first be converted to Go code. To do that, Packr, ships with a few tools to help build binaries. See below. 145 | 146 | During development, however, it is painful to have to keep running a tool to compile those files. 147 | 148 | Packr uses the following resolution rules when looking for a file: 149 | 150 | 1. Look for the file in-memory (inside a Go binary) 151 | 1. Look for the file on disk (during development) 152 | 153 | Because Packr knows how to fall through to the file system, developers don't need to worry about constantly compiling their static files into a binary. They can work unimpeded. 154 | 155 | Packr takes file resolution a step further. When declaring a new box you use a relative path, `./templates`. When Packr receives this call it calculates out the absolute path to that directory. By doing this it means you can be guaranteed that Packr can find your files correctly, even if you're not running in the directory that the box was created in. This helps with the problem of testing, where Go changes the `pwd` for each package, making relative paths difficult to work with. This is not a problem when using Packr. 156 | 157 | --- 158 | 159 | ## Usage with HTTP 160 | 161 | A box implements the [`http.FileSystem`](https://golang.org/pkg/net/http/#FileSystem) interface, meaning it can be used to serve static files. 162 | 163 | ```go 164 | package main 165 | 166 | import ( 167 | "net/http" 168 | 169 | "github.com/gobuffalo/packr/v2" 170 | ) 171 | 172 | func main() { 173 | box := packr.New("someBoxName", "./templates") 174 | 175 | http.Handle("/", http.FileServer(box)) 176 | http.ListenAndServe(":3000", nil) 177 | } 178 | ``` 179 | 180 | --- 181 | 182 | ## Building a Binary 183 | 184 | Before you build your Go binary, run the `packr2` command first. It will look for all the boxes in your code and then generate `.go` files that pack the static files into bytes that can be bundled into the Go binary. 185 | 186 | ``` 187 | $ packr2 188 | ``` 189 | 190 | Then run your `go build command` like normal. 191 | 192 | *NOTE*: It is not recommended to check-in these generated `-packr.go` files. They can be large, and can easily become out of date if not careful. It is recommended that you always run `packr2 clean` after running the `packr2` tool. 193 | 194 | #### Cleaning Up 195 | 196 | When you're done it is recommended that you run the `packr2 clean` command. This will remove all of the generated files that Packr created for you. 197 | 198 | ``` 199 | $ packr2 clean 200 | ``` 201 | 202 | Why do you want to do this? Packr first looks to the information stored in these generated files, if the information isn't there it looks to disk. This makes it easy to work with in development. 203 | 204 | --- 205 | 206 | ## Debugging 207 | 208 | The `packr2` command passes all arguments down to the underlying `go` command, this includes the `-v` flag to print out `go build` information. Packr looks for the `-v` flag, and will turn on its own verbose logging. This is very useful for trying to understand what the `packr` command is doing when it is run. 209 | 210 | --- 211 | 212 | ## FAQ 213 | 214 | ### Compilation Errors with Go Templates 215 | 216 | Q: I have a program with Go template files, those files are named `foo.go` and look like the following: 217 | 218 | ``` 219 | // Copyright {{.Year}} {{.Author}}. All rights reserved. 220 | // Use of this source code is governed by a BSD-style 221 | // license that can be found in the LICENSE file. 222 | 223 | package {{.Project}} 224 | ``` 225 | 226 | When I run `packr2` I get errors like: 227 | 228 | ``` 229 | expected 'IDENT', found '{' 230 | ``` 231 | 232 | A: Packr works by searching your `.go` files for [`github.com/gobuffalo/packr/v2#New`](https://godoc.org/github.com/gobuffalo/packr/v2#New) or [`github.com/gobuffalo/packr/v2#NewBox`](https://godoc.org/github.com/gobuffalo/packr/v2#NewBox) calls. Because those files aren't "proper" Go files, Packr can't parse them to find the box declarations. To fix this you need to tell Packr to ignore those files when searching for boxes. A couple solutions to this problem are: 233 | 234 | * Name the files something else. The `.tmpl` extension is the idiomatic way of naming these types of files. 235 | * Rename the folder containing these files to start with an `_`, for example `_templates`. Packr, like Go, will ignore folders starting with the `_` character when searching for boxes. 236 | 237 | ### Dynamic Box Paths 238 | 239 | Q: I need to set the path of a box using a variable, but `packr.New("foo", myVar)` doesn't work correctly. 240 | 241 | A: Packr attempts to "automagically" set it's resolution directory when using [`github.com/gobuffalo/packr/v2#New`](https://godoc.org/github.com/gobuffalo/packr/v2#New), however, for dynamic paths you need to set it manually: 242 | 243 | ```go 244 | box := packr.New("foo", "|") 245 | box.ResolutionDir = myVar 246 | ``` 247 | 248 | ### I don't want to pack files, but still use the Packr interface. 249 | 250 | Q: I want to write code that using the Packr tools, but doesn't actually pack the files into my binary. How can I do that? 251 | 252 | A: Using [`github.com/gobuffalo/packr/v2#Folder`](https://godoc.org/github.com/gobuffalo/packr/v2#Folder) gives you back a `*packr.Box` that can be used as normal, but is excluded by the Packr tool when compiling. 253 | 254 | ### Packr Finds No Boxes 255 | 256 | Q: I run `packr2 -v` but it doesn't find my boxes: 257 | 258 | ``` 259 | DEBU[2019-03-18T18:48:52+01:00] *parser.Parser#NewFromRoots found prospects=0 260 | DEBU[2019-03-18T18:48:52+01:00] found 0 boxes 261 | ``` 262 | 263 | A: Packr works by parsing `.go` files to find [`github.com/gobuffalo/packr/v2#Box`](https://godoc.org/github.com/gobuffalo/packr/v2#Box) and [`github.com/gobuffalo/packr/v2#NewBox`](https://godoc.org/github.com/gobuffalo/packr/v2#NewBox) declarations. If there aren't any `.go` in the folder that `packr2` is run in it can not find those declarations. To fix this problem run the `packr2` command in the directory containing your `.go` files. 264 | 265 | ### Box Interfaces 266 | 267 | Q: I want to be able to easily test my applications by passing in mock boxes. How do I do that? 268 | 269 | A: Packr boxes and files conform to the interfaces found at [`github.com/gobuffalo/packd`](https://godoc.org/github.com/gobuffalo/packd). Change your application to use those interfaces instead of the concrete Packr types. 270 | 271 | ```go 272 | // using concrete type 273 | func myFunc(box *packr.Box) {} 274 | 275 | // using interfaces 276 | func myFunc(box packd.Box) {} 277 | ``` 278 | -------------------------------------------------------------------------------- /SHOULDERS.md: -------------------------------------------------------------------------------- 1 | # github.com/gobuffalo/packr/v2 Stands on the Shoulders of Giants 2 | 3 | github.com/gobuffalo/packr/v2 does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work. 4 | 5 | Thank you to the following **GIANTS**: 6 | 7 | 8 | * [github.com/gobuffalo/logger](https://godoc.org/github.com/gobuffalo/logger) 9 | 10 | * [github.com/gobuffalo/packd](https://godoc.org/github.com/gobuffalo/packd) 11 | 12 | * [github.com/karrick/godirwalk](https://godoc.org/github.com/karrick/godirwalk) 13 | 14 | * [github.com/konsorten/go-windows-terminal-sequences](https://godoc.org/github.com/konsorten/go-windows-terminal-sequences) 15 | 16 | * [github.com/markbates/errx](https://godoc.org/github.com/markbates/errx) 17 | 18 | * [github.com/markbates/oncer](https://godoc.org/github.com/markbates/oncer) 19 | 20 | * [github.com/markbates/safe](https://godoc.org/github.com/markbates/safe) 21 | 22 | * [github.com/rogpeppe/go-internal](https://godoc.org/github.com/rogpeppe/go-internal) 23 | 24 | * [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus) 25 | 26 | * [github.com/spf13/cobra](https://godoc.org/github.com/spf13/cobra) 27 | 28 | * [github.com/stretchr/testify](https://godoc.org/github.com/stretchr/testify) 29 | 30 | * [golang.org/x/sync](https://godoc.org/golang.org/x/sync) 31 | 32 | * [golang.org/x/tools](https://godoc.org/golang.org/x/tools) 33 | -------------------------------------------------------------------------------- /_fixtures/http_test/css/main.css: -------------------------------------------------------------------------------- 1 | Css 2 | -------------------------------------------------------------------------------- /_fixtures/http_test/footer.html: -------------------------------------------------------------------------------- 1 | Footer 2 | -------------------------------------------------------------------------------- /_fixtures/http_test/index.html: -------------------------------------------------------------------------------- 1 | Index 2 | -------------------------------------------------------------------------------- /_fixtures/http_test/sub/index.html: -------------------------------------------------------------------------------- 1 | Sub 2 | -------------------------------------------------------------------------------- /_fixtures/import_pkg/import_pkg.go: -------------------------------------------------------------------------------- 1 | package import_pkg 2 | 3 | import ( 4 | "github.com/gobuffalo/packr/v2" 5 | ) 6 | 7 | var BoxTestNew = packr.New("pkg_test", "./pkg_test") 8 | var BoxTestNewBox = packr.NewBox("./pkg_test") 9 | -------------------------------------------------------------------------------- /_fixtures/import_pkg/import_pkg_test.go: -------------------------------------------------------------------------------- 1 | package import_pkg 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gobuffalo/packr/v2" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_NewBox(t *testing.T) { 11 | r := require.New(t) 12 | 13 | box := packr.NewBox("./pkg_test") 14 | r.Len(box.List(), 2) 15 | } 16 | 17 | func Test_New(t *testing.T) { 18 | r := require.New(t) 19 | 20 | box := packr.New("pkg_test", "./pkg_test") 21 | r.Len(box.List(), 2) 22 | } 23 | -------------------------------------------------------------------------------- /_fixtures/import_pkg/pkg_test/1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobuffalo/packr/c4f590022d777e2d8e7906bb913019c1f6992d15/_fixtures/import_pkg/pkg_test/1.txt -------------------------------------------------------------------------------- /_fixtures/import_pkg/pkg_test/2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobuffalo/packr/c4f590022d777e2d8e7906bb913019c1f6992d15/_fixtures/import_pkg/pkg_test/2.txt -------------------------------------------------------------------------------- /_fixtures/list_test/a.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobuffalo/packr/c4f590022d777e2d8e7906bb913019c1f6992d15/_fixtures/list_test/a.txt -------------------------------------------------------------------------------- /_fixtures/list_test/b/b.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobuffalo/packr/c4f590022d777e2d8e7906bb913019c1f6992d15/_fixtures/list_test/b/b.txt -------------------------------------------------------------------------------- /_fixtures/list_test/b/b2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobuffalo/packr/c4f590022d777e2d8e7906bb913019c1f6992d15/_fixtures/list_test/b/b2.txt -------------------------------------------------------------------------------- /_fixtures/list_test/c/c.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobuffalo/packr/c4f590022d777e2d8e7906bb913019c1f6992d15/_fixtures/list_test/c/c.txt -------------------------------------------------------------------------------- /_fixtures/templates/foo.txt: -------------------------------------------------------------------------------- 1 | FOO!!! 2 | -------------------------------------------------------------------------------- /box.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "path" 11 | "path/filepath" 12 | "sort" 13 | "strings" 14 | 15 | "github.com/gobuffalo/packd" 16 | "github.com/gobuffalo/packr/v2/file" 17 | "github.com/gobuffalo/packr/v2/file/resolver" 18 | "github.com/gobuffalo/packr/v2/plog" 19 | "github.com/markbates/oncer" 20 | ) 21 | 22 | var _ packd.Box = &Box{} 23 | var _ packd.HTTPBox = &Box{} 24 | var _ packd.Addable = &Box{} 25 | var _ packd.Walkable = &Box{} 26 | var _ packd.Finder = &Box{} 27 | 28 | // Box represent a folder on a disk you want to 29 | // have access to in the built Go binary. 30 | type Box struct { 31 | Path string `json:"path"` 32 | Name string `json:"name"` 33 | ResolutionDir string `json:"resolution_dir"` 34 | DefaultResolver resolver.Resolver `json:"default_resolver"` 35 | resolvers resolversMap 36 | dirs dirsMap 37 | } 38 | 39 | // NewBox returns a Box that can be used to 40 | // retrieve files from either disk or the embedded 41 | // binary. 42 | // Deprecated: Use New instead. 43 | func NewBox(path string) *Box { 44 | oncer.Deprecate(0, "packr.NewBox", "Use packr.New instead.") 45 | return New(path, path) 46 | } 47 | 48 | // New returns a new Box with the name of the box 49 | // and the path of the box. 50 | func New(name string, path string) *Box { 51 | plog.Debug("packr", "New", "name", name, "path", path) 52 | b, _ := findBox(name) 53 | if b != nil { 54 | return b 55 | } 56 | 57 | b = construct(name, path) 58 | plog.Debug(b, "New", "Box", b, "ResolutionDir", b.ResolutionDir) 59 | b, err := placeBox(b) 60 | if err != nil { 61 | panic(err) 62 | } 63 | 64 | return b 65 | } 66 | 67 | // Folder returns a Box that will NOT be packed. 68 | // This is useful for writing tests or tools that 69 | // need to work with a folder at runtime. 70 | func Folder(path string) *Box { 71 | return New(path, path) 72 | } 73 | 74 | // SetResolver allows for the use of a custom resolver for 75 | // the specified file 76 | func (b *Box) SetResolver(file string, res resolver.Resolver) { 77 | d := filepath.Dir(file) 78 | b.dirs.Store(d, true) 79 | plog.Debug(b, "SetResolver", "file", file, "resolver", fmt.Sprintf("%T", res)) 80 | b.resolvers.Store(resolver.Key(file), res) 81 | } 82 | 83 | // AddString converts t to a byteslice and delegates to AddBytes to add to b.data 84 | func (b *Box) AddString(path string, t string) error { 85 | return b.AddBytes(path, []byte(t)) 86 | } 87 | 88 | // AddBytes sets t in b.data by the given path 89 | func (b *Box) AddBytes(path string, t []byte) error { 90 | m := map[string]file.File{} 91 | f, err := file.NewFile(path, t) 92 | if err != nil { 93 | return err 94 | } 95 | m[resolver.Key(path)] = f 96 | res := resolver.NewInMemory(m) 97 | b.SetResolver(path, res) 98 | return nil 99 | } 100 | 101 | // FindString returns either the string of the requested 102 | // file or an error if it can not be found. 103 | func (b *Box) FindString(name string) (string, error) { 104 | bb, err := b.Find(name) 105 | return string(bb), err 106 | } 107 | 108 | // Find returns either the byte slice of the requested 109 | // file or an error if it can not be found. 110 | func (b *Box) Find(name string) ([]byte, error) { 111 | f, err := b.Resolve(name) 112 | if err != nil { 113 | return []byte(""), err 114 | } 115 | bb := &bytes.Buffer{} 116 | io.Copy(bb, f) 117 | return bb.Bytes(), nil 118 | } 119 | 120 | // Has returns true if the resource exists in the box 121 | func (b *Box) Has(name string) bool { 122 | _, err := b.Find(name) 123 | return err == nil 124 | } 125 | 126 | // HasDir returns true if the directory exists in the box 127 | func (b *Box) HasDir(name string) bool { 128 | oncer.Do("packr2/box/HasDir"+b.Name, func() { 129 | for _, f := range b.List() { 130 | for d := filepath.Dir(f); d != "."; d = filepath.Dir(d) { 131 | b.dirs.Store(d, true) 132 | } 133 | } 134 | }) 135 | if name == "/" { 136 | return b.Has("index.html") 137 | } 138 | _, ok := b.dirs.Load(name) 139 | return ok 140 | } 141 | 142 | // Open returns a File using the http.File interface 143 | func (b *Box) Open(name string) (http.File, error) { 144 | plog.Debug(b, "Open", "name", name) 145 | f, err := b.Resolve(name) 146 | if err != nil { 147 | if len(filepath.Ext(name)) == 0 { 148 | return b.openWoExt(name) 149 | } 150 | return f, err 151 | } 152 | f, err = file.NewFileR(name, f) 153 | plog.Debug(b, "Open", "name", f.Name(), "file", f.Name()) 154 | return f, err 155 | } 156 | 157 | func (b *Box) openWoExt(name string) (http.File, error) { 158 | if !b.HasDir(name) { 159 | id := path.Join(name, "index.html") 160 | if b.Has(id) { 161 | return b.Open(id) 162 | } 163 | return nil, os.ErrNotExist 164 | } 165 | d, err := file.NewDir(name) 166 | plog.Debug(b, "Open", "name", name, "dir", d) 167 | return d, err 168 | } 169 | 170 | // List shows "What's in the box?" 171 | func (b *Box) List() []string { 172 | var keys []string 173 | 174 | b.Walk(func(path string, info File) error { 175 | if info == nil { 176 | return nil 177 | } 178 | finfo, _ := info.FileInfo() 179 | if !finfo.IsDir() { 180 | keys = append(keys, path) 181 | } 182 | return nil 183 | }) 184 | sort.Strings(keys) 185 | return keys 186 | } 187 | 188 | // Resolve will attempt to find the file in the box, 189 | // returning an error if the find can not be found. 190 | func (b *Box) Resolve(key string) (file.File, error) { 191 | key = strings.TrimPrefix(key, "/") 192 | 193 | var r resolver.Resolver 194 | 195 | b.resolvers.Range(func(k string, vr resolver.Resolver) bool { 196 | lk := strings.ToLower(resolver.Key(k)) 197 | lkey := strings.ToLower(resolver.Key(key)) 198 | if lk == lkey { 199 | r = vr 200 | return false 201 | } 202 | return true 203 | }) 204 | 205 | if r == nil { 206 | r = b.DefaultResolver 207 | if r == nil { 208 | r = resolver.DefaultResolver 209 | if r == nil { 210 | return nil, fmt.Errorf("resolver.DefaultResolver is nil") 211 | } 212 | } 213 | } 214 | plog.Debug(r, "Resolve", "box", b.Name, "key", key) 215 | 216 | f, err := r.Resolve(b.Name, key) 217 | if err != nil { 218 | z, err := resolver.ResolvePathInBase(resolver.OsPath(b.ResolutionDir), filepath.FromSlash(path.Clean("/"+resolver.OsPath(key)))) 219 | if err != nil { 220 | plog.Debug(r, "Resolve", "box", b.Name, "key", key, "err", err) 221 | return f, err 222 | } 223 | 224 | f, err = r.Resolve(b.Name, z) 225 | if err != nil { 226 | plog.Debug(r, "Resolve", "box", b.Name, "key", z, "err", err) 227 | return f, err 228 | } 229 | b, err := ioutil.ReadAll(f) 230 | if err != nil { 231 | return f, err 232 | } 233 | f, err = file.NewFile(key, b) 234 | if err != nil { 235 | return f, err 236 | } 237 | } 238 | plog.Debug(r, "Resolve", "box", b.Name, "key", key, "file", f.Name()) 239 | return f, nil 240 | } 241 | -------------------------------------------------------------------------------- /box_import_test.go: -------------------------------------------------------------------------------- 1 | package packr_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gobuffalo/packr/v2/_fixtures/import_pkg" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_ImportWithBox(t *testing.T) { 11 | r := require.New(t) 12 | 13 | r.Len(import_pkg.BoxTestNew.List(), 2) 14 | 15 | r.Len(import_pkg.BoxTestNewBox.List(), 2) 16 | } 17 | -------------------------------------------------------------------------------- /box_map.go: -------------------------------------------------------------------------------- 1 | //go:generate mapgen -name "box" -zero "nil" -go-type "*Box" -pkg "" -a "New(`test-a`, ``)" -b "New(`test-b`, ``)" -c "New(`test-c`, ``)" -bb "New(`test-bb`, ``)" -destination "packr" 2 | // Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT. 3 | 4 | package packr 5 | 6 | import ( 7 | "sort" 8 | "sync" 9 | ) 10 | 11 | // boxMap wraps sync.Map and uses the following types: 12 | // key: string 13 | // value: *Box 14 | type boxMap struct { 15 | data sync.Map 16 | } 17 | 18 | // Delete the key from the map 19 | func (m *boxMap) Delete(key string) { 20 | m.data.Delete(key) 21 | } 22 | 23 | // Load the key from the map. 24 | // Returns *Box or bool. 25 | // A false return indicates either the key was not found 26 | // or the value is not of type *Box 27 | func (m *boxMap) Load(key string) (*Box, bool) { 28 | i, ok := m.data.Load(key) 29 | if !ok { 30 | return nil, false 31 | } 32 | s, ok := i.(*Box) 33 | return s, ok 34 | } 35 | 36 | // LoadOrStore will return an existing key or 37 | // store the value if not already in the map 38 | func (m *boxMap) LoadOrStore(key string, value *Box) (*Box, bool) { 39 | i, _ := m.data.LoadOrStore(key, value) 40 | s, ok := i.(*Box) 41 | return s, ok 42 | } 43 | 44 | // Range over the *Box values in the map 45 | func (m *boxMap) Range(f func(key string, value *Box) bool) { 46 | m.data.Range(func(k, v interface{}) bool { 47 | key, ok := k.(string) 48 | if !ok { 49 | return false 50 | } 51 | value, ok := v.(*Box) 52 | if !ok { 53 | return false 54 | } 55 | return f(key, value) 56 | }) 57 | } 58 | 59 | // Store a *Box in the map 60 | func (m *boxMap) Store(key string, value *Box) { 61 | m.data.Store(key, value) 62 | } 63 | 64 | // Keys returns a list of keys in the map 65 | func (m *boxMap) Keys() []string { 66 | var keys []string 67 | m.Range(func(key string, value *Box) bool { 68 | keys = append(keys, key) 69 | return true 70 | }) 71 | sort.Strings(keys) 72 | return keys 73 | } 74 | -------------------------------------------------------------------------------- /box_map_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT. 2 | 3 | package packr 4 | 5 | import ( 6 | "sort" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func Test_boxMap(t *testing.T) { 13 | r := require.New(t) 14 | 15 | sm := &boxMap{} 16 | 17 | sm.Store("a", New(`test-a`, ``)) 18 | 19 | s, ok := sm.Load("a") 20 | r.True(ok) 21 | r.Equal(New(`test-a`, ``), s) 22 | 23 | s, ok = sm.LoadOrStore("b", New(`test-b`, ``)) 24 | r.True(ok) 25 | r.Equal(New(`test-b`, ``), s) 26 | 27 | s, ok = sm.LoadOrStore("b", New(`test-bb`, ``)) 28 | r.True(ok) 29 | r.Equal(New(`test-b`, ``), s) 30 | 31 | var keys []string 32 | 33 | sm.Range(func(key string, value *Box) bool { 34 | keys = append(keys, key) 35 | return true 36 | }) 37 | 38 | sort.Strings(keys) 39 | 40 | r.Equal(sm.Keys(), keys) 41 | 42 | sm.Delete("b") 43 | r.Equal([]string{"a", "b"}, keys) 44 | 45 | sm.Delete("b") 46 | _, ok = sm.Load("b") 47 | r.False(ok) 48 | 49 | func(m *boxMap) { 50 | m.Store("c", New(`test-c`, ``)) 51 | }(sm) 52 | s, ok = sm.Load("c") 53 | r.True(ok) 54 | r.Equal(New(`test-c`, ``), s) 55 | } 56 | -------------------------------------------------------------------------------- /box_test.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "bytes" 5 | "path/filepath" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/gobuffalo/packr/v2/file" 10 | "github.com/gobuffalo/packr/v2/file/resolver" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func Test_New(t *testing.T) { 15 | r := require.New(t) 16 | 17 | box := New("Test_NewBox", filepath.Join("_fixtures", "list_test")) 18 | r.Len(box.List(), 4) 19 | 20 | } 21 | func Test_Box_AddString(t *testing.T) { 22 | r := require.New(t) 23 | 24 | box := New("Test_Box_AddString", "./templates") 25 | s, err := box.FindString("foo.txt") 26 | r.Error(err) 27 | r.Equal("", s) 28 | 29 | r.NoError(box.AddString("foo.txt", "foo!!")) 30 | s, err = box.FindString("foo.txt") 31 | r.NoError(err) 32 | r.Equal("foo!!", s) 33 | } 34 | 35 | func Test_Box_AddBytes(t *testing.T) { 36 | r := require.New(t) 37 | 38 | box := New("Test_Box_AddBytes", "") 39 | s, err := box.FindString("foo.txt") 40 | r.Error(err) 41 | r.Equal("", s) 42 | 43 | r.NoError(box.AddBytes("foo.txt", []byte("foo!!"))) 44 | s, err = box.FindString("foo.txt") 45 | r.NoError(err) 46 | r.Equal("foo!!", s) 47 | } 48 | 49 | func Test_Box_String(t *testing.T) { 50 | r := require.New(t) 51 | 52 | box := New("Test_Box_String", "./templates") 53 | d := resolver.NewInMemory(map[string]file.File{ 54 | "foo.txt": qfile("foo.txt", "foo!"), 55 | }) 56 | box.SetResolver("foo.txt", d) 57 | 58 | s := box.String("foo.txt") 59 | r.Equal("foo!", s) 60 | 61 | s = box.String("idontexist") 62 | r.Equal("", s) 63 | } 64 | 65 | func Test_Box_String_Miss(t *testing.T) { 66 | r := require.New(t) 67 | 68 | box := New("Test_Box_String_Miss", filepath.Join("_fixtures", "templates")) 69 | 70 | s := box.String("foo.txt") 71 | r.Equal("FOO!!!", strings.TrimSpace(s)) 72 | 73 | s = box.String("idontexist") 74 | r.Equal("", s) 75 | } 76 | 77 | func Test_Box_FindString(t *testing.T) { 78 | r := require.New(t) 79 | 80 | box := New("Test_Box_FindString", "./templates") 81 | d := resolver.NewInMemory(map[string]file.File{ 82 | "foo.txt": qfile("foo.txt", "foo!"), 83 | }) 84 | box.SetResolver("foo.txt", d) 85 | 86 | s, err := box.FindString("foo.txt") 87 | r.NoError(err) 88 | r.Equal("foo!", s) 89 | 90 | s, err = box.FindString("idontexist") 91 | r.Error(err) 92 | r.Equal("", s) 93 | } 94 | 95 | func Test_Box_FindString_Miss(t *testing.T) { 96 | r := require.New(t) 97 | 98 | box := New("Test_Box_FindString_Miss", filepath.Join("_fixtures", "templates")) 99 | 100 | s, err := box.FindString("foo.txt") 101 | r.NoError(err) 102 | r.Equal("FOO!!!", strings.TrimSpace(s)) 103 | 104 | s, err = box.FindString("idontexist") 105 | r.Error(err) 106 | r.Equal("", s) 107 | } 108 | 109 | func Test_Box_Bytes(t *testing.T) { 110 | r := require.New(t) 111 | 112 | box := New("Test_Box_Bytes", "./templates") 113 | d := resolver.NewInMemory(map[string]file.File{ 114 | "foo.txt": qfile("foo.txt", "foo!"), 115 | }) 116 | box.SetResolver("foo.txt", d) 117 | 118 | s := box.Bytes("foo.txt") 119 | r.Equal([]byte("foo!"), s) 120 | 121 | s = box.Bytes("idontexist") 122 | r.Equal([]byte(""), s) 123 | } 124 | 125 | func Test_Box_Bytes_Miss(t *testing.T) { 126 | r := require.New(t) 127 | 128 | box := New("Test_Box_Bytes_Miss", filepath.Join("_fixtures", "templates")) 129 | 130 | s := box.Bytes("foo.txt") 131 | r.Equal([]byte("FOO!!!"), bytes.TrimSpace(s)) 132 | 133 | s = box.Bytes("idontexist") 134 | r.Equal([]byte(""), s) 135 | } 136 | 137 | func Test_Box_Find(t *testing.T) { 138 | r := require.New(t) 139 | 140 | box := New("Test_Box_Find", "./templates") 141 | d := resolver.NewInMemory(map[string]file.File{ 142 | "foo.txt": qfile("foo.txt", "foo!"), 143 | }) 144 | box.SetResolver("foo.txt", d) 145 | 146 | s, err := box.Find("foo.txt") 147 | r.NoError(err) 148 | r.Equal("foo!", string(s)) 149 | 150 | s, err = box.Find("idontexist") 151 | r.Error(err) 152 | r.Equal("", string(s)) 153 | } 154 | 155 | func Test_Box_Find_Miss(t *testing.T) { 156 | r := require.New(t) 157 | 158 | box := New("Test_Box_Find_Miss", "./_fixtures/templates") 159 | s, err := box.Find("foo.txt") 160 | r.NoError(err) 161 | r.Equal("FOO!!!", strings.TrimSpace(string(s))) 162 | 163 | s, err = box.Find("idontexist") 164 | r.Error(err) 165 | r.Equal("", string(s)) 166 | } 167 | 168 | func Test_Box_Has(t *testing.T) { 169 | r := require.New(t) 170 | 171 | box := New("Test_Box_Has", "./templates") 172 | d := resolver.NewInMemory(map[string]file.File{ 173 | "foo.txt": qfile("foo.txt", "foo!"), 174 | }) 175 | box.SetResolver("foo.txt", d) 176 | 177 | r.True(box.Has("foo.txt")) 178 | r.False(box.Has("idontexist")) 179 | } 180 | 181 | func Test_Box_Open(t *testing.T) { 182 | r := require.New(t) 183 | 184 | d := resolver.NewInMemory(map[string]file.File{ 185 | "foo.txt": qfile("foo.txt", "foo!"), 186 | "bar": qfile("bar", "bar!"), 187 | "baz/index.html": qfile("baz", "baz!"), 188 | }) 189 | box := New("Test_Box_Open", "./templates") 190 | 191 | box.DefaultResolver = d 192 | 193 | for _, x := range []string{"foo.txt", "/foo.txt", "bar", "/bar", "baz", "/baz"} { 194 | f, err := box.Open(x) 195 | r.NoError(err) 196 | r.NotZero(f) 197 | } 198 | 199 | f, err := box.Open("idontexist.txt") 200 | r.Error(err) 201 | r.Zero(f) 202 | } 203 | 204 | func Test_Box_List(t *testing.T) { 205 | r := require.New(t) 206 | 207 | box := New("Test_Box_List", filepath.Join("_fixtures", "list_test")) 208 | r.NoError(box.AddString(filepath.Join("d", "d.txt"), "D")) 209 | 210 | act := box.List() 211 | exp := []string{"a.txt", filepath.Join("b", "b.txt"), filepath.Join("b", "b2.txt"), filepath.Join("c", "c.txt"), filepath.Join("d", "d.txt")} 212 | r.Equal(exp, act) 213 | } 214 | 215 | func Test_Box_HasDir(t *testing.T) { 216 | r := require.New(t) 217 | 218 | box := New("Test_Box_HasDir", filepath.Join("_fixtures", "list_test")) 219 | r.NoError(box.AddString("d/e/f.txt", "D")) 220 | 221 | r.True(box.HasDir("d/e")) 222 | r.True(box.HasDir("d")) 223 | r.True(box.HasDir("c")) 224 | r.False(box.HasDir("a")) 225 | } 226 | 227 | func Test_Box_Traversal_Standard(t *testing.T) { 228 | r := require.New(t) 229 | box := New("Test_Box_Traversal_Standard", "") 230 | _, err := box.FindString("../fixtures/hello.txt") 231 | r.Error(err) 232 | } 233 | 234 | func Test_Box_Traversal_Standard_Depth2(t *testing.T) { 235 | r := require.New(t) 236 | box := New("Test_Box_Traversal_Standard_Depth2", "") 237 | _, err := box.FindString("../../packr/fixtures/hello.txt") 238 | r.Error(err) 239 | } 240 | 241 | func Test_Box_Traversal_Backslash(t *testing.T) { 242 | r := require.New(t) 243 | box := New("Test_Box_Traversal_Backslash", "") 244 | _, err := box.FindString("..\\fixtures\\hello.txt") 245 | r.Error(err) 246 | } 247 | 248 | func Test_Box_Traversal_Backslash_Depth2(t *testing.T) { 249 | r := require.New(t) 250 | box := New("Test_Box_Traversal_Backslash_Depth2", "") 251 | _, err := box.FindString("..\\..\\packr2\\fixtures\\hello.txt") 252 | r.Error(err) 253 | } 254 | -------------------------------------------------------------------------------- /deprecated.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/gobuffalo/packr/v2/file" 8 | "github.com/gobuffalo/packr/v2/file/resolver" 9 | "github.com/markbates/oncer" 10 | ) 11 | 12 | // File has been deprecated and file.File should be used instead 13 | type File = file.File 14 | 15 | var ( 16 | // ErrResOutsideBox gets returned in case of the requested resources being outside the box 17 | // Deprecated 18 | ErrResOutsideBox = fmt.Errorf("can't find a resource outside the box") 19 | ) 20 | 21 | // PackBytes packs bytes for a file into a box. 22 | // Deprecated 23 | func PackBytes(box string, name string, bb []byte) { 24 | b := NewBox(box) 25 | d := resolver.NewInMemory(map[string]file.File{}) 26 | f, err := file.NewFile(name, bb) 27 | if err != nil { 28 | panic(err) 29 | } 30 | if err := d.Pack(name, f); err != nil { 31 | panic(err) 32 | } 33 | b.SetResolver(name, d) 34 | } 35 | 36 | // PackBytesGzip packets the gzipped compressed bytes into a box. 37 | // Deprecated 38 | func PackBytesGzip(box string, name string, bb []byte) error { 39 | // TODO: this function never did what it was supposed to do! 40 | PackBytes(box, name, bb) 41 | return nil 42 | } 43 | 44 | // PackJSONBytes packs JSON encoded bytes for a file into a box. 45 | // Deprecated 46 | func PackJSONBytes(box string, name string, jbb string) error { 47 | var bb []byte 48 | err := json.Unmarshal([]byte(jbb), &bb) 49 | if err != nil { 50 | return err 51 | } 52 | PackBytes(box, name, bb) 53 | return nil 54 | } 55 | 56 | // Bytes is deprecated. Use Find instead 57 | func (b *Box) Bytes(name string) []byte { 58 | bb, _ := b.Find(name) 59 | oncer.Deprecate(0, "github.com/gobuffalo/packr/v2#Box.Bytes", "Use github.com/gobuffalo/packr/v2#Box.Find instead.") 60 | return bb 61 | } 62 | 63 | // MustBytes is deprecated. Use Find instead. 64 | func (b *Box) MustBytes(name string) ([]byte, error) { 65 | oncer.Deprecate(0, "github.com/gobuffalo/packr/v2#Box.MustBytes", "Use github.com/gobuffalo/packr/v2#Box.Find instead.") 66 | return b.Find(name) 67 | } 68 | 69 | // String is deprecated. Use FindString instead 70 | func (b *Box) String(name string) string { 71 | oncer.Deprecate(0, "github.com/gobuffalo/packr/v2#Box.String", "Use github.com/gobuffalo/packr/v2#Box.FindString instead.") 72 | return string(b.Bytes(name)) 73 | } 74 | 75 | // MustString is deprecated. Use FindString instead 76 | func (b *Box) MustString(name string) (string, error) { 77 | oncer.Deprecate(0, "github.com/gobuffalo/packr/v2#Box.MustString", "Use github.com/gobuffalo/packr/v2#Box.FindString instead.") 78 | return b.FindString(name) 79 | } 80 | -------------------------------------------------------------------------------- /deprecated_test.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func Test_PackBytes(t *testing.T) { 10 | r := require.New(t) 11 | 12 | box := NewBox("my/box") 13 | name := "foo.txt" 14 | body := []byte("foo!!") 15 | PackBytes(box.Name, name, body) 16 | 17 | f, err := box.FindString(name) 18 | r.NoError(err) 19 | r.Equal(string(body), f) 20 | } 21 | 22 | func Test_PackBytesGzip(t *testing.T) { 23 | r := require.New(t) 24 | 25 | box := NewBox("my/box") 26 | name := "foo.txt" 27 | body := []byte("foo!!") 28 | PackBytesGzip(box.Name, name, body) 29 | 30 | f, err := box.FindString(name) 31 | r.NoError(err) 32 | r.Equal(string(body), f) 33 | } 34 | 35 | func Test_PackJSONBytes(t *testing.T) { 36 | r := require.New(t) 37 | 38 | box := NewBox("my/box") 39 | name := "foo.txt" 40 | body := "\"PGgxPnRlbXBsYXRlcy9tYWlsZXJzL2xheW91dC5odG1sPC9oMT4KCjwlPSB5aWVsZCAlPgo=\"" 41 | PackJSONBytes(box.Name, name, body) 42 | 43 | f, err := box.FindString(name) 44 | r.NoError(err) 45 | r.Equal("

templates/mailers/layout.html

\n\n<%= yield %>\n", f) 46 | } 47 | -------------------------------------------------------------------------------- /dirs_map.go: -------------------------------------------------------------------------------- 1 | //go:generate mapgen -name "dirs" -zero "false" -go-type "bool" -pkg "" -a "nil" -b "nil" -c "nil" -bb "nil" -destination "packr" 2 | // Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT. 3 | 4 | package packr 5 | 6 | import ( 7 | "sort" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | // dirsMap wraps sync.Map and uses the following types: 13 | // key: string 14 | // value: bool 15 | type dirsMap struct { 16 | data sync.Map 17 | } 18 | 19 | // Delete the key from the map 20 | func (m *dirsMap) Delete(key string) { 21 | m.data.Delete(m.normalizeKey(key)) 22 | } 23 | 24 | // Load the key from the map. 25 | // Returns bool or bool. 26 | // A false return indicates either the key was not found 27 | // or the value is not of type bool 28 | func (m *dirsMap) Load(key string) (bool, bool) { 29 | i, ok := m.data.Load(m.normalizeKey(key)) 30 | if !ok { 31 | return false, false 32 | } 33 | s, ok := i.(bool) 34 | return s, ok 35 | } 36 | 37 | // LoadOrStore will return an existing key or 38 | // store the value if not already in the map 39 | func (m *dirsMap) LoadOrStore(key string, value bool) (bool, bool) { 40 | i, _ := m.data.LoadOrStore(m.normalizeKey(key), value) 41 | s, ok := i.(bool) 42 | return s, ok 43 | } 44 | 45 | // Range over the bool values in the map 46 | func (m *dirsMap) Range(f func(key string, value bool) bool) { 47 | m.data.Range(func(k, v interface{}) bool { 48 | key, ok := k.(string) 49 | if !ok { 50 | return false 51 | } 52 | value, ok := v.(bool) 53 | if !ok { 54 | return false 55 | } 56 | return f(key, value) 57 | }) 58 | } 59 | 60 | // Store a bool in the map 61 | func (m *dirsMap) Store(key string, value bool) { 62 | d := m.normalizeKey(key) 63 | m.data.Store(d, value) 64 | m.data.Store(strings.TrimPrefix(d, "/"), value) 65 | } 66 | 67 | // Keys returns a list of keys in the map 68 | func (m *dirsMap) Keys() []string { 69 | var keys []string 70 | m.Range(func(key string, value bool) bool { 71 | keys = append(keys, key) 72 | return true 73 | }) 74 | sort.Strings(keys) 75 | return keys 76 | } 77 | 78 | func (m *dirsMap) normalizeKey(key string) string { 79 | key = strings.Replace(key, "\\", "/", -1) 80 | 81 | return key 82 | } 83 | -------------------------------------------------------------------------------- /file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | 7 | "github.com/gobuffalo/packd" 8 | ) 9 | 10 | // File represents a virtual, or physical, backing of 11 | // a file object in a Box 12 | type File = packd.File 13 | 14 | // FileMappable types are capable of returning a map of 15 | // path => File 16 | type FileMappable interface { 17 | FileMap() map[string]File 18 | } 19 | 20 | // NewFile returns a virtual File implementation 21 | func NewFile(name string, b []byte) (File, error) { 22 | return packd.NewFile(name, bytes.NewReader(b)) 23 | } 24 | 25 | // NewDir returns a virtual dir implementation 26 | func NewDir(name string) (File, error) { 27 | return packd.NewDir(name) 28 | } 29 | 30 | func NewFileR(name string, r io.Reader) (File, error) { 31 | return packd.NewFile(name, r) 32 | } 33 | -------------------------------------------------------------------------------- /file/info.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "os" 5 | "time" 6 | ) 7 | 8 | type info struct { 9 | Path string 10 | Contents []byte 11 | size int64 12 | modTime time.Time 13 | isDir bool 14 | } 15 | 16 | func (f info) Name() string { 17 | return f.Path 18 | } 19 | 20 | func (f info) Size() int64 { 21 | return f.size 22 | } 23 | 24 | func (f info) Mode() os.FileMode { 25 | return 0444 26 | } 27 | 28 | func (f info) ModTime() time.Time { 29 | return f.modTime 30 | } 31 | 32 | func (f info) IsDir() bool { 33 | return f.isDir 34 | } 35 | 36 | func (f info) Sys() interface{} { 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /file/resolver/_fixtures/templates/foo.txt: -------------------------------------------------------------------------------- 1 | foo! 2 | -------------------------------------------------------------------------------- /file/resolver/disk.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "sync" 9 | 10 | "github.com/gobuffalo/packr/v2/file" 11 | "github.com/gobuffalo/packr/v2/plog" 12 | "github.com/karrick/godirwalk" 13 | ) 14 | 15 | var _ Resolver = &Disk{} 16 | 17 | type Disk struct { 18 | Root string 19 | } 20 | 21 | func (d Disk) String() string { 22 | return String(&d) 23 | } 24 | 25 | func (d *Disk) Resolve(box string, name string) (file.File, error) { 26 | var err error 27 | path := OsPath(name) 28 | if !filepath.IsAbs(path) { 29 | path, err = ResolvePathInBase(OsPath(d.Root), path) 30 | if err != nil { 31 | return nil, err 32 | } 33 | } 34 | 35 | fi, err := os.Stat(path) 36 | if err != nil { 37 | return nil, err 38 | } 39 | if fi.IsDir() { 40 | return nil, os.ErrNotExist 41 | } 42 | if bb, err := ioutil.ReadFile(path); err == nil { 43 | return file.NewFile(OsPath(name), bb) 44 | } 45 | return nil, os.ErrNotExist 46 | } 47 | 48 | // ResolvePathInBase returns a path that is guaranteed to be inside of the base directory or an error 49 | func ResolvePathInBase(base, path string) (string, error) { 50 | // Determine the absolute file path of the base directory 51 | d, err := filepath.Abs(base) 52 | if err != nil { 53 | return "", err 54 | } 55 | 56 | // Return the base directory if no file was requested 57 | if path == "/" || path == "\\" { 58 | return d, nil 59 | } 60 | 61 | // Resolve the absolute file path after combining the key with base 62 | p, err := filepath.Abs(filepath.Join(d, path)) 63 | if err != nil { 64 | return "", err 65 | } 66 | 67 | // Verify that the resolved path is inside of the base directory 68 | if !strings.HasPrefix(p, d+string(filepath.Separator)) { 69 | return "", os.ErrNotExist 70 | } 71 | return p, nil 72 | } 73 | 74 | var _ file.FileMappable = &Disk{} 75 | 76 | func (d *Disk) FileMap() map[string]file.File { 77 | moot := &sync.Mutex{} 78 | m := map[string]file.File{} 79 | root := OsPath(d.Root) 80 | if _, err := os.Stat(root); err != nil { 81 | return m 82 | } 83 | callback := func(path string, de *godirwalk.Dirent) error { 84 | if _, err := os.Stat(root); err != nil { 85 | return nil 86 | } 87 | if !de.IsRegular() { 88 | return nil 89 | } 90 | moot.Lock() 91 | name := strings.TrimPrefix(path, root+string(filepath.Separator)) 92 | b, err := ioutil.ReadFile(path) 93 | if err != nil { 94 | return err 95 | } 96 | m[name], err = file.NewFile(name, b) 97 | if err != nil { 98 | return err 99 | } 100 | moot.Unlock() 101 | return nil 102 | } 103 | err := godirwalk.Walk(root, &godirwalk.Options{ 104 | FollowSymbolicLinks: true, 105 | Callback: callback, 106 | }) 107 | if err != nil { 108 | plog.Logger.Errorf("[%s] error walking %v", root, err) 109 | } 110 | return m 111 | } 112 | -------------------------------------------------------------------------------- /file/resolver/disk_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "io/ioutil" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func Test_Disk_Find(t *testing.T) { 12 | r := require.New(t) 13 | 14 | d := &Disk{ 15 | Root: "_fixtures\\templates", 16 | } 17 | 18 | f, err := d.Resolve("", "foo.txt") 19 | r.NoError(err) 20 | 21 | fi, err := f.FileInfo() 22 | r.NoError(err) 23 | r.Equal("foo.txt", fi.Name()) 24 | 25 | b, err := ioutil.ReadAll(f) 26 | r.NoError(err) 27 | r.Equal("foo!", strings.TrimSpace(string(b))) 28 | } 29 | -------------------------------------------------------------------------------- /file/resolver/encoding/hex/hex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package hex implements hexadecimal encoding and decoding. 6 | package hex 7 | 8 | import ( 9 | "bytes" 10 | "fmt" 11 | "io" 12 | ) 13 | 14 | const hextable = "0123456789abcdef" 15 | 16 | // EncodedLen returns the length of an encoding of n source bytes. 17 | // Specifically, it returns n * 2. 18 | func EncodedLen(n int) int { return n * 2 } 19 | 20 | // Encode encodes src into EncodedLen(len(src)) 21 | // bytes of dst. As a convenience, it returns the number 22 | // of bytes written to dst, but this value is always EncodedLen(len(src)). 23 | // Encode implements hexadecimal encoding. 24 | func Encode(dst, src []byte) int { 25 | for i, v := range src { 26 | dst[i*2] = hextable[v>>4] 27 | dst[i*2+1] = hextable[v&0x0f] 28 | } 29 | 30 | return len(src) * 2 31 | } 32 | 33 | // ErrLength reports an attempt to decode an odd-length input 34 | // using Decode or DecodeString. 35 | // The stream-based Decoder returns io.ErrUnexpectedEOF instead of ErrLength. 36 | var ErrLength = fmt.Errorf("encoding/hex: odd length hex string") 37 | 38 | // InvalidByteError values describe errors resulting from an invalid byte in a hex string. 39 | type InvalidByteError byte 40 | 41 | func (e InvalidByteError) Error() string { 42 | return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e)) 43 | } 44 | 45 | // DecodedLen returns the length of a decoding of x source bytes. 46 | // Specifically, it returns x / 2. 47 | func DecodedLen(x int) int { return x / 2 } 48 | 49 | // Decode decodes src into DecodedLen(len(src)) bytes, 50 | // returning the actual number of bytes written to dst. 51 | // 52 | // Decode expects that src contains only hexadecimal 53 | // characters and that src has even length. 54 | // If the input is malformed, Decode returns the number 55 | // of bytes decoded before the error. 56 | func Decode(dst, src []byte) (int, error) { 57 | var i int 58 | for i = 0; i < len(src)/2; i++ { 59 | a, ok := fromHexChar(src[i*2]) 60 | if !ok { 61 | return i, InvalidByteError(src[i*2]) 62 | } 63 | b, ok := fromHexChar(src[i*2+1]) 64 | if !ok { 65 | return i, InvalidByteError(src[i*2+1]) 66 | } 67 | dst[i] = (a << 4) | b 68 | } 69 | if len(src)%2 == 1 { 70 | // Check for invalid char before reporting bad length, 71 | // since the invalid char (if present) is an earlier problem. 72 | if _, ok := fromHexChar(src[i*2]); !ok { 73 | return i, InvalidByteError(src[i*2]) 74 | } 75 | return i, ErrLength 76 | } 77 | return i, nil 78 | } 79 | 80 | // fromHexChar converts a hex character into its value and a success flag. 81 | func fromHexChar(c byte) (byte, bool) { 82 | switch { 83 | case '0' <= c && c <= '9': 84 | return c - '0', true 85 | case 'a' <= c && c <= 'f': 86 | return c - 'a' + 10, true 87 | case 'A' <= c && c <= 'F': 88 | return c - 'A' + 10, true 89 | } 90 | 91 | return 0, false 92 | } 93 | 94 | // EncodeToString returns the hexadecimal encoding of src. 95 | func EncodeToString(src []byte) string { 96 | dst := make([]byte, EncodedLen(len(src))) 97 | Encode(dst, src) 98 | return string(dst) 99 | } 100 | 101 | // DecodeString returns the bytes represented by the hexadecimal string s. 102 | // 103 | // DecodeString expects that src contains only hexadecimal 104 | // characters and that src has even length. 105 | // If the input is malformed, DecodeString returns 106 | // the bytes decoded before the error. 107 | func DecodeString(s string) ([]byte, error) { 108 | src := []byte(s) 109 | // We can use the source slice itself as the destination 110 | // because the decode loop increments by one and then the 'seen' byte is not used anymore. 111 | n, err := Decode(src, src) 112 | return src[:n], err 113 | } 114 | 115 | // Dump returns a string that contains a hex dump of the given data. The format 116 | // of the hex dump matches the output of `hexdump -C` on the command line. 117 | func Dump(data []byte) string { 118 | var buf bytes.Buffer 119 | dumper := Dumper(&buf) 120 | dumper.Write(data) 121 | dumper.Close() 122 | return buf.String() 123 | } 124 | 125 | // bufferSize is the number of hexadecimal characters to buffer in encoder and decoder. 126 | const bufferSize = 1024 127 | 128 | type encoder struct { 129 | w io.Writer 130 | err error 131 | out [bufferSize]byte // output buffer 132 | } 133 | 134 | // NewEncoder returns an io.Writer that writes lowercase hexadecimal characters to w. 135 | func NewEncoder(w io.Writer) io.Writer { 136 | return &encoder{w: w} 137 | } 138 | 139 | func (e *encoder) Write(p []byte) (n int, err error) { 140 | for len(p) > 0 && e.err == nil { 141 | chunkSize := bufferSize / 2 142 | if len(p) < chunkSize { 143 | chunkSize = len(p) 144 | } 145 | 146 | var written int 147 | encoded := Encode(e.out[:], p[:chunkSize]) 148 | written, e.err = e.w.Write(e.out[:encoded]) 149 | n += written / 2 150 | p = p[chunkSize:] 151 | } 152 | return n, e.err 153 | } 154 | 155 | type decoder struct { 156 | r io.Reader 157 | err error 158 | in []byte // input buffer (encoded form) 159 | arr [bufferSize]byte // backing array for in 160 | } 161 | 162 | // NewDecoder returns an io.Reader that decodes hexadecimal characters from r. 163 | // NewDecoder expects that r contain only an even number of hexadecimal characters. 164 | func NewDecoder(r io.Reader) io.Reader { 165 | return &decoder{r: r} 166 | } 167 | 168 | func (d *decoder) Read(p []byte) (n int, err error) { 169 | // Fill internal buffer with sufficient bytes to decode 170 | if len(d.in) < 2 && d.err == nil { 171 | var numCopy, numRead int 172 | numCopy = copy(d.arr[:], d.in) // Copies either 0 or 1 bytes 173 | numRead, d.err = d.r.Read(d.arr[numCopy:]) 174 | d.in = d.arr[:numCopy+numRead] 175 | if d.err == io.EOF && len(d.in)%2 != 0 { 176 | if _, ok := fromHexChar(d.in[len(d.in)-1]); !ok { 177 | d.err = InvalidByteError(d.in[len(d.in)-1]) 178 | } else { 179 | d.err = io.ErrUnexpectedEOF 180 | } 181 | } 182 | } 183 | 184 | // Decode internal buffer into output buffer 185 | if numAvail := len(d.in) / 2; len(p) > numAvail { 186 | p = p[:numAvail] 187 | } 188 | numDec, err := Decode(p, d.in[:len(p)*2]) 189 | d.in = d.in[2*numDec:] 190 | if err != nil { 191 | d.in, d.err = nil, err // Decode error; discard input remainder 192 | } 193 | 194 | if len(d.in) < 2 { 195 | return numDec, d.err // Only expose errors when buffer fully consumed 196 | } 197 | return numDec, nil 198 | } 199 | 200 | // Dumper returns a WriteCloser that writes a hex dump of all written data to 201 | // w. The format of the dump matches the output of `hexdump -C` on the command 202 | // line. 203 | func Dumper(w io.Writer) io.WriteCloser { 204 | return &dumper{w: w} 205 | } 206 | 207 | type dumper struct { 208 | w io.Writer 209 | rightChars [18]byte 210 | buf [14]byte 211 | used int // number of bytes in the current line 212 | n uint // number of bytes, total 213 | closed bool 214 | } 215 | 216 | func toChar(b byte) byte { 217 | if b < 32 || b > 126 { 218 | return '.' 219 | } 220 | return b 221 | } 222 | 223 | func (h *dumper) Write(data []byte) (n int, err error) { 224 | if h.closed { 225 | return 0, fmt.Errorf("encoding/hex: dumper closed") 226 | } 227 | 228 | // Output lines look like: 229 | // 00000010 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d |./0123456789:;<=| 230 | // ^ offset ^ extra space ^ ASCII of line. 231 | for i := range data { 232 | if h.used == 0 { 233 | // At the beginning of a line we print the current 234 | // offset in hex. 235 | h.buf[0] = byte(h.n >> 24) 236 | h.buf[1] = byte(h.n >> 16) 237 | h.buf[2] = byte(h.n >> 8) 238 | h.buf[3] = byte(h.n) 239 | Encode(h.buf[4:], h.buf[:4]) 240 | h.buf[12] = ' ' 241 | h.buf[13] = ' ' 242 | _, err = h.w.Write(h.buf[4:]) 243 | if err != nil { 244 | return 245 | } 246 | } 247 | Encode(h.buf[:], data[i:i+1]) 248 | h.buf[2] = ' ' 249 | l := 3 250 | if h.used == 7 { 251 | // There's an additional space after the 8th byte. 252 | h.buf[3] = ' ' 253 | l = 4 254 | } else if h.used == 15 { 255 | // At the end of the line there's an extra space and 256 | // the bar for the right column. 257 | h.buf[3] = ' ' 258 | h.buf[4] = '|' 259 | l = 5 260 | } 261 | _, err = h.w.Write(h.buf[:l]) 262 | if err != nil { 263 | return 264 | } 265 | n++ 266 | h.rightChars[h.used] = toChar(data[i]) 267 | h.used++ 268 | h.n++ 269 | if h.used == 16 { 270 | h.rightChars[16] = '|' 271 | h.rightChars[17] = '\n' 272 | _, err = h.w.Write(h.rightChars[:]) 273 | if err != nil { 274 | return 275 | } 276 | h.used = 0 277 | } 278 | } 279 | return 280 | } 281 | 282 | func (h *dumper) Close() (err error) { 283 | // See the comments in Write() for the details of this format. 284 | if h.closed { 285 | return 286 | } 287 | h.closed = true 288 | if h.used == 0 { 289 | return 290 | } 291 | h.buf[0] = ' ' 292 | h.buf[1] = ' ' 293 | h.buf[2] = ' ' 294 | h.buf[3] = ' ' 295 | h.buf[4] = '|' 296 | nBytes := h.used 297 | for h.used < 16 { 298 | l := 3 299 | if h.used == 7 { 300 | l = 4 301 | } else if h.used == 15 { 302 | l = 5 303 | } 304 | _, err = h.w.Write(h.buf[:l]) 305 | if err != nil { 306 | return 307 | } 308 | h.used++ 309 | } 310 | h.rightChars[nBytes] = '|' 311 | h.rightChars[nBytes+1] = '\n' 312 | _, err = h.w.Write(h.rightChars[:nBytes+2]) 313 | return 314 | } 315 | -------------------------------------------------------------------------------- /file/resolver/hex_gzip.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "strings" 10 | "sync" 11 | 12 | "github.com/gobuffalo/packr/v2/file/resolver/encoding/hex" 13 | "github.com/gobuffalo/packr/v2/plog" 14 | 15 | "github.com/gobuffalo/packr/v2/file" 16 | ) 17 | 18 | var _ Resolver = &HexGzip{} 19 | 20 | type HexGzip struct { 21 | packed map[string]string 22 | unpacked map[string]string 23 | moot *sync.RWMutex 24 | } 25 | 26 | func (hg HexGzip) String() string { 27 | return String(&hg) 28 | } 29 | 30 | var _ file.FileMappable = &HexGzip{} 31 | 32 | func (hg *HexGzip) FileMap() map[string]file.File { 33 | hg.moot.RLock() 34 | var names []string 35 | for k := range hg.packed { 36 | names = append(names, k) 37 | } 38 | hg.moot.RUnlock() 39 | m := map[string]file.File{} 40 | for _, n := range names { 41 | if f, err := hg.Resolve("", n); err == nil { 42 | m[n] = f 43 | } 44 | } 45 | return m 46 | } 47 | 48 | func (hg *HexGzip) Resolve(box string, name string) (file.File, error) { 49 | plog.Debug(hg, "Resolve", "box", box, "name", name) 50 | hg.moot.Lock() 51 | defer hg.moot.Unlock() 52 | 53 | if s, ok := hg.unpacked[name]; ok { 54 | return file.NewFile(name, []byte(s)) 55 | } 56 | packed, ok := hg.packed[name] 57 | if !ok { 58 | return nil, os.ErrNotExist 59 | } 60 | 61 | unpacked, err := UnHexGzipString(packed) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | f, err := file.NewFile(OsPath(name), []byte(unpacked)) 67 | if err != nil { 68 | return nil, err 69 | } 70 | hg.unpacked[name] = f.String() 71 | return f, nil 72 | } 73 | 74 | func NewHexGzip(files map[string]string) (*HexGzip, error) { 75 | if files == nil { 76 | files = map[string]string{} 77 | } 78 | 79 | hg := &HexGzip{ 80 | packed: files, 81 | unpacked: map[string]string{}, 82 | moot: &sync.RWMutex{}, 83 | } 84 | 85 | return hg, nil 86 | } 87 | 88 | func HexGzipString(s string) (string, error) { 89 | bb := &bytes.Buffer{} 90 | enc := hex.NewEncoder(bb) 91 | zw := gzip.NewWriter(enc) 92 | io.Copy(zw, strings.NewReader(s)) 93 | zw.Close() 94 | 95 | return bb.String(), nil 96 | } 97 | 98 | func UnHexGzipString(packed string) (string, error) { 99 | br := bytes.NewBufferString(packed) 100 | dec := hex.NewDecoder(br) 101 | zr, err := gzip.NewReader(dec) 102 | if err != nil { 103 | return "", err 104 | } 105 | defer zr.Close() 106 | 107 | b, err := ioutil.ReadAll(zr) 108 | if err != nil { 109 | return "", err 110 | } 111 | return string(b), nil 112 | } 113 | -------------------------------------------------------------------------------- /file/resolver/hex_gzip_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "io/ioutil" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func Test_HexGzip_Find(t *testing.T) { 12 | r := require.New(t) 13 | 14 | x, err := HexGzipString("foo!") 15 | r.NoError(err) 16 | files := map[string]string{ 17 | "foo.txt": x, 18 | } 19 | d, err := NewHexGzip(files) 20 | r.NoError(err) 21 | 22 | f, err := d.Resolve("", "foo.txt") 23 | r.NoError(err) 24 | 25 | fi, err := f.FileInfo() 26 | r.NoError(err) 27 | r.Equal("foo.txt", fi.Name()) 28 | 29 | b, err := ioutil.ReadAll(f) 30 | r.NoError(err) 31 | r.Equal("foo!", strings.TrimSpace(string(b))) 32 | } 33 | 34 | func Test_fo(t *testing.T) { 35 | r := require.New(t) 36 | x := "1f8b08000000000000ff9c575f6fe3b8117fd7a798ea490e6c0a7d2b5ce4806c76b3b9c29718916f53e0b04818696cb34b715892b2e306feeec5905262a7ebddb6010249e4f037bff9c399b195f537b942907550647c96a9d6920b506400f94a8575f7246a6acb153d75cba5d454a2d9ecf293bbfd930596e46af45e9f906cb7934182a5ad74b2d5b45aa13b7de040e83d85ce38d21a9bd263dd39ccb30ce0e505d41204d9e0c5bd0aeb3959d8eff9e0cb4b5afd8d1ad47efe6db5dfffd4a689255bf6ffed364ff8681a98ecf7efb55df87b7c8a1b00b577cbd326f12e63a93fffc59c96e2ddd3043988ee67842ee6bff6840e419c2f6b72fe34f673e9d17bce0d96a9c9043421ec2cfec0a437a177a446595696f0e9e60b280f9dc70602c11ab505bf55a15e83c7109459797892bc4b06b66b7408618d7c525aab552d395519e109955981eb8c808fb8949d0ebc9a37b8414db6451372916da48b1acf8133577cc650e49f6f1f3edd7cc9c7c7b2a3282bad85b3de1a71616df6fdb8b2e802ce383062e1a4f15a0672596f2c07a02ce1c25a66946c905a83a32ea007691a6855d368dc4a877c53a0d7c8a7fc9a3addc01342834b65b011b0582bcf48618d60d06d106a34011dd01276d4b977ce115959f2d21d75eccef181b231ac1c75d68f01432d840026d060ada5c30616b77398fc021f6fef6f1823e96d511acf3ed85107b269401e920f048fd2da473893cb80eeac07e3c8488688eac610d632a477d82aade1e676016bb9c1b461707b80c9f62278d9c6a0b3d9ae4336d5a1a7ced503e168ab07e9618b5a0f46ff1a406a4f3ded083fb8dd21d46bacbf6103ca4467926bd0f1dbeec811d1f8c78a3d7da534fa476621e1f26271793db998cd12e2183c810a43c0a4deca1d67251fb65ad6d880963e0cbae29923ea63e6ae42f28875b8413390658cd7b044cfb2602030b841c7c9514bae77225b76a6e64c2b4647790b2f5c5596319fcfcfc1281d57202d0cf9266e705b0cefb736b24a62009fcc66ca5767dc7f9faa27fc57a5225105723885a164889b4eeb97fd003077782f1dfa29fcf175d0d9af0d3ab9c4382ffaeb5c8cc4b5348d463740ec0fc80c55e588c18d6c710af9c350e1f95b700cf7fb879e569e30f6a32c3ef99e76815a1914bb74070e1be5b08edeaeaad9e034f1bbc722b6acaa9a15a3b7d3335a81c37f76e8436a6318d07928964ac717be98bb9138c239e864623e9c99c5ef1ef8b4b7cb122a0c29a57ab57dc985589803c1dfaadb9b237d073559545c020faa45f90f4f261f8db203bfa60e72baa94516734781fd245752191fe0b2babb021982acbf7901eb10ac9f96e576bb15b495de0a72ab5299069f855ddbf2d291f7934a057cb84b663c5c915ba1db3d148c341ad4dc614b9b6856a3bc7cd25c33943ff627b751cee59f1a71340744f87b27ad0794f5fad59dca8084c0553dcd456290855a7c91bac3220fcff948146796acb8246330cafd4f94e318917a47d252b4711a111f3ffc1fb1a83074363696ce63e2ae5391991e693ddc29beaf87453f7f5a1479998fe19a5aecefe07713f390061f7cab99e9bcf41e83ff40cf2366e963ef5af2362c1db531896df7a4550de9d291db65ff71bf233187a173869564fbec44572ecb23d35361d5241bd0544bddab1ef39c019d8ddac36bf386c77e0a168bc771eca8a6490883ea777d2f7a9a123e43d5e462dd1874918b69c5507d66c5bec2eb2d39046596e4da4875fa7a5d5e4729a178d82e1baa7d1911d5bfa2682af8c7717c2de7bfbd12bc62292eab3ca9a08bffe4524f588ce3ca799c3a630b882324bf7da0e722172269449f8fc690a399fc5ee5a3bfc6437f7ad74b4415c816e8dc2806ea354e8b032ec528dbbf1b8d8642fa030773e7ee77fb822c0d2853531bc7bee4d138212cb9372a0f86025c2f16f34a40cefe9c96253ecbd66ae4293587f35fd2ba7fb77130ebbce96f083d9c9d190a67678026dee2aa9a89183f1eba8e262e581034144933d8966b734d6d8bf146c66908aca3e7dd4f221d65fe8b2c39f9cba74cf9f1d6a87e941bbddf871f6287214b68c7334155cdeefa584cd388c083f539e4d651d3c5cbd337d7aa9acdd9926b940d3a3f8556da3f7ce0b1f06b7abce47f9f5c91db4ad76033e15642f9b40f4f1edbfc9eb3e6df010000ffffa0d4ca87a10e0000" 37 | y, err := UnHexGzipString(x) 38 | r.NoError(err) 39 | r.NotEqual("", y) 40 | } 41 | -------------------------------------------------------------------------------- /file/resolver/ident.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | "strings" 7 | ) 8 | 9 | func Key(s string) string { 10 | s = strings.Replace(s, "\\", "/", -1) 11 | return s 12 | } 13 | 14 | func OsPath(s string) string { 15 | if runtime.GOOS == "windows" { 16 | s = strings.Replace(s, "/", string(filepath.Separator), -1) 17 | } else { 18 | s = strings.Replace(s, "\\", string(filepath.Separator), -1) 19 | } 20 | return s 21 | } 22 | -------------------------------------------------------------------------------- /file/resolver/ident_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_Ident_OsPath(t *testing.T) { 11 | table := map[string]string{ 12 | "foo/bar/baz": "foo/bar/baz", 13 | "foo\\bar\\baz": "foo/bar/baz", 14 | } 15 | 16 | if runtime.GOOS == "windows" { 17 | table = ident_OsPath_Windows_Table() 18 | } 19 | 20 | for in, out := range table { 21 | t.Run(in, func(st *testing.T) { 22 | r := require.New(st) 23 | r.Equal(out, OsPath(in)) 24 | }) 25 | } 26 | } 27 | 28 | func ident_OsPath_Windows_Table() map[string]string { 29 | return map[string]string{ 30 | "foo/bar/baz": "foo\\bar\\baz", 31 | "foo\\bar\\baz": "foo\\bar\\baz", 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /file/resolver/in_memory.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/gobuffalo/packd" 7 | "github.com/gobuffalo/packr/v2/file" 8 | "github.com/gobuffalo/packr/v2/plog" 9 | ) 10 | 11 | var _ Resolver = &InMemory{} 12 | 13 | type InMemory struct { 14 | *packd.MemoryBox 15 | } 16 | 17 | func (d InMemory) String() string { 18 | return String(&d) 19 | } 20 | 21 | func (d *InMemory) Resolve(box string, name string) (file.File, error) { 22 | b, err := d.MemoryBox.Find(name) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return file.NewFile(name, b) 27 | } 28 | 29 | func (d *InMemory) Pack(name string, f file.File) error { 30 | plog.Debug(d, "Pack", "name", name) 31 | b, err := ioutil.ReadAll(f) 32 | if err != nil { 33 | return err 34 | } 35 | d.AddBytes(name, b) 36 | return nil 37 | } 38 | 39 | func (d *InMemory) FileMap() map[string]file.File { 40 | m := map[string]file.File{} 41 | d.Walk(func(path string, file file.File) error { 42 | m[path] = file 43 | return nil 44 | }) 45 | return m 46 | } 47 | 48 | func NewInMemory(files map[string]file.File) *InMemory { 49 | if files == nil { 50 | files = map[string]file.File{} 51 | } 52 | box := packd.NewMemoryBox() 53 | 54 | for p, f := range files { 55 | if b, err := ioutil.ReadAll(f); err == nil { 56 | box.AddBytes(p, b) 57 | } 58 | } 59 | 60 | return &InMemory{ 61 | MemoryBox: box, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /file/resolver/in_memory_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "io/ioutil" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/gobuffalo/packr/v2/file" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func Test_inMemory_Find(t *testing.T) { 13 | r := require.New(t) 14 | 15 | files := map[string]file.File{ 16 | "foo.txt": qfile("foo.txt", "foo!"), 17 | } 18 | d := NewInMemory(files) 19 | 20 | f, err := d.Resolve("", "foo.txt") 21 | r.NoError(err) 22 | 23 | fi, err := f.FileInfo() 24 | r.NoError(err) 25 | r.Equal("foo.txt", fi.Name()) 26 | 27 | b, err := ioutil.ReadAll(f) 28 | r.NoError(err) 29 | r.Equal("foo!", strings.TrimSpace(string(b))) 30 | } 31 | -------------------------------------------------------------------------------- /file/resolver/packable.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import "github.com/gobuffalo/packr/v2/file" 4 | 5 | type Packable interface { 6 | Pack(name string, f file.File) error 7 | } 8 | -------------------------------------------------------------------------------- /file/resolver/resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/gobuffalo/packr/v2/file" 9 | ) 10 | 11 | type Resolver interface { 12 | Resolve(string, string) (file.File, error) 13 | } 14 | 15 | func defaultResolver() Resolver { 16 | pwd, _ := os.Getwd() 17 | return &Disk{ 18 | Root: pwd, 19 | } 20 | } 21 | 22 | var DefaultResolver = defaultResolver() 23 | 24 | func String(r Resolver) string { 25 | m := map[string]interface{}{ 26 | "name": fmt.Sprintf("%T", r), 27 | } 28 | if fm, ok := r.(file.FileMappable); ok { 29 | m["files"] = fm 30 | } 31 | b, _ := json.Marshal(m) 32 | return string(b) 33 | } 34 | -------------------------------------------------------------------------------- /file/resolver/resolver_test.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import "github.com/gobuffalo/packr/v2/file" 4 | 5 | func qfile(name string, body string) file.File { 6 | f, err := file.NewFile(name, []byte(body)) 7 | if err != nil { 8 | panic(err) 9 | } 10 | return f 11 | } 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gobuffalo/packr/v2 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gobuffalo/logger v1.0.6 7 | github.com/gobuffalo/packd v1.0.1 8 | github.com/karrick/godirwalk v1.16.1 9 | github.com/markbates/errx v1.1.0 10 | github.com/markbates/oncer v1.0.0 11 | github.com/markbates/safe v1.0.1 12 | github.com/rogpeppe/go-internal v1.8.0 13 | github.com/sirupsen/logrus v1.8.1 14 | github.com/spf13/cobra v1.2.1 15 | github.com/stretchr/testify v1.7.0 16 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 17 | golang.org/x/tools v0.1.7 18 | ) 19 | -------------------------------------------------------------------------------- /helpers.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "runtime" 7 | "strings" 8 | 9 | "github.com/gobuffalo/packr/v2/plog" 10 | ) 11 | 12 | func construct(name string, path string) *Box { 13 | return &Box{ 14 | Path: path, 15 | Name: name, 16 | ResolutionDir: resolutionDir(path), 17 | resolvers: resolversMap{}, 18 | dirs: dirsMap{}, 19 | } 20 | } 21 | 22 | func resolutionDirTestFilename(filename, og string) (string, bool) { 23 | ng := filepath.Join(filepath.Dir(filename), og) 24 | 25 | // // this little hack courtesy of the `-cover` flag!! 26 | cov := filepath.Join("_test", "_obj_test") 27 | ng = strings.Replace(ng, string(filepath.Separator)+cov, "", 1) 28 | 29 | if resolutionDirExists(ng, og) { 30 | return ng, true 31 | } 32 | 33 | ng = filepath.Join(os.Getenv("GOPATH"), "src", ng) 34 | if resolutionDirExists(ng, og) { 35 | return ng, true 36 | } 37 | 38 | return og, false 39 | } 40 | 41 | func resolutionDirExists(s, og string) bool { 42 | _, err := os.Stat(s) 43 | if err != nil { 44 | return false 45 | } 46 | plog.Debug("packr", "resolutionDir", "original", og, "resolved", s) 47 | return true 48 | } 49 | 50 | func resolutionDir(og string) string { 51 | ng, _ := filepath.Abs(og) 52 | 53 | if resolutionDirExists(ng, og) { 54 | return ng 55 | } 56 | 57 | // packr.New 58 | _, filename, _, _ := runtime.Caller(3) 59 | ng, ok := resolutionDirTestFilename(filename, og) 60 | if ok { 61 | return ng 62 | } 63 | 64 | // packr.NewBox (deprecated) 65 | _, filename, _, _ = runtime.Caller(4) 66 | ng, ok = resolutionDirTestFilename(filename, og) 67 | if ok { 68 | return ng 69 | } 70 | 71 | return og 72 | } 73 | -------------------------------------------------------------------------------- /http_box_test.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/gobuffalo/packd" 10 | "github.com/gobuffalo/packr/v2/file/resolver" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | var httpBox = func() packd.Box { 15 | box := New("http box", "") 16 | 17 | ind, err := resolver.HexGzipString("

Index!

") 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | hello, err := resolver.HexGzipString("hello world!") 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | hg, err := resolver.NewHexGzip(map[string]string{ 28 | "index.html": ind, 29 | "hello.txt": hello, 30 | }) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | box.DefaultResolver = hg 36 | return box 37 | }() 38 | 39 | func Test_HTTPBox(t *testing.T) { 40 | r := require.New(t) 41 | 42 | mux := http.NewServeMux() 43 | mux.Handle("/", http.FileServer(httpBox)) 44 | 45 | req, err := http.NewRequest("GET", "/hello.txt", nil) 46 | r.NoError(err) 47 | 48 | res := httptest.NewRecorder() 49 | 50 | mux.ServeHTTP(res, req) 51 | 52 | r.Equal(200, res.Code) 53 | r.Equal("hello world!", strings.TrimSpace(res.Body.String())) 54 | } 55 | 56 | func Test_HTTPBox_NotFound(t *testing.T) { 57 | r := require.New(t) 58 | 59 | mux := http.NewServeMux() 60 | mux.Handle("/", http.FileServer(httpBox)) 61 | 62 | req, err := http.NewRequest("GET", "/notInBox.txt", nil) 63 | r.NoError(err) 64 | 65 | res := httptest.NewRecorder() 66 | 67 | mux.ServeHTTP(res, req) 68 | 69 | r.Equal(404, res.Code) 70 | } 71 | 72 | func Test_HTTPBox_Handles_IndexHTML_Nested(t *testing.T) { 73 | r := require.New(t) 74 | 75 | box := New("Test_HTTPBox_Handles_IndexHTML_Nested", "!") 76 | box.AddString("foo/index.html", "foo") 77 | 78 | mux := http.NewServeMux() 79 | mux.Handle("/", http.FileServer(box)) 80 | 81 | req, err := http.NewRequest("GET", "/foo", nil) 82 | r.NoError(err) 83 | 84 | res := httptest.NewRecorder() 85 | 86 | mux.ServeHTTP(res, req) 87 | 88 | r.Equal(200, res.Code) 89 | 90 | r.Equal("foo", strings.TrimSpace(res.Body.String())) 91 | } 92 | 93 | func Test_HTTPBox_Handles_IndexHTML(t *testing.T) { 94 | r := require.New(t) 95 | 96 | mux := http.NewServeMux() 97 | mux.Handle("/", http.FileServer(httpBox)) 98 | 99 | req, err := http.NewRequest("GET", "/", nil) 100 | r.NoError(err) 101 | 102 | res := httptest.NewRecorder() 103 | 104 | mux.ServeHTTP(res, req) 105 | 106 | r.Equal(200, res.Code) 107 | 108 | r.Equal("

Index!

", strings.TrimSpace(res.Body.String())) 109 | } 110 | 111 | func Test_HTTPBox_CaseInsensitive(t *testing.T) { 112 | mux := http.NewServeMux() 113 | httpBox.AddString("myfile.txt", "this is my file") 114 | mux.Handle("/", http.FileServer(httpBox)) 115 | 116 | for _, path := range []string{"/MyFile.txt", "/myfile.txt", "/Myfile.txt"} { 117 | t.Run(path, func(st *testing.T) { 118 | r := require.New(st) 119 | 120 | req, err := http.NewRequest("GET", path, nil) 121 | r.NoError(err) 122 | 123 | res := httptest.NewRecorder() 124 | 125 | mux.ServeHTTP(res, req) 126 | 127 | r.Equal(200, res.Code) 128 | r.Equal("this is my file", strings.TrimSpace(res.Body.String())) 129 | }) 130 | } 131 | } 132 | 133 | func Test_HTTPBox_Disk(t *testing.T) { 134 | r := require.New(t) 135 | 136 | box := New("http disk box", "./_fixtures/http_test") 137 | mux := http.NewServeMux() 138 | mux.Handle("/", http.FileServer(box)) 139 | 140 | type testcase struct { 141 | URL, Content, Location string 142 | Code int 143 | } 144 | 145 | testcases := []testcase{ 146 | {"/", "Index", "", 200}, 147 | {"/sub", "Sub", "", 200}, 148 | {"/index.html", "", "./", 301}, 149 | {"/sub/index.html", "", "./", 301}, 150 | {"/sub/", "", "../sub", 301}, 151 | {"/footer.html", "Footer", "", 200}, 152 | {"/css/main.css", "Css", "", 200}, 153 | {"/css", "404 page not found", "", 404}, 154 | {"/css/", "404 page not found", "", 404}, 155 | } 156 | 157 | for _, tc := range testcases { 158 | t.Run("path"+tc.URL, func(t *testing.T) { 159 | req, err := http.NewRequest("GET", tc.URL, nil) 160 | r.NoError(err) 161 | res := httptest.NewRecorder() 162 | mux.ServeHTTP(res, req) 163 | 164 | r.Equal(tc.Code, res.Code) 165 | r.Equal(tc.Location, res.Header().Get("location")) 166 | r.Equal(tc.Content, strings.TrimSpace(res.Body.String())) 167 | }) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /jam/pack.go: -------------------------------------------------------------------------------- 1 | package jam 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | "os" 8 | "os/exec" 9 | "time" 10 | 11 | "github.com/gobuffalo/packr/v2/jam/parser" 12 | "github.com/gobuffalo/packr/v2/jam/store" 13 | "github.com/gobuffalo/packr/v2/plog" 14 | ) 15 | 16 | // PackOptions ... 17 | type PackOptions struct { 18 | IgnoreImports bool 19 | Legacy bool 20 | StoreCmd string 21 | Roots []string 22 | RootsOptions *parser.RootsOptions 23 | } 24 | 25 | // Pack the roots given + PWD 26 | func Pack(opts PackOptions) error { 27 | pwd, err := os.Getwd() 28 | if err != nil { 29 | return err 30 | } 31 | 32 | opts.Roots = append(opts.Roots, pwd) 33 | if err := Clean(opts.Roots...); err != nil { 34 | return err 35 | } 36 | 37 | if opts.RootsOptions == nil { 38 | opts.RootsOptions = &parser.RootsOptions{} 39 | } 40 | 41 | if opts.IgnoreImports { 42 | opts.RootsOptions.IgnoreImports = true 43 | } 44 | 45 | p, err := parser.NewFromRoots(opts.Roots, opts.RootsOptions) 46 | if err != nil { 47 | return err 48 | } 49 | boxes, err := p.Run() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | // reduce boxes - remove ones we don't want 55 | // MB: current assumption is we want all these 56 | // boxes, just adding a comment suggesting they're 57 | // might be a reason to exclude some 58 | 59 | plog.Logger.Debugf("found %d boxes", len(boxes)) 60 | 61 | if len(opts.StoreCmd) != 0 { 62 | return ShellPack(opts, boxes) 63 | } 64 | 65 | var st store.Store = store.NewDisk("", "") 66 | 67 | if opts.Legacy { 68 | st = store.NewLegacy() 69 | } 70 | 71 | for _, b := range boxes { 72 | if b.Name == store.DISK_GLOBAL_KEY { 73 | continue 74 | } 75 | if err := st.Pack(b); err != nil { 76 | return err 77 | } 78 | } 79 | if cl, ok := st.(io.Closer); ok { 80 | return cl.Close() 81 | } 82 | return nil 83 | } 84 | 85 | // ShellPack ... 86 | func ShellPack(opts PackOptions, boxes parser.Boxes) error { 87 | b, err := json.Marshal(boxes) 88 | if err != nil { 89 | return err 90 | } 91 | ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 92 | defer cancel() 93 | c := exec.CommandContext(ctx, opts.StoreCmd, string(b)) 94 | c.Stdout = os.Stdout 95 | c.Stderr = os.Stderr 96 | return c.Run() 97 | 98 | } 99 | 100 | // Clean ... 101 | func Clean(args ...string) error { 102 | pwd, err := os.Getwd() 103 | if err != nil { 104 | return err 105 | } 106 | args = append(args, pwd) 107 | for _, root := range args { 108 | if err := store.Clean(root); err != nil { 109 | return err 110 | } 111 | } 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /jam/parser/_fixtures/new_from_roots/_r/r.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("bob", "dylan") 7 | } 8 | -------------------------------------------------------------------------------- /jam/parser/_fixtures/new_from_roots/e/e.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("tom", "petty") 7 | packr.NewBox("./heartbreakers") 8 | } 9 | -------------------------------------------------------------------------------- /jam/parser/_fixtures/new_from_roots/q.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("aretha", "franklin") 7 | } 8 | -------------------------------------------------------------------------------- /jam/parser/_fixtures/new_from_roots/w/w.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /jam/parser/args.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // FromArgs is useful when writing packr store-cmd binaries. 9 | /* 10 | package main 11 | 12 | import ( 13 | "log" 14 | "os" 15 | 16 | "github.com/gobuffalo/packr/v2/jam/parser" 17 | "github.com/markbates/s3packr/s3packr" 18 | ) 19 | 20 | func main() { 21 | err := parser.FromArgs(os.Args[1:], func(boxes parser.Boxes) error { 22 | for _, box := range boxes { 23 | s3 := s3packr.New(box) 24 | if err := s3.Pack(box); err != nil { 25 | return err 26 | } 27 | } 28 | return nil 29 | }) 30 | 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | } 35 | */ 36 | func FromArgs(args []string, fn func(Boxes) error) error { 37 | if len(args) == 0 { 38 | return fmt.Errorf("you must supply a payload") 39 | } 40 | payload := args[0] 41 | if len(payload) == 0 { 42 | return fmt.Errorf("you must supply a payload") 43 | } 44 | 45 | var boxes Boxes 46 | err := json.Unmarshal([]byte(payload), &boxes) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | return fn(boxes) 52 | } 53 | -------------------------------------------------------------------------------- /jam/parser/box.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | // Box found while parsing a file 10 | type Box struct { 11 | Name string // name of the box 12 | Path string // relative path of folder NewBox("./templates") 13 | AbsPath string // absolute path of Path 14 | Package string // the package name the box was found in 15 | PWD string // the PWD when the parser was run 16 | PackageDir string // the absolute path of the package where the box was found 17 | } 18 | 19 | type Boxes []*Box 20 | 21 | // String - json returned 22 | func (b Box) String() string { 23 | x, _ := json.Marshal(b) 24 | return string(x) 25 | } 26 | 27 | // NewBox stub from the name and the path provided 28 | func NewBox(name string, path string) *Box { 29 | if len(name) == 0 { 30 | name = path 31 | } 32 | name = strings.Replace(name, "\"", "", -1) 33 | pwd, _ := os.Getwd() 34 | box := &Box{ 35 | Name: name, 36 | Path: path, 37 | PWD: pwd, 38 | } 39 | return box 40 | } 41 | -------------------------------------------------------------------------------- /jam/parser/file.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | "path/filepath" 8 | ) 9 | 10 | // File that is to be parsed 11 | type File struct { 12 | io.Reader 13 | Path string 14 | AbsPath string 15 | } 16 | 17 | // Name of the file "app.go" 18 | func (f File) Name() string { 19 | return f.Path 20 | } 21 | 22 | // String returns the contents of the reader 23 | func (f *File) String() string { 24 | src, _ := ioutil.ReadAll(f) 25 | f.Reader = bytes.NewReader(src) 26 | return string(src) 27 | } 28 | 29 | func (s *File) Write(p []byte) (int, error) { 30 | bb := &bytes.Buffer{} 31 | i, err := bb.Write(p) 32 | s.Reader = bb 33 | return i, err 34 | } 35 | 36 | // NewFile takes the name of the file you want to 37 | // write to and a reader to reader from 38 | func NewFile(path string, r io.Reader) *File { 39 | if r == nil { 40 | r = &bytes.Buffer{} 41 | } 42 | if seek, ok := r.(io.Seeker); ok { 43 | seek.Seek(0, 0) 44 | } 45 | abs := path 46 | if !filepath.IsAbs(path) { 47 | abs, _ = filepath.Abs(path) 48 | } 49 | return &File{ 50 | Reader: r, 51 | Path: path, 52 | AbsPath: abs, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jam/parser/finder.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | "github.com/gobuffalo/packr/v2/plog" 12 | "github.com/karrick/godirwalk" 13 | "github.com/markbates/errx" 14 | "github.com/markbates/oncer" 15 | ) 16 | 17 | type finder struct { 18 | id time.Time 19 | } 20 | 21 | func (fd *finder) key(m, dir string) string { 22 | return fmt.Sprintf("%s-*parser.finder#%s-%s", fd.id, m, dir) 23 | } 24 | 25 | // findAllGoFiles *.go files for a given diretory 26 | func (fd *finder) findAllGoFiles(dir string) ([]string, error) { 27 | var err error 28 | var names []string 29 | oncer.Do(fd.key("findAllGoFiles", dir), func() { 30 | plog.Debug(fd, "findAllGoFiles", "dir", dir) 31 | 32 | callback := func(path string, do *godirwalk.Dirent) error { 33 | ext := filepath.Ext(path) 34 | if ext != ".go" { 35 | return nil 36 | } 37 | //check if path is a dir 38 | fi, err := os.Stat(path) 39 | if err != nil { 40 | return nil 41 | } 42 | 43 | if fi.IsDir() { 44 | return nil 45 | } 46 | 47 | names = append(names, path) 48 | return nil 49 | } 50 | err = godirwalk.Walk(dir, &godirwalk.Options{ 51 | FollowSymbolicLinks: true, 52 | Callback: callback, 53 | }) 54 | }) 55 | 56 | return names, err 57 | } 58 | 59 | func (fd *finder) findAllGoFilesImports(dir string) ([]string, error) { 60 | var err error 61 | var names []string 62 | oncer.Do(fd.key("findAllGoFilesImports", dir), func() { 63 | ctx := build.Default 64 | 65 | if len(ctx.SrcDirs()) == 0 { 66 | err = fmt.Errorf("no src directories found") 67 | return 68 | } 69 | 70 | pkg, err := ctx.ImportDir(dir, 0) 71 | if strings.HasPrefix(pkg.ImportPath, "github.com/gobuffalo/packr") { 72 | return 73 | } 74 | 75 | if err != nil { 76 | if !strings.Contains(err.Error(), "cannot find package") { 77 | if _, ok := errx.Unwrap(err).(*build.NoGoError); !ok { 78 | err = err 79 | return 80 | } 81 | } 82 | } 83 | 84 | if pkg.Goroot { 85 | return 86 | } 87 | if len(pkg.GoFiles) <= 0 { 88 | return 89 | } 90 | 91 | plog.Debug(fd, "findAllGoFilesImports", "dir", dir) 92 | 93 | names, _ = fd.findAllGoFiles(dir) 94 | for _, n := range pkg.GoFiles { 95 | names = append(names, filepath.Join(pkg.Dir, n)) 96 | } 97 | for _, imp := range pkg.Imports { 98 | if len(ctx.SrcDirs()) == 0 { 99 | continue 100 | } 101 | d := ctx.SrcDirs()[len(ctx.SrcDirs())-1] 102 | ip := filepath.Join(d, imp) 103 | n, err := fd.findAllGoFilesImports(ip) 104 | if err != nil && len(n) != 0 { 105 | names = n 106 | return 107 | } 108 | names = append(names, n...) 109 | } 110 | }) 111 | return names, err 112 | } 113 | -------------------------------------------------------------------------------- /jam/parser/gogen.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "go/ast" 5 | "go/parser" 6 | "go/token" 7 | "io" 8 | "strings" 9 | 10 | "github.com/gobuffalo/packd" 11 | "github.com/markbates/errx" 12 | ) 13 | 14 | // ParsedFile ... 15 | type ParsedFile struct { 16 | File packd.SimpleFile 17 | FileSet *token.FileSet 18 | Ast *ast.File 19 | Lines []string 20 | } 21 | 22 | // ParseFileMode ... 23 | func ParseFileMode(gf packd.SimpleFile, mode parser.Mode) (ParsedFile, error) { 24 | pf := ParsedFile{ 25 | FileSet: token.NewFileSet(), 26 | File: gf, 27 | } 28 | 29 | src := gf.String() 30 | f, err := parser.ParseFile(pf.FileSet, gf.Name(), src, mode) 31 | if err != nil && errx.Unwrap(err) != io.EOF { 32 | return pf, err 33 | } 34 | pf.Ast = f 35 | 36 | pf.Lines = strings.Split(src, "\n") 37 | return pf, nil 38 | } 39 | 40 | // ParseFile ... 41 | func ParseFile(gf packd.SimpleFile) (ParsedFile, error) { 42 | return ParseFileMode(gf, 0) 43 | } 44 | -------------------------------------------------------------------------------- /jam/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "os" 5 | "sort" 6 | "strings" 7 | 8 | "github.com/gobuffalo/packr/v2/plog" 9 | ) 10 | 11 | // Parser to find boxes 12 | type Parser struct { 13 | Prospects []*File // a list of files to check for boxes 14 | IgnoreImports bool 15 | } 16 | 17 | // Run the parser and run any boxes found 18 | func (p *Parser) Run() (Boxes, error) { 19 | var boxes Boxes 20 | for _, pros := range p.Prospects { 21 | plog.Debug(p, "Run", "parsing", pros.Name()) 22 | v := NewVisitor(pros) 23 | pbr, err := v.Run() 24 | if err != nil { 25 | return boxes, err 26 | } 27 | for _, b := range pbr { 28 | plog.Debug(p, "Run", "file", pros.Name(), "box", b.Name) 29 | boxes = append(boxes, b) 30 | } 31 | } 32 | 33 | pwd, _ := os.Getwd() 34 | sort.Slice(boxes, func(a, b int) bool { 35 | b1 := boxes[a] 36 | return !strings.HasPrefix(b1.AbsPath, pwd) 37 | }) 38 | return boxes, nil 39 | } 40 | 41 | // New Parser from a list of File 42 | func New(prospects ...*File) *Parser { 43 | return &Parser{ 44 | Prospects: prospects, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jam/parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser_test 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/gobuffalo/packr/v2/jam/parser" 9 | "github.com/gobuffalo/packr/v2/jam/store" 10 | "github.com/markbates/oncer" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func init() { 15 | parser.DefaultIgnoredFolders = []string{"vendor", ".git", "node_modules", ".idea"} 16 | } 17 | 18 | func Test_Parser_Run(t *testing.T) { 19 | r := require.New(t) 20 | 21 | f1 := parser.NewFile("a/a.x", strings.NewReader(fmt.Sprintf(basicGoTmpl, "a"))) 22 | f2 := parser.NewFile("b/b.x", strings.NewReader(fmt.Sprintf(basicGoTmpl, "b"))) 23 | 24 | p := parser.New(f1, f2) 25 | boxes, err := p.Run() 26 | r.NoError(err) 27 | 28 | r.Len(boxes, 4) 29 | } 30 | 31 | func Test_NewFrom_Roots_Imports(t *testing.T) { 32 | r := require.New(t) 33 | store.Clean("./_fixtures") 34 | p, err := parser.NewFromRoots([]string{"./_fixtures/new_from_roots"}, &parser.RootsOptions{}) 35 | r.NoError(err) 36 | 37 | boxes, err := p.Run() 38 | r.NoError(err) 39 | r.Len(boxes, 3) 40 | } 41 | 42 | func Test_NewFrom_Roots_Disk(t *testing.T) { 43 | r := require.New(t) 44 | oncer.Reset() 45 | store.Clean("./_fixtures") 46 | p, err := parser.NewFromRoots([]string{"./_fixtures/new_from_roots"}, &parser.RootsOptions{ 47 | IgnoreImports: true, 48 | }) 49 | r.NoError(err) 50 | 51 | boxes, err := p.Run() 52 | r.NoError(err) 53 | r.Len(boxes, 3) 54 | } 55 | 56 | const basicGoTmpl = `package %s 57 | 58 | import "github.com/gobuffalo/packr/v2" 59 | 60 | func init() { 61 | packr.New("elvis", "./presley") 62 | packr.NewBox("./buddy-holly") 63 | } 64 | ` 65 | -------------------------------------------------------------------------------- /jam/parser/prospect.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | 8 | "github.com/gobuffalo/packr/v2/file/resolver" 9 | "github.com/gobuffalo/packr/v2/plog" 10 | ) 11 | 12 | var DefaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "_fixtures", "testdata"} 13 | 14 | func IsProspect(path string, ignore ...string) (status bool) { 15 | // plog.Debug("parser", "IsProspect", "path", path, "ignore", ignore) 16 | defer func() { 17 | if status { 18 | plog.Debug("parser", "IsProspect (TRUE)", "path", path, "status", status) 19 | } 20 | }() 21 | if path == "." { 22 | return true 23 | } 24 | 25 | ext := filepath.Ext(path) 26 | dir := filepath.Dir(path) 27 | 28 | fi, _ := os.Stat(path) 29 | if fi != nil { 30 | if fi.IsDir() { 31 | dir = filepath.Base(path) 32 | } else { 33 | if len(ext) > 0 { 34 | dir = filepath.Base(filepath.Dir(path)) 35 | } 36 | } 37 | } 38 | 39 | path = strings.ToLower(path) 40 | dir = strings.ToLower(dir) 41 | 42 | if strings.HasSuffix(path, "-packr.go") { 43 | return false 44 | } 45 | 46 | if strings.HasSuffix(path, "_test.go") { 47 | return false 48 | } 49 | 50 | ignore = append(ignore, DefaultIgnoredFolders...) 51 | for i, x := range ignore { 52 | ignore[i] = strings.TrimSpace(strings.ToLower(x)) 53 | } 54 | 55 | parts := strings.Split(resolver.OsPath(path), string(filepath.Separator)) 56 | if len(parts) == 0 { 57 | return false 58 | } 59 | 60 | for _, i := range ignore { 61 | for _, p := range parts { 62 | if strings.HasPrefix(p, i) { 63 | return false 64 | } 65 | } 66 | } 67 | 68 | un := filepath.Base(path) 69 | if len(ext) != 0 { 70 | un = filepath.Base(filepath.Dir(path)) 71 | } 72 | if strings.HasPrefix(un, "_") { 73 | return false 74 | } 75 | 76 | return ext == ".go" 77 | } 78 | -------------------------------------------------------------------------------- /jam/parser/prospect_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func Test_IsProspect(t *testing.T) { 10 | table := []struct { 11 | path string 12 | pass bool 13 | }{ 14 | {"foo/.git/config", false}, 15 | {"foo/.git/baz.go", false}, 16 | {"a.go", true}, 17 | {".", true}, 18 | {"a/b.go", true}, 19 | {"a/b_test.go", false}, 20 | {"a/b-packr.go", false}, 21 | {"a/vendor/b.go", false}, 22 | {"a/_c/c.go", false}, 23 | {"a/_c/e/fe/f/c.go", false}, 24 | {"a/d/_d.go", false}, 25 | {"a/d/", false}, 26 | } 27 | 28 | for _, tt := range table { 29 | t.Run(tt.path, func(st *testing.T) { 30 | r := require.New(st) 31 | if tt.pass { 32 | r.True(IsProspect(tt.path, ".", "_")) 33 | } else { 34 | r.False(IsProspect(tt.path, ".", "_")) 35 | } 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /jam/parser/roots.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "time" 10 | 11 | "github.com/gobuffalo/packr/v2/plog" 12 | "github.com/karrick/godirwalk" 13 | ) 14 | 15 | type RootsOptions struct { 16 | IgnoreImports bool 17 | Ignores []string 18 | } 19 | 20 | func (r RootsOptions) String() string { 21 | x, _ := json.Marshal(r) 22 | return string(x) 23 | } 24 | 25 | // NewFromRoots scans the file roots provided and returns a 26 | // new Parser containing the prospects 27 | func NewFromRoots(roots []string, opts *RootsOptions) (*Parser, error) { 28 | if opts == nil { 29 | opts = &RootsOptions{} 30 | } 31 | 32 | if len(roots) == 0 { 33 | pwd, _ := os.Getwd() 34 | roots = append(roots, pwd) 35 | } 36 | p := New() 37 | plog.Debug(p, "NewFromRoots", "roots", roots, "options", opts) 38 | callback := func(path string, de *godirwalk.Dirent) error { 39 | if IsProspect(path, opts.Ignores...) { 40 | if de.IsDir() { 41 | return nil 42 | } 43 | roots = append(roots, path) 44 | return nil 45 | } 46 | if de.IsDir() { 47 | return filepath.SkipDir 48 | } 49 | return nil 50 | } 51 | wopts := &godirwalk.Options{ 52 | FollowSymbolicLinks: true, 53 | Callback: callback, 54 | } 55 | for _, root := range roots { 56 | plog.Debug(p, "NewFromRoots", "walking", root) 57 | err := godirwalk.Walk(root, wopts) 58 | if err != nil { 59 | return p, err 60 | } 61 | } 62 | 63 | dd := map[string]string{} 64 | fd := &finder{id: time.Now()} 65 | for _, r := range roots { 66 | var names []string 67 | if opts.IgnoreImports { 68 | names, _ = fd.findAllGoFiles(r) 69 | } else { 70 | names, _ = fd.findAllGoFilesImports(r) 71 | } 72 | for _, n := range names { 73 | if IsProspect(n) { 74 | plog.Debug(p, "NewFromRoots", "mapping", n) 75 | dd[n] = n 76 | } 77 | } 78 | } 79 | for path := range dd { 80 | plog.Debug(p, "NewFromRoots", "reading file", path) 81 | b, err := ioutil.ReadFile(path) 82 | if err != nil { 83 | return nil, err 84 | } 85 | p.Prospects = append(p.Prospects, NewFile(path, bytes.NewReader(b))) 86 | } 87 | plog.Debug(p, "NewFromRoots", "found prospects", len(p.Prospects)) 88 | return p, nil 89 | } 90 | -------------------------------------------------------------------------------- /jam/parser/visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "os" 7 | "path/filepath" 8 | "sort" 9 | "strings" 10 | 11 | "github.com/gobuffalo/packd" 12 | ) 13 | 14 | type Visitor struct { 15 | File packd.SimpleFile 16 | Package string 17 | boxes map[string]*Box 18 | errors []error 19 | } 20 | 21 | func NewVisitor(f *File) *Visitor { 22 | return &Visitor{ 23 | File: f, 24 | boxes: map[string]*Box{}, 25 | errors: []error{}, 26 | } 27 | } 28 | 29 | func (v *Visitor) Run() (Boxes, error) { 30 | var boxes Boxes 31 | pf, err := ParseFile(v.File) 32 | if err != nil { 33 | return boxes, err 34 | } 35 | 36 | v.Package = pf.Ast.Name.Name 37 | ast.Walk(v, pf.Ast) 38 | 39 | for _, vb := range v.boxes { 40 | boxes = append(boxes, vb) 41 | } 42 | 43 | sort.Slice(boxes, func(i, j int) bool { 44 | return boxes[i].Name < boxes[j].Name 45 | }) 46 | 47 | if len(v.errors) > 0 { 48 | s := make([]string, len(v.errors)) 49 | for i, e := range v.errors { 50 | s[i] = e.Error() 51 | } 52 | return boxes, err 53 | } 54 | return boxes, nil 55 | } 56 | 57 | func (v *Visitor) Visit(node ast.Node) ast.Visitor { 58 | if node == nil { 59 | return v 60 | } 61 | if err := v.eval(node); err != nil { 62 | v.errors = append(v.errors, err) 63 | } 64 | 65 | return v 66 | } 67 | 68 | func (v *Visitor) eval(node ast.Node) error { 69 | switch t := node.(type) { 70 | case *ast.CallExpr: 71 | return v.evalExpr(t) 72 | case *ast.Ident: 73 | return v.evalIdent(t) 74 | case *ast.GenDecl: 75 | for _, n := range t.Specs { 76 | if err := v.eval(n); err != nil { 77 | return err 78 | } 79 | } 80 | case *ast.FuncDecl: 81 | if t.Body == nil { 82 | return nil 83 | } 84 | for _, b := range t.Body.List { 85 | if err := v.evalStmt(b); err != nil { 86 | return err 87 | } 88 | } 89 | return nil 90 | case *ast.ValueSpec: 91 | for _, e := range t.Values { 92 | if err := v.evalExpr(e); err != nil { 93 | return err 94 | } 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | func (v *Visitor) evalStmt(stmt ast.Stmt) error { 101 | switch t := stmt.(type) { 102 | case *ast.ExprStmt: 103 | return v.evalExpr(t.X) 104 | case *ast.AssignStmt: 105 | for _, e := range t.Rhs { 106 | if err := v.evalArgs(e); err != nil { 107 | return err 108 | } 109 | } 110 | } 111 | return nil 112 | } 113 | 114 | func (v *Visitor) evalExpr(expr ast.Expr) error { 115 | switch t := expr.(type) { 116 | case *ast.CallExpr: 117 | if t.Fun == nil { 118 | return nil 119 | } 120 | for _, a := range t.Args { 121 | switch at := a.(type) { 122 | case *ast.CallExpr: 123 | if sel, ok := t.Fun.(*ast.SelectorExpr); ok { 124 | return v.evalSelector(at, sel) 125 | } 126 | 127 | if err := v.evalArgs(at); err != nil { 128 | return err 129 | } 130 | case *ast.CompositeLit: 131 | for _, e := range at.Elts { 132 | if err := v.evalExpr(e); err != nil { 133 | return err 134 | } 135 | } 136 | } 137 | } 138 | if ft, ok := t.Fun.(*ast.SelectorExpr); ok { 139 | return v.evalSelector(t, ft) 140 | } 141 | case *ast.KeyValueExpr: 142 | return v.evalExpr(t.Value) 143 | } 144 | return nil 145 | } 146 | 147 | func (v *Visitor) evalArgs(expr ast.Expr) error { 148 | switch at := expr.(type) { 149 | case *ast.CompositeLit: 150 | for _, e := range at.Elts { 151 | if err := v.evalExpr(e); err != nil { 152 | return err 153 | } 154 | } 155 | case *ast.CallExpr: 156 | if at.Fun == nil { 157 | return nil 158 | } 159 | switch st := at.Fun.(type) { 160 | case *ast.SelectorExpr: 161 | if err := v.evalSelector(at, st); err != nil { 162 | return err 163 | } 164 | case *ast.Ident: 165 | return v.evalIdent(st) 166 | } 167 | for _, a := range at.Args { 168 | if err := v.evalArgs(a); err != nil { 169 | return err 170 | } 171 | } 172 | } 173 | return nil 174 | } 175 | 176 | func (v *Visitor) evalSelector(expr *ast.CallExpr, sel *ast.SelectorExpr) error { 177 | x, ok := sel.X.(*ast.Ident) 178 | if !ok { 179 | return nil 180 | } 181 | if x.Name == "packr" { 182 | switch sel.Sel.Name { 183 | case "New": 184 | if len(expr.Args) != 2 { 185 | return fmt.Errorf("`New` requires two arguments") 186 | } 187 | 188 | zz := func(e ast.Expr) (string, error) { 189 | switch at := e.(type) { 190 | case *ast.Ident: 191 | switch at.Obj.Kind { 192 | case ast.Var: 193 | if as, ok := at.Obj.Decl.(*ast.AssignStmt); ok { 194 | return v.fromVariable(as) 195 | } 196 | case ast.Con: 197 | if vs, ok := at.Obj.Decl.(*ast.ValueSpec); ok { 198 | return v.fromConstant(vs) 199 | } 200 | } 201 | return "", v.evalIdent(at) 202 | case *ast.BasicLit: 203 | return at.Value, nil 204 | case *ast.CallExpr: 205 | return "", v.evalExpr(at) 206 | } 207 | return "", fmt.Errorf("can't handle %T", e) 208 | } 209 | 210 | k1, err := zz(expr.Args[0]) 211 | if err != nil { 212 | return err 213 | } 214 | k2, err := zz(expr.Args[1]) 215 | if err != nil { 216 | return err 217 | } 218 | v.addBox(k1, k2) 219 | 220 | return nil 221 | case "NewBox": 222 | for _, e := range expr.Args { 223 | switch at := e.(type) { 224 | case *ast.Ident: 225 | switch at.Obj.Kind { 226 | case ast.Var: 227 | if as, ok := at.Obj.Decl.(*ast.AssignStmt); ok { 228 | v.addVariable("", as) 229 | } 230 | case ast.Con: 231 | if vs, ok := at.Obj.Decl.(*ast.ValueSpec); ok { 232 | v.addConstant("", vs) 233 | } 234 | } 235 | return v.evalIdent(at) 236 | case *ast.BasicLit: 237 | v.addBox("", at.Value) 238 | case *ast.CallExpr: 239 | return v.evalExpr(at) 240 | } 241 | } 242 | } 243 | } 244 | 245 | return nil 246 | } 247 | 248 | func (v *Visitor) evalIdent(i *ast.Ident) error { 249 | if i.Obj == nil { 250 | return nil 251 | } 252 | if s, ok := i.Obj.Decl.(*ast.AssignStmt); ok { 253 | return v.evalStmt(s) 254 | } 255 | return nil 256 | } 257 | 258 | func (v *Visitor) addBox(name string, path string) { 259 | if len(name) == 0 { 260 | name = path 261 | } 262 | name = strings.Replace(name, "\"", "", -1) 263 | path = strings.Replace(path, "\"", "", -1) 264 | abs := path 265 | if _, ok := v.boxes[name]; !ok { 266 | box := NewBox(name, path) 267 | box.Package = v.Package 268 | 269 | pd := filepath.Dir(v.File.Name()) 270 | pwd, _ := os.Getwd() 271 | if !filepath.IsAbs(pd) { 272 | pd = filepath.Join(pwd, pd) 273 | } 274 | box.PackageDir = pd 275 | 276 | if !filepath.IsAbs(abs) { 277 | abs = filepath.Join(pd, abs) 278 | } 279 | box.AbsPath = abs 280 | v.boxes[name] = box 281 | } 282 | } 283 | func (v *Visitor) fromVariable(as *ast.AssignStmt) (string, error) { 284 | if len(as.Rhs) == 1 { 285 | if bs, ok := as.Rhs[0].(*ast.BasicLit); ok { 286 | return bs.Value, nil 287 | } 288 | } 289 | return "", fmt.Errorf("unable to find value from variable %v", as) 290 | } 291 | 292 | func (v *Visitor) addVariable(bn string, as *ast.AssignStmt) error { 293 | bv, err := v.fromVariable(as) 294 | if err != nil { 295 | return nil 296 | } 297 | if len(bn) == 0 { 298 | bn = bv 299 | } 300 | v.addBox(bn, bv) 301 | return nil 302 | } 303 | 304 | func (v *Visitor) fromConstant(vs *ast.ValueSpec) (string, error) { 305 | if len(vs.Values) == 1 { 306 | if bs, ok := vs.Values[0].(*ast.BasicLit); ok { 307 | return bs.Value, nil 308 | } 309 | } 310 | return "", fmt.Errorf("unable to find value from constant %v", vs) 311 | } 312 | 313 | func (v *Visitor) addConstant(bn string, vs *ast.ValueSpec) error { 314 | if len(vs.Values) == 1 { 315 | if bs, ok := vs.Values[0].(*ast.BasicLit); ok { 316 | bv := bs.Value 317 | if len(bn) == 0 { 318 | bn = bv 319 | } 320 | v.addBox(bn, bv) 321 | } 322 | } 323 | return nil 324 | } 325 | -------------------------------------------------------------------------------- /jam/parser/visitor_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_Visitor(t *testing.T) { 11 | r := require.New(t) 12 | v := NewVisitor(NewFile("example/example.go", strings.NewReader(example))) 13 | 14 | boxes, err := v.Run() 15 | r.NoError(err) 16 | 17 | r.Equal("example", v.Package) 18 | r.Len(v.errors, 0) 19 | 20 | var act []string 21 | for _, b := range boxes { 22 | act = append(act, b.Name) 23 | } 24 | 25 | exp := []string{"./assets", "./bar", "./constant", "./foo", "./sf", "./templates", "./variable", "beatles"} 26 | r.Len(act, len(exp)) 27 | r.Equal(exp, act) 28 | } 29 | 30 | const example = `package example 31 | 32 | import ( 33 | "github.com/gobuffalo/packr/v2" 34 | ) 35 | 36 | var a = packr.NewBox("./foo") 37 | var pw = packr.New("beatles", "./paperback-writer") 38 | 39 | const constString = "./constant" 40 | 41 | type S struct{} 42 | 43 | func (S) f(packr.Box) {} 44 | 45 | func init() { 46 | // packr.NewBox("../idontexists") 47 | 48 | b := "./variable" 49 | packr.NewBox(b) 50 | 51 | packr.New("beatles", "./day-tripper") 52 | 53 | packr.NewBox(constString) 54 | 55 | // Cannot work from a function 56 | packr.NewBox(strFromFunc()) 57 | 58 | // This variable should not be added 59 | fromFunc := strFromFunc() 60 | packr.NewBox(fromFunc) 61 | 62 | foo("/templates", packr.NewBox("./templates")) 63 | packr.NewBox("./assets") 64 | 65 | packr.NewBox("./bar") 66 | 67 | s := S{} 68 | s.f(packr.NewBox("./sf")) 69 | } 70 | 71 | func strFromFunc() string { 72 | return "./fromFunc" 73 | } 74 | 75 | func foo(s string, box packr.Box) {} 76 | ` 77 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/a/a.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("a-box", "../c") 7 | } 8 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/b/b.go: -------------------------------------------------------------------------------- 1 | package b 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("b-box", "../c") 7 | packr.New("cb-box", "../c") 8 | } 9 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/c/d.txt: -------------------------------------------------------------------------------- 1 | D 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/c/e.txt: -------------------------------------------------------------------------------- 1 | E 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/c/f.txt: -------------------------------------------------------------------------------- 1 | F 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/go.mod: -------------------------------------------------------------------------------- 1 | module foo 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/gobuffalo/packr/v2 v2.0.0-rc.2 // indirect 7 | golang.org/x/tools v0.0.0-20181116193547-e77c06808af6 // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk-pack/go.sum: -------------------------------------------------------------------------------- 1 | errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 2 | errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 3 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 4 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 5 | github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 6 | github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= 7 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 8 | github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= 9 | github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 13 | github.com/dustin/go-humanize v0.0.0-20180713052910-9f541cc9db5d/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 14 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 15 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 16 | github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 17 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 18 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 19 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 20 | github.com/gobuffalo/buffalo v0.12.8-0.20181004233540-fac9bb505aa8/go.mod h1:sLyT7/dceRXJUxSsE813JTQtA3Eb1vjxWfo/N//vXIY= 21 | github.com/gobuffalo/buffalo v0.13.0/go.mod h1:Mjn1Ba9wpIbpbrD+lIDMy99pQ0H0LiddMIIDGse7qT4= 22 | github.com/gobuffalo/buffalo-plugins v1.0.2/go.mod h1:pOp/uF7X3IShFHyobahTkTLZaeUXwb0GrUTb9ngJWTs= 23 | github.com/gobuffalo/buffalo-plugins v1.0.4/go.mod h1:pWS1vjtQ6uD17MVFWf7i3zfThrEKWlI5+PYLw/NaDB4= 24 | github.com/gobuffalo/buffalo-plugins v1.4.3/go.mod h1:uCzTY0woez4nDMdQjkcOYKanngeUVRO2HZi7ezmAjWY= 25 | github.com/gobuffalo/buffalo-plugins v1.5.1/go.mod h1:jbmwSZK5+PiAP9cC09VQOrGMZFCa/P0UMlIS3O12r5w= 26 | github.com/gobuffalo/buffalo-plugins v1.6.4/go.mod h1:/+N1aophkA2jZ1ifB2O3Y9yGwu6gKOVMtUmJnbg+OZI= 27 | github.com/gobuffalo/buffalo-plugins v1.6.5/go.mod h1:0HVkbgrVs/MnPZ/FOseDMVanCTm2RNcdM0PuXcL1NNI= 28 | github.com/gobuffalo/buffalo-plugins v1.6.7/go.mod h1:ZGZRkzz2PiKWHs0z7QsPBOTo2EpcGRArMEym6ghKYgk= 29 | github.com/gobuffalo/buffalo-plugins v1.6.9 h1:Z4WSEWPSxHMxrxVwCdy9x0QVZjirywhGpZaB9yeCwdc= 30 | github.com/gobuffalo/buffalo-plugins v1.6.9/go.mod h1:yYlYTrPdMCz+6/+UaXg5Jm4gN3xhsvsQ2ygVatZV5vw= 31 | github.com/gobuffalo/buffalo-pop v1.0.5/go.mod h1:Fw/LfFDnSmB/vvQXPvcXEjzP98Tc+AudyNWUBWKCwQ8= 32 | github.com/gobuffalo/envy v1.6.4/go.mod h1:Abh+Jfw475/NWtYMEt+hnJWRiC8INKWibIMyNt1w2Mc= 33 | github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= 34 | github.com/gobuffalo/envy v1.6.6/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= 35 | github.com/gobuffalo/envy v1.6.7/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= 36 | github.com/gobuffalo/envy v1.6.8/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= 37 | github.com/gobuffalo/envy v1.6.9 h1:ShEJ/fUg/wr5qIYmTTOnUQ0sy1yGo+4uYQJNgg753S8= 38 | github.com/gobuffalo/envy v1.6.9/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= 39 | github.com/gobuffalo/events v1.0.3/go.mod h1:Txo8WmqScapa7zimEQIwgiJBvMECMe9gJjsKNPN3uZw= 40 | github.com/gobuffalo/events v1.0.7/go.mod h1:z8txf6H9jWhQ5Scr7YPLWg/cgXBRj8Q4uYI+rsVCCSQ= 41 | github.com/gobuffalo/events v1.0.8/go.mod h1:A5KyqT1sA+3GJiBE4QKZibse9mtOcI9nw8gGrDdqYGs= 42 | github.com/gobuffalo/events v1.1.3/go.mod h1:9yPGWYv11GENtzrIRApwQRMYSbUgCsZ1w6R503fCfrk= 43 | github.com/gobuffalo/events v1.1.4/go.mod h1:09/YRRgZHEOts5Isov+g9X2xajxdvOAcUuAHIX/O//A= 44 | github.com/gobuffalo/events v1.1.5/go.mod h1:3YUSzgHfYctSjEjLCWbkXP6djH2M+MLaVRzb4ymbAK0= 45 | github.com/gobuffalo/events v1.1.7 h1:X+wjuT7c1rZNKqhfevIA30oFCgO5JBlnW6hvAJcv1Vg= 46 | github.com/gobuffalo/events v1.1.7/go.mod h1:6fGqxH2ing5XMb3EYRq9LEkVlyPGs4oO/eLzh+S8CxY= 47 | github.com/gobuffalo/fizz v1.0.12/go.mod h1:C0sltPxpYK8Ftvf64kbsQa2yiCZY4RZviurNxXdAKwc= 48 | github.com/gobuffalo/flect v0.0.0-20180907193754-dc14d8acaf9f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 49 | github.com/gobuffalo/flect v0.0.0-20181002182613-4571df4b1daf/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 50 | github.com/gobuffalo/flect v0.0.0-20181007231023-ae7ed6bfe683/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 51 | github.com/gobuffalo/flect v0.0.0-20181018182602-fd24a256709f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 52 | github.com/gobuffalo/flect v0.0.0-20181019110701-3d6f0b585514/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 53 | github.com/gobuffalo/flect v0.0.0-20181024204909-8f6be1a8c6c2/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 54 | github.com/gobuffalo/flect v0.0.0-20181104133451-1f6e9779237a/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA= 55 | github.com/gobuffalo/flect v0.0.0-20181114183036-47375f6d8328 h1:nvA0/snr4wQeCwBYmrbftniJun/kxOjK/Pz3ivb7wis= 56 | github.com/gobuffalo/flect v0.0.0-20181114183036-47375f6d8328/go.mod h1:0HvNbHdfh+WOvDSIASqJOSxTOWSxCCUF++k/Y53v9rI= 57 | github.com/gobuffalo/genny v0.0.0-20180924032338-7af3a40f2252/go.mod h1:tUTQOogrr7tAQnhajMSH6rv1BVev34H2sa1xNHMy94g= 58 | github.com/gobuffalo/genny v0.0.0-20181003150629-3786a0744c5d/go.mod h1:WAd8HmjMVrnkAZbmfgH5dLBUchsZfqzp/WS5sQz+uTM= 59 | github.com/gobuffalo/genny v0.0.0-20181005145118-318a41a134cc/go.mod h1:WAd8HmjMVrnkAZbmfgH5dLBUchsZfqzp/WS5sQz+uTM= 60 | github.com/gobuffalo/genny v0.0.0-20181007153042-b8de7d566757/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= 61 | github.com/gobuffalo/genny v0.0.0-20181012161047-33e5f43d83a6/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= 62 | github.com/gobuffalo/genny v0.0.0-20181017160347-90a774534246/go.mod h1:+oG5Ljrw04czAHbPXREwaFojJbpUvcIy4DiOnbEJFTA= 63 | github.com/gobuffalo/genny v0.0.0-20181024195656-51392254bf53/go.mod h1:o9GEH5gn5sCKLVB5rHFC4tq40rQ3VRUzmx6WwmaqISE= 64 | github.com/gobuffalo/genny v0.0.0-20181025145300-af3f81d526b8/go.mod h1:uZ1fFYvdcP8mu0B/Ynarf6dsGvp7QFIpk/QACUuFUVI= 65 | github.com/gobuffalo/genny v0.0.0-20181027191429-94d6cfb5c7fc/go.mod h1:x7SkrQQBx204Y+O9EwRXeszLJDTaWN0GnEasxgLrQTA= 66 | github.com/gobuffalo/genny v0.0.0-20181027195209-3887b7171c4f/go.mod h1:JbKx8HSWICu5zyqWOa0dVV1pbbXOHusrSzQUprW6g+w= 67 | github.com/gobuffalo/genny v0.0.0-20181106193839-7dcb0924caf1/go.mod h1:x61yHxvbDCgQ/7cOAbJCacZQuHgB0KMSzoYcw5debjU= 68 | github.com/gobuffalo/genny v0.0.0-20181114215459-0a4decd77f5d h1:mznfLmbY1+EhkTIWhk19fZHuxoOLqZ/wzFUcKr5OGY8= 69 | github.com/gobuffalo/genny v0.0.0-20181114215459-0a4decd77f5d/go.mod h1:kN2KZ8VgXF9VIIOj/GM0Eo7YK+un4Q3tTreKOf0q1ng= 70 | github.com/gobuffalo/github_flavored_markdown v1.0.4/go.mod h1:uRowCdK+q8d/RF0Kt3/DSalaIXbb0De/dmTqMQdkQ4I= 71 | github.com/gobuffalo/github_flavored_markdown v1.0.5/go.mod h1:U0643QShPF+OF2tJvYNiYDLDGDuQmJZXsf/bHOJPsMY= 72 | github.com/gobuffalo/github_flavored_markdown v1.0.7/go.mod h1:w93Pd9Lz6LvyQXEG6DktTPHkOtCbr+arAD5mkwMzXLI= 73 | github.com/gobuffalo/httptest v1.0.2/go.mod h1:7T1IbSrg60ankme0aDLVnEY0h056g9M1/ZvpVThtB7E= 74 | github.com/gobuffalo/licenser v0.0.0-20180924033006-eae28e638a42/go.mod h1:Ubo90Np8gpsSZqNScZZkVXXAo5DGhTb+WYFIjlnog8w= 75 | github.com/gobuffalo/licenser v0.0.0-20181025145548-437d89de4f75/go.mod h1:x3lEpYxkRG/XtGCUNkio+6RZ/dlOvLzTI9M1auIwFcw= 76 | github.com/gobuffalo/licenser v0.0.0-20181027200154-58051a75da95/go.mod h1:BzhaaxGd1tq1+OLKObzgdCV9kqVhbTulxOpYbvMQWS0= 77 | github.com/gobuffalo/logger v0.0.0-20181022175615-46cfb361fc27/go.mod h1:8sQkgyhWipz1mIctHF4jTxmJh1Vxhp7mP8IqbljgJZo= 78 | github.com/gobuffalo/logger v0.0.0-20181027144941-73d08d2bb969/go.mod h1:7uGg2duHKpWnN4+YmyKBdLXfhopkAdVM6H3nKbyFbz8= 79 | github.com/gobuffalo/logger v0.0.0-20181027193913-9cf4dd0efe46/go.mod h1:7uGg2duHKpWnN4+YmyKBdLXfhopkAdVM6H3nKbyFbz8= 80 | github.com/gobuffalo/logger v0.0.0-20181109185836-3feeab578c17 h1:DnrA/oPJXI+mdlhX8LS8Gwj3uiTQhKOZCDAAcWSCYk0= 81 | github.com/gobuffalo/logger v0.0.0-20181109185836-3feeab578c17/go.mod h1:oNErH0xLe+utO+OW8ptXMSA5DkiSEDW1u3zGIt8F9Ew= 82 | github.com/gobuffalo/makr v1.1.5/go.mod h1:Y+o0btAH1kYAMDJW/TX3+oAXEu0bmSLLoC9mIFxtzOw= 83 | github.com/gobuffalo/mapi v1.0.0/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 84 | github.com/gobuffalo/mapi v1.0.1 h1:JRuTiZzDEZhBHkFiHTxJkYRT6CbYuL0K/rn+1byJoEA= 85 | github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 86 | github.com/gobuffalo/meta v0.0.0-20181018155829-df62557efcd3/go.mod h1:XTTOhwMNryif3x9LkTTBO/Llrveezd71u3quLd0u7CM= 87 | github.com/gobuffalo/meta v0.0.0-20181018192820-8c6cef77dab3/go.mod h1:E94EPzx9NERGCY69UWlcj6Hipf2uK/vnfrF4QD0plVE= 88 | github.com/gobuffalo/meta v0.0.0-20181025145500-3a985a084b0a h1:kROH1vRfBoxH1QIQjINB4qi4TmQTdg2Z/yzrKq1f2qM= 89 | github.com/gobuffalo/meta v0.0.0-20181025145500-3a985a084b0a/go.mod h1:YDAKBud2FP7NZdruCSlmTmDOZbVSa6bpK7LJ/A/nlKg= 90 | github.com/gobuffalo/mw-basicauth v1.0.3/go.mod h1:dg7+ilMZOKnQFHDefUzUHufNyTswVUviCBgF244C1+0= 91 | github.com/gobuffalo/mw-contenttype v0.0.0-20180802152300-74f5a47f4d56/go.mod h1:7EvcmzBbeCvFtQm5GqF9ys6QnCxz2UM1x0moiWLq1No= 92 | github.com/gobuffalo/mw-csrf v0.0.0-20180802151833-446ff26e108b/go.mod h1:sbGtb8DmDZuDUQoxjr8hG1ZbLtZboD9xsn6p77ppcHo= 93 | github.com/gobuffalo/mw-forcessl v0.0.0-20180802152810-73921ae7a130/go.mod h1:JvNHRj7bYNAMUr/5XMkZaDcw3jZhUZpsmzhd//FFWmQ= 94 | github.com/gobuffalo/mw-i18n v0.0.0-20180802152014-e3060b7e13d6/go.mod h1:91AQfukc52A6hdfIfkxzyr+kpVYDodgAeT5cjX1UIj4= 95 | github.com/gobuffalo/mw-paramlogger v0.0.0-20181005191442-d6ee392ec72e/go.mod h1:6OJr6VwSzgJMqWMj7TYmRUqzNe2LXu/W1rRW4MAz/ME= 96 | github.com/gobuffalo/mw-tokenauth v0.0.0-20181001105134-8545f626c189/go.mod h1:UqBF00IfKvd39ni5+yI5MLMjAf4gX7cDKN/26zDOD6c= 97 | github.com/gobuffalo/packd v0.0.0-20181027182251-01ad393492c8/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= 98 | github.com/gobuffalo/packd v0.0.0-20181027190505-aafc0d02c411/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= 99 | github.com/gobuffalo/packd v0.0.0-20181027194105-7ae579e6d213/go.mod h1:SmdBdhj6uhOsg1Ui4SFAyrhuc7U4VCildosO5IDJ3lc= 100 | github.com/gobuffalo/packd v0.0.0-20181031195726-c82734870264/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= 101 | github.com/gobuffalo/packd v0.0.0-20181104210303-d376b15f8e96/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= 102 | github.com/gobuffalo/packd v0.0.0-20181111195323-b2e760a5f0ff/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= 103 | github.com/gobuffalo/packd v0.0.0-20181114190715-f25c5d2471d7 h1:7AZMyDyRxIm2cbSXRvUEUJrankvMV1xcAOrrWUWp7yE= 104 | github.com/gobuffalo/packd v0.0.0-20181114190715-f25c5d2471d7/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= 105 | github.com/gobuffalo/packr v1.13.7/go.mod h1:KkinLIn/n6+3tVXMwg6KkNvWwVsrRAz4ph+jgpk3Z24= 106 | github.com/gobuffalo/packr v1.15.0/go.mod h1:t5gXzEhIviQwVlNx/+3SfS07GS+cZ2hn76WLzPp6MGI= 107 | github.com/gobuffalo/packr v1.15.1/go.mod h1:IeqicJ7jm8182yrVmNbM6PR4g79SjN9tZLH8KduZZwE= 108 | github.com/gobuffalo/packr v1.19.0/go.mod h1:MstrNkfCQhd5o+Ct4IJ0skWlxN8emOq8DsoT1G98VIU= 109 | github.com/gobuffalo/packr v1.20.0 h1:XDHu3L931kHjr0v80vJ9hAxOMavbSpzuwAXDONsMYcM= 110 | github.com/gobuffalo/packr v1.20.0/go.mod h1:JDytk1t2gP+my1ig7iI4NcVaXr886+N0ecUga6884zw= 111 | github.com/gobuffalo/packr/v2 v2.0.0-rc.2 h1:22PapDc3YjlxFvfJ5ukc47/CwmwSQgZhma2wGf05eC8= 112 | github.com/gobuffalo/packr/v2 v2.0.0-rc.2/go.mod h1:4Xjr6aIxzyYQN6TBp/oJZ4L+X17DGfq3dy0DCidBebw= 113 | github.com/gobuffalo/plush v3.7.16+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= 114 | github.com/gobuffalo/plush v3.7.20+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= 115 | github.com/gobuffalo/plush v3.7.21+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= 116 | github.com/gobuffalo/plush v3.7.22+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= 117 | github.com/gobuffalo/pop v4.8.2+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= 118 | github.com/gobuffalo/pop v4.8.3+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= 119 | github.com/gobuffalo/pop v4.8.4+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= 120 | github.com/gobuffalo/release v1.0.35/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= 121 | github.com/gobuffalo/release v1.0.38/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4= 122 | github.com/gobuffalo/release v1.0.42/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= 123 | github.com/gobuffalo/release v1.0.52/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug= 124 | github.com/gobuffalo/release v1.0.53/go.mod h1:FdF257nd8rqhNaqtDWFGhxdJ/Ig4J7VcS3KL7n/a+aA= 125 | github.com/gobuffalo/release v1.0.54/go.mod h1:Pe5/RxRa/BE8whDpGfRqSI7D1a0evGK1T4JDm339tJc= 126 | github.com/gobuffalo/release v1.0.61/go.mod h1:mfIO38ujUNVDlBziIYqXquYfBF+8FDHUjKZgYC1Hj24= 127 | github.com/gobuffalo/shoulders v1.0.1/go.mod h1:V33CcVmaQ4gRUmHKwq1fiTXuf8Gp/qjQBUL5tHPmvbA= 128 | github.com/gobuffalo/tags v2.0.11+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= 129 | github.com/gobuffalo/uuid v2.0.3+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= 130 | github.com/gobuffalo/uuid v2.0.4+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= 131 | github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= 132 | github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= 133 | github.com/gobuffalo/x v0.0.0-20181003152136-452098b06085/go.mod h1:WevpGD+5YOreDJznWevcn8NTmQEW5STSBgIkpkjzqXc= 134 | github.com/gobuffalo/x v0.0.0-20181007152206-913e47c59ca7/go.mod h1:9rDPXaB3kXdKWzMc4odGQQdG2e2DIEmANy5aSJ9yesY= 135 | github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 136 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 137 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 138 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 139 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 140 | github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY= 141 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 142 | github.com/gorilla/sessions v1.1.2/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= 143 | github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= 144 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 145 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 146 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 147 | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= 148 | github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= 149 | github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= 150 | github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 151 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 152 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 153 | github.com/karrick/godirwalk v1.7.5 h1:JQFiMR65pT543bkWP46+k194gS999qo/OYccos9cOXg= 154 | github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= 155 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 156 | github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 157 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 158 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 159 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 160 | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 161 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 162 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 163 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 164 | github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= 165 | github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= 166 | github.com/markbates/going v1.0.2/go.mod h1:UWCk3zm0UKefHZ7l8BNqi26UyiEMniznk8naLdTcy6c= 167 | github.com/markbates/grift v1.0.4/go.mod h1:wbmtW74veyx+cgfwFhlnnMWqhoz55rnHR47oMXzsyVs= 168 | github.com/markbates/hmax v1.0.0/go.mod h1:cOkR9dktiESxIMu+65oc/r/bdY4bE8zZw3OLhLx0X2c= 169 | github.com/markbates/inflect v1.0.0/go.mod h1:oTeZL2KHA7CUX6X+fovmK9OvIOFuqu0TwdQrZjLTh88= 170 | github.com/markbates/inflect v1.0.1/go.mod h1:uv3UVNBe5qBIfCm8O8Q+DW+S1EopeyINj+Ikhc7rnCk= 171 | github.com/markbates/inflect v1.0.3/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= 172 | github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= 173 | github.com/markbates/oncer v0.0.0-20180924031910-e862a676800b/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= 174 | github.com/markbates/oncer v0.0.0-20180924034138-723ad0170a46/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= 175 | github.com/markbates/oncer v0.0.0-20181014194634-05fccaae8fc4 h1:Mlji5gkcpzkqTROyE4ZxZ8hN7osunMb2RuGVrbvMvCc= 176 | github.com/markbates/oncer v0.0.0-20181014194634-05fccaae8fc4/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= 177 | github.com/markbates/refresh v1.4.10/go.mod h1:NDPHvotuZmTmesXxr95C9bjlw1/0frJwtME2dzcVKhc= 178 | github.com/markbates/safe v1.0.0/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= 179 | github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= 180 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= 181 | github.com/markbates/sigtx v1.0.0/go.mod h1:QF1Hv6Ic6Ca6W+T+DL0Y/ypborFKyvUY9HmuCD4VeTc= 182 | github.com/markbates/willie v1.0.9/go.mod h1:fsrFVWl91+gXpx/6dv715j7i11fYPfZ9ZGfH0DQzY7w= 183 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 184 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 185 | github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 186 | github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 187 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 188 | github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 189 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 190 | github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= 191 | github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= 192 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 193 | github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 194 | github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 195 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 196 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 197 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 198 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 199 | github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= 200 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 201 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 202 | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 203 | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= 204 | github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= 205 | github.com/shurcooL/highlight_go v0.0.0-20170515013102-78fb10f4a5f8/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= 206 | github.com/shurcooL/octicon v0.0.0-20180602230221-c42b0e3b24d9/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= 207 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 208 | github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= 209 | github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= 210 | github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= 211 | github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= 212 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 213 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= 214 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= 215 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 216 | github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= 217 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 218 | github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= 219 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 220 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 221 | github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 222 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 223 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 224 | github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= 225 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 226 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 227 | github.com/unrolled/secure v0.0.0-20180918153822-f340ee86eb8b/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= 228 | github.com/unrolled/secure v0.0.0-20181005190816-ff9db2ff917f/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= 229 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 230 | golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 231 | golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 232 | golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 233 | golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 234 | golang.org/x/crypto v0.0.0-20181024171144-74cb1d3d52f4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 235 | golang.org/x/crypto v0.0.0-20181025113841-85e1b3f9139a/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 236 | golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 237 | golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd h1:VtIkGDhk0ph3t+THbvXHfMZ8QHgsBO39Nh52+74pq7w= 238 | golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 239 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 240 | golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 241 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 242 | golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 243 | golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 244 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 245 | golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 246 | golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 247 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 248 | golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 249 | golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 250 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 251 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 252 | golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 253 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 254 | golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 255 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 256 | golang.org/x/sys v0.0.0-20180921163948-d47a0f339242/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 257 | golang.org/x/sys v0.0.0-20180927150500-dad3d9fb7b6e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 258 | golang.org/x/sys v0.0.0-20181005133103-4497e2df6f9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 259 | golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 260 | golang.org/x/sys v0.0.0-20181022134430-8a28ead16f52/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 261 | golang.org/x/sys v0.0.0-20181024145615-5cd93ef61a7c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 262 | golang.org/x/sys v0.0.0-20181025063200-d989b31c8746/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 263 | golang.org/x/sys v0.0.0-20181026064943-731415f00dce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 264 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 265 | golang.org/x/sys v0.0.0-20181106135930-3a76605856fd h1:5lx5yH6109ClL0rlBzOj++ZkX/njUT+RVgTO2RMbmZo= 266 | golang.org/x/sys v0.0.0-20181106135930-3a76605856fd/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 267 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 268 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 269 | golang.org/x/tools v0.0.0-20181003024731-2f84ea8ef872/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 270 | golang.org/x/tools v0.0.0-20181006002542-f60d9635b16a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 271 | golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 272 | golang.org/x/tools v0.0.0-20181013182035-5e66757b835f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 273 | golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 274 | golang.org/x/tools v0.0.0-20181024171208-a2dc47679d30/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 275 | golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 276 | golang.org/x/tools v0.0.0-20181105230042-78dc5bac0cac/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 277 | golang.org/x/tools v0.0.0-20181114190951-94339b83286c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 278 | golang.org/x/tools v0.0.0-20181115011154-2a3f5192be2e h1:C6Dd9cORPM5l1agULNKGE7TsHE1f4fKb6u/C03xjFxo= 279 | golang.org/x/tools v0.0.0-20181115011154-2a3f5192be2e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 280 | golang.org/x/tools v0.0.0-20181116193547-e77c06808af6 h1:eiQyQ1ZGSmM3I3XUf/clCROlBlOlmAUmTJ9l2GjX2GE= 281 | golang.org/x/tools v0.0.0-20181116193547-e77c06808af6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 282 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 283 | gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 284 | gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= 285 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 286 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 287 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 288 | gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 289 | gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= 290 | gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= 291 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 292 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 293 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/_r/r.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("bob", "dylan") 7 | } 8 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/e/e.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("tom", "./petty") 7 | packr.NewBox("../e/heartbreakers") 8 | } 9 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/e/heartbreakers/refugee.txt: -------------------------------------------------------------------------------- 1 | YOU DON'T HAVE TO BE A REFUGEE! 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/e/petty/fallin.txt: -------------------------------------------------------------------------------- 1 | FREE FALLIN! 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/franklin/aretha.txt: -------------------------------------------------------------------------------- 1 | RESPECT! 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/franklin/think.txt: -------------------------------------------------------------------------------- 1 | THINK! 2 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/q.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | import "github.com/gobuffalo/packr/v2" 4 | 5 | func init() { 6 | packr.New("aretha", "./franklin") 7 | } 8 | -------------------------------------------------------------------------------- /jam/store/_fixtures/disk/w/w.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /jam/store/clean.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | 8 | "github.com/gobuffalo/packr/v2/jam/parser" 9 | ) 10 | 11 | func Clean(root string) error { 12 | defer func() { 13 | packd := filepath.Join(root, "packrd") 14 | os.RemoveAll(packd) 15 | }() 16 | 17 | p, err := parser.NewFromRoots([]string{root}, &parser.RootsOptions{}) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | boxes, err := p.Run() 23 | if err != nil { 24 | return err 25 | } 26 | 27 | d := NewDisk("", "") 28 | for _, box := range boxes { 29 | if err := d.Clean(box); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | 36 | func clean(root string) error { 37 | if len(root) == 0 { 38 | pwd, err := os.Getwd() 39 | if err != nil { 40 | return err 41 | } 42 | root = pwd 43 | } 44 | if _, err := os.Stat(root); err != nil { 45 | return nil 46 | } 47 | defer func() { 48 | packd := filepath.Join(root, "packrd") 49 | os.RemoveAll(packd) 50 | }() 51 | 52 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 53 | if err != nil { 54 | return err 55 | } 56 | if info.IsDir() { 57 | if filepath.Base(path) == "packrd" { 58 | os.RemoveAll(path) 59 | return filepath.SkipDir 60 | } 61 | } 62 | if strings.HasSuffix(path, "-packr.go") { 63 | err := os.RemoveAll(path) 64 | if err != nil { 65 | return err 66 | } 67 | } 68 | return nil 69 | }) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /jam/store/disk.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "crypto/md5" 7 | "fmt" 8 | "html/template" 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | "os/exec" 13 | "path" 14 | "path/filepath" 15 | "sort" 16 | "strings" 17 | "sync" 18 | 19 | "github.com/karrick/godirwalk" 20 | 21 | "github.com/gobuffalo/packr/v2/file/resolver/encoding/hex" 22 | "github.com/gobuffalo/packr/v2/plog" 23 | "github.com/rogpeppe/go-internal/modfile" 24 | 25 | "github.com/gobuffalo/packr/v2/jam/parser" 26 | "golang.org/x/sync/errgroup" 27 | ) 28 | 29 | var _ Store = &Disk{} 30 | 31 | const DISK_GLOBAL_KEY = "__packr_global__" 32 | 33 | type Disk struct { 34 | DBPath string 35 | DBPackage string 36 | global map[string]string 37 | boxes map[string]*parser.Box 38 | moot *sync.RWMutex 39 | } 40 | 41 | func NewDisk(path string, pkg string) *Disk { 42 | if len(path) == 0 { 43 | path = "packrd" 44 | } 45 | if len(pkg) == 0 { 46 | pkg = "packrd" 47 | } 48 | if !filepath.IsAbs(path) { 49 | path, _ = filepath.Abs(path) 50 | } 51 | return &Disk{ 52 | DBPath: path, 53 | DBPackage: pkg, 54 | global: map[string]string{}, 55 | boxes: map[string]*parser.Box{}, 56 | moot: &sync.RWMutex{}, 57 | } 58 | } 59 | 60 | func (d *Disk) FileNames(box *parser.Box) ([]string, error) { 61 | path := box.AbsPath 62 | if len(box.AbsPath) == 0 { 63 | path = box.Path 64 | } 65 | var names []string 66 | if _, err := os.Stat(path); err != nil { 67 | return names, nil 68 | } 69 | err := godirwalk.Walk(path, &godirwalk.Options{ 70 | FollowSymbolicLinks: true, 71 | Callback: func(path string, de *godirwalk.Dirent) error { 72 | if !de.IsRegular() { 73 | return nil 74 | } 75 | names = append(names, path) 76 | return nil 77 | }, 78 | }) 79 | return names, err 80 | } 81 | 82 | func (d *Disk) Files(box *parser.Box) ([]*parser.File, error) { 83 | var files []*parser.File 84 | names, err := d.FileNames(box) 85 | if err != nil { 86 | return files, err 87 | } 88 | for _, n := range names { 89 | b, err := ioutil.ReadFile(n) 90 | if err != nil { 91 | return files, err 92 | } 93 | f := parser.NewFile(n, bytes.NewReader(b)) 94 | files = append(files, f) 95 | } 96 | return files, nil 97 | } 98 | 99 | func (d *Disk) Pack(box *parser.Box) error { 100 | plog.Debug(d, "Pack", "box", box.Name) 101 | d.boxes[box.Name] = box 102 | names, err := d.FileNames(box) 103 | if err != nil { 104 | return err 105 | } 106 | for _, n := range names { 107 | _, ok := d.global[n] 108 | if ok { 109 | continue 110 | } 111 | k := makeKey(box, n) 112 | // not in the global, so add it! 113 | d.global[n] = k 114 | } 115 | return nil 116 | } 117 | 118 | func (d *Disk) Clean(box *parser.Box) error { 119 | root := box.PackageDir 120 | if len(root) == 0 { 121 | return fmt.Errorf("can't clean an empty box.PackageDir") 122 | } 123 | plog.Debug(d, "Clean", "box", box.Name, "root", root) 124 | return clean(root) 125 | } 126 | 127 | type options struct { 128 | Package string 129 | GlobalFiles map[string]string 130 | Boxes []optsBox 131 | GK string 132 | } 133 | 134 | type optsBox struct { 135 | Name string 136 | Path string 137 | } 138 | 139 | // Close ... 140 | func (d *Disk) Close() error { 141 | if len(d.boxes) == 0 { 142 | return nil 143 | } 144 | 145 | xb := &parser.Box{Name: DISK_GLOBAL_KEY} 146 | opts := options{ 147 | Package: d.DBPackage, 148 | GlobalFiles: map[string]string{}, 149 | GK: makeKey(xb, d.DBPath), 150 | } 151 | 152 | wg := errgroup.Group{} 153 | for k, v := range d.global { 154 | func(k, v string) { 155 | wg.Go(func() error { 156 | bb := &bytes.Buffer{} 157 | enc := hex.NewEncoder(bb) 158 | zw := gzip.NewWriter(enc) 159 | f, err := os.Open(k) 160 | if err != nil { 161 | return err 162 | } 163 | defer f.Close() 164 | io.Copy(zw, f) 165 | if err := zw.Close(); err != nil { 166 | return err 167 | } 168 | d.moot.Lock() 169 | opts.GlobalFiles[makeKey(xb, k)] = bb.String() 170 | d.moot.Unlock() 171 | return nil 172 | }) 173 | }(k, v) 174 | } 175 | 176 | if err := wg.Wait(); err != nil { 177 | return err 178 | } 179 | 180 | for _, b := range d.boxes { 181 | ob := optsBox{ 182 | Name: b.Name, 183 | } 184 | opts.Boxes = append(opts.Boxes, ob) 185 | } 186 | 187 | sort.Slice(opts.Boxes, func(a, b int) bool { 188 | return opts.Boxes[a].Name < opts.Boxes[b].Name 189 | }) 190 | 191 | fm := template.FuncMap{ 192 | "printBox": func(ob optsBox) (template.HTML, error) { 193 | box := d.boxes[ob.Name] 194 | if box == nil { 195 | return "", fmt.Errorf("could not find box %s", ob.Name) 196 | } 197 | fn, err := d.FileNames(box) 198 | if err != nil { 199 | return "", err 200 | } 201 | if len(fn) == 0 { 202 | return "", nil 203 | } 204 | 205 | type file struct { 206 | Resolver string 207 | ForwardPath string 208 | } 209 | 210 | tmpl, err := template.New("box.go").Parse(diskGlobalBoxTmpl) 211 | if err != nil { 212 | return "", err 213 | } 214 | 215 | var files []file 216 | for _, s := range fn { 217 | p := strings.TrimPrefix(s, box.AbsPath) 218 | p = strings.TrimPrefix(p, string(filepath.Separator)) 219 | files = append(files, file{ 220 | Resolver: strings.Replace(p, "\\", "/", -1), 221 | ForwardPath: makeKey(box, s), 222 | }) 223 | } 224 | opts := map[string]interface{}{ 225 | "Box": box, 226 | "Files": files, 227 | } 228 | 229 | bb := &bytes.Buffer{} 230 | if err := tmpl.Execute(bb, opts); err != nil { 231 | return "", err 232 | } 233 | return template.HTML(bb.String()), nil 234 | }, 235 | } 236 | 237 | os.MkdirAll(d.DBPath, 0755) 238 | fp := filepath.Join(d.DBPath, "packed-packr.go") 239 | global, err := os.Create(fp) 240 | if err != nil { 241 | return err 242 | } 243 | defer global.Close() 244 | 245 | tmpl := template.New(fp).Funcs(fm) 246 | tmpl, err = tmpl.Parse(diskGlobalTmpl) 247 | if err != nil { 248 | return err 249 | } 250 | 251 | if err := tmpl.Execute(global, opts); err != nil { 252 | return err 253 | } 254 | 255 | var ip string 256 | // Starting in 1.12, we can rely on Go's method for 257 | // resolving where go.mod resides. Prior versions will 258 | // simply return an empty string. 259 | cmd := exec.Command("go", "env", "GOMOD") 260 | out, err := cmd.Output() 261 | if err != nil { 262 | return fmt.Errorf("go.mod cannot be read or does not exist while go module is enabled") 263 | } 264 | mp := strings.TrimSpace(string(out)) 265 | if mp == "" { 266 | // We are on a prior version of Go; try and do 267 | // the resolution ourselves. 268 | mp = filepath.Join(filepath.Dir(d.DBPath), "go.mod") 269 | if _, err := os.Stat(mp); err != nil { 270 | mp = filepath.Join(d.DBPath, "go.mod") 271 | } 272 | } 273 | 274 | moddata, err := ioutil.ReadFile(mp) 275 | if err != nil { 276 | return fmt.Errorf("go.mod cannot be read or does not exist while go module is enabled") 277 | } 278 | ip = modfile.ModulePath(moddata) 279 | if ip == "" { 280 | return fmt.Errorf("go.mod is malformed") 281 | } 282 | ip = filepath.Join(ip, strings.TrimPrefix(filepath.Dir(d.DBPath), filepath.Dir(mp))) 283 | ip = strings.Replace(ip, "\\", "/", -1) 284 | ip = path.Join(ip, d.DBPackage) 285 | 286 | for _, n := range opts.Boxes { 287 | b := d.boxes[n.Name] 288 | if b == nil { 289 | continue 290 | } 291 | p := filepath.Join(b.PackageDir, b.Package+"-packr.go") 292 | f, err := os.Create(p) 293 | if err != nil { 294 | return err 295 | } 296 | defer f.Close() 297 | 298 | o := struct { 299 | Package string 300 | Import string 301 | }{ 302 | Package: b.Package, 303 | Import: ip, 304 | } 305 | 306 | tmpl, err := template.New(p).Parse(diskImportTmpl) 307 | if err != nil { 308 | return err 309 | } 310 | if err := tmpl.Execute(f, o); err != nil { 311 | return err 312 | } 313 | 314 | } 315 | 316 | return nil 317 | } 318 | 319 | // resolve file paths (only) for the boxes 320 | // compile "global" db 321 | // resolve files for boxes to point at global db 322 | // write global db to disk (default internal/packr) 323 | // write boxes db to disk (default internal/packr) 324 | // write -packr.go files in each package (1 per package) that init the global db 325 | 326 | func makeKey(box *parser.Box, path string) string { 327 | w := md5.New() 328 | fmt.Fprint(w, path) 329 | h := hex.EncodeToString(w.Sum(nil)) 330 | return h 331 | } 332 | -------------------------------------------------------------------------------- /jam/store/disk_packed_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "github.com/gobuffalo/packr/v2" 5 | "github.com/gobuffalo/packr/v2/file/resolver" 6 | ) 7 | 8 | var _ = func() error { 9 | const gk = DISK_GLOBAL_KEY 10 | g := packr.New(gk, "") 11 | 12 | hgr, err := resolver.NewHexGzip(map[string]string{ 13 | "4abf3a9b652ecec6b347eb6acb7ce363": "1f8b08000000000000fff2750c72775508cecc2d28cecfe302040000fffffb1d273b0e000000", 14 | "5cfc8f95f98237a10affc14a76e3e20b": "1f8b08000000000000fff2757477f7745508cecc2d28cecfe302040000ffffb09167470f000000", 15 | "6d8be986fa35821e7e869fbb118e51ba": "1f8b08000000000000fff2f0f7750d5208cecc2d28cecfe302040000fffffb2ef0a60e000000", 16 | "99e5497ae5f5988fafafbcd15ed74d22": "1f8b08000000000000fff2f10c765408cecc2d28cecfe302040000ffffab9bc93e0d000000", 17 | "bb006aa6261a80f6c52c640f713659c1": "1f8b08000000000000ff72720c0a5108cecc2d28cecfe302040000ffff89742ac20d000000", 18 | }) 19 | if err != nil { 20 | return err 21 | } 22 | g.DefaultResolver = hgr 23 | func() { 24 | b := packr.New("simpsons", "") 25 | b.SetResolver("kids/bart.txt", packr.Pointer{ForwardBox: gk, ForwardPath: "bb006aa6261a80f6c52c640f713659c1"}) 26 | b.SetResolver("kids/lisa.txt", packr.Pointer{ForwardBox: gk, ForwardPath: "99e5497ae5f5988fafafbcd15ed74d22"}) 27 | b.SetResolver("kids/maggie.txt", packr.Pointer{ForwardBox: gk, ForwardPath: "5cfc8f95f98237a10affc14a76e3e20b"}) 28 | b.SetResolver("parents/homer.txt", packr.Pointer{ForwardBox: gk, ForwardPath: "6d8be986fa35821e7e869fbb118e51ba"}) 29 | b.SetResolver("parents/marge.txt", packr.Pointer{ForwardBox: gk, ForwardPath: "4abf3a9b652ecec6b347eb6acb7ce363"}) 30 | }() 31 | return nil 32 | }() 33 | -------------------------------------------------------------------------------- /jam/store/disk_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | // 4 | // import ( 5 | // "path/filepath" 6 | // "strings" 7 | // "testing" 8 | // 9 | // "github.com/gobuffalo/envy" 10 | // "github.com/gobuffalo/genny/gentest" 11 | // "github.com/gobuffalo/gogen/gomods" 12 | // "github.com/gobuffalo/packr/v2" 13 | // "github.com/gobuffalo/packr/v2/jam/parser" 14 | // "github.com/markbates/oncer" 15 | // "github.com/stretchr/testify/require" 16 | // ) 17 | // 18 | // func init() { 19 | // parser.DefaultIgnoredFolders = []string{"vendor", ".git", "node_modules", ".idea"} 20 | // } 21 | // 22 | // func Test_Disk_Generator(t *testing.T) { 23 | // gomods.Disable(func() error { 24 | // 25 | // r := require.New(t) 26 | // 27 | // p, err := parser.NewFromRoots([]string{"./_fixtures/disk-pack"}, &parser.RootsOptions{ 28 | // IgnoreImports: true, 29 | // }) 30 | // r.NoError(err) 31 | // 32 | // boxes, err := p.Run() 33 | // r.NoError(err) 34 | // 35 | // d := NewDisk(".", "") 36 | // for _, b := range boxes { 37 | // r.NoError(d.Pack(b)) 38 | // } 39 | // 40 | // r.NoError(d.Close()) 41 | // 42 | // res := run.Results() 43 | // r.Len(res.Files, 3) 44 | // 45 | // f := res.Files[0] 46 | // r.Equal("a-packr.go", filepath.Base(f.Name())) 47 | // r.Contains(f.String(), `import _ "github.com/gobuffalo/packr/v2/jam/packrd"`) 48 | // return nil 49 | // }) 50 | // } 51 | // 52 | // func Test_Disk_Generator_GoMod(t *testing.T) { 53 | // oe := envy.Get(gomods.ENV, "off") 54 | // _ = envy.MustSet(gomods.ENV, "on") 55 | // defer envy.MustSet(gomods.ENV, oe) 56 | // 57 | // r := require.New(t) 58 | // 59 | // p, err := parser.NewFromRoots([]string{"./_fixtures/disk-pack"}, &parser.RootsOptions{ 60 | // IgnoreImports: true, 61 | // }) 62 | // r.NoError(err) 63 | // 64 | // boxes, err := p.Run() 65 | // r.NoError(err) 66 | // 67 | // d := NewDisk(".", "") 68 | // for _, b := range boxes { 69 | // r.NoError(d.Pack(b)) 70 | // } 71 | // 72 | // run := gentest.NewRunner() 73 | // run.WithNew(d.Generator()) 74 | // r.NoError(run.Run()) 75 | // 76 | // res := run.Results() 77 | // r.Len(res.Files, 3) 78 | // 79 | // f := res.Files[0] 80 | // r.Equal("a-packr.go", filepath.Base(f.Name())) 81 | // r.Contains(f.String(), `import _ "github.com/gobuffalo/packr/v2/jam/packrd"`) 82 | // } 83 | // 84 | // func Test_Disk_FileNames(t *testing.T) { 85 | // r := require.New(t) 86 | // 87 | // d := &Disk{} 88 | // 89 | // box := parser.NewBox("Test_Disk_FileNames", "./_fixtures/disk/franklin") 90 | // names, err := d.FileNames(box) 91 | // r.NoError(err) 92 | // r.Len(names, 2) 93 | // 94 | // r.Equal("aretha.txt", filepath.Base(names[0])) 95 | // r.Equal("think.txt", filepath.Base(names[1])) 96 | // } 97 | // 98 | // func Test_Disk_Files(t *testing.T) { 99 | // r := require.New(t) 100 | // 101 | // d := &Disk{} 102 | // 103 | // box := parser.NewBox("Test_Disk_Files", "./_fixtures/disk/franklin") 104 | // files, err := d.Files(box) 105 | // r.NoError(err) 106 | // r.Len(files, 2) 107 | // 108 | // f := files[0] 109 | // r.Equal("aretha.txt", filepath.Base(f.Name())) 110 | // r.Equal("RESPECT!", strings.TrimSpace(f.String())) 111 | // 112 | // f = files[1] 113 | // r.Equal("think.txt", filepath.Base(f.Name())) 114 | // r.Equal("THINK!", strings.TrimSpace(f.String())) 115 | // } 116 | // 117 | // func Test_Disk_Pack(t *testing.T) { 118 | // oncer.Reset() 119 | // r := require.New(t) 120 | // 121 | // d := NewDisk("", "") 122 | // 123 | // p, err := parser.NewFromRoots([]string{"./_fixtures/disk-pack"}, &parser.RootsOptions{ 124 | // IgnoreImports: true, 125 | // }) 126 | // r.NoError(err) 127 | // boxes, err := p.Run() 128 | // r.NoError(err) 129 | // 130 | // for _, b := range boxes { 131 | // r.NoError(d.Pack(b)) 132 | // } 133 | // 134 | // global := d.global 135 | // r.Len(global, 3) 136 | // 137 | // r.Len(d.boxes, 3) 138 | // 139 | // } 140 | // 141 | // func Test_Disk_Packed_Test(t *testing.T) { 142 | // r := require.New(t) 143 | // 144 | // b := packr.NewBox("simpsons") 145 | // 146 | // s, err := b.FindString("parents/homer.txt") 147 | // r.NoError(err) 148 | // r.Equal("HOMER Simpson", strings.TrimSpace(s)) 149 | // 150 | // s, err = b.FindString("parents/marge.txt") 151 | // r.NoError(err) 152 | // r.Equal("MARGE Simpson", strings.TrimSpace(s)) 153 | // 154 | // _, err = b.FindString("idontexist") 155 | // r.Error(err) 156 | // } 157 | // 158 | // func Test_Disk_Close(t *testing.T) { 159 | // gomods.Disable(func() error { 160 | // r := require.New(t) 161 | // 162 | // p, err := parser.NewFromRoots([]string{"./_fixtures/disk-pack"}, nil) 163 | // r.NoError(err) 164 | // boxes, err := p.Run() 165 | // r.NoError(err) 166 | // 167 | // d := NewDisk("./_fixtures/disk-pack", "") 168 | // for _, b := range boxes { 169 | // r.NoError(d.Pack(b)) 170 | // } 171 | // r.NoError(d.Close()) 172 | // return nil 173 | // }) 174 | // } 175 | // 176 | // func Test_Disk_Generator_NoFiles(t *testing.T) { 177 | // gomods.Disable(func() error { 178 | // 179 | // r := require.New(t) 180 | // 181 | // d := NewDisk(".", "") 182 | // r.Len(d.boxes, 0) 183 | // 184 | // run := gentest.NewRunner() 185 | // run.WithNew(d.Generator()) 186 | // r.NoError(run.Run()) 187 | // 188 | // res := run.Results() 189 | // r.Len(res.Files, 0) 190 | // 191 | // return nil 192 | // }) 193 | // } 194 | -------------------------------------------------------------------------------- /jam/store/disk_tmpl.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | const diskGlobalTmpl = `// +build !skippackr 4 | // Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT. 5 | 6 | // You can use the "packr2 clean" command to clean up this, 7 | // and any other packr generated files. 8 | package {{.Package}} 9 | 10 | import ( 11 | "github.com/gobuffalo/packr/v2" 12 | "github.com/gobuffalo/packr/v2/file/resolver" 13 | ) 14 | 15 | var _ = func() error { 16 | const gk = "{{.GK}}" 17 | g := packr.New(gk, "") 18 | hgr, err := resolver.NewHexGzip(map[string]string{ 19 | {{- range $k, $v := .GlobalFiles }} 20 | "{{$k}}": "{{$v}}", 21 | {{- end }} 22 | }) 23 | if err != nil { 24 | panic(err) 25 | } 26 | g.DefaultResolver = hgr 27 | 28 | {{- range $box := .Boxes}} 29 | {{ printBox $box -}} 30 | {{ end }} 31 | return nil 32 | }() 33 | ` 34 | 35 | const diskImportTmpl = `// +build !skippackr 36 | // Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT. 37 | 38 | // You can use the "packr clean" command to clean up this, 39 | // and any other packr generated files. 40 | package {{.Package}} 41 | 42 | import _ "{{.Import}}" 43 | ` 44 | 45 | const diskGlobalBoxTmpl = ` 46 | func() { 47 | b := packr.New("{{.Box.Name}}", "{{.Box.Path}}") 48 | {{- range $file := .Files }} 49 | b.SetResolver("{{$file.Resolver}}", packr.Pointer{ForwardBox: gk, ForwardPath: "{{$file.ForwardPath}}"}) 50 | {{- end }} 51 | }()` 52 | -------------------------------------------------------------------------------- /jam/store/env.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path/filepath" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | var goPath = filepath.Join(os.Getenv("HOME"), "go") 12 | 13 | func init() { 14 | var once sync.Once 15 | once.Do(func() { 16 | cmd := exec.Command("go", "env", "GOPATH") 17 | b, err := cmd.CombinedOutput() 18 | if err != nil { 19 | return 20 | } 21 | goPath = strings.TrimSpace(string(b)) 22 | }) 23 | } 24 | 25 | // GoPath returns the current GOPATH env var 26 | // or if it's missing, the default. 27 | func GoPath() string { 28 | return goPath 29 | } 30 | 31 | // GoBin returns the current GO_BIN env var 32 | // or if it's missing, a default of "go" 33 | func GoBin() string { 34 | go_bin := os.Getenv("GO_BIN") 35 | if go_bin == "" { 36 | return "go" 37 | } 38 | return go_bin 39 | } 40 | -------------------------------------------------------------------------------- /jam/store/fn.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gobuffalo/packr/v2/jam/parser" 7 | ) 8 | 9 | var _ Store = &FnStore{} 10 | 11 | type FnStore struct { 12 | FileNamesFn func(*parser.Box) ([]string, error) 13 | FilesFn func(*parser.Box) ([]*parser.File, error) 14 | PackFn func(*parser.Box) error 15 | CleanFn func(*parser.Box) error 16 | } 17 | 18 | func (f *FnStore) FileNames(box *parser.Box) ([]string, error) { 19 | if f.FileNamesFn == nil { 20 | return []string{}, fmt.Errorf("FileNames not implemented") 21 | } 22 | return f.FileNames(box) 23 | } 24 | 25 | func (f *FnStore) Files(box *parser.Box) ([]*parser.File, error) { 26 | if f.FilesFn == nil { 27 | return []*parser.File{}, fmt.Errorf("Files not implemented") 28 | } 29 | return f.FilesFn(box) 30 | } 31 | 32 | func (f *FnStore) Pack(box *parser.Box) error { 33 | if f.PackFn == nil { 34 | return fmt.Errorf("Pack not implemented") 35 | } 36 | return f.PackFn(box) 37 | } 38 | 39 | func (f *FnStore) Clean(box *parser.Box) error { 40 | if f.CleanFn == nil { 41 | return fmt.Errorf("Clean not implemented") 42 | } 43 | return f.Clean(box) 44 | } 45 | -------------------------------------------------------------------------------- /jam/store/legacy.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "html/template" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "sort" 11 | "strings" 12 | 13 | "github.com/gobuffalo/packr/v2/jam/parser" 14 | ) 15 | 16 | var _ Store = &Legacy{} 17 | 18 | type Legacy struct { 19 | *Disk 20 | boxes map[string][]legacyBox 21 | } 22 | 23 | func NewLegacy() *Legacy { 24 | return &Legacy{ 25 | Disk: NewDisk("", ""), 26 | boxes: map[string][]legacyBox{}, 27 | } 28 | } 29 | 30 | func (l *Legacy) Pack(box *parser.Box) error { 31 | files, err := l.Files(box) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | var fcs []legacyFile 37 | 38 | for _, f := range files { 39 | n := strings.TrimPrefix(f.Name(), box.AbsPath+string(filepath.Separator)) 40 | c, err := l.prepFile(f) 41 | if err != nil { 42 | return err 43 | } 44 | fcs = append(fcs, legacyFile{Name: n, Contents: c}) 45 | } 46 | 47 | sort.Slice(fcs, func(a, b int) bool { 48 | return fcs[a].Name < fcs[b].Name 49 | }) 50 | 51 | lbs := l.boxes[box.PackageDir] 52 | lbs = append(lbs, legacyBox{ 53 | Box: box, 54 | Files: fcs, 55 | }) 56 | l.boxes[box.PackageDir] = lbs 57 | return nil 58 | } 59 | 60 | func (l *Legacy) prepFile(r io.Reader) (string, error) { 61 | bb := &bytes.Buffer{} 62 | if _, err := io.Copy(bb, r); err != nil { 63 | return "", err 64 | } 65 | b, err := json.Marshal(bb.Bytes()) 66 | if err != nil { 67 | return "", err 68 | } 69 | return strings.Replace(string(b), "\"", "\\\"", -1), nil 70 | } 71 | 72 | // Close ... 73 | func (l *Legacy) Close() error { 74 | for _, b := range l.boxes { 75 | if len(b) == 0 { 76 | continue 77 | } 78 | bx := b[0].Box 79 | pkg := bx.Package 80 | opts := map[string]interface{}{ 81 | "Package": pkg, 82 | "Boxes": b, 83 | } 84 | p := filepath.Join(bx.PackageDir, "a_"+bx.Package+"-packr.go.tmpl") 85 | tmpl, err := template.New(p).Parse(legacyTmpl) 86 | 87 | if err != nil { 88 | return err 89 | } 90 | 91 | f, err := os.Create(p) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | if err := tmpl.Execute(f, opts); err != nil { 97 | return err 98 | } 99 | 100 | } 101 | return nil 102 | } 103 | 104 | type legacyBox struct { 105 | Box *parser.Box 106 | Files []legacyFile 107 | } 108 | 109 | type legacyFile struct { 110 | Name string 111 | Contents string 112 | } 113 | 114 | var legacyTmpl = `// Code generated by github.com/gobuffalo/packr. DO NOT EDIT. 115 | 116 | package {{.Package}} 117 | 118 | import "github.com/gobuffalo/packr" 119 | 120 | // You can use the "packr clean" command to clean up this, 121 | // and any other packr generated files. 122 | func init() { 123 | {{- range $box := .Boxes }} 124 | {{- range $box.Files }} 125 | _ = packr.PackJSONBytes("{{$box.Box.Name}}", "{{.Name}}", "{{.Contents}}") 126 | {{- end }} 127 | {{- end }} 128 | } 129 | ` 130 | -------------------------------------------------------------------------------- /jam/store/legacy_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | // func Test_Legacy_Pack(t *testing.T) { 4 | // r := require.New(t) 5 | // 6 | // d := NewLegacy() 7 | // 8 | // p, err := parser.NewFromRoots([]string{"./_fixtures/disk"}, &parser.RootsOptions{ 9 | // IgnoreImports: true, 10 | // }) 11 | // r.NoError(err) 12 | // boxes, err := p.Run() 13 | // r.NoError(err) 14 | // 15 | // for _, b := range boxes { 16 | // r.NoError(d.Pack(b)) 17 | // } 18 | // 19 | // db := d.boxes 20 | // r.Len(db, 2) 21 | // for k, v := range db { 22 | // switch filepath.Base(k) { 23 | // case "disk": 24 | // r.Len(v, 1) 25 | // case "e": 26 | // r.Len(v, 2) 27 | // default: 28 | // r.Fail(k) 29 | // } 30 | // } 31 | // } 32 | // 33 | // func Test_Legacy_Close(t *testing.T) { 34 | // oncer.Reset() 35 | // r := require.New(t) 36 | // 37 | // d := NewLegacy() 38 | // 39 | // p, err := parser.NewFromRoots([]string{"./_fixtures/disk"}, &parser.RootsOptions{ 40 | // IgnoreImports: true, 41 | // }) 42 | // r.NoError(err) 43 | // boxes, err := p.Run() 44 | // r.NoError(err) 45 | // 46 | // for _, b := range boxes { 47 | // r.NoError(d.Pack(b)) 48 | // } 49 | // r.Len(d.boxes, 2) 50 | // 51 | // run := gentest.NewRunner() 52 | // r.NoError(run.WithNew(d.Generator())) 53 | // r.NoError(run.Run()) 54 | // 55 | // res := run.Results() 56 | // r.Len(res.Files, 2) 57 | // } 58 | -------------------------------------------------------------------------------- /jam/store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "github.com/gobuffalo/packr/v2/jam/parser" 5 | ) 6 | 7 | type Store interface { 8 | FileNames(*parser.Box) ([]string, error) 9 | Files(*parser.Box) ([]*parser.File, error) 10 | Pack(*parser.Box) error 11 | Clean(*parser.Box) error 12 | } 13 | -------------------------------------------------------------------------------- /jam/store/store_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | -------------------------------------------------------------------------------- /packr.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gobuffalo/packr/v2/file/resolver" 7 | "github.com/gobuffalo/packr/v2/jam/parser" 8 | "github.com/gobuffalo/packr/v2/plog" 9 | "github.com/markbates/safe" 10 | ) 11 | 12 | var boxes = &boxMap{} 13 | 14 | var _ = safe.Run(func() { 15 | p, err := parser.NewFromRoots([]string{}, nil) 16 | if err != nil { 17 | plog.Logger.Error(err) 18 | return 19 | } 20 | boxes, err := p.Run() 21 | if err != nil { 22 | plog.Logger.Error(err) 23 | return 24 | } 25 | for _, box := range boxes { 26 | b := construct(box.Name, box.AbsPath) 27 | _, err = placeBox(b) 28 | if err != nil { 29 | plog.Logger.Error(err) 30 | return 31 | } 32 | } 33 | 34 | }) 35 | 36 | func findBox(name string) (*Box, error) { 37 | key := resolver.Key(name) 38 | plog.Debug("packr", "findBox", "name", name, "key", key) 39 | 40 | b, ok := boxes.Load(key) 41 | if !ok { 42 | plog.Debug("packr", "findBox", "name", name, "key", key, "found", ok) 43 | return nil, fmt.Errorf("could not find box %s", name) 44 | } 45 | 46 | plog.Debug(b, "found", "box", b) 47 | return b, nil 48 | } 49 | 50 | func placeBox(b *Box) (*Box, error) { 51 | key := resolver.Key(b.Name) 52 | eb, _ := boxes.LoadOrStore(key, b) 53 | 54 | plog.Debug("packr", "placeBox", "name", eb.Name, "path", eb.Path, "resolution directory", eb.ResolutionDir) 55 | return eb, nil 56 | } 57 | -------------------------------------------------------------------------------- /packr2/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2018 Mark Bates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packr2/cmd/build.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gobuffalo/packr/v2/jam" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var buildCmd = &cobra.Command{ 11 | Use: "build", 12 | Short: "Wraps the go build command with packr", 13 | DisableFlagParsing: true, 14 | RunE: func(cmd *cobra.Command, args []string) error { 15 | cargs := parseArgs(args) 16 | if globalOptions.Verbose { 17 | fmt.Println(dont) 18 | } 19 | if err := jam.Pack(globalOptions.PackOptions); err != nil { 20 | return err 21 | } 22 | return goCmd("build", cargs...) 23 | }, 24 | } 25 | 26 | func init() { 27 | rootCmd.AddCommand(buildCmd) 28 | } 29 | -------------------------------------------------------------------------------- /packr2/cmd/clean.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/gobuffalo/packr/v2/jam" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var cleanCmd = &cobra.Command{ 9 | Use: "clean", 10 | Short: "removes any *-packr.go files", 11 | RunE: func(cmd *cobra.Command, args []string) error { 12 | return jam.Clean(args...) 13 | }, 14 | } 15 | 16 | func init() { 17 | rootCmd.AddCommand(cleanCmd) 18 | } 19 | -------------------------------------------------------------------------------- /packr2/cmd/fix.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | packr "github.com/gobuffalo/packr/v2" 7 | "github.com/gobuffalo/packr/v2/packr2/cmd/fix" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | // fixCmd represents the info command 12 | var fixCmd = &cobra.Command{ 13 | Use: "fix", 14 | Short: fmt.Sprintf("will attempt to fix a application's API to match packr version %s", packr.Version), 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | return fix.Run() 17 | }, 18 | } 19 | 20 | func init() { 21 | fixCmd.Flags().BoolVarP(&fix.YesToAll, "y", "", false, "update all without asking for confirmation") 22 | rootCmd.AddCommand(fixCmd) 23 | } 24 | -------------------------------------------------------------------------------- /packr2/cmd/fix/fix.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/gobuffalo/packr/v2/jam/store" 10 | ) 11 | 12 | //YesToAll will be used by the command to skip the questions 13 | var YesToAll bool 14 | 15 | var replace = map[string]string{ 16 | "github.com/gobuffalo/packr": "github.com/gobuffalo/packr/v2", 17 | } 18 | 19 | var ic = ImportConverter{ 20 | Data: replace, 21 | } 22 | 23 | var checks = []Check{ 24 | // packrClean, 25 | ic.Process, 26 | } 27 | 28 | func packrClean(r *Runner) error { 29 | pwd, err := os.Getwd() 30 | if err != nil { 31 | return err 32 | } 33 | store.Clean(pwd) 34 | return nil 35 | } 36 | 37 | func ask(q string) bool { 38 | if YesToAll { 39 | return true 40 | } 41 | 42 | fmt.Printf("? %s [y/n]\n", q) 43 | 44 | reader := bufio.NewReader(os.Stdin) 45 | text, _ := reader.ReadString('\n') 46 | 47 | text = strings.ToLower(strings.TrimSpace(text)) 48 | return text == "y" || text == "yes" 49 | } 50 | -------------------------------------------------------------------------------- /packr2/cmd/fix/imports.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/printer" 9 | "go/token" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | "strconv" 14 | "strings" 15 | 16 | "golang.org/x/tools/go/ast/astutil" 17 | ) 18 | 19 | // ImportConverter will changes imports from a -> b 20 | type ImportConverter struct { 21 | Data map[string]string 22 | } 23 | 24 | // Process will walk all the .go files in an application, excluding ./vendor. 25 | // It will then attempt to convert any old import paths to any new import paths 26 | // used by this version Buffalo. 27 | func (c ImportConverter) Process(r *Runner) error { 28 | fmt.Println("~~~ Rewriting Imports ~~~") 29 | 30 | err := filepath.Walk(".", c.processFile) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | if _, err := os.Stat("Gopkg.toml"); err != nil { 36 | return nil 37 | } 38 | 39 | b, err := ioutil.ReadFile("Gopkg.toml") 40 | if err != nil { 41 | return err 42 | } 43 | 44 | for k := range c.Data { 45 | if bytes.Contains(b, []byte(k)) { 46 | r.Warnings = append(r.Warnings, fmt.Sprintf("Your Gopkg.toml contains the following import that need to be changed MANUALLY: %s", k)) 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func (c ImportConverter) processFile(p string, info os.FileInfo, err error) error { 54 | er := onlyRelevantFiles(p, info, err, func(p string) error { 55 | err := c.rewriteFile(p) 56 | if err != nil { 57 | err = err 58 | } 59 | 60 | return err 61 | }) 62 | 63 | return er 64 | } 65 | 66 | func (c ImportConverter) rewriteFile(name string) error { 67 | // create an empty fileset. 68 | fset := token.NewFileSet() 69 | 70 | // parse the .go file. 71 | // we are parsing the entire file with comments, so we don't lose anything 72 | // if we need to write it back out. 73 | f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) 74 | if err != nil { 75 | e := err.Error() 76 | msg := "expected 'package', found 'EOF'" 77 | if e[len(e)-len(msg):] == msg { 78 | return nil 79 | } 80 | return err 81 | } 82 | 83 | changed := false 84 | funcs := []*ast.FuncDecl{} 85 | for _, d := range f.Decls { 86 | if fn, isFn := d.(*ast.FuncDecl); isFn { 87 | funcs = append(funcs, fn) 88 | } 89 | } 90 | 91 | for _, fun := range funcs { 92 | ast.Inspect(fun, func(node ast.Node) bool { 93 | switch n := node.(type) { 94 | case *ast.CallExpr: 95 | fn, ok := n.Fun.(*ast.SelectorExpr) 96 | if !ok || fn.Sel == nil { 97 | return true 98 | } 99 | 100 | sel := fn.Sel 101 | i, ok := fn.X.(*ast.Ident) 102 | if !ok { 103 | return true 104 | } 105 | if i.Name != "packr" { 106 | return true 107 | } 108 | if sel.Name == "NewBox" { 109 | sel.Name = "New" 110 | n.Args = append(n.Args, n.Args[0]) 111 | changed = true 112 | } 113 | if sel.Name == "MustBytes" { 114 | sel.Name = "Find" 115 | changed = true 116 | } 117 | if sel.Name == "MustBytes" { 118 | sel.Name = "Find" 119 | changed = true 120 | } 121 | } 122 | return true 123 | }) 124 | } 125 | 126 | for key, value := range c.Data { 127 | if !astutil.DeleteImport(fset, f, key) { 128 | continue 129 | } 130 | 131 | astutil.AddImport(fset, f, value) 132 | changed = true 133 | } 134 | 135 | commentsChanged, err := c.handleFileComments(f) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | changed = changed || commentsChanged 141 | 142 | // if no change occurred, then we don't need to write to disk, just return. 143 | if !changed { 144 | return nil 145 | } 146 | 147 | // since the imports changed, resort them. 148 | ast.SortImports(fset, f) 149 | 150 | // create a temporary file, this easily avoids conflicts. 151 | temp, err := writeTempResult(name, fset, f) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | // rename the .temp to .go 157 | return os.Rename(temp, name) 158 | } 159 | 160 | func (c ImportConverter) handleFileComments(f *ast.File) (bool, error) { 161 | change := false 162 | 163 | for _, cg := range f.Comments { 164 | for _, cl := range cg.List { 165 | if !strings.HasPrefix(cl.Text, "// import \"") { 166 | continue 167 | } 168 | 169 | // trim off extra comment stuff 170 | ctext := cl.Text 171 | ctext = strings.TrimPrefix(ctext, "// import") 172 | ctext = strings.TrimSpace(ctext) 173 | 174 | // unquote the comment import path value 175 | ctext, err := strconv.Unquote(ctext) 176 | if err != nil { 177 | return false, err 178 | } 179 | 180 | // match the comment import path with the given replacement map 181 | if ctext, ok := c.match(ctext); ok { 182 | cl.Text = "// import " + strconv.Quote(ctext) 183 | change = true 184 | } 185 | 186 | } 187 | } 188 | 189 | return change, nil 190 | } 191 | 192 | // match takes an import path and replacement map. 193 | func (c ImportConverter) match(importpath string) (string, bool) { 194 | for key, value := range c.Data { 195 | if !strings.HasPrefix(importpath, key) { 196 | continue 197 | } 198 | 199 | result := strings.Replace(importpath, key, value, 1) 200 | return result, true 201 | } 202 | 203 | return importpath, false 204 | } 205 | 206 | //onlyRelevantFiles processes only .go files excluding folders like node_modules and vendor. 207 | func onlyRelevantFiles(p string, fi os.FileInfo, err error, fn func(p string) error) error { 208 | if err != nil { 209 | return err 210 | } 211 | 212 | if fi.IsDir() && p != "." { 213 | for _, n := range []string{"_", ".", "vendor", "node_modules", ".git"} { 214 | base := filepath.Base(p) 215 | if strings.HasPrefix(base, n) { 216 | return filepath.SkipDir 217 | } 218 | } 219 | 220 | return nil 221 | } 222 | 223 | ext := filepath.Ext(p) 224 | if ext != ".go" { 225 | return nil 226 | } 227 | 228 | return fn(p) 229 | } 230 | 231 | func writeTempResult(name string, fset *token.FileSet, f *ast.File) (string, error) { 232 | temp := name + ".temp" 233 | w, err := os.Create(temp) 234 | if err != nil { 235 | return "", err 236 | } 237 | 238 | // write changes to .temp file, and include proper formatting. 239 | err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(w, fset, f) 240 | if err != nil { 241 | return "", err 242 | } 243 | 244 | // close the writer 245 | err = w.Close() 246 | if err != nil { 247 | return "", err 248 | } 249 | 250 | return temp, nil 251 | } 252 | -------------------------------------------------------------------------------- /packr2/cmd/fix/runner.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import ( 4 | "fmt" 5 | 6 | packr "github.com/gobuffalo/packr/v2" 7 | ) 8 | 9 | // Check interface for runnable checker functions 10 | type Check func(*Runner) error 11 | 12 | // Runner will run all compatible checks 13 | type Runner struct { 14 | Warnings []string 15 | } 16 | 17 | // Run all compatible checks 18 | func Run() error { 19 | fmt.Printf("! This updater will attempt to update your application to packr version: %s\n", packr.Version) 20 | if !ask("Do you wish to continue?") { 21 | fmt.Println("~~~ cancelling update ~~~") 22 | return nil 23 | } 24 | 25 | r := &Runner{ 26 | Warnings: []string{}, 27 | } 28 | 29 | defer func() { 30 | if len(r.Warnings) == 0 { 31 | return 32 | } 33 | 34 | fmt.Println("\n\n----------------------------") 35 | fmt.Printf("!!! (%d) Warnings Were Found !!!\n\n", len(r.Warnings)) 36 | for _, w := range r.Warnings { 37 | fmt.Printf("[WARNING]: %s\n", w) 38 | } 39 | }() 40 | 41 | for _, c := range checks { 42 | if err := c(r); err != nil { 43 | return err 44 | } 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /packr2/cmd/gocmd.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/gobuffalo/packr/v2/plog" 11 | ) 12 | 13 | func goCmd(name string, args ...string) error { 14 | cargs := []string{name} 15 | cargs = append(cargs, args...) 16 | if len(args) > 0 { 17 | err := func() error { 18 | path := "." 19 | 20 | pwd, err := os.Getwd() 21 | if err != nil { 22 | return err 23 | } 24 | 25 | if fi, err := os.Stat(filepath.Join(pwd, args[len(args)-1])); err == nil { 26 | if fi.IsDir() { 27 | return nil 28 | } 29 | path = fi.Name() 30 | } 31 | 32 | if filepath.Ext(path) != ".go" { 33 | return nil 34 | } 35 | 36 | path, err = filepath.Abs(filepath.Dir(path)) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | files, err := ioutil.ReadDir(path) 42 | if err != nil { 43 | return err 44 | } 45 | for _, f := range files { 46 | if strings.HasSuffix(f.Name(), "-packr.go") { 47 | cargs = append(cargs, f.Name()) 48 | } 49 | } 50 | return nil 51 | }() 52 | if err != nil { 53 | return err 54 | } 55 | } 56 | 57 | goBin := os.Getenv("GO_BIN") 58 | if goBin == "" { 59 | goBin = "go" 60 | } 61 | cp := exec.Command(goBin, cargs...) 62 | plog.Logger.Debug(strings.Join(cp.Args, " ")) 63 | cp.Stderr = os.Stderr 64 | cp.Stdin = os.Stdin 65 | cp.Stdout = os.Stdout 66 | return cp.Run() 67 | } 68 | -------------------------------------------------------------------------------- /packr2/cmd/install.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gobuffalo/packr/v2/jam" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | const dont = `Please don't. 11 | The following commands have been deprecated and should not be used: 12 | 13 | * packr2 build 14 | * packr2 install 15 | 16 | They are, I'll be kind and say, "problematic" and cause more issues 17 | than than the actually solve. Sorry about that. My bad. 18 | 19 | It is recommended you use two commands instead: 20 | 21 | $ packr2 22 | $ go build/install 23 | ` 24 | 25 | var installCmd = &cobra.Command{ 26 | Use: "install", 27 | Short: "Don't. ru", 28 | DisableFlagParsing: true, 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | cargs := parseArgs(args) 31 | if globalOptions.Verbose { 32 | fmt.Println(dont) 33 | } 34 | if err := jam.Pack(globalOptions.PackOptions); err != nil { 35 | return err 36 | } 37 | return goCmd("install", cargs...) 38 | }, 39 | } 40 | 41 | func init() { 42 | rootCmd.AddCommand(installCmd) 43 | } 44 | -------------------------------------------------------------------------------- /packr2/cmd/pack.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | func parseArgs(args []string) []string { 4 | var cargs []string 5 | for _, a := range args { 6 | if a == "--legacy" { 7 | globalOptions.Legacy = true 8 | continue 9 | } 10 | if a == "--verbose" { 11 | globalOptions.Verbose = true 12 | continue 13 | } 14 | if a == "--silent" { 15 | globalOptions.Silent = true 16 | continue 17 | } 18 | if a == "--ignore-imports" { 19 | globalOptions.IgnoreImports = true 20 | continue 21 | } 22 | cargs = append(cargs, a) 23 | } 24 | return cargs 25 | } 26 | -------------------------------------------------------------------------------- /packr2/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/gobuffalo/logger" 8 | "github.com/gobuffalo/packr/v2/jam" 9 | "github.com/gobuffalo/packr/v2/plog" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var globalOptions = struct { 14 | jam.PackOptions 15 | Verbose bool 16 | Silent bool 17 | }{ 18 | PackOptions: jam.PackOptions{}, 19 | } 20 | 21 | var rootCmd = &cobra.Command{ 22 | Use: "packr2", 23 | Short: "Packr is a simple solution for bundling static assets inside of Go binaries.", 24 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 25 | for _, a := range args { 26 | if a == "--legacy" { 27 | globalOptions.Legacy = true 28 | continue 29 | } 30 | if a == "-v" || a == "--verbose" { 31 | globalOptions.Verbose = true 32 | continue 33 | } 34 | } 35 | 36 | // if the last argument is a .go file or directory we should 37 | // find boxes from there, not from the current directory. 38 | // packr2 build -v cmd/main.go 39 | if len(args) > 0 { 40 | i := len(args) - 1 41 | dir := args[i] 42 | if _, err := os.Stat(dir); err == nil { 43 | if filepath.Ext(dir) == ".go" { 44 | dir = filepath.Dir(dir) 45 | } 46 | os.Chdir(dir) 47 | args[i] = filepath.Base(args[i]) 48 | } 49 | } 50 | 51 | if globalOptions.Verbose { 52 | plog.Logger = logger.New(logger.DebugLevel) 53 | } 54 | if globalOptions.Silent { 55 | plog.Logger = logger.New(logger.FatalLevel) 56 | } 57 | return nil 58 | }, 59 | RunE: func(cmd *cobra.Command, args []string) error { 60 | opts := globalOptions.PackOptions 61 | roots := opts.Roots 62 | roots = append(roots, args...) 63 | opts.Roots = roots 64 | return jam.Pack(opts) 65 | }, 66 | } 67 | 68 | // Execute adds all child commands to the root command and sets flags appropriately. 69 | // This is called by main.main(). It only needs to happen once to the rootCmd. 70 | func Execute() { 71 | if err := rootCmd.Execute(); err != nil { 72 | os.Exit(1) 73 | } 74 | } 75 | 76 | func init() { 77 | rootCmd.PersistentFlags().BoolVarP(&globalOptions.Verbose, "verbose", "v", false, "enables verbose logging") 78 | rootCmd.PersistentFlags().BoolVar(&globalOptions.Legacy, "legacy", false, "uses the legacy resolution and packing system (assumes first arg || pwd for input path)") 79 | rootCmd.PersistentFlags().BoolVar(&globalOptions.Silent, "silent", false, "silences all output") 80 | rootCmd.PersistentFlags().BoolVar(&globalOptions.IgnoreImports, "ignore-imports", false, "when set to true packr won't resolve imports for boxes") 81 | rootCmd.PersistentFlags().StringVar(&globalOptions.StoreCmd, "store-cmd", "", "sub command to use for packing") 82 | } 83 | -------------------------------------------------------------------------------- /packr2/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | packr "github.com/gobuffalo/packr/v2" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var versionCmd = &cobra.Command{ 11 | Use: "version", 12 | Short: "shows packr version", 13 | RunE: func(cmd *cobra.Command, args []string) error { 14 | fmt.Println(packr.Version) 15 | return nil 16 | }, 17 | } 18 | 19 | func init() { 20 | rootCmd.AddCommand(versionCmd) 21 | } 22 | -------------------------------------------------------------------------------- /packr2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gobuffalo/packr/v2/packr2/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /packr_test.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import "github.com/gobuffalo/packr/v2/file" 4 | 5 | func qfile(name string, body string) File { 6 | f, err := file.NewFile(name, []byte(body)) 7 | if err != nil { 8 | panic(err) 9 | } 10 | return f 11 | } 12 | -------------------------------------------------------------------------------- /plog/plog.go: -------------------------------------------------------------------------------- 1 | package plog 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/gobuffalo/logger" 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | var Logger = logger.New(logger.ErrorLevel) 12 | 13 | func Debug(t interface{}, m string, args ...interface{}) { 14 | if len(args)%2 == 1 { 15 | args = append(args, "") 16 | } 17 | f := logrus.Fields{} 18 | for i := 0; i < len(args); i += 2 { 19 | k := fmt.Sprint(args[i]) 20 | v := args[i+1] 21 | if s, ok := v.(fmt.Stringer); ok { 22 | f[k] = s.String() 23 | continue 24 | } 25 | if s, ok := v.(string); ok { 26 | f[k] = s 27 | continue 28 | } 29 | if b, err := json.Marshal(v); err == nil { 30 | f[k] = string(b) 31 | continue 32 | } 33 | f[k] = v 34 | } 35 | e := Logger.WithFields(f) 36 | if s, ok := t.(string); ok { 37 | e.Debugf("%s#%s", s, m) 38 | return 39 | } 40 | e.Debugf("%T#%s", t, m) 41 | } 42 | -------------------------------------------------------------------------------- /pointer.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "github.com/gobuffalo/packr/v2/file" 5 | "github.com/gobuffalo/packr/v2/file/resolver" 6 | "github.com/gobuffalo/packr/v2/plog" 7 | ) 8 | 9 | // Pointer is a resolvr which resolves 10 | // a file from a different box. 11 | type Pointer struct { 12 | ForwardBox string 13 | ForwardPath string 14 | } 15 | 16 | var _ resolver.Resolver = Pointer{} 17 | 18 | // Resolve attempts to find the file in the specific box 19 | // with the specified key 20 | func (p Pointer) Resolve(box string, path string) (file.File, error) { 21 | plog.Debug(p, "Resolve", "box", box, "path", path, "forward-box", p.ForwardBox, "forward-path", p.ForwardPath) 22 | b, err := findBox(p.ForwardBox) 23 | if err != nil { 24 | return nil, err 25 | } 26 | f, err := b.Resolve(p.ForwardPath) 27 | if err != nil { 28 | return f, err 29 | } 30 | plog.Debug(p, "Resolve", "box", box, "path", path, "file", f) 31 | return file.NewFileR(path, f) 32 | } 33 | -------------------------------------------------------------------------------- /pointer_test.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gobuffalo/packr/v2/file/resolver" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_Pointer_Find(t *testing.T) { 11 | r := require.New(t) 12 | 13 | b1 := New("b1", "") 14 | r.NoError(b1.AddString("foo.txt", "FOO!")) 15 | 16 | b2 := New("b2", "") 17 | b2.SetResolver("bar.txt", &Pointer{ 18 | ForwardBox: "b1", 19 | ForwardPath: "foo.txt", 20 | }) 21 | 22 | s, err := b2.FindString("bar.txt") 23 | r.NoError(err) 24 | r.Equal("FOO!", s) 25 | } 26 | 27 | func Test_Pointer_Find_CorrectName(t *testing.T) { 28 | r := require.New(t) 29 | 30 | gk := "0b5bab905480ad8c6d0695f615dcd644" 31 | g := New(gk, "") 32 | hgr, err := resolver.NewHexGzip(map[string]string{ 33 | "48df4e44f4202fe5f6093beee782cb10": "1f8b08000000000000ff4c8ebdaec2300c46f7fb14bed94b5606a70b3f6283a1083186c46a2225354aad56bc3d6a2304933fdbc73ac6fffd79d7dd2f07089253fb87b5006020eb97008099c4820bb68c24465dbb63b355a07f9783cd64d414697e7211058e07a1418c9aa397603c4dd151b336df4b8992a83d514a0c372ec9a3aea345af3f7e7cb07fad21e61ec6e28cd2897bde8c53af2a5a09d4f5f777000000ffffcfb8b477d3000000", 34 | }) 35 | r.NoError(err) 36 | g.DefaultResolver = hgr 37 | 38 | b := New("my box", "./templates") 39 | b.SetResolver("index.html", Pointer{ForwardBox: gk, ForwardPath: "48df4e44f4202fe5f6093beee782cb10"}) 40 | f, err := b.Resolve("index.html") 41 | r.NoError(err) 42 | fi, err := f.Stat() 43 | r.NoError(err) 44 | r.Equal("index.html", fi.Name()) 45 | } 46 | -------------------------------------------------------------------------------- /resolvers_map.go: -------------------------------------------------------------------------------- 1 | //go:generate mapgen -name "resolvers" -zero "nil" -go-type "resolver.Resolver" -pkg "" -a "nil" -b "nil" -c "nil" -bb "nil" -destination "packr" 2 | // Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT. 3 | 4 | package packr 5 | 6 | import ( 7 | "sort" 8 | "sync" 9 | 10 | "github.com/gobuffalo/packr/v2/file/resolver" 11 | ) 12 | 13 | // resolversMap wraps sync.Map and uses the following types: 14 | // key: string 15 | // value: resolver.Resolver 16 | type resolversMap struct { 17 | data sync.Map 18 | } 19 | 20 | // Delete the key from the map 21 | func (m *resolversMap) Delete(key string) { 22 | m.data.Delete(key) 23 | } 24 | 25 | // Load the key from the map. 26 | // Returns resolver.Resolver or bool. 27 | // A false return indicates either the key was not found 28 | // or the value is not of type resolver.Resolver 29 | func (m *resolversMap) Load(key string) (resolver.Resolver, bool) { 30 | i, ok := m.data.Load(key) 31 | if !ok { 32 | return nil, false 33 | } 34 | s, ok := i.(resolver.Resolver) 35 | return s, ok 36 | } 37 | 38 | // LoadOrStore will return an existing key or 39 | // store the value if not already in the map 40 | func (m *resolversMap) LoadOrStore(key string, value resolver.Resolver) (resolver.Resolver, bool) { 41 | i, _ := m.data.LoadOrStore(key, value) 42 | s, ok := i.(resolver.Resolver) 43 | return s, ok 44 | } 45 | 46 | // Range over the resolver.Resolver values in the map 47 | func (m *resolversMap) Range(f func(key string, value resolver.Resolver) bool) { 48 | m.data.Range(func(k, v interface{}) bool { 49 | key, ok := k.(string) 50 | if !ok { 51 | return false 52 | } 53 | value, ok := v.(resolver.Resolver) 54 | if !ok { 55 | return false 56 | } 57 | return f(key, value) 58 | }) 59 | } 60 | 61 | // Store a resolver.Resolver in the map 62 | func (m *resolversMap) Store(key string, value resolver.Resolver) { 63 | m.data.Store(key, value) 64 | } 65 | 66 | // Keys returns a list of keys in the map 67 | func (m *resolversMap) Keys() []string { 68 | var keys []string 69 | m.Range(func(key string, value resolver.Resolver) bool { 70 | keys = append(keys, key) 71 | return true 72 | }) 73 | sort.Strings(keys) 74 | return keys 75 | } 76 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | // Version of Packr 4 | const Version = "v2.8.3" 5 | -------------------------------------------------------------------------------- /walk.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | "github.com/gobuffalo/packd" 8 | "github.com/gobuffalo/packr/v2/file" 9 | "github.com/gobuffalo/packr/v2/file/resolver" 10 | "github.com/gobuffalo/packr/v2/plog" 11 | ) 12 | 13 | // WalkFunc is used to walk a box 14 | type WalkFunc = packd.WalkFunc 15 | 16 | // Walk will traverse the box and call the WalkFunc for each file in the box/folder. 17 | func (b *Box) Walk(wf WalkFunc) error { 18 | m := map[string]file.File{} 19 | 20 | dr := b.DefaultResolver 21 | if dr == nil { 22 | cd := resolver.OsPath(b.ResolutionDir) 23 | dr = &resolver.Disk{Root: cd} 24 | } 25 | if fm, ok := dr.(file.FileMappable); ok { 26 | for n, f := range fm.FileMap() { 27 | m[n] = f 28 | } 29 | } 30 | var err error 31 | b.resolvers.Range(func(n string, r resolver.Resolver) bool { 32 | var f file.File 33 | f, err = r.Resolve("", n) 34 | if err != nil { 35 | return false 36 | } 37 | keep := true 38 | for k := range m { 39 | if strings.EqualFold(k, n) { 40 | keep = false 41 | } 42 | } 43 | if keep { 44 | m[n] = f 45 | } 46 | return true 47 | }) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | var keys = make([]string, 0, len(m)) 53 | for k := range m { 54 | keys = append(keys, k) 55 | } 56 | sort.Strings(keys) 57 | 58 | for _, k := range keys { 59 | osPath := resolver.OsPath(k) 60 | plog.Debug(b, "Walk", "path", k, "osPath", osPath) 61 | if err := wf(osPath, m[k]); err != nil { 62 | return err 63 | } 64 | } 65 | return nil 66 | } 67 | 68 | // WalkPrefix will call box.Walk and call the WalkFunc when it finds paths that have a matching prefix 69 | func (b *Box) WalkPrefix(prefix string, wf WalkFunc) error { 70 | ipref := resolver.OsPath(prefix) 71 | return b.Walk(func(path string, f File) error { 72 | ipath := resolver.OsPath(path) 73 | if strings.HasPrefix(ipath, ipref) { 74 | if err := wf(path, f); err != nil { 75 | return err 76 | } 77 | } 78 | return nil 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /walk_test.go: -------------------------------------------------------------------------------- 1 | package packr 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/gobuffalo/packr/v2/file" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func Test_Box_Walk(t *testing.T) { 12 | r := require.New(t) 13 | 14 | box := NewBox(filepath.Join("_fixtures", "list_test")) 15 | r.NoError(box.AddString(filepath.Join("d", "d.txt"), "D")) 16 | 17 | var act []string 18 | r.NoError(box.Walk(func(path string, f file.File) error { 19 | act = append(act, path) 20 | return nil 21 | })) 22 | exp := []string{"a.txt", filepath.Join("b", "b.txt"), filepath.Join("b", "b2.txt"), filepath.Join("c", "c.txt"), filepath.Join("d", "d.txt")} 23 | r.Equal(exp, act) 24 | } 25 | 26 | func Test_Box_WalkPrefix(t *testing.T) { 27 | r := require.New(t) 28 | 29 | box := NewBox(filepath.Join("_fixtures", "list_test")) 30 | r.NoError(box.AddString(filepath.Join("d", "d.txt"), "D")) 31 | 32 | var act []string 33 | r.NoError(box.WalkPrefix("b/", func(path string, f file.File) error { 34 | act = append(act, path) 35 | return nil 36 | })) 37 | exp := []string{filepath.Join("b", "b.txt"), filepath.Join("b", "b2.txt")} 38 | r.Equal(exp, act) 39 | } 40 | --------------------------------------------------------------------------------