add refresh "button"
This commit is contained in:
parent
8d3f49061b
commit
e43d9520f8
28
fs/group.go
28
fs/group.go
|
@ -12,7 +12,9 @@ import (
|
||||||
type groupNode struct {
|
type groupNode struct {
|
||||||
fs.Inode
|
fs.Inode
|
||||||
param *FSParam
|
param *FSParam
|
||||||
|
|
||||||
group *gitlab.Group
|
group *gitlab.Group
|
||||||
|
staticNodes map[string]staticNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we are implementing the NodeReaddirer interface
|
// Ensure we are implementing the NodeReaddirer interface
|
||||||
|
@ -29,6 +31,9 @@ func newGroupNodeByID(gid int, param *FSParam) (*groupNode, error) {
|
||||||
node := &groupNode{
|
node := &groupNode{
|
||||||
param: param,
|
param: param,
|
||||||
group: group,
|
group: group,
|
||||||
|
staticNodes: map[string]staticNode{
|
||||||
|
".refresh": newRefreshNode(group, param),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
@ -37,13 +42,16 @@ func newGroupNode(group *gitlab.Group, param *FSParam) (*groupNode, error) {
|
||||||
node := &groupNode{
|
node := &groupNode{
|
||||||
param: param,
|
param: param,
|
||||||
group: group,
|
group: group,
|
||||||
|
staticNodes: map[string]staticNode{
|
||||||
|
".refresh": newRefreshNode(group, param),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
||||||
groupContent, _ := n.param.Gitlab.FetchGroupContent(n.group)
|
groupContent, _ := n.param.Gitlab.FetchGroupContent(n.group)
|
||||||
entries := make([]fuse.DirEntry, 0, len(groupContent.Groups)+len(groupContent.Projects))
|
entries := make([]fuse.DirEntry, 0, len(groupContent.Groups)+len(groupContent.Projects)+len(n.staticNodes))
|
||||||
for _, group := range groupContent.Groups {
|
for _, group := range groupContent.Groups {
|
||||||
entries = append(entries, fuse.DirEntry{
|
entries = append(entries, fuse.DirEntry{
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
|
@ -58,6 +66,13 @@ func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
||||||
Mode: fuse.S_IFLNK,
|
Mode: fuse.S_IFLNK,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
for name, staticNode := range n.staticNodes {
|
||||||
|
entries = append(entries, fuse.DirEntry{
|
||||||
|
Name: name,
|
||||||
|
Ino: staticNode.Ino(),
|
||||||
|
Mode: staticNode.Mode(),
|
||||||
|
})
|
||||||
|
}
|
||||||
return fs.NewListDirStream(entries), 0
|
return fs.NewListDirStream(entries), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,5 +100,16 @@ func (n *groupNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
||||||
repositoryNode, _ := newRepositoryNode(project, n.param)
|
repositoryNode, _ := newRepositoryNode(project, n.param)
|
||||||
return n.NewInode(ctx, repositoryNode, attrs), 0
|
return n.NewInode(ctx, repositoryNode, attrs), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the map of static nodes contains it
|
||||||
|
staticNode, ok := n.staticNodes[name]
|
||||||
|
if ok {
|
||||||
|
attrs := fs.StableAttr{
|
||||||
|
Ino: staticNode.Ino(),
|
||||||
|
Mode: staticNode.Mode(),
|
||||||
|
}
|
||||||
|
return n.NewInode(ctx, staticNode, attrs), 0
|
||||||
|
}
|
||||||
|
|
||||||
return nil, syscall.ENOENT
|
return nil, syscall.ENOENT
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/badjware/gitlabfs/gitlab"
|
||||||
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
|
)
|
||||||
|
|
||||||
|
type refreshNode struct {
|
||||||
|
fs.Inode
|
||||||
|
ino uint64
|
||||||
|
refresher gitlab.Refresher
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we are implementing the NodeSetattrer interface
|
||||||
|
var _ = (fs.NodeSetattrer)((*refreshNode)(nil))
|
||||||
|
|
||||||
|
// Ensure we are implementing the NodeOpener interface
|
||||||
|
var _ = (fs.NodeOpener)((*refreshNode)(nil))
|
||||||
|
|
||||||
|
func newRefreshNode(refresher gitlab.Refresher, param *FSParam) *refreshNode {
|
||||||
|
return &refreshNode{
|
||||||
|
ino: <-param.staticInoChan,
|
||||||
|
refresher: refresher,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *refreshNode) Ino() uint64 {
|
||||||
|
return n.ino
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *refreshNode) Mode() uint32 {
|
||||||
|
return fuse.S_IFREG
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *refreshNode) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *refreshNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||||
|
n.refresher.InvalidateCache()
|
||||||
|
return nil, 0, 0
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ type RepositoryNode struct {
|
||||||
var _ = (fs.NodeReadlinker)((*RepositoryNode)(nil))
|
var _ = (fs.NodeReadlinker)((*RepositoryNode)(nil))
|
||||||
|
|
||||||
func newRepositoryNode(project *gitlab.Project, param *FSParam) (*RepositoryNode, error) {
|
func newRepositoryNode(project *gitlab.Project, param *FSParam) (*RepositoryNode, error) {
|
||||||
|
|
||||||
node := &RepositoryNode{
|
node := &RepositoryNode{
|
||||||
param: param,
|
param: param,
|
||||||
project: project,
|
project: project,
|
||||||
|
|
|
@ -15,6 +15,12 @@ const (
|
||||||
staticInodeStart = uint64(int(^(uint(0))>>1)) + 1
|
staticInodeStart = uint64(int(^(uint(0))>>1)) + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type staticNode interface {
|
||||||
|
fs.InodeEmbedder
|
||||||
|
Ino() uint64
|
||||||
|
Mode() uint32
|
||||||
|
}
|
||||||
|
|
||||||
type FSParam struct {
|
type FSParam struct {
|
||||||
Git git.GitClonerPuller
|
Git git.GitClonerPuller
|
||||||
Gitlab gitlab.GitlabFetcher
|
Gitlab gitlab.GitlabFetcher
|
||||||
|
|
30
fs/users.go
30
fs/users.go
|
@ -71,7 +71,9 @@ func (n *usersNode) OnAdd(ctx context.Context) {
|
||||||
type userNode struct {
|
type userNode struct {
|
||||||
fs.Inode
|
fs.Inode
|
||||||
param *FSParam
|
param *FSParam
|
||||||
|
|
||||||
user *gitlab.User
|
user *gitlab.User
|
||||||
|
staticNodes map[string]staticNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we are implementing the NodeReaddirer interface
|
// Ensure we are implementing the NodeReaddirer interface
|
||||||
|
@ -88,6 +90,9 @@ func newUserNodeByID(uid int, param *FSParam) (*userNode, error) {
|
||||||
node := &userNode{
|
node := &userNode{
|
||||||
param: param,
|
param: param,
|
||||||
user: user,
|
user: user,
|
||||||
|
staticNodes: map[string]staticNode{
|
||||||
|
".refresh": newRefreshNode(user, param),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
@ -96,13 +101,16 @@ func newUserNode(user *gitlab.User, param *FSParam) (*userNode, error) {
|
||||||
node := &userNode{
|
node := &userNode{
|
||||||
param: param,
|
param: param,
|
||||||
user: user,
|
user: user,
|
||||||
|
staticNodes: map[string]staticNode{
|
||||||
|
".refresh": newRefreshNode(user, param),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *userNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
func (n *userNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
||||||
userContent, _ := n.param.Gitlab.FetchUserContent(n.user)
|
userContent, _ := n.param.Gitlab.FetchUserContent(n.user)
|
||||||
entries := make([]fuse.DirEntry, 0, len(userContent.Projects))
|
entries := make([]fuse.DirEntry, 0, len(userContent.Projects)+len(n.staticNodes))
|
||||||
for _, project := range userContent.Projects {
|
for _, project := range userContent.Projects {
|
||||||
entries = append(entries, fuse.DirEntry{
|
entries = append(entries, fuse.DirEntry{
|
||||||
Name: project.Name,
|
Name: project.Name,
|
||||||
|
@ -110,11 +118,20 @@ func (n *userNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
|
||||||
Mode: fuse.S_IFLNK,
|
Mode: fuse.S_IFLNK,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
for name, staticNode := range n.staticNodes {
|
||||||
|
entries = append(entries, fuse.DirEntry{
|
||||||
|
Name: name,
|
||||||
|
Ino: staticNode.Ino(),
|
||||||
|
Mode: staticNode.Mode(),
|
||||||
|
})
|
||||||
|
}
|
||||||
return fs.NewListDirStream(entries), 0
|
return fs.NewListDirStream(entries), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *userNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
|
func (n *userNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
|
||||||
userContent, _ := n.param.Gitlab.FetchUserContent(n.user)
|
userContent, _ := n.param.Gitlab.FetchUserContent(n.user)
|
||||||
|
|
||||||
|
// Check if the map of projects contains it
|
||||||
project, ok := userContent.Projects[name]
|
project, ok := userContent.Projects[name]
|
||||||
if ok {
|
if ok {
|
||||||
attrs := fs.StableAttr{
|
attrs := fs.StableAttr{
|
||||||
|
@ -124,5 +141,16 @@ func (n *userNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
||||||
repositoryNode, _ := newRepositoryNode(project, n.param)
|
repositoryNode, _ := newRepositoryNode(project, n.param)
|
||||||
return n.NewInode(ctx, repositoryNode, attrs), 0
|
return n.NewInode(ctx, repositoryNode, attrs), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the map of static nodes contains it
|
||||||
|
staticNode, ok := n.staticNodes[name]
|
||||||
|
if ok {
|
||||||
|
attrs := fs.StableAttr{
|
||||||
|
Ino: staticNode.Ino(),
|
||||||
|
Mode: staticNode.Mode(),
|
||||||
|
}
|
||||||
|
return n.NewInode(ctx, staticNode, attrs), 0
|
||||||
|
}
|
||||||
|
|
||||||
return nil, syscall.ENOENT
|
return nil, syscall.ENOENT
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ type GitlabFetcher interface {
|
||||||
UserFetcher
|
UserFetcher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Refresher interface {
|
||||||
|
InvalidateCache()
|
||||||
|
}
|
||||||
|
|
||||||
type GitlabClientParam struct {
|
type GitlabClientParam struct {
|
||||||
PullMethod string
|
PullMethod string
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/xanzy/go-gitlab"
|
"github.com/xanzy/go-gitlab"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,7 @@ type Group struct {
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
mux sync.Mutex
|
||||||
content *GroupContent
|
content *GroupContent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +33,13 @@ func NewGroupFromGitlabGroup(group *gitlab.Group) Group {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Group) InvalidateCache() {
|
||||||
|
g.mux.Lock()
|
||||||
|
defer g.mux.Unlock()
|
||||||
|
|
||||||
|
g.content = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *gitlabClient) FetchGroup(gid int) (*Group, error) {
|
func (c *gitlabClient) FetchGroup(gid int) (*Group, error) {
|
||||||
gitlabGroup, _, err := c.client.Groups.GetGroup(gid)
|
gitlabGroup, _, err := c.client.Groups.GetGroup(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -41,6 +50,10 @@ func (c *gitlabClient) FetchGroup(gid int) (*Group, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *gitlabClient) FetchGroupContent(group *Group) (*GroupContent, error) {
|
func (c *gitlabClient) FetchGroupContent(group *Group) (*GroupContent, error) {
|
||||||
|
group.mux.Lock()
|
||||||
|
defer group.mux.Unlock()
|
||||||
|
|
||||||
|
// Get cached data if available
|
||||||
if group.content != nil {
|
if group.content != nil {
|
||||||
return group.content, nil
|
return group.content, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/xanzy/go-gitlab"
|
"github.com/xanzy/go-gitlab"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,7 @@ type User struct {
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
mux sync.Mutex
|
||||||
content *UserContent
|
content *UserContent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +33,13 @@ func NewUserFromGitlabUser(user *gitlab.User) User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) InvalidateCache() {
|
||||||
|
u.mux.Lock()
|
||||||
|
defer u.mux.Unlock()
|
||||||
|
|
||||||
|
u.content = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *gitlabClient) FetchUser(uid int) (*User, error) {
|
func (c *gitlabClient) FetchUser(uid int) (*User, error) {
|
||||||
gitlabUser, _, err := c.client.Users.GetUser(uid)
|
gitlabUser, _, err := c.client.Users.GetUser(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -50,6 +59,10 @@ func (c *gitlabClient) FetchCurrentUser() (*User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *gitlabClient) FetchUserContent(user *User) (*UserContent, error) {
|
func (c *gitlabClient) FetchUserContent(user *User) (*UserContent, error) {
|
||||||
|
user.mux.Lock()
|
||||||
|
defer user.mux.Unlock()
|
||||||
|
|
||||||
|
// Get cached data if available
|
||||||
if user.content != nil {
|
if user.content != nil {
|
||||||
return user.content, nil
|
return user.content, nil
|
||||||
}
|
}
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -19,6 +19,7 @@ github.com/go-git/go-git v4.7.0+incompatible/go.mod h1:6+421e08gnZWn30y26Vchf7ef
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||||
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
|
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
|
||||||
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
|
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
@ -96,6 +97,7 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqG
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
|
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
Loading…
Reference in New Issue