Git Product home page Git Product logo

gitfs's Introduction

gitfs

codecov GoDoc

Package gitfs is a complete solution for static files in Go code.

When Go code uses non-Go files, they are not packaged into the binary. The common approach to the problem, as implemented by go-bindata is to convert all the required static files into Go code, which eventually compiled into the binary.

This library takes a different approach, in which the static files are not required to be "binary-packed", and even no required to be in the same repository as the Go code. This package enables loading static content from a remote git repository, or packing it to the binary if desired or loaded from local path for development process. The transition from remote repository to binary packed content, to local content is completely smooth.

The API is simple and minimalistic. The New method returns a (sub)tree of a Git repository, represented by the standard http.FileSystem interface. This object enables anything that is possible to do with a regular filesystem, such as opening a file or listing a directory. Additionally, the ./fsutil package provides enhancements over the http.FileSystem object (They can work with any object that implements the interface) such as loading Go templates in the standard way, walking over the filesystem, and applying glob patterns on a filesystem.

Supported features:

  • Loading of specific version/tag/branch.

  • For debug purposes, the files can be loaded from local path instead of the remote repository.

  • Files are loaded lazily by default or they can be preloaded if required.

  • Files can be packed to the Go binary using a command line tool.

  • This project is using the standard http.FileSystem interface.

  • In ./fsutil there are some general useful tools around the http.FileSystem interace.

Usage

To create a filesystem using the New function, provide the Git project with the pattern: github.com/<owner>/<repo>(/<path>)?(@<ref>)?. If no path is specified, the root of the project will be used. ref can be any git branch using heads/<branch name> or any git tag using tags/<tag>. If the tag is of Semver format, the tags/ prefix is not required. If no ref is specified, the default branch will be used.

In the following example, the repository github.com/x/y at tag v1.2.3 and internal path "static" is loaded:

fs, err := gitfs.New(ctx, "github.com/x/y/[email protected]")

The variable fs implements the http.FileSystem interface. Reading a file from the repository can be done using the Open method. This function accepts a path, relative to the root of the defined filesystem.

f, err := fs.Open("index.html")

The fs variable can be used in anything that accept the standard interface. For example, it can be used for serving static content using the standard library:

http.Handle("/", http.FileServer(fs))

Private Repositories

When used with private github repository, the Github API calls should be instrumented with the appropriate credentials. The credentials can be passed by providing an HTTP client.

For example, to use a Github Token from environnement variable GITHUB_TOKEN:

token := os.Getenv("GITHUB_TOKEN")
client := oauth2.NewClient(
	context.Background(),
	oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}))
fs, err := gitfs.New(ctx, "github.com/x/y", gitfs.OptClient(client))

Development

For quick development workflows, it is easier and faster to use local static content and not remote content that was pushed to a remote repository. This is enabled by the OptLocal option. To use this option only in local development and not in production system, it can be used as follow:

local := os.Getenv("LOCAL_DEBUG")
fs, err := gitfs.New(ctx, "github.com/x/y", gitfs.OptLocal(local))

In this example, we stored the value for OptLocal in an environment variable. As a result, when running the program with LOCAL_DEBUG=. local files will be used, while running without it will result in using the remote files. (the value of the environment variable should point to any directory within the github project).

Binary Packing

Using gitfs does not mean that files are required to be remotely fetched. When binary packing of the files is needed, a command line tool can pack them for you.

To get the tool run: go get github.com/posener/gitfs/cmd/gitfs.

Running the tool is by gitfs <patterns>. This generates a gitfs.go file in the current directory that contains all the used filesystems' data. This will cause all gitfs.New calls to automatically use the packed data, insted of fetching the data on runtime.

By default, a test will also be generated with the code. This test fails when the local files are modified without updating the binary content.

Use binary-packing with go generate: To generate all filesystems used by a project add //go:generate gitfs ./... in the root of the project. To generate only a specific filesystem add //go:generate gitfs $GOFILE in the file it is being used.

An interesting anecdote is that gitfs command is using itself for generating its own templates.

Excluding files

Files exclusion can be done by including only specific files using a glob pattern with OptGlob option, using the Glob options. This will affect both local loading of files, remote loading and binary packing (may reduce binary size). For example:

fs, err := gitfs.New(ctx,
	"github.com/x/y/templates",
	gitfs.OptGlob("*.gotmpl", "*/*.gotmpl"))

Sub Packages

  • bin: Package bin is a proxy to the internal/binfs.Register function.

  • cmd/gitfs: gitfs command line tool, for generating binary conetent of the used filesystems.

  • examples/godoc: An example locally serves files from github.com/golang/go/doc.

  • examples/templates: An example that shows how gitfs helps using template files with Go code smoothly.

  • fsutil: Package fsutil provides useful utility functions for http.FileSystem.

Examples

Fsutil

The ./fsutil package is a collection of useful functions that can work with any http.FileSystem implementation. For example, here we will use a function that loads go templates from the filesystem.

ctx := context.Background()

// Open a git remote repository `posener/gitfs` in path `examples/templates`.
fs, err := New(ctx, "github.com/posener/gitfs/examples/templates")
if err != nil {
    log.Fatalf("Failed initialize filesystem: %s", err)
}

// Use util function that loads all templates according to a glob pattern.
tmpls, err := fsutil.TmplParseGlob(fs, nil, "*.gotmpl")
if err != nil {
    log.Fatalf("Failed parsing templates: %s", err)
}

// Execute the template and write to stdout.
tmpls.ExecuteTemplate(os.Stdout, "tmpl1.gotmpl", "Foo")

Output:

Hello, Foo

Open

With gitfs you can open a remote git repository, and load any file, including non-go files. In this example, the README.md file of a remote repository is loaded.

ctx := context.Background()

// The load path is of the form: github.com/<owner>/<repo>(/<path>)?(@<ref>)?.
// `ref` can reference any git tag or branch. If github releases are in Semver format,
// the `tags/` prefix is not needed in the `ref` part.
fs, err := New(ctx, "github.com/kelseyhightower/[email protected]")
if err != nil {
    log.Fatalf("Failed initialize filesystem: %s", err)
}

// Open any file in the github repository, using the `Open` function. Both files
// and directory can be opened. The content is not loaded until it is actually being
// read. The content is loaded only once.
f, err := fs.Open("README.md")
if err != nil {
    log.Fatalf("Failed opening file: %s", err)
}

// Copy the content to stdout.
io.Copy(os.Stdout, f)

Output:

# helloworld

Readme created from Go doc with goreadme

gitfs's People

Contributors

goreadme[bot] avatar posener avatar sams96 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

gitfs's Issues

Binary Packing

1. I wan Using Binary Packing. so I test with the following code

func main() {

	ctx := context.Background()
	fs, err := gitfs.New(ctx, "metest/public", gitfs.OptLocal("."))
	if err != nil {
		log.Fatalf("Failed initialize filesystem: %s", err)
	}
	f, err := fs.Open("go.mod")
	if err != nil {
		log.Fatalf("Failed opening file: %s", err)
	}
	io.Copy(os.Stdout, f)
}

2. It can run successfully before Using Binary Packing。The result is:

module test

go 1.13

require (
        github.com/posener/gitfs v1.1.0
        golang.org/x/tools v0.0.0-20200115044656-831fdb1e1868 // indirect
)

3. Using Binary Packing run successfully .The result is:

github.com/posener/gitfs/cmd/gitfs/gitfs .

[gitfs] 2020/01/15 23:06:19 FileSystem "github.com/posener/gitfs/cmd/gitfs/templates" from binary
[gitfs] 2020/01/15 23:06:19 Loaded file binary.go.gotmpl in 0.0s
[gitfs] 2020/01/15 23:06:19 Loaded file test.go.gotmpl in 0.0s
2020/01/15 23:06:22 Encoding project: metest/public
[gitfs] 2020/01/15 23:06:22 FileSystem "metest/public" from local directory
2020/01/15 23:06:22 Encoded path: go.mod
2020/01/15 23:06:22 Encoded size: 332

4. go build the test code ,and run it .run time error is:

panic: Failed decoding project "metest/public": decoding gob: gob: encoded unsigned integer out of range

goroutine 1 [running]:
github.com/posener/gitfs/internal/binfs.Register(0xb12c6f, 0xd, 0x1, 0xb2ec8f, 0x14c)
        /home/star/gopro/pkg/mod/github.com/posener/[email protected]/internal/binfs/binfs.go:61 +0x203
github.com/posener/gitfs/bin.Register(...)
        /home/star/gopro/pkg/mod/github.com/posener/[email protected]/bin/bin.go:10
main.init.0()
        /home/star/gopro/src/test/gitfs.go:7 +0x54

5. I don't know where the mistake happened. Can you help me. Thank you.

Error when `go get github.com/posener/gitfs/cmd/gitfs`

Catch strange bug on v1.1.0 (I use Go v1.13.5):

$ go get github.com/posener/gitfs/cmd/gitfs

# github.com/posener/gitfs/fsutil
../.go/src/github.com/posener/gitfs/fsutil/diff.go:171:18: too many arguments in call to diff.Format
../.go/src/github.com/posener/gitfs/fsutil/diff.go:171:49: undefined: diff.OptSuppressCommon

Ok. Go to github.com/posener/gitfs/fsutil/diff.go:171:49 (link) and see:

// ...
d := diff.Format(string(aData), string(bData), diff.OptSuppressCommon())
// ...

Third arg for function diff.Format() is diff.OptSuppressCommon() and probably it's wrong.

Feature request: Print current ref (branch/commit), if one was not provided explicitly

Hi,

I'd find it very handy to be able to print the current ref (latest branch/commit), if one was not provided explicitly.

type HasCommitSHA interface {
    CommitSHA() string
}

fs, _ := gitfs.New(ctx, "github.com/x/y/static")

if v, ok := fs.(HasCommitSHA); ok {
    fmt.Println("current commit:",  v.CommitSHA())
}

Is this somehow possible right now? Would you be open to such functionality?

Thanks,
Vojtech


P.S.: gitfs works very well, thank you for this project! We started using it in https://github.com/webrpc/webrpc.

error when using go run github.com/posener/gitfs/cmd/gitfs ./...

this is the error

Failed encoding filesystem "github.com/segator/transcoderd": opening file .git\COMMIT_EDITMSG: http: invalid character in file path

in my main.go i have this

err := gitfs.New(ctx,
		"github.com/segator/transcoderd",)

the working dir where main.go exists is a git repo poiting to github.com/segator/transcoderd

any idea?

Huge memory usage

It seems when opening a file with gitfs for example to copy it to another file it use a lot of memory!
but only in the moment you open and read the file, or unless you use precaching
I also notice if use optLocal then no memory is used.

This can somehow be fixed? I really like this lib :)

Test binary content

An idea: when binary content is generated, it also generate a test that checks that the binary content is identical to the remote content that it was generated from.

Feature request: support SSH-style git URLs

When the git repo is configured with SSH (i.e. the remote starts with "[email protected]:krokicki/myproject.git") then binary packing fails as follows (gitfs.New fails the same way):

$ gitfs ./...
panic: failed parsing URL: [email protected]:krokicki/myproject.git

goroutine 1 [running]:
github.com/posener/gitfs/internal/localfs.urlProjectName(0xc0001a2c60, 0x26, 0x1, 0x1)
        /Users/tester/go/pkg/mod/github.com/posener/[email protected]/internal/localfs/localfs.go:99 +0x1fb
github.com/posener/gitfs/internal/localfs.computeSubdir(0x189fe74, 0x28, 0xc0001b9540, 0x18, 0x0, 0x0, 0x20, 0x18)
        /Users/tester/go/pkg/mod/github.com/posener/[email protected]/internal/localfs/localfs.go:45 +0x14b
github.com/posener/gitfs/internal/localfs.New(0x189fe74, 0x28, 0xc00003600c, 0x1, 0xc00019a040, 0x1e7c801, 0x2, 0xc00024fb38)
        /Users/tester/go/pkg/mod/github.com/posener/[email protected]/internal/localfs/localfs.go:24 +0x84
github.com/posener/gitfs.New(0x19a95e0, 0xc0001a4000, 0x189fe74, 0x28, 0xc00024fbf0, 0x1, 0x1, 0x30, 0xc000275170, 0x0, ...)
        /Users/tester/go/pkg/mod/github.com/posener/[email protected]/gitfs.go:199 +0x136
project/cmd.generateDockerfile(0x1891c4c, 0x18, 0x17e6760, 0xc000275170)
        /Users/tester/dev/project/cmd/init.go:207 +0xf8
project/cmd.initProjectJavaMaven()
        /Users/tester/dev/project/cmd/init.go:161 +0x17f
project/cmd.glob..func1(0x1ecaf40, 0xc000241110, 0x1, 0x1)
        /Users/tester/dev/project/cmd/init.go:59 +0x1f8
github.com/spf13/cobra.(*Command).execute(0x1ecaf40, 0xc0002410d0, 0x1, 0x1, 0x1ecaf40, 0xc0002410d0)
        /Users/tester/go/pkg/mod/github.com/spf13/[email protected]/command.go:846 +0x29d
github.com/spf13/cobra.(*Command).ExecuteC(0x1ecb1e0, 0x10437aa, 0x1e87fe0, 0xc000000180)
        /Users/tester/go/pkg/mod/github.com/spf13/[email protected]/command.go:950 +0x349
github.com/spf13/cobra.(*Command).Execute(...)
        /Users/tester/go/pkg/mod/github.com/spf13/[email protected]/command.go:887
project/cmd.Execute()
        /Users/tester/dev/project/cmd/root.go:24 +0x31
main.main()
        /Users/tester/dev/project/main.go:6 +0x20

It worked after switching to HTTPS style remote ("https://github.com/krokicki/myproject.git") It would be nice if the SSH style was also supported.

Feature Request: Compression for Binary Packing

Feature Request

Several other binary packing libraries support Compression, and it would be nice if gitfs did too.

Bonus Points Feature: Optional Decompression

If your static files are stored in your executable with gzip compression, and you are going to potentially serve up those files to your client with gzip compression, why not just send them the already compressed file data? The ideal library would give you the option, at runtime, to retrieve the already compressed file without decompressing it first.

See Opt. decompression

URL Format When Specifying Path & Non-Master Branch

Hello,
I'm trying to use gitfs to retrieve files from a repository using a path and pointing to a branch that isn't master. What is the correct format for the URL to use with gitfs.New()? The documentation doesn't give a specify example when using both a path and a branch in the URL. I've tried the format github.com/owner-name/project-name/some/path/to/director@heads/PRO-72 but I just get a 404.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.