Compare commits

..

No commits in common. "master" and "out-flag" have entirely different histories.

61 changed files with 8119 additions and 871 deletions

4
.github/FUNDING.yml vendored
View File

@ -1,4 +0,0 @@
# These are supported funding model platforms
github: markbates
patreon: buffalo

View File

@ -1,28 +0,0 @@
name: Release
on:
release:
types:
- published
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
-
name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
-
name: Checkout Code
uses: actions/checkout@master
-
name: Run GoReleaser
env:
GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --rm-dist

View File

@ -1,25 +0,0 @@
name: Tests
on: [push]
jobs:
tests:
name: ${{matrix.os}} Tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Test
run: |
go mod download
go mod tidy -v
go test -race ./...

View File

@ -1,45 +1,33 @@
# Code generated by github.com/gobuffalo/release. DO NOT EDIT.
# Edit .goreleaser.yml.plush instead
builds:
-
main: ./cmd/pkger/main.go
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X "github.com/markbates/pkger.Version={{.Tag}}"
goos:
- darwin
- linux
- windows
goarch:
- amd64
- 386
- arm
- arm64
goarm:
- 6
- 7
archives:
-
replacements:
'386': i386
darwin: Darwin
linux: Linux
windows: Windows
amd64: x86_64
-
goos:
- darwin
- linux
- windows
env:
- CGO_ENABLED=0
main: ./cmd/pkger/main.go
checksum:
name_template: checksums.txt
name_template: 'checksums.txt'
snapshot:
name_template: '{{ .Tag }}-next'
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
brews:
-
name: 'pkger'
github:
owner: 'markbates'
name: 'homebrew-tap'
install: |
bin.install "pkger"
-
name: pkger
github:
owner: markbates
name: homebrew-tap

30
.goreleaser.yml.plush Normal file
View File

@ -0,0 +1,30 @@
builds:
-
goos:
- darwin
- linux
- windows
env:
- CGO_ENABLED=0
main: ./cmd/pkger/main.go
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
<%= if (brew) { %>
brews:
-
name: pkger
github:
owner: markbates
name: homebrew-tap
<% } %>

View File

@ -17,7 +17,7 @@ build: tidy
make tidy
test: tidy
$(GO_BIN) test -count 1 -cover -tags ${TAGS} -timeout 1m ./...
$(GO_BIN) test -count 1 -cover -tags ${TAGS} -timeout 10s ./...
make tidy
cov:

387
README.md
View File

@ -2,12 +2,7 @@
[`github.com/markbates/pkger`](https://godoc.org/github.com/markbates/pkger) is a tool for embedding static files into Go binaries. It will, hopefully, be a replacement for [`github.com/gobuffalo/packr/v2`](https://godoc.org/github.com/gobuffalo/packr/v2).
### Requirements
* Go 1.13+
* Go Modules
## How it Works (Module Aware Pathing)
## How it Works
Pkger is powered by the dark magic of Go Modules, so they're like, totally required.
@ -24,142 +19,16 @@ Paths:
* Packages can specified in at the beginning of a path with a `:` seperator.
github.com/markbates/pkger:/cmd/pkger/main.go
* There are no relative paths. All paths are absolute to the modules root.
* Fully-qualified paths are embedded into the metadata of your static assets. If this behavior is undesirable, a preference is to build in a containerized environ, like docker, where the path strings are not ex-filtrating data about your development environment.
```
"github.com/gobuffalo/buffalo:/go.mod" => /go/pkg/mod/github.com/gobuffalo/buffalo@v0.14.7/go.mod
"github.com/gobuffalo/buffalo:/go.mod" => $GOPATH/pkg/mod/github.com/gobuffalo/buffalo@v0.14.7/go.mod
```
## CLI
### Installation
```bash
$ go get github.com/markbates/pkger/cmd/pkger
$ pkger -h
```
### Usage
```bash
$ pkger
```
The result will be a `pkged.go` file in the **root** of the module with the embedded information and the package name of the module.
```go
// ./pkged.go
package <.>
// Pkger stuff here
```
The `-o` flag can be used to specify the directory of the `pkged.go` file.
```bash
$ pkger -o cmd/reader
```
The result will be a `pkged.go` file in the **cmd/reader** folder with the embedded information and the package name of that folder.
```go
// cmd/reader/pkged.go
package <reader>
// Pkger stuff here
```
### Including Files at Package Time
There may be reasons where you don't reference a particular file, or folder, that you want embedded in your application, such as a build artifact.
To do this you may use either the [`github.com/markbates/pkger#Include`](https://godoc.org/github.com/markbates/pkger#Include) function to set a no-op parser directive to include the specified path.
Alternatively, you may use the `-include` flag with the `pkger` and `pkger list` commands.
```bash
$ pkger list -include /actions -include github.com/gobuffalo/buffalo:/app.go
app
> app:/actions
> app:/actions/actions.go
> app:/assets
> app:/assets/css
> app:/assets/css/_buffalo.scss
> app:/assets/css/application.scss
> app:/assets/images
> app:/assets/images/favicon.ico
> app:/assets/images/logo.svg
> app:/assets/js
> app:/assets/js/application.js
> app:/go.mod
> app:/locales/all.en-us.yaml
> app:/public
> app:/public/assets
> app:/public/assets/.keep
> app:/public/assets/app.css
> app:/public/images
> app:/public/images/img1.png
> app:/public/index.html
> app:/public/robots.txt
> app:/templates
> app:/templates/_flash.plush.html
> app:/templates/application.plush.html
> app:/templates/index.plush.html
> app:/web
> app:/web/web.go
> github.com/gobuffalo/buffalo:/app.go
> github.com/gobuffalo/buffalo:/logo.svg
```
## Reference Application
The reference application for the `README` examples, as well as all testing, can be found at [https://github.com/markbates/pkger/tree/master/pkging/pkgtest/testdata/ref](https://github.com/markbates/pkger/tree/master/pkging/pkgtest/testdata/ref).
```
├── actions
│   └── actions.go
├── assets
│   ├── css
│   │   ├── _buffalo.scss
│   │   └── application.scss
│   ├── images
│   │   ├── favicon.ico
│   │   └── logo.svg
│   └── js
│   └── application.js
├── go.mod
├── go.sum
├── locales
│   └── all.en-us.yaml
├── main.go
├── mod
│   └── mod.go
├── models
│   └── models.go
├── public
│   ├── assets
│   │   └── app.css
│   ├── images
│   │   └── img1.png
│   ├── index.html
│   └── robots.txt
├── templates
│   ├── _flash.plush.html
│   ├── application.plush.html
│   └── index.plush.html
└── web
└── web.go
13 directories, 20 files
```
## API Usage
## Usage
Pkger's API is modeled on that of the [`os`](https://godoc.org/os) package in Go's standard library. This makes Pkger usage familiar to Go developers.
The two most important interfaces are [`github.com/markbates/pkger/pkging#Pkger`](https://godoc.org/github.com/markbates/pkger/pkging#Pkger) and [`github.com/markbates/pkger/pkging#File`](https://godoc.org/github.com/markbates/pkger/pkging#File).
```go
type Pkger interface {
@ -189,56 +58,38 @@ type File interface {
}
```
These two interfaces, along with the [`os#FileInfo`](https://godoc.org/os#FileInfo), provide the bulk of the API surface area.
### Open
```go
func run() error {
f, err := pkger.Open("/public/index.html")
if err != nil {
return err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return err
}
fmt.Println("Name: ", info.Name())
fmt.Println("Size: ", info.Size())
fmt.Println("Mode: ", info.Mode())
fmt.Println("ModTime: ", info.ModTime())
if _, err := io.Copy(os.Stdout, f); err != nil {
return err
}
return nil
}
```bash
├── go.mod
├── go.sum
├── main.go
├── public
│   ├── images
│   │   ├── mark-small.png
│   │   ├── img1.png
│   │   ├── mark_250px.png
│   │   └── mark_400px.png
│   └── index.html
```
### Stat
```go
func run() error {
info, err := pkger.Stat("/public/index.html")
if err != nil {
return err
package main
import (
"fmt"
"log"
"os"
"text/tabwriter"
"time"
"github.com/markbates/pkger"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
fmt.Println("Name: ", info.Name())
fmt.Println("Size: ", info.Size())
fmt.Println("Mode: ", info.Mode())
fmt.Println("ModTime: ", info.ModTime())
return nil
}
```
### Walk
```go
func run() error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.Debug)
defer w.Flush()
@ -262,171 +113,31 @@ func run() error {
}
```
## Understanding the Parser
The [`github.com/markbates/pkger/parser#Parser`](https://godoc.org/github.com/markbates/pkger/parser#Parser) works by statically analyzing the source code of your module using the [`go/parser`](https://godoc.org/go/parser) to find a selection of declarations.
The following declarations in your source code will tell the parser to embed files or folders.
* `pkger.Dir("<path>")` - Embeds all files under the specified path.
* `pkger.Open("<path>")` - Embeds the file, or folder, of the specified path.
* `pkger.Stat("<path>")` - Embeds the file, or folder, of the specified path.
* `pkger.Walk("<path>", filepath.WalkFunc)` - Embeds all files under the specified path.
* `pkger.Include("<path>")` - `Include` is a no-op that directs the pkger tool to include the desired file or folder.
### CLI Usage
To see what declarations the parser has found, you can use the `pkger parse` command to get a `JSON` list of the declarations.
### Output Without Packing
```bash
$ pkger parse
# compile the go binary as usual and run the app:
$ go build -v; ./app
{
".": [
{
"file": {
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/foo/bar/baz",
"Path": {
"Pkg": "app",
"Name": "/foo/bar/baz"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"ImportPath": "app",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "main"
}
},
"pos": {
"Filename": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/main.go",
"Offset": 629,
"Line": 47,
"Column": 27
},
"type": "pkger.MkdirAll",
"value": "/foo/bar/baz"
},
{
"file": {
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/foo/bar/baz/biz.txt",
"Path": {
"Pkg": "app",
"Name": "/foo/bar/baz/biz.txt"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"ImportPath": "app",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "main"
}
},
"pos": {
"Filename": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/main.go",
"Offset": 706,
"Line": 51,
"Column": 25
},
"type": "pkger.Create",
"value": "/foo/bar/baz/biz.txt"
},
...
]
}
public | 128 | drwxr-xr-x | 2019-10-18T16:24:55-04:00 |
images | 128 | drwxr-xr-x | 2019-10-18T16:24:55-04:00 |
img1.png | 27718 | -rw-r--r-- | 2019-10-18T16:24:55-04:00 |
img2.png | 27718 | -rw-r--r-- | 2019-10-18T16:24:55-04:00 |
index.html | 257 | -rw-r--r-- | 2019-10-18T16:24:55-04:00 |
```
For a module aware list use the `pkger list` command.
### Output With Packing
```bash
$ pkger list
# run the pkger cli to generate a pkged.go file:
$ pkger
app
> app:/assets
> app:/assets/css
> app:/assets/css/_buffalo.scss
> app:/assets/css/application.scss
> app:/assets/images
> app:/assets/images/favicon.ico
> app:/assets/images/logo.svg
> app:/assets/js
> app:/assets/js/application.js
> app:/go.mod
> app:/locales/all.en-us.yaml
> app:/public
> app:/public/assets
> app:/public/assets/.keep
> app:/public/assets/app.css
> app:/public/images
> app:/public/images/img1.png
> app:/public/index.html
> app:/public/robots.txt
> app:/templates
> app:/templates/_flash.plush.html
> app:/templates/application.plush.html
> app:/templates/index.plush.html
> app:/web
> app:/web/web.go
> github.com/gobuffalo/buffalo:/logo.svg
# compile the go binary as usual and run the app:
$ go build -v; ./app
public | 128 | drwxr-xr-x | 2019-10-18T16:24:55-04:00 |
images | 128 | drwxr-xr-x | 2019-10-18T16:24:55-04:00 |
img1.png | 27718 | -rw-r--r-- | 2019-10-18T16:24:55-04:00 |
img2.png | 27718 | -rw-r--r-- | 2019-10-18T16:24:55-04:00 |
index.html | 257 | -rw-r--r-- | 2019-10-18T16:24:55-04:00 |
```
The `-json` flag can be used to get a more detailed list in JSON.
```bash
$ pkger list -json
{
"ImportPath": "app",
"Files": [
{
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"Path": {
"Pkg": "app",
"Name": "/assets"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"ImportPath": "",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "assets"
}
},
{
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets/css",
"Path": {
"Pkg": "app",
"Name": "/assets/css"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"ImportPath": "",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "assets"
}
},
...
}
```

View File

@ -1,3 +1,5 @@
// +build !debug
package pkger
import (

39
azure-pipelines.yml Normal file
View File

@ -0,0 +1,39 @@
variables:
GOPROXY: "https://proxy.golang.org"
GOBIN: "$(GOPATH)/bin" # Go binaries path
GOPATH: "$(system.defaultWorkingDirectory)/gopath" # Go workspace path
modulePath: "$(GOPATH)/src/github.com/$(build.repository.name)" # Path to the module"s code
jobs:
- job: Windows
pool:
vmImage: "vs2017-win2016"
strategy:
matrix:
go 1.13 (on):
go_version: "1.13.3"
GO111MODULE: "on"
steps:
- template: azure-tests.yml
- job: macOS
pool:
vmImage: "macOS-10.13"
strategy:
matrix:
go 1.13 (on):
go_version: "1.13.3"
GO111MODULE: "on"
steps:
- template: azure-tests.yml
- job: Linux
pool:
vmImage: "ubuntu-16.04"
strategy:
matrix:
go 1.13 (on):
go_version: "1.13.3"
GO111MODULE: "on"
steps:
- template: azure-tests.yml

20
azure-tests.yml Normal file
View File

@ -0,0 +1,20 @@
steps:
- task: GoTool@0
inputs:
version: $(go_version)
- task: Bash@3
inputs:
targetType: inline
script: |
mkdir -p "$(GOBIN)"
mkdir -p "$(GOPATH)/pkg"
mkdir -p "$(modulePath)"
shopt -s extglob
mv !(gopath) "$(modulePath)"
displayName: "Setup Go Workspace"
- script: |
go get github.com/gobuffalo/buffalo
go get -t -v ./...
go test -race ./...
workingDirectory: "$(modulePath)"
displayName: "Tests"

View File

@ -9,15 +9,16 @@ import (
"path/filepath"
"github.com/markbates/pkger"
"github.com/markbates/pkger/internal/takeon/github.com/markbates/hepa"
"github.com/markbates/pkger/internal/takeon/github.com/markbates/hepa/filters"
"github.com/markbates/pkger/parser"
)
type listCmd struct {
*flag.FlagSet
help bool
json bool
include slice
subs []command
help bool
json bool
subs []command
}
func (e *listCmd) Name() string {
@ -42,14 +43,14 @@ func (e *listCmd) Exec(args []string) error {
fp := filepath.Join(info.Dir, outName)
os.RemoveAll(fp)
decls, err := parser.Parse(info, e.include...)
decls, err := parser.Parse(info)
if err != nil {
return err
}
jay := struct {
ImportPath string
Files []*parser.File
ImportPath string `json:"import_path"`
Files []*parser.File `json:"files"`
}{
ImportPath: info.ImportPath,
}
@ -69,7 +70,15 @@ func (e *listCmd) Exec(args []string) error {
return err
}
_, err = os.Stdout.Write(bb.Bytes())
hep := hepa.New()
hep = hepa.With(hep, filters.Home())
hep = hepa.With(hep, filters.Golang())
b, err := hep.Filter(bb.Bytes())
if err != nil {
return err
}
_, err = os.Stdout.Write(b)
return err
}
@ -84,7 +93,6 @@ func (e *listCmd) Flags() *flag.FlagSet {
if e.FlagSet == nil {
e.FlagSet = flag.NewFlagSet("list", flag.ExitOnError)
e.BoolVar(&e.json, "json", false, "prints in JSON format")
e.Var(&e.include, "include", "packages the specified file or directory")
}
e.Usage = Usage(os.Stderr, e.FlagSet)
return e.FlagSet

View File

@ -14,25 +14,13 @@ import (
"github.com/markbates/pkger/pkging/pkgutil"
)
type slice []string
func (i slice) String() string {
return fmt.Sprintf("%s", []string(i))
}
func (i *slice) Set(value string) error {
*i = append(*i, value)
return nil
}
const outName = "pkged.go"
type packCmd struct {
*flag.FlagSet
out string
help bool
include slice
subs []command
out string
help bool
subs []command
}
func (e *packCmd) Name() string {
@ -48,12 +36,12 @@ func (e *packCmd) Exec(args []string) error {
fp := filepath.Join(info.Dir, e.out, outName)
os.RemoveAll(fp)
decls, err := parser.Parse(info, e.include...)
decls, err := parser.Parse(info)
if err != nil {
return err
}
if err := Package(info, fp, decls); err != nil {
if err := Package(fp, decls); err != nil {
return err
}
@ -104,7 +92,6 @@ func New() (*packCmd, error) {
c.FlagSet = flag.NewFlagSet("pkger", flag.ExitOnError)
c.BoolVar(&c.help, "h", false, "prints help information")
c.StringVar(&c.out, "o", "", "output directory for pkged.go")
c.Var(&c.include, "include", "packages the specified file or directory")
c.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage:\n\n")
Usage(os.Stderr, c.FlagSet)()
@ -123,18 +110,8 @@ func (e *packCmd) Flags() *flag.FlagSet {
return e.FlagSet
}
func Package(info here.Info, out string, decls parser.Decls) error {
c, err := here.Dir(filepath.Dir(out))
if err != nil {
return err
}
func Package(out string, decls parser.Decls) error {
os.RemoveAll(out)
defer func() {
if err := recover(); err != nil {
os.RemoveAll(out)
}
}()
f, err := os.Create(out)
if err != nil {
@ -142,14 +119,16 @@ func Package(info here.Info, out string, decls parser.Decls) error {
}
defer f.Close()
fmt.Fprintf(f, "// Code generated by pkger; DO NOT EDIT.\n\n")
fmt.Fprintf(f, "// +build !skippkger\n\n")
c, err := here.Dir(filepath.Dir(out))
if err != nil {
return err
}
fmt.Fprintf(f, "package %s\n\n", c.Name)
fmt.Fprintf(f, "import (\n\t\"github.com/markbates/pkger\"\n\t")
fmt.Fprintf(f, "\"github.com/markbates/pkger/pkging/mem\"\n)\n\n")
fmt.Fprintf(f, "var _ = pkger.Apply(mem.UnmarshalEmbed([]byte(`")
fmt.Fprintf(f, "import \"github.com/markbates/pkger\"\n\n")
fmt.Fprintf(f, "import \"github.com/markbates/pkger/pkging/mem\"\n\n")
fmt.Fprintf(f, "\nvar _ = pkger.Apply(mem.UnmarshalEmbed([]byte(`")
if err := pkgutil.Stuff(f, info, decls); err != nil {
if err := pkgutil.Stuff(f, c, decls); err != nil {
return err
}

View File

@ -45,21 +45,11 @@ func (c *parseCmd) Exec(args []string) error {
m := map[string]parser.Decls{}
for _, a := range args {
var info here.Info
var err error
if a == "." {
info, err = here.Dir(a)
if err != nil {
return err
}
} else {
info, err = here.Package(a)
if err != nil {
return err
}
info, err := here.Package(a)
if err != nil {
return err
}
decls, err := parser.Parse(info)
if err != nil {
return err

View File

@ -10,7 +10,7 @@ import (
func main() {
clean := func() {
c := exec.Command("go", "mod", "tidy")
c := exec.Command("go", "mod", "tidy", "-v")
c.Stdout = os.Stdout
c.Stderr = os.Stderr
c.Stdin = os.Stdin
@ -18,13 +18,6 @@ func main() {
}
defer clean()
defer func() {
if err := recover(); err != nil {
clean()
log.Fatal(err)
}
}()
if err := run(); err != nil {
clean()
log.Fatal(err)

View File

@ -1,10 +1,6 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobuffalo/here v0.5.2-0.20191203194143-8e97fc9ad6d3 h1:9NGxHWbGFujaZ5vT+uXurb9m3QoWa175cYM2Qs97nQo=
github.com/gobuffalo/here v0.5.2-0.20191203194143-8e97fc9ad6d3/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -22,5 +18,3 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

5
go.mod
View File

@ -3,6 +3,9 @@ module github.com/markbates/pkger
go 1.13
require (
github.com/gobuffalo/here v0.6.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/stretchr/testify v1.4.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.4 // indirect
)

10
go.sum
View File

@ -1,8 +1,7 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -10,13 +9,14 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

29
here/current.go Normal file
View File

@ -0,0 +1,29 @@
package here
import (
"path/filepath"
"sync"
)
var curOnce sync.Once
var curErr error
var current Info
func Current() (Info, error) {
(&curOnce).Do(func() {
b, err := run("go", "env", "GOMOD")
if err != nil {
curErr = err
return
}
root := filepath.Dir(string(b))
i, err := Dir(root)
if err != nil {
curErr = err
return
}
current = i
})
return current, curErr
}

77
here/dir.go Normal file
View File

@ -0,0 +1,77 @@
package here
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
// Dir attempts to gather info for the requested directory.
func Dir(p string) (Info, error) {
i, err := Cache(p, func(p string) (Info, error) {
var i Info
fi, err := os.Stat(p)
if err != nil {
return i, err
}
if !fi.IsDir() {
p = filepath.Dir(p)
}
pwd, err := os.Getwd()
if err != nil {
return i, err
}
defer os.Chdir(pwd)
os.Chdir(p)
b, err := run("go", "list", "-json")
// go: cannot find main module; see 'go help modules'
// build .: cannot find module for path .
// no Go files in
if err != nil {
if nonGoDirRx.MatchString(err.Error()) {
return fromNonGoDir(p)
}
return i, fmt.Errorf("%w %s", err, p)
}
if err := json.Unmarshal(b, &i); err != nil {
return i, err
}
return i, nil
})
if err != nil {
return i, err
}
return Cache(i.ImportPath, func(p string) (Info, error) {
return i, nil
})
}
func fromNonGoDir(dir string) (Info, error) {
i := Info{
Dir: dir,
Name: filepath.Base(dir),
}
b, err := run("go", "list", "-json", "-m")
if err != nil {
return i, err
}
if err := json.Unmarshal(b, &i.Module); err != nil {
return i, err
}
return i, err
}

67
here/dir_test.go Normal file
View File

@ -0,0 +1,67 @@
package here_test
import (
"os"
"path/filepath"
"testing"
"github.com/markbates/pkger/here"
"github.com/markbates/pkger/pkging/pkgtest"
"github.com/stretchr/testify/require"
)
func Test_Dir(t *testing.T) {
r := require.New(t)
ref, err := pkgtest.NewRef()
r.NoError(err)
root := ref.Dir
r.NoError(err)
defer os.RemoveAll(root)
public := filepath.Join(root, "public")
r.NoError(os.MkdirAll(public, 0755))
gf := filepath.Join(root, "cmd", "main.go")
r.NoError(os.MkdirAll(filepath.Dir(gf), 0755))
f, err := os.Create(gf)
r.NoError(err)
_, err = f.Write([]byte("package main"))
r.NoError(err)
r.NoError(f.Close())
table := []struct {
in string
err bool
}{
{in: root, err: false},
{in: public, err: false},
{in: gf, err: false},
{in: filepath.Join(root, "."), err: false},
{in: "/unknown", err: true},
}
for _, tt := range table {
t.Run(tt.in, func(st *testing.T) {
here.ClearCache()
r := require.New(st)
info, err := here.Dir(tt.in)
if tt.err {
r.Error(err)
return
}
r.NoError(err)
r.NotZero(info)
r.NotZero(info.Dir)
r.NotZero(info.Name)
r.NotZero(info.Module)
})
}
}

View File

@ -1,14 +1,48 @@
package here
import (
"github.com/gobuffalo/here"
"bytes"
"fmt"
"os/exec"
"regexp"
"sync"
)
type Info = here.Info
type Module = here.Module
type Path = here.Path
var cache = &infoMap{
data: &sync.Map{},
}
var Here = here.New()
var Dir = Here.Dir
var Package = Here.Package
var Current = Here.Current
func run(n string, args ...string) ([]byte, error) {
c := exec.Command(n, args...)
bb := &bytes.Buffer{}
c.Stdout = bb
c.Stderr = bb
err := c.Run()
if err != nil {
return nil, fmt.Errorf("%w: %s", err, bb)
}
return bb.Bytes(), nil
}
func Cache(p string, fn func(string) (Info, error)) (Info, error) {
i, ok := cache.Load(p)
if ok {
return i, nil
}
i, err := fn(p)
if err != nil {
return i, err
}
cache.Store(p, i)
return i, nil
}
func ClearCache() {
cache = &infoMap{
data: &sync.Map{},
}
}
var nonGoDirRx = regexp.MustCompile(`cannot find main|go help modules|go: |build .:|no Go files`)

32
here/here_test.go Normal file
View File

@ -0,0 +1,32 @@
package here
import (
"testing"
"github.com/stretchr/testify/require"
)
func Test_nonGoDirRx(t *testing.T) {
r := require.New(t)
r.False(nonGoDirRx.MatchString(""))
r.False(nonGoDirRx.MatchString("hello"))
table := []string{
"go: cannot find main module; see 'go help modules'",
"go help modules",
"go: ",
"build .:",
"no Go files",
}
for _, tt := range table {
t.Run(tt, func(st *testing.T) {
r := require.New(st)
b := nonGoDirRx.MatchString(tt)
r.True(b)
})
}
}

39
here/info.go Normal file
View File

@ -0,0 +1,39 @@
package here
import (
"encoding/json"
)
// Info represents details about the directory/package
type Info struct {
Dir string
ImportPath string
Name string
Module Module
}
func (i Info) MarshalJSON() ([]byte, error) {
mm := map[string]interface{}{
"ImportPath": i.ImportPath,
"Name": i.Name,
"Module": i.Module,
"Dir": i.Dir,
}
return json.Marshal(mm)
}
// IsZero checks if the type has been filled
// with rich chocolately data goodness
func (i Info) IsZero() bool {
return i.String() == Info{}.String()
}
func (i Info) String() string {
b, err := json.MarshalIndent(i, "", " ")
if err != nil {
return err.Error()
}
s := string(b)
return s
}

87
here/info_map.go Normal file
View File

@ -0,0 +1,87 @@
// Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT.
package here
import (
"sort"
"sync"
)
// infoMap wraps sync.Map and uses the following types:
// key: string
// value: Info
type infoMap struct {
data *sync.Map
}
// Delete the key from the map
func (m *infoMap) Delete(key string) {
m.data.Delete(key)
}
// Load the key from the map.
// Returns Info or bool.
// A false return indicates either the key was not found
// or the value is not of type Info
func (m *infoMap) Load(key string) (Info, bool) {
i, ok := m.data.Load(key)
if !ok {
return Info{}, false
}
s, ok := i.(Info)
return s, ok
}
// LoadOrStore will return an existing key or
// store the value if not already in the map
func (m *infoMap) LoadOrStore(key string, value Info) (Info, bool) {
i, _ := m.data.LoadOrStore(key, value)
s, ok := i.(Info)
return s, ok
}
// LoadOr will return an existing key or
// run the function and store the results
func (m *infoMap) LoadOr(key string, fn func(*infoMap) (Info, bool)) (Info, bool) {
i, ok := m.Load(key)
if ok {
return i, ok
}
i, ok = fn(m)
if ok {
m.Store(key, i)
return i, ok
}
return i, false
}
// Range over the Info values in the map
func (m *infoMap) Range(f func(key string, value Info) bool) {
m.data.Range(func(k, v interface{}) bool {
key, ok := k.(string)
if !ok {
return false
}
value, ok := v.(Info)
if !ok {
return false
}
return f(key, value)
})
}
// Store a Info in the map
func (m *infoMap) Store(key string, value Info) {
m.data.Store(key, value)
}
// Keys returns a list of keys in the map
func (m *infoMap) Keys() []string {
var keys []string
m.Range(func(key string, value Info) bool {
keys = append(keys, key)
return true
})
sort.Strings(keys)
return keys
}

25
here/module.go Normal file
View File

@ -0,0 +1,25 @@
package here
import (
"encoding/json"
)
type Module struct {
Path string
Main bool
Dir string
GoMod string
GoVersion string
}
func (i Module) String() string {
b, err := json.MarshalIndent(i, "", " ")
if err != nil {
return err.Error()
}
return string(b)
}
func (i Module) IsZero() bool {
return i.String() == Module{}.String()
}

63
here/parse.go Normal file
View File

@ -0,0 +1,63 @@
package here
import (
"fmt"
"path/filepath"
"regexp"
"strings"
)
func (i Info) Parse(p string) (Path, error) {
p = strings.TrimSpace(p)
p = filepath.Clean(p)
p = strings.TrimPrefix(p, i.Dir)
p = strings.Replace(p, "\\", "/", -1)
p = strings.TrimSpace(p)
if len(p) == 0 || p == ":" || p == "." {
return i.build("", "", "")
}
res := pathrx.FindAllStringSubmatch(p, -1)
if len(res) == 0 {
return Path{}, fmt.Errorf("could not parse %q", p)
}
matches := res[0]
if len(matches) != 4 {
return Path{}, fmt.Errorf("could not parse %q", p)
}
return i.build(p, matches[1], matches[3])
}
func (i Info) build(p, pkg, name string) (Path, error) {
pt := Path{
Pkg: pkg,
Name: name,
}
if strings.HasPrefix(pt.Pkg, "/") || len(pt.Pkg) == 0 {
pt.Name = pt.Pkg
pt.Pkg = i.Module.Path
}
if len(pt.Name) == 0 {
pt.Name = "/"
}
if pt.Pkg == pt.Name {
pt.Pkg = i.Module.Path
pt.Name = "/"
}
if !strings.HasPrefix(pt.Name, "/") {
pt.Name = "/" + pt.Name
}
pt.Name = strings.TrimPrefix(pt.Name, i.Dir)
return pt, nil
}
var pathrx = regexp.MustCompile("([^:]+)(:(/.+))?")

63
here/parse_test.go Normal file
View File

@ -0,0 +1,63 @@
package here_test
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/markbates/pkger/here"
"github.com/markbates/pkger/pkging/pkgtest"
"github.com/stretchr/testify/require"
)
func Test_Info_Parse(t *testing.T) {
const name = "/public/index.html"
r := require.New(t)
app, err := pkgtest.NewRef()
r.NoError(err)
defer os.RemoveAll(app.Dir)
ip := app.Info.ImportPath
ip2 := "another/app"
table := []struct {
in string
exp here.Path
err bool
}{
{in: name, exp: here.Path{Pkg: ip, Name: name}},
{in: "", exp: here.Path{Pkg: ip, Name: "/"}},
{in: "/", exp: here.Path{Pkg: ip, Name: "/"}},
{in: filepath.Join(app.Info.Dir, name), exp: here.Path{Pkg: ip, Name: name}},
{in: ":" + name, exp: here.Path{Pkg: ip, Name: name}},
{in: ip + ":" + name, exp: here.Path{Pkg: ip, Name: name}},
{in: ip, exp: here.Path{Pkg: ip, Name: "/"}},
{in: ":", exp: here.Path{Pkg: ip, Name: "/"}},
{in: ip2 + ":" + name, exp: here.Path{Pkg: ip2, Name: name}},
{in: ip2, exp: here.Path{Pkg: ip2, Name: "/"}},
{in: ip2 + ":", exp: here.Path{Pkg: ip2, Name: "/"}},
{in: filepath.Join(app.Info.Dir, "public"), exp: here.Path{Pkg: ip, Name: "/public"}},
}
for _, tt := range table {
for _, in := range []string{tt.in, strings.ReplaceAll(tt.in, "/", "\\")} {
t.Run(in, func(st *testing.T) {
r := require.New(st)
pt, err := app.Info.Parse(in)
if tt.err {
r.Error(err)
return
}
r.NoError(err)
r.Equal(tt.exp, pt)
})
}
}
}

20
here/path.go Normal file
View File

@ -0,0 +1,20 @@
package here
import (
"fmt"
)
type Path struct {
Pkg string `json:"pkg"`
Name string `json:"name"`
}
func (p Path) String() string {
if p.Name == "" {
p.Name = "/"
}
if p.Pkg == "" {
return p.Name
}
return fmt.Sprintf("%s:%s", p.Pkg, p.Name)
}

47
here/pkg.go Normal file
View File

@ -0,0 +1,47 @@
package here
import (
"encoding/json"
"path"
"strings"
)
// Package attempts to gather info for the requested package.
//
// From the `go help list` docs:
// The -find flag causes list to identify the named packages but not
// resolve their dependencies: the Imports and Deps lists will be empty.
//
// A workaround for this issue is to use the `Dir` field in the
// returned `Info` value and pass it to the `Dir(string) (Info, error)`
// function to return the complete data.
func Package(p string) (Info, error) {
i, err := Cache(p, func(p string) (Info, error) {
var i Info
b, err := run("go", "list", "-json", "-find", p)
if err != nil {
if !strings.Contains(err.Error(), "can't load package: package") {
return i, err
}
p, _ = path.Split(p)
return Package(p)
}
if err := json.Unmarshal(b, &i); err != nil {
return i, err
}
return i, nil
})
if err != nil {
return i, err
}
Cache(i.Dir, func(p string) (Info, error) {
return i, nil
})
return i, nil
}

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2019 Mark Bates
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

View File

@ -0,0 +1,35 @@
package hepa
import "bytes"
type Filter interface {
Filter([]byte) ([]byte, error)
}
type FilterFunc func([]byte) ([]byte, error)
func (f FilterFunc) Filter(b []byte) ([]byte, error) {
return f(b)
}
func Rinse(p Purifier, s, r []byte) Purifier {
return WithFunc(p, func(b []byte) ([]byte, error) {
b = bytes.ReplaceAll(b, s, r)
return b, nil
})
}
func Clean(p Purifier, s []byte) Purifier {
return WithFunc(p, func(b []byte) ([]byte, error) {
if bytes.Contains(b, s) {
return []byte{}, nil
}
return b, nil
})
}
func Noop() FilterFunc {
return func(b []byte) ([]byte, error) {
return b, nil
}
}

View File

@ -0,0 +1,42 @@
package filters
import (
"os"
"strconv"
"strings"
)
var env = func() map[string]string {
m := map[string]string{}
for _, line := range os.Environ() {
kv := strings.Split(line, "=")
k, v := kv[0], kv[1]
kt, vt := strings.TrimSpace(k), strings.TrimSpace(v)
if len(kt) == 0 || len(vt) == 0 {
continue
}
switch k {
case "GO111MODULE":
continue
}
switch v {
case "true", "TRUE", "false", "FALSE", "null", "nil", "NULL":
continue
}
if _, err := strconv.Atoi(k); err == nil {
continue
}
if _, err := strconv.Atoi(v); err == nil {
continue
}
m[k] = v
}
return m
}()

View File

@ -0,0 +1,12 @@
package filters
type FilterFunc func([]byte) ([]byte, error)
func (f FilterFunc) Filter(b []byte) ([]byte, error) {
return f(b)
}
type dir struct {
Dir string
Err error
}

View File

@ -0,0 +1,51 @@
package filters
import (
"bytes"
"os"
"path/filepath"
)
func Golang() FilterFunc {
return func(b []byte) ([]byte, error) {
gp, err := gopath(home)
if err != nil {
return nil, err
}
b = bytes.ReplaceAll(b, []byte(gp.Dir), []byte("$GOPATH"))
gru, err := goroot(gp)
if err != nil {
return nil, err
}
b = bytes.ReplaceAll(b, []byte(gru.Dir), []byte("$GOROOT"))
return b, nil
}
}
func goroot(gp dir) (dir, error) {
gru, ok := os.LookupEnv("GOROOT")
if !ok {
if gp.Err != nil {
return gp, gp.Err
}
gru = filepath.Join(string(gp.Dir), "go")
}
return dir{
Dir: gru,
}, nil
}
func gopath(home dir) (dir, error) {
gp, ok := os.LookupEnv("GOPATH")
if !ok {
if home.Err != nil {
return home, home.Err
}
gp = filepath.Join(string(home.Dir), "go")
}
return dir{
Dir: gp,
}, nil
}

View File

@ -0,0 +1,31 @@
package filters
import (
"bytes"
"os"
)
var home = func() dir {
var d dir
home, ok := os.LookupEnv("HOME")
if !ok {
pwd, err := os.Getwd()
if err != nil {
d.Err = err
return d
}
home = pwd
}
d.Dir = home
return d
}()
func Home() FilterFunc {
return func(b []byte) ([]byte, error) {
if home.Err != nil {
return b, home.Err
}
return bytes.ReplaceAll(b, []byte(home.Dir), []byte("$HOME")), nil
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
package filters
import (
"bytes"
"strings"
)
func Secrets() FilterFunc {
return func(b []byte) ([]byte, error) {
for k, v := range env {
for _, s := range secretSuffixes {
if !strings.HasSuffix(k, s) {
continue
}
b = bytes.ReplaceAll(b, []byte(v), []byte(mask()))
break
}
}
return b, nil
}
}
var secretSuffixes = []string{
"_KEY",
"_SECRET",
"_TOKEN",
"_PASSWORD",
"_PASS",
}

View File

@ -0,0 +1,15 @@
package hepa
func WithFunc(p Purifier, fn FilterFunc) Purifier {
c := New()
c.parent = &p
c.filter = fn
return c
}
func With(p Purifier, f Filter) Purifier {
c := New()
c.parent = &p
c.filter = f
return c
}

View File

@ -0,0 +1,67 @@
package hepa
import (
"bufio"
"bytes"
"io"
"github.com/markbates/pkger/internal/takeon/github.com/markbates/hepa/filters"
)
type Purifier struct {
parent *Purifier
filter Filter
}
func (p Purifier) Filter(b []byte) ([]byte, error) {
if p.filter == nil {
p.filter = filters.Home()
}
b, err := p.filter.Filter(b)
if err != nil {
return b, err
}
if p.parent != nil {
return p.parent.Filter(b)
}
return b, nil
}
func (p Purifier) Clean(r io.Reader) ([]byte, error) {
bb := &bytes.Buffer{}
if p.filter == nil {
if p.parent != nil {
return p.parent.Clean(r)
}
_, err := io.Copy(bb, r)
return bb.Bytes(), err
}
home := filters.Home()
reader := bufio.NewReader(r)
for {
input, _, err := reader.ReadLine()
if err != nil && err == io.EOF {
break
}
input, err = p.Filter(input)
if err != nil {
return nil, err
}
input, err = home(input)
if err != nil {
return nil, err
}
bb.Write(input)
// if len(input) > 0 {
bb.Write([]byte("\n"))
// }
}
return bb.Bytes(), nil
}
func New() Purifier {
return Purifier{}
}

View File

@ -0,0 +1,4 @@
package hepa
// Version of hepa
const Version = "v0.0.1"

View File

@ -40,7 +40,7 @@ func (decls Decls) Files() ([]*File, error) {
files, err := fl.Files(v)
if err != nil {
return nil, fmt.Errorf("%s: %s", err, d)
return nil, fmt.Errorf("%w: %s", err, d)
}
for _, f := range files {

View File

@ -1,88 +0,0 @@
package parser
import (
"encoding/json"
"fmt"
"go/token"
"os"
"path/filepath"
"github.com/markbates/pkger/here"
)
var _ Decl = IncludeDecl{}
type IncludeDecl struct {
file *File
pos token.Position
value string
}
func NewInclude(her here.Info, inc string) (IncludeDecl, error) {
var id IncludeDecl
pt, err := her.Parse(inc)
if err != nil {
return id, err
}
if pt.Pkg != her.ImportPath {
her, err = here.Package(pt.Pkg)
if err != nil {
return id, err
}
}
abs := filepath.Join(her.Module.Dir, pt.Name)
f := &File{
Abs: abs,
Path: pt,
Here: her,
}
return IncludeDecl{
value: inc,
file: f,
}, nil
}
func (d IncludeDecl) String() string {
return fmt.Sprintf("pkger.Include(%q)", d.value)
}
func (d IncludeDecl) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"type": "pkger.Include",
"file": d.file,
"pos": d.pos,
"value": d.value,
})
}
func (d IncludeDecl) File() (*File, error) {
if d.file == nil {
return nil, os.ErrNotExist
}
return d.file, nil
}
func (d IncludeDecl) Position() (token.Position, error) {
return d.pos, nil
}
func (d IncludeDecl) Value() (string, error) {
if d.value == "" {
return "", os.ErrNotExist
}
return d.value, nil
}
func (d IncludeDecl) Files(virtual map[string]string) ([]*File, error) {
od := OpenDecl{
file: d.file,
pos: d.pos,
value: d.value,
}
return od.Files(virtual)
}

View File

@ -70,7 +70,6 @@ func (d OpenDecl) Files(virtual map[string]string) ([]*File, error) {
}
return wd.Files(virtual)
}
var files []*File
files = append(files, d.file)

View File

@ -16,32 +16,15 @@ import (
var defaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "testdata"}
func New(her here.Info) (*Parser, error) {
return &Parser{
Info: her,
decls: map[string]Decls{},
}, nil
}
type Parser struct {
here.Info
decls map[string]Decls
once sync.Once
includes []string
err error
}
func Parse(her here.Info, includes ...string) (Decls, error) {
func Parse(her here.Info) (Decls, error) {
p, err := New(her)
if err != nil {
return nil, err
}
p.includes = includes
return p.Decls()
}
func (p *Parser) ParseSource(source Source, mode parser.Mode) (*ParsedSource, error) {
func ParseSource(source Source, mode parser.Mode) (*ParsedSource, error) {
pf := &ParsedSource{
Source: source,
FileSet: token.NewFileSet(),
@ -62,7 +45,7 @@ func (p *Parser) ParseSource(source Source, mode parser.Mode) (*ParsedSource, er
return pf, nil
}
func (p *Parser) ParseFile(abs string, mode parser.Mode) (*ParsedSource, error) {
func ParseFile(abs string, mode parser.Mode) (*ParsedSource, error) {
s := Source{
Abs: abs,
}
@ -85,10 +68,10 @@ func (p *Parser) ParseFile(abs string, mode parser.Mode) (*ParsedSource, error)
s.Path, err = s.Here.Parse(strings.TrimPrefix(abs, dir))
return p.ParseSource(s, 0)
return ParseSource(s, 0)
}
func (p *Parser) ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error) {
func ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error) {
info, err := os.Stat(abs)
if err != nil {
return nil, err
@ -100,22 +83,19 @@ func (p *Parser) ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error)
her, err := here.Dir(abs)
if err != nil {
return nil, fmt.Errorf("%s: here.Dir failed %s", err, abs)
return nil, fmt.Errorf("%w: here.Dir failed %s", err, abs)
}
pt, err := her.Parse(strings.TrimPrefix(abs, filepath.Dir(abs)))
if err != nil {
return nil, fmt.Errorf("%s: here.Parse failed %s", err, abs)
}
filter := func(f os.FileInfo) bool {
return !f.IsDir()
return nil, fmt.Errorf("%w: here.Parse failed %s", err, abs)
}
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, abs, filter, 0)
pkgs, err := parser.ParseDir(fset, abs, nil, 0)
if err != nil {
return nil, fmt.Errorf("%s: ParseDir failed %s", err, abs)
return nil, fmt.Errorf("%w: ParseDir failed %s", err, abs)
}
var srcs []*ParsedSource
@ -136,6 +116,19 @@ func (p *Parser) ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error)
return srcs, nil
}
func New(her here.Info) (*Parser, error) {
return &Parser{
Info: her,
decls: map[string]Decls{},
}, nil
}
type Parser struct {
here.Info
decls map[string]Decls
once sync.Once
err error
}
func (p *Parser) Decls() (Decls, error) {
if err := p.parse(); err != nil {
@ -146,7 +139,6 @@ func (p *Parser) Decls() (Decls, error) {
orderedNames := []string{
"MkdirAll",
"Create",
"Include",
"Stat",
"Open",
"Dir",
@ -174,18 +166,12 @@ func (p *Parser) Parse() error {
func (p *Parser) parse() error {
p.decls = map[string]Decls{}
root := p.Dir
if err := p.parseIncludes(); err != nil {
return err
}
fi, err := os.Stat(root)
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", root)
}
@ -206,15 +192,15 @@ func (p *Parser) parse() error {
}
}
srcs, err := p.ParseDir(path, 0)
srcs, err := ParseDir(path, 0)
if err != nil {
return fmt.Errorf("%s: %s", err, path)
return fmt.Errorf("%w: %s", err, path)
}
for _, src := range srcs {
mm, err := src.DeclsMap()
if err != nil {
return fmt.Errorf("%s: %s", err, src.Abs)
return fmt.Errorf("%w: %s", err, src.Abs)
}
for k, v := range mm {
p.decls[k] = append(p.decls[k], v...)
@ -226,15 +212,3 @@ func (p *Parser) parse() error {
return err
}
func (p *Parser) parseIncludes() error {
for _, i := range p.includes {
d, err := NewInclude(p.Info, i)
if err != nil {
return err
}
p.decls["Include"] = append(p.decls["Include"], d)
}
return nil
}

View File

@ -1,7 +1,6 @@
package parser
import (
"fmt"
"os"
"os/exec"
"path/filepath"
@ -37,64 +36,21 @@ func Test_Parser_Ref(t *testing.T) {
files, err := res.Files()
r.NoError(err)
r.Len(files, 23)
for _, f := range files {
fmt.Println(f.Path)
if f.Path.Pkg == ref.Module.Path {
r.True(strings.HasPrefix(f.Abs, ref.Dir), "%q %q", f.Abs, ref.Dir)
} else {
r.False(strings.HasPrefix(f.Abs, ref.Dir), "%q %q", f.Abs, ref.Dir)
}
}
r.Len(files, 25)
}
func Test_Parser_Ref_Include(t *testing.T) {
defer func() {
c := exec.Command("go", "mod", "tidy", "-v")
c.Run()
}()
r := require.New(t)
ref, err := pkgtest.NewRef()
r.NoError(err)
defer os.RemoveAll(ref.Dir)
disk, err := stdos.New(ref.Info)
r.NoError(err)
_, err = pkgtest.LoadFiles("/", ref, disk)
r.NoError(err)
res, err := Parse(ref.Info, "github.com/stretchr/testify:/go.mod")
r.NoError(err)
files, err := res.Files()
r.NoError(err)
l := len(files)
r.Equal(26, l)
}
func Test_Parser_dotGo_Directory(t *testing.T) {
r := require.New(t)
ref, err := pkgtest.NewRef()
r.NoError(err)
defer os.RemoveAll(ref.Dir)
err = os.Mkdir(filepath.Join(ref.Dir, ".go"), 0755)
r.NoError(err)
disk, err := stdos.New(ref.Info)
r.NoError(err)
_, err = pkgtest.LoadFiles("/", ref, disk)
r.NoError(err)
res, err := Parse(ref.Info)
r.NoError(err)
r.Equal(11, len(res))
}
func Test_Parser_Example_HTTP(t *testing.T) {
r := require.New(t)
here.ClearCache()
cur, err := here.Package("github.com/markbates/pkger")
r.NoError(err)

View File

@ -33,39 +33,16 @@ func (p *ParsedSource) Parse() error {
return p.err
}
func (p *ParsedSource) valueIdent(node *ast.Ident) (s string) {
s = node.Name
if node.Obj.Kind != ast.Con {
return
}
// As per ast package a Con object is always a *ValueSpec,
// but double-checking to avoid panics
if x, ok := node.Obj.Decl.(*ast.ValueSpec); ok {
// The const var can be defined inline with other vars,
// as in `const a, b = "a", "b"`.
for i, v := range x.Names {
if v.Name == node.Name {
s = p.valueNode(x.Values[i])
break
}
}
}
return
}
func (p *ParsedSource) valueNode(node ast.Node) string {
func (p *ParsedSource) value(node ast.Node) (string, error) {
var s string
switch x := node.(type) {
case *ast.BasicLit:
s = x.Value
case *ast.Ident:
s = p.valueIdent(x)
s = x.Name
}
return s
}
func (p *ParsedSource) value(node ast.Node) (string, error) {
s := p.valueNode(node)
return strconv.Unquote(s)
}
@ -111,14 +88,6 @@ func (p *ParsedSource) parse() error {
value: value,
}
}
case "Include":
fn = func(f File, pos token.Position, value string) Decl {
return IncludeDecl{
file: &f,
pos: pos,
value: value,
}
}
case "Stat":
fn = func(f File, pos token.Position, value string) Decl {
return StatDecl{
@ -163,26 +132,26 @@ func (p *ParsedSource) parse() error {
n := ce.Args[0]
val, err := p.value(n)
if err != nil {
p.err = fmt.Errorf("%s: %s", err, n)
p.err = fmt.Errorf("%w: %s", err, n)
return false
}
info, err := here.Dir(filepath.Dir(p.Abs))
if err != nil {
p.err = fmt.Errorf("%s: %s", err, p.Abs)
p.err = fmt.Errorf("%w: %s", err, p.Abs)
return false
}
pt, err := info.Parse(val)
if err != nil {
p.err = fmt.Errorf("%s: %s", err, p.Abs)
p.err = fmt.Errorf("%w: %s", err, p.Abs)
return false
}
if pt.Pkg != info.Module.Path {
info, err = here.Package(pt.Pkg)
if err != nil {
p.err = fmt.Errorf("%s: %s", err, p.Abs)
p.err = fmt.Errorf("%w: %s", err, p.Abs)
return false
}
}

View File

@ -1,6 +1,7 @@
package pkger
import (
"log"
"net/http"
"os"
"path/filepath"
@ -15,8 +16,14 @@ var current pkging.Pkger
var gil = &sync.RWMutex{}
var disk = func() pkging.Pkger {
her, _ := here.Current()
n, _ := stdos.New(her)
her, err := here.Current()
if err != nil {
log.Println(err)
}
n, err := stdos.New(her)
if err != nil {
log.Println(err)
}
return n
}()
@ -88,8 +95,3 @@ func Remove(name string) error {
func RemoveAll(name string) error {
return impl().RemoveAll(name)
}
// Include is a no-op that directs the pkger tool to include the desired file or folder.
func Include(name string) string {
return name
}

View File

@ -7,6 +7,8 @@ import (
"io"
"github.com/markbates/pkger/here"
"github.com/markbates/pkger/internal/takeon/github.com/markbates/hepa"
"github.com/markbates/pkger/internal/takeon/github.com/markbates/hepa/filters"
)
func Decode(src []byte) ([]byte, error) {
@ -44,7 +46,16 @@ func Encode(b []byte) ([]byte, error) {
return nil, err
}
s := hex.EncodeToString(bb.Bytes())
hep := hepa.New()
hep = hepa.With(hep, filters.Home())
hep = hepa.With(hep, filters.Golang())
b, err := hep.Filter(bb.Bytes())
if err != nil {
return nil, err
}
s := hex.EncodeToString(b)
return []byte(s), nil
}

View File

@ -19,6 +19,7 @@ func (fx *Pkger) Add(files ...*os.File) error {
if err != nil {
return err
}
pt, err := fx.Parse(f.Name())
if err != nil {
return err

View File

@ -31,10 +31,6 @@ type File struct {
// Seek sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end. It returns the new offset and an error, if any.
func (f *File) Seek(ofpkginget int64, whence int) (int64, error) {
if len(f.data) > 0 && f.reader == nil {
f.reader = bytes.NewReader(f.data)
}
if sk, ok := f.reader.(io.Seeker); ok {
return sk.Seek(ofpkginget, whence)
}

View File

@ -27,16 +27,6 @@ func Test_File_Seek(t *testing.T) {
f, err = pkg.Open(":/wilco.band")
r.NoError(err)
// seek to end of file before read
pos, err := f.Seek(0, 2)
r.NoError(err)
r.Equal(int64(len(data)), pos)
// reset seek
pos, err = f.Seek(0, 0)
r.NoError(err)
r.Equal(int64(0), pos)
b, err := ioutil.ReadAll(f)
r.NoError(err)
r.Equal(data, b)

View File

@ -24,15 +24,12 @@ type Pkger interface {
MkdirAll(p string, perm os.FileMode) error
// Open opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode O_RDONLY.
// @Parser Directive
Open(name string) (File, error)
// Stat returns a FileInfo describing the named file.
// @Parser Directive
Stat(name string) (os.FileInfo, error)
// Walk walks the file tree rooted at root, calling walkFn for each file or directory in the tree, including root. All errors that arise visiting files and directories are filtered by walkFn. The files are walked in lexical order, which makes the output deterministic but means that for very large directories Walk can be inefficient. Walk does not follow symbolic links. - That is from the standard library. I know. Their grammar teachers can not be happy with them right now.
// @Parser Directive
Walk(p string, wf filepath.WalkFunc) error
// Remove removes the named file or (empty) directory.

View File

@ -21,6 +21,7 @@ func NewRef() (*Ref, error) {
if err != nil {
return nil, err
}
root := filepath.Join(
her.Module.Dir,
"pkging",
@ -67,25 +68,32 @@ func newRef(root string) (*Ref, error) {
return nil, err
}
for _, n := range []string{"go.mod", "go.sum"} {
b, err = ioutil.ReadFile(filepath.Join(root, n))
if err != nil {
return nil, err
}
f, err := os.Create(filepath.Join(dir, n))
if err != nil {
return nil, err
}
if _, err := f.Write(b); err != nil {
return nil, err
}
if err := f.Close(); err != nil {
return nil, err
}
b, err = ioutil.ReadFile(filepath.Join(root, "go.mod"))
if err != nil {
return nil, err
}
f, err := os.Create(filepath.Join(dir, "go.mod"))
if err != nil {
return nil, err
}
if _, err := f.Write(b); err != nil {
return nil, err
}
if err := f.Close(); err != nil {
return nil, err
}
// c := exec.Command("cp", "-rv", root, dir)
// fmt.Println(strings.Join(c.Args, " "))
// c.Stdout = os.Stdout
// c.Stderr = os.Stderr
// c.Stdin = os.Stdin
// if err := c.Run(); err != nil {
// return nil, err
// }
return ref, nil
}

View File

@ -2,6 +2,9 @@ module app
go 1.13
require github.com/markbates/pkger v0.0.0
require (
github.com/gobuffalo/buffalo v0.15.0 // indirect
github.com/markbates/pkger v0.0.0
)
replace github.com/markbates/pkger => ../../../../

File diff suppressed because it is too large Load Diff

View File

@ -12,22 +12,17 @@ import (
)
func main() {
pkger.Include("/web")
if err := run(); err != nil {
log.Fatal(err)
}
}
const (
unused, pathAsset = "", "/assets"
)
func run() error {
if err := actions.WalkTemplates(os.Stdout); err != nil {
return err
}
err := pkger.Walk(pathAsset, func(path string, info os.FileInfo, err error) error {
err := pkger.Walk("/assets", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

View File

@ -7,7 +7,7 @@ import (
)
func Serve() {
pkger.Stat("github.com/markbates/pkger:/README.md")
pkger.Stat("github.com/gobuffalo/buffalo:/logo.svg")
dir := http.FileServer(pkger.Dir("/public"))
http.ListenAndServe(":3000", dir)
}

View File

@ -1,10 +1,8 @@
package pkgutil
import (
"fmt"
"io"
"os"
"strings"
"github.com/markbates/pkger/here"
"github.com/markbates/pkger/parser"
@ -25,12 +23,9 @@ func Stuff(w io.Writer, c here.Info, decls parser.Decls) error {
for _, pf := range files {
err = func() error {
if strings.HasSuffix(pf.Abs, ".tmp") {
return nil
}
df, err := os.Open(pf.Abs)
if err != nil {
return fmt.Errorf("could open stuff %s: %s", pf.Abs, err)
return err
}
defer df.Close()

View File

@ -19,12 +19,6 @@ func Test_Stuff(t *testing.T) {
r.NoError(err)
defer os.RemoveAll(ref.Dir)
pwd, err := os.Getwd()
r.NoError(err)
defer os.Chdir(pwd)
os.Chdir(ref.Dir)
disk, err := stdos.New(ref.Info)
r.NoError(err)
@ -35,7 +29,7 @@ func Test_Stuff(t *testing.T) {
decls, err := parser.Parse(ref.Info)
r.NoError(err)
r.Len(decls, 11)
r.Len(decls, 10)
files, err := decls.Files()
r.NoError(err)
@ -48,7 +42,7 @@ func Test_Stuff(t *testing.T) {
}
}
r.Len(files, 25)
r.Len(files, 23)
bb := &bytes.Buffer{}

View File

@ -35,17 +35,16 @@ func (fx *Pkger) Create(name string) (pkging.File, error) {
return nil, err
}
her, err := fx.Info(pt.Pkg)
if err != nil {
return nil, err
}
name = filepath.Join(her.Dir, pt.Name)
name = filepath.Join(fx.Here.Dir, pt.Name)
f, err := os.Create(name)
if err != nil {
return nil, err
}
her, err := fx.Info(pt.Pkg)
if err != nil {
return nil, err
}
nf := &File{
File: f,
her: her,
@ -83,15 +82,7 @@ func (f *Pkger) Info(p string) (here.Info, error) {
// MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm (before umask) are used for all directories that MkdirAll creates. If path is already a directory, MkdirAll does nothing and returns nil.
func (f *Pkger) MkdirAll(p string, perm os.FileMode) error {
pt, err := f.Parse(p)
if err != nil {
return err
}
info, err := f.Info(pt.Pkg)
if err != nil {
return err
}
return os.MkdirAll(filepath.Join(info.Dir, p), perm)
return os.MkdirAll(filepath.Join(f.Here.Dir, p), perm)
}
// Open opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode O_RDONLY.
@ -101,13 +92,13 @@ func (fx *Pkger) Open(name string) (pkging.File, error) {
return nil, err
}
her, err := fx.Info(pt.Pkg)
name = filepath.Join(fx.Here.Dir, pt.Name)
f, err := os.Open(name)
if err != nil {
return nil, err
}
name = filepath.Join(her.Dir, pt.Name)
f, err := os.Open(name)
her, err := fx.Info(pt.Pkg)
if err != nil {
return nil, err
}
@ -140,12 +131,7 @@ func (fx *Pkger) Stat(name string) (os.FileInfo, error) {
return nil, err
}
her, err := fx.Info(pt.Pkg)
if err != nil {
return nil, err
}
info, err := os.Stat(filepath.Join(her.Dir, pt.Name))
info, err := os.Stat(filepath.Join(fx.Here.Dir, pt.Name))
if err != nil {
return nil, err
}
@ -162,27 +148,19 @@ func (f *Pkger) Walk(p string, wf filepath.WalkFunc) error {
return err
}
info, err := f.Info(pt.Pkg)
if err != nil {
return err
}
fp := filepath.Join(info.Dir, pt.Name)
fp := filepath.Join(f.Here.Dir, pt.Name)
err = filepath.Walk(fp, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
pt, err := f.Parse(fmt.Sprintf("%s:%s", pt.Pkg, path))
if err != nil {
return err
}
info, err := f.Info(pt.Pkg)
if err != nil {
return err
}
path = strings.TrimPrefix(path, info.Dir)
path = strings.ReplaceAll(path, "\\", "/")
pt.Name = path
@ -198,13 +176,7 @@ func (fx *Pkger) Remove(name string) error {
if err != nil {
return err
}
info, err := fx.Info(pt.Pkg)
if err != nil {
return err
}
return os.Remove(filepath.Join(info.Dir, pt.Name))
return os.Remove(filepath.Join(fx.Here.Dir, pt.Name))
}
// RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error).
@ -213,11 +185,5 @@ func (fx *Pkger) RemoveAll(name string) error {
if err != nil {
return err
}
info, err := fx.Info(pt.Pkg)
if err != nil {
return err
}
return os.RemoveAll(filepath.Join(info.Dir, pt.Name))
return os.RemoveAll(filepath.Join(fx.Here.Dir, pt.Name))
}

View File

@ -1,4 +1,4 @@
package pkger
// Version of pkger
var Version = "development"
const Version = "v0.10.1"