Compare commits
No commits in common. "main" and "v1.0.1" have entirely different histories.
6 changed files with 61 additions and 378 deletions
|
|
@ -8,9 +8,8 @@ import (
|
||||||
|
|
||||||
func (a *Action) Client() *Client {
|
func (a *Action) Client() *Client {
|
||||||
c := &Client{Client: &http.Client{}}
|
c := &Client{Client: &http.Client{}}
|
||||||
context := a.Context()
|
c.base = a.env("GITHUB_API_URL")
|
||||||
c.base = context.APIURL
|
c.token = fmt.Sprintf("Bearer %s", a.env("GITHUB_TOKEN"))
|
||||||
c.token = fmt.Sprintf("Bearer %s", context.Token)
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
78
context.go
78
context.go
|
|
@ -2,8 +2,10 @@ package sdk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GitHubContext of current workflow.
|
// GitHubContext of current workflow.
|
||||||
|
|
@ -12,7 +14,7 @@ type GitHubContext struct {
|
||||||
Action string `env:"GITHUB_ACTION"`
|
Action string `env:"GITHUB_ACTION"`
|
||||||
ActionPath string `env:"GITHUB_ACTION_PATH"`
|
ActionPath string `env:"GITHUB_ACTION_PATH"`
|
||||||
ActionRepository string `env:"GITHUB_ACTION_REPOSITORY"`
|
ActionRepository string `env:"GITHUB_ACTION_REPOSITORY"`
|
||||||
Actions string `env:"GITHUB_ACTIONS"`
|
Actions bool `env:"GITHUB_ACTIONS"`
|
||||||
Actor string `env:"GITHUB_ACTOR"`
|
Actor string `env:"GITHUB_ACTOR"`
|
||||||
APIURL string `env:"GITHUB_API_URL,default=https://api.github.com"`
|
APIURL string `env:"GITHUB_API_URL,default=https://api.github.com"`
|
||||||
BaseRef string `env:"GITHUB_BASE_REF"`
|
BaseRef string `env:"GITHUB_BASE_REF"`
|
||||||
|
|
@ -25,36 +27,34 @@ type GitHubContext struct {
|
||||||
Path string `env:"GITHUB_PATH"`
|
Path string `env:"GITHUB_PATH"`
|
||||||
Ref string `env:"GITHUB_REF"`
|
Ref string `env:"GITHUB_REF"`
|
||||||
RefName string `env:"GITHUB_REF_NAME"`
|
RefName string `env:"GITHUB_REF_NAME"`
|
||||||
RefProtected string `env:"GITHUB_REF_PROTECTED"`
|
RefProtected bool `env:"GITHUB_REF_PROTECTED"`
|
||||||
RefType string `env:"GITHUB_REF_TYPE"`
|
RefType string `env:"GITHUB_REF_TYPE"`
|
||||||
|
|
||||||
Repository string `env:"GITHUB_REPOSITORY"`
|
Repository string `env:"GITHUB_REPOSITORY"`
|
||||||
RepositoryOwner string `env:"GITHUB_REPOSITORY_OWNER"`
|
RepositoryOwner string `env:"GITHUB_REPOSITORY_OWNER"`
|
||||||
|
|
||||||
RetentionDays string `env:"GITHUB_RETENTION_DAYS"`
|
RetentionDays int64 `env:"GITHUB_RETENTION_DAYS"`
|
||||||
RunAttempt string `env:"GITHUB_RUN_ATTEMPT"`
|
RunAttempt int64 `env:"GITHUB_RUN_ATTEMPT"`
|
||||||
RunID string `env:"GITHUB_RUN_ID"`
|
RunID int64 `env:"GITHUB_RUN_ID"`
|
||||||
RunNumber string `env:"GITHUB_RUN_NUMBER"`
|
RunNumber int64 `env:"GITHUB_RUN_NUMBER"`
|
||||||
ServerURL string `env:"GITHUB_SERVER_URL,default=https://github.com"`
|
ServerURL string `env:"GITHUB_SERVER_URL,default=https://github.com"`
|
||||||
SHA string `env:"GITHUB_SHA"`
|
SHA string `env:"GITHUB_SHA"`
|
||||||
StepSummary string `env:"GITHUB_STEP_SUMMARY"`
|
StepSummary string `env:"GITHUB_STEP_SUMMARY"`
|
||||||
Workflow string `env:"GITHUB_WORKFLOW"`
|
Workflow string `env:"GITHUB_WORKFLOW"`
|
||||||
Workspace string `env:"GITHUB_WORKSPACE"`
|
Workspace string `env:"GITHUB_WORKSPACE"`
|
||||||
|
|
||||||
Token string `env:"GITHUB_TOKEN"`
|
|
||||||
|
|
||||||
// Event is populated by parsing the file at EventPath, if it exists.
|
// Event is populated by parsing the file at EventPath, if it exists.
|
||||||
event map[string]any
|
Event map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context returns the context of current action with the payload object
|
// Context returns the context of current action with the payload object
|
||||||
// that triggered the workflow
|
// that triggered the workflow
|
||||||
func (c *Action) Context() *GitHubContext {
|
func (c *Action) Context() (*GitHubContext, error) {
|
||||||
|
var merr error
|
||||||
context := &GitHubContext{
|
context := &GitHubContext{
|
||||||
APIURL: "https://api.github.com",
|
APIURL: "https://api.github.com",
|
||||||
GraphqlURL: "https://api.github.com/graphql",
|
GraphqlURL: "https://api.github.com/graphql",
|
||||||
ServerURL: "https://github.com",
|
ServerURL: "https://github.com",
|
||||||
event: map[string]any{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := c.env("GITHUB_ACTION"); v != "" {
|
if v := c.env("GITHUB_ACTION"); v != "" {
|
||||||
|
|
@ -66,8 +66,10 @@ func (c *Action) Context() *GitHubContext {
|
||||||
if v := c.env("GITHUB_ACTION_REPOSITORY"); v != "" {
|
if v := c.env("GITHUB_ACTION_REPOSITORY"); v != "" {
|
||||||
context.ActionRepository = v
|
context.ActionRepository = v
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_ACTIONS"); v != "" {
|
if v, err := parseBool(c.env("GITHUB_ACTIONS")); err == nil {
|
||||||
context.Actions = v
|
context.Actions = v
|
||||||
|
} else {
|
||||||
|
merr = errors.Join(merr, err)
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_ACTOR"); v != "" {
|
if v := c.env("GITHUB_ACTOR"); v != "" {
|
||||||
context.Actor = v
|
context.Actor = v
|
||||||
|
|
@ -105,29 +107,41 @@ func (c *Action) Context() *GitHubContext {
|
||||||
if v := c.env("GITHUB_REF_NAME"); v != "" {
|
if v := c.env("GITHUB_REF_NAME"); v != "" {
|
||||||
context.RefName = v
|
context.RefName = v
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_REF_PROTECTED"); v != "" {
|
if v, err := parseBool(c.env("GITHUB_REF_PROTECTED")); err == nil {
|
||||||
context.RefProtected = v
|
context.RefProtected = v
|
||||||
|
} else {
|
||||||
|
merr = errors.Join(merr, err)
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_REF_TYPE"); v != "" {
|
if v := c.env("GITHUB_REF_TYPE"); v != "" {
|
||||||
context.RefType = v
|
context.RefType = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := c.env("GITHUB_REPOSITORY"); v != "" {
|
if v := c.env("GITHUB_REPOSITORY"); v != "" {
|
||||||
context.Repository = v
|
context.Repository = v
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_REPOSITORY_OWNER"); v != "" {
|
if v := c.env("GITHUB_REPOSITORY_OWNER"); v != "" {
|
||||||
context.RepositoryOwner = v
|
context.RepositoryOwner = v
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_RETENTION_DAYS"); v != "" {
|
|
||||||
|
if v, err := parseInt(c.env("GITHUB_RETENTION_DAYS")); err == nil {
|
||||||
context.RetentionDays = v
|
context.RetentionDays = v
|
||||||
|
} else {
|
||||||
|
merr = errors.Join(merr, err)
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_RUN_ATTEMPT"); v != "" {
|
if v, err := parseInt(c.env("GITHUB_RUN_ATTEMPT")); err == nil {
|
||||||
context.RunAttempt = v
|
context.RunAttempt = v
|
||||||
|
} else {
|
||||||
|
merr = errors.Join(merr, err)
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_RUN_ID"); v != "" {
|
if v, err := parseInt(c.env("GITHUB_RUN_ID")); err == nil {
|
||||||
context.RunID = v
|
context.RunID = v
|
||||||
|
} else {
|
||||||
|
merr = errors.Join(merr, err)
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_RUN_NUMBER"); v != "" {
|
if v, err := parseInt(c.env("GITHUB_RUN_NUMBER")); err == nil {
|
||||||
context.RunNumber = v
|
context.RunNumber = v
|
||||||
|
} else {
|
||||||
|
merr = errors.Join(merr, err)
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_SERVER_URL"); v != "" {
|
if v := c.env("GITHUB_SERVER_URL"); v != "" {
|
||||||
context.ServerURL = v
|
context.ServerURL = v
|
||||||
|
|
@ -144,24 +158,32 @@ func (c *Action) Context() *GitHubContext {
|
||||||
if v := c.env("GITHUB_WORKSPACE"); v != "" {
|
if v := c.env("GITHUB_WORKSPACE"); v != "" {
|
||||||
context.Workspace = v
|
context.Workspace = v
|
||||||
}
|
}
|
||||||
if v := c.env("GITHUB_TOKEN"); v != "" {
|
|
||||||
context.Token = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return context
|
if context.EventPath != "" {
|
||||||
}
|
eventData, err := os.ReadFile(context.EventPath)
|
||||||
|
|
||||||
func (c *GitHubContext) Event() (map[string]any, error) {
|
|
||||||
if c.EventPath != "" {
|
|
||||||
eventData, err := os.ReadFile(c.EventPath)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("could not read event file: %w", err)
|
return nil, fmt.Errorf("could not read event file: %w", err)
|
||||||
}
|
}
|
||||||
if eventData != nil {
|
if eventData != nil {
|
||||||
if err := json.Unmarshal(eventData, &c.event); err != nil {
|
if err := json.Unmarshal(eventData, &context.Event); err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal event payload: %w", err)
|
return nil, fmt.Errorf("failed to unmarshal event payload: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.event, nil
|
|
||||||
|
return context, merr
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBool(v string) (bool, error) {
|
||||||
|
if v == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return strconv.ParseBool(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInt(v string) (int64, error) {
|
||||||
|
if v == "" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(v, 10, 64)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ func TestAction_Context(t *testing.T) {
|
||||||
APIURL: "https://api.github.com",
|
APIURL: "https://api.github.com",
|
||||||
ServerURL: "https://github.com",
|
ServerURL: "https://github.com",
|
||||||
GraphqlURL: "https://api.github.com/graphql",
|
GraphqlURL: "https://api.github.com/graphql",
|
||||||
event: map[string]any{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -71,13 +70,12 @@ func TestAction_Context(t *testing.T) {
|
||||||
"GITHUB_STEP_SUMMARY": "/path/to/summary",
|
"GITHUB_STEP_SUMMARY": "/path/to/summary",
|
||||||
"GITHUB_WORKFLOW": "test",
|
"GITHUB_WORKFLOW": "test",
|
||||||
"GITHUB_WORKSPACE": "/path/to/workspace",
|
"GITHUB_WORKSPACE": "/path/to/workspace",
|
||||||
"GITHUB_TOKEN": "somerandomtoken",
|
|
||||||
},
|
},
|
||||||
exp: &GitHubContext{
|
exp: &GitHubContext{
|
||||||
Action: "__repo-owner_name-of-action-repo",
|
Action: "__repo-owner_name-of-action-repo",
|
||||||
ActionPath: "/path/to/action",
|
ActionPath: "/path/to/action",
|
||||||
ActionRepository: "repo-owner/name-of-action-repo",
|
ActionRepository: "repo-owner/name-of-action-repo",
|
||||||
Actions: "true",
|
Actions: true,
|
||||||
Actor: "sethvargo",
|
Actor: "sethvargo",
|
||||||
APIURL: "https://foo.com",
|
APIURL: "https://foo.com",
|
||||||
BaseRef: "main",
|
BaseRef: "main",
|
||||||
|
|
@ -90,21 +88,19 @@ func TestAction_Context(t *testing.T) {
|
||||||
Path: "/path/to/path",
|
Path: "/path/to/path",
|
||||||
Ref: "refs/tags/v1.0",
|
Ref: "refs/tags/v1.0",
|
||||||
RefName: "v1.0",
|
RefName: "v1.0",
|
||||||
RefProtected: "true",
|
RefProtected: true,
|
||||||
RefType: "tag",
|
RefType: "tag",
|
||||||
Repository: "sethvargo/baz",
|
Repository: "sethvargo/baz",
|
||||||
RepositoryOwner: "sethvargo",
|
RepositoryOwner: "sethvargo",
|
||||||
RetentionDays: "90",
|
RetentionDays: 90,
|
||||||
RunAttempt: "6",
|
RunAttempt: 6,
|
||||||
RunID: "56",
|
RunID: 56,
|
||||||
RunNumber: "34",
|
RunNumber: 34,
|
||||||
ServerURL: "https://bar.com",
|
ServerURL: "https://bar.com",
|
||||||
SHA: "abcd1234",
|
SHA: "abcd1234",
|
||||||
StepSummary: "/path/to/summary",
|
StepSummary: "/path/to/summary",
|
||||||
Workflow: "test",
|
Workflow: "test",
|
||||||
Workspace: "/path/to/workspace",
|
Workspace: "/path/to/workspace",
|
||||||
Token: "somerandomtoken",
|
|
||||||
event: map[string]any{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +116,7 @@ func TestAction_Context(t *testing.T) {
|
||||||
ServerURL: "https://github.com",
|
ServerURL: "https://github.com",
|
||||||
GraphqlURL: "https://api.github.com/graphql",
|
GraphqlURL: "https://api.github.com/graphql",
|
||||||
|
|
||||||
event: map[string]any{
|
Event: map[string]any{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -135,8 +131,7 @@ func TestAction_Context(t *testing.T) {
|
||||||
|
|
||||||
a := New()
|
a := New()
|
||||||
a.env = func(s string) string { return tc.env[s] }
|
a.env = func(s string) string { return tc.env[s] }
|
||||||
got := a.Context()
|
got, err := a.Context()
|
||||||
_, err := got.Event()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -1,3 +1,3 @@
|
||||||
module code.geekeey.de/actions/sdk
|
module git.geekeey.de/actions/sdk
|
||||||
|
|
||||||
go 1.22.5
|
go 1.22.5
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
package iox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GlobFS struct {
|
|
||||||
base fs.FS
|
|
||||||
patterns []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGlobFS creates a new GlobFS that exposes only files matching any of the given glob patterns.
|
|
||||||
func NewGlobFS(base fs.FS, patterns ...string) *GlobFS {
|
|
||||||
return &GlobFS{base: base, patterns: patterns}
|
|
||||||
}
|
|
||||||
|
|
||||||
// match reports whether the given path matches any of the configured patterns.
|
|
||||||
func (g *GlobFS) match(name string) bool {
|
|
||||||
for _, pat := range g.patterns {
|
|
||||||
if matched, _ := path.Match(pat, name); matched {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GlobFS) contains(dir string) bool {
|
|
||||||
queue := []string{dir}
|
|
||||||
visited := make(map[string]struct{})
|
|
||||||
for len(queue) > 0 {
|
|
||||||
current := queue[0]
|
|
||||||
queue = queue[1:] // dequeue
|
|
||||||
|
|
||||||
// Prevent visiting same dir multiple times
|
|
||||||
if _, seen := visited[current]; seen {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited[current] = struct{}{}
|
|
||||||
|
|
||||||
entries, err := fs.ReadDir(g.base, current)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
rel := path.Join(current, entry.Name())
|
|
||||||
if g.match(rel) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if entry.IsDir() {
|
|
||||||
queue = append(queue, rel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GlobFS) Open(name string) (fs.File, error) {
|
|
||||||
if g.match(name) {
|
|
||||||
return g.base.Open(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := fs.Stat(g.base, name)
|
|
||||||
if err != nil || !fi.IsDir() {
|
|
||||||
return nil, fs.ErrNotExist
|
|
||||||
}
|
|
||||||
if g.contains(name) {
|
|
||||||
return g.base.Open(name)
|
|
||||||
}
|
|
||||||
return nil, fs.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GlobFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
|
||||||
if g.match(name) {
|
|
||||||
return fs.ReadDir(g.base, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
entries, err := fs.ReadDir(g.base, name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var children []fs.DirEntry
|
|
||||||
for _, entry := range entries {
|
|
||||||
rel := path.Join(name, entry.Name())
|
|
||||||
if g.match(rel) {
|
|
||||||
children = append(children, entry)
|
|
||||||
}
|
|
||||||
if entry.IsDir() && g.contains(rel) {
|
|
||||||
children = append(children, entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(children) == 0 {
|
|
||||||
return nil, os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return children, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
package iox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/fs"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
"testing/fstest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setupFS() fs.ReadDirFS {
|
|
||||||
// Create an in-memory FS with a mix of files and directories
|
|
||||||
return fstest.MapFS{
|
|
||||||
"main.go": &fstest.MapFile{Data: []byte("package main")},
|
|
||||||
"main_test.go": &fstest.MapFile{Data: []byte("package main_test")},
|
|
||||||
"README.md": &fstest.MapFile{Data: []byte("# readme")},
|
|
||||||
"LICENSE": &fstest.MapFile{Data: []byte("MIT")},
|
|
||||||
"docs/guide.md": &fstest.MapFile{Data: []byte("Docs")},
|
|
||||||
"docs/other.txt": &fstest.MapFile{Data: []byte("Other")},
|
|
||||||
"docs/hidden/.keep": &fstest.MapFile{Data: []byte("")},
|
|
||||||
"assets/img.png": &fstest.MapFile{Data: []byte("PNG")},
|
|
||||||
"assets/style.css": &fstest.MapFile{Data: []byte("CSS")},
|
|
||||||
".gitignore": &fstest.MapFile{Data: []byte("*.log")},
|
|
||||||
".hiddenfile": &fstest.MapFile{Data: []byte("")},
|
|
||||||
"emptydir/": &fstest.MapFile{Mode: fs.ModeDir},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper to get base names for easier comparison
|
|
||||||
func basenames(entries []fs.DirEntry) []string {
|
|
||||||
names := []string{}
|
|
||||||
for _, e := range entries {
|
|
||||||
names = append(names, e.Name())
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSMultiplePatterns(t *testing.T) {
|
|
||||||
memfs := setupFS()
|
|
||||||
gfs := NewGlobFS(memfs, "*.go", "*.md", "assets/*", "docs/guide.md", ".gitignore")
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
path string
|
|
||||||
want []string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{path: ".", want: []string{"README.md", "assets", "docs", "main.go", "main_test.go", ".gitignore"}},
|
|
||||||
{path: "assets", want: []string{"img.png", "style.css"}},
|
|
||||||
{path: "docs", want: []string{"guide.md"}},
|
|
||||||
{path: "docs/hidden", want: []string{}, wantErr: true},
|
|
||||||
{path: "emptydir", want: []string{}, wantErr: true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc // capture range variable
|
|
||||||
t.Run(tc.path, func(t *testing.T) {
|
|
||||||
entries, err := fs.ReadDir(gfs, tc.path)
|
|
||||||
if tc.wantErr && err == nil {
|
|
||||||
t.Errorf("expected error, got nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !tc.wantErr && err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
got := basenames(entries)
|
|
||||||
sort.Strings(tc.want)
|
|
||||||
if !reflect.DeepEqual(got, tc.want) {
|
|
||||||
t.Errorf("got %v; want %v", got, tc.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSOpen(t *testing.T) {
|
|
||||||
memfs := setupFS()
|
|
||||||
gfs := NewGlobFS(memfs, "*.go", "*.md", "assets/*", "docs/guide.md", ".gitignore")
|
|
||||||
|
|
||||||
type test struct {
|
|
||||||
path string
|
|
||||||
wantErr bool
|
|
||||||
}
|
|
||||||
tests := []test{
|
|
||||||
{path: "main.go"},
|
|
||||||
{path: "README.md"},
|
|
||||||
{path: "LICENSE", wantErr: true},
|
|
||||||
{path: "assets/img.png"},
|
|
||||||
{path: "assets/style.css"},
|
|
||||||
{path: "assets/nonexistent.png", wantErr: true},
|
|
||||||
{path: "docs/guide.md"},
|
|
||||||
{path: "docs/other.txt", wantErr: true},
|
|
||||||
{path: ".gitignore"},
|
|
||||||
{path: ".hiddenfile", wantErr: true},
|
|
||||||
{path: "docs/hidden/.keep", wantErr: true},
|
|
||||||
{path: "emptydir", wantErr: true},
|
|
||||||
{path: "docs"}, // allowed because it contains matching file(s)
|
|
||||||
{path: "assets"}, // allowed because it contains matching file(s)
|
|
||||||
}
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.path, func(t *testing.T) {
|
|
||||||
f, err := gfs.Open(tc.path)
|
|
||||||
if tc.wantErr && err == nil {
|
|
||||||
t.Errorf("expected error, got file")
|
|
||||||
if f != nil {
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
} else if !tc.wantErr && err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
} else if !tc.wantErr && err == nil {
|
|
||||||
info, _ := f.Stat()
|
|
||||||
if info.IsDir() {
|
|
||||||
_, derr := fs.ReadDir(gfs, tc.path)
|
|
||||||
if derr != nil && !tc.wantErr {
|
|
||||||
t.Errorf("unexpected error: %v", derr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSReadFile(t *testing.T) {
|
|
||||||
memfs := setupFS()
|
|
||||||
gfs := NewGlobFS(memfs, "*.go", "*.md", "assets/*", ".gitignore")
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
want []byte
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{name: "main.go", want: []byte("package main")},
|
|
||||||
{name: "main_test.go", want: []byte("package main_test")},
|
|
||||||
{name: "README.md", want: []byte("# readme")},
|
|
||||||
{name: "assets/img.png", want: []byte("PNG")},
|
|
||||||
{name: "assets/style.css", want: []byte("CSS")},
|
|
||||||
{name: ".gitignore", want: []byte("*.log")},
|
|
||||||
{name: "LICENSE", wantErr: true}, // not allowed by filter
|
|
||||||
{name: "docs/guide.md", wantErr: true}, // not allowed by filter
|
|
||||||
{name: "docs/hidden/.keep", wantErr: true}, // not allowed by filter
|
|
||||||
{name: "doesnotexist.txt", wantErr: true}, // does not exist
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
got, err := fs.ReadFile(gfs, tc.name)
|
|
||||||
if tc.wantErr {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error, got nil (got=%q)", got)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if string(got) != string(tc.want) {
|
|
||||||
t.Errorf("got %q; want %q", got, tc.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSRelativePaths(t *testing.T) {
|
|
||||||
memfs := setupFS()
|
|
||||||
gfs := NewGlobFS(memfs, "docs/*.md")
|
|
||||||
entries, err := fs.ReadDir(gfs, "docs")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
got := basenames(entries)
|
|
||||||
want := []string{"guide.md"}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("docs/*.md: got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSNoMatchesOpen(t *testing.T) {
|
|
||||||
gfs := NewGlobFS(setupFS(), "*.xyz")
|
|
||||||
_, err := gfs.Open("main.go")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error when opening file with no matches")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSNoMatchesStat(t *testing.T) {
|
|
||||||
gfs := NewGlobFS(setupFS(), "*.xyz")
|
|
||||||
_, err := fs.Stat(gfs, "main.go")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error with no matches: stat")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSNoMatchesReadDir(t *testing.T) {
|
|
||||||
gfs := NewGlobFS(setupFS(), "*.xyz")
|
|
||||||
_, err := fs.ReadDir(gfs, "main.go")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error with no matches: readdir")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFSNoMatchesReadFile(t *testing.T) {
|
|
||||||
gfs := NewGlobFS(setupFS(), "*.xyz")
|
|
||||||
_, err := fs.ReadFile(gfs, "main.go")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error with no matches: readfile")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobFS_IntegrationWithStdlib(t *testing.T) {
|
|
||||||
memfs := setupFS()
|
|
||||||
gfs := NewGlobFS(memfs, "*.go", "docs/guide.md")
|
|
||||||
// Use fs.WalkDir with our filtered FS
|
|
||||||
var walked []string
|
|
||||||
err := fs.WalkDir(gfs, ".", func(path string, d fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
walked = append(walked, path)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Only files and dirs matching or containing matches should appear
|
|
||||||
for _, p := range walked {
|
|
||||||
if p == "." || p == "main.go" || p == "main_test.go" || p == "docs" || p == "docs/guide.md" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.Errorf("WalkDir: unexpected path %q", p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue