@@ -0,0 +1,31 @@ | |||
name: Run tests | |||
on: | |||
push: | |||
branches: | |||
- master | |||
pull_request: | |||
branches: | |||
- master | |||
jobs: | |||
test-package: | |||
name: Test | |||
runs-on: 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 | |||
uses: actions/checkout@v1 | |||
- name: Get dependencies | |||
run: go mod download | |||
- name: Tests | |||
run: go test | |||
# - name: Version bump | |||
# if: github.ref == "master" | |||
# run: "automatically bump the version here" |
@@ -0,0 +1,20 @@ | |||
name: Create new Github release | |||
on: | |||
push: | |||
tags: | |||
- v* | |||
jobs: | |||
build: | |||
name: Build | |||
runs-on: ubuntu-latest | |||
steps: | |||
- name: Check out code | |||
uses: actions/checkout@v1 | |||
- name: Release new version | |||
uses: actions/create-release@v1 | |||
env: | |||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |||
with: | |||
tag_name: ${{ github.ref }} | |||
release_name: Release ${{ github.ref }} |
@@ -0,0 +1,25 @@ | |||
# Created by https://www.gitignore.io/api/go | |||
# Edit at https://www.gitignore.io/?templates=go | |||
### Go ### | |||
# Binaries for programs and plugins | |||
*.exe | |||
*.exe~ | |||
*.dll | |||
*.so | |||
*.dylib | |||
# Test binary, built with `go test -c` | |||
*.test | |||
# Output of the go coverage tool, specifically when used with LiteIDE | |||
*.out | |||
# Dependency directories (remove the comment below to include it) | |||
# vendor/ | |||
### Go Patch ### | |||
/vendor/ | |||
/Godeps/ | |||
# End of https://www.gitignore.io/api/go |
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) 2019 Ameer A. | |||
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. |
@@ -0,0 +1,27 @@ | |||
# Go Find Up | |||
> Find a file or directory by walking up parent directories. | |||
## Usage | |||
Install latest version | |||
```sh | |||
go get github.com/ojizero/gofindup | |||
``` | |||
Import it into your code | |||
```go | |||
import "github.com/ojizero/gofindup" | |||
``` | |||
This package only exposes 2 functions, `Findup` and `FindupFrom`, | |||
```go | |||
// looks the file recursively in parents starting from "./" | |||
gofindup.Findup("some-file-or-directory") | |||
// looks the file recursively in parents starting from "./some-starting-directory" | |||
gofindup.Findup("some-file-or-directory", "some-starting-directory") | |||
``` |
@@ -0,0 +1,63 @@ | |||
package gofindup | |||
import ( | |||
"path/filepath" | |||
"github.com/spf13/afero" | |||
) | |||
var fs = afero.NewOsFs() | |||
func findIn(name, dir string, fs afero.Fs) (bool, error) { | |||
files, err := afero.ReadDir(fs, dir) | |||
if err != nil { | |||
return false, err | |||
} | |||
for _, f := range files { | |||
if name == f.Name() { | |||
return true, nil | |||
} | |||
} | |||
return false, nil | |||
} | |||
func findupFrom(name, dir string, fs afero.Fs) (bool, error) { | |||
for { | |||
found, err := findIn(name, dir, fs) | |||
if err != nil { | |||
return false, err | |||
} | |||
if found { | |||
return true, nil | |||
} | |||
parent := filepath.Dir(dir) | |||
if parent == dir { | |||
return false, nil | |||
} | |||
dir = parent | |||
} | |||
} | |||
func findup(name string, fs afero.Fs) (bool, error) { | |||
return findupFrom(name, ".", fs) | |||
} | |||
// Recursively find a file by walking up parents in the file tree | |||
// starting from a specific directory. | |||
func FindupFrom(name, dir string) (bool, error) { | |||
return findupFrom(name, dir, fs) | |||
} | |||
// Recursively find a file by walking up parents in the file tree | |||
// starting from the current working directory. | |||
func Findup(name string) (bool, error) { | |||
return findup(name, fs) | |||
} |
@@ -0,0 +1,95 @@ | |||
package gofindup | |||
import ( | |||
"path/filepath" | |||
"testing" | |||
"github.com/spf13/afero" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
type given struct { | |||
file string | |||
base string | |||
} | |||
type expect struct { | |||
found bool | |||
err string | |||
} | |||
type assertion struct { | |||
given given | |||
expect expect | |||
} | |||
type assertions []assertion | |||
type finderFunc func(string, string, afero.Fs) (bool, error) | |||
var fakefs = afero.NewMemMapFs() | |||
func init() { | |||
// Build fake file system | |||
fakefs.MkdirAll("/test", 0755) | |||
fakefs.MkdirAll("/test/a", 0755) | |||
fakefs.MkdirAll("/test/a/ab", 0755) | |||
afero.WriteFile(fakefs, "/test/a/ab/f.txt", []byte("some mock file"), 0644) | |||
fakefs.MkdirAll("/test/a/ac", 0755) | |||
afero.WriteFile(fakefs, "/test/a/ad.txt", []byte("some mock file"), 0644) | |||
fakefs.MkdirAll("/test/b", 0755) | |||
fakefs.MkdirAll("/test/b/bc", 0755) | |||
afero.WriteFile(fakefs, "/test/b/bc/f.txt", []byte("some mock file"), 0644) | |||
afero.WriteFile(fakefs, "/test/b/bd.txt", []byte("some mock file"), 0644) | |||
fakefs.MkdirAll("/test/c", 0755) | |||
fakefs.MkdirAll("/test/c/cd", 0755) | |||
afero.WriteFile(fakefs, "/test/c/cd/f.txt", []byte("some mock file"), 0644) | |||
afero.WriteFile(fakefs, "/test/t.txt", []byte("some mock file"), 0644) | |||
afero.WriteFile(fakefs, "r.txt", []byte("some mock file"), 0644) | |||
} | |||
func testAssertions(t *testing.T, as assertions, fn finderFunc) { | |||
for _, a := range as { | |||
found, err := fn(a.given.file, a.given.base, fakefs) | |||
if a.expect.found { | |||
assert.True(t, found) | |||
} else { | |||
assert.False(t, found) | |||
} | |||
if a.expect.err == "" { | |||
assert.Nil(t, err) | |||
} else { | |||
assert.EqualError(t, err, a.expect.err) | |||
} | |||
} | |||
} | |||
func TestFindIn(t *testing.T) { | |||
assertions := assertions{ | |||
{given{"ab", "/test/a"}, expect{true, ""}}, | |||
{given{"ac", "/test/a"}, expect{true, ""}}, | |||
{given{"ae", "/test/a"}, expect{false, ""}}, | |||
{given{"de", "/test/d"}, expect{false, "open " + filepath.Join("/", "test", "d") + ": file does not exist"}}, | |||
} | |||
testAssertions(t, assertions, findIn) | |||
} | |||
func TestFindUpFrom(t *testing.T) { | |||
assertions := assertions{ | |||
{given{"ab", "/test/a"}, expect{true, ""}}, | |||
{given{"ab", "/test/a/ac"}, expect{true, ""}}, | |||
{given{"t.txt", "/test/a/ac"}, expect{true, ""}}, | |||
{given{"r.txt", "/test/a/ac"}, expect{true, ""}}, | |||
{given{"r.txt", "/test/d"}, expect{false, "open " + filepath.Join("/", "test", "d") + ": file does not exist"}}, | |||
} | |||
testAssertions(t, assertions, findupFrom) | |||
} |
@@ -0,0 +1,8 @@ | |||
module github.com/ojizero/gofindup | |||
go 1.13 | |||
require ( | |||
github.com/spf13/afero v1.2.2 | |||
github.com/stretchr/testify v1.4.0 | |||
) |
@@ -0,0 +1,14 @@ | |||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= | |||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= | |||
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= | |||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | |||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= |