drone/gitrpc/internal/gitea/tree.go

157 lines
4.5 KiB
Go

// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package gitea
import (
"context"
"fmt"
"path"
"path/filepath"
"strings"
"github.com/harness/gitness/gitrpc/internal/types"
gitea "code.gitea.io/gitea/modules/git"
)
func cleanTreePath(treePath string) string {
return strings.Trim(path.Clean("/"+treePath), "/")
}
// GetTreeNode returns the tree node at the given path as found for the provided reference.
// Note: ref can be Branch / Tag / CommitSHA.
func (g Adapter) GetTreeNode(ctx context.Context, repoPath string,
ref string, treePath string) (*types.TreeNode, error) {
treePath = cleanTreePath(treePath)
giteaRepo, err := gitea.OpenRepository(ctx, repoPath)
if err != nil {
return nil, err
}
defer giteaRepo.Close()
// Get the giteaCommit object for the ref
giteaCommit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
}
// TODO: handle ErrNotExist :)
giteaTreeEntry, err := giteaCommit.GetTreeEntryByPath(treePath)
if err != nil {
return nil, processGiteaErrorf(err, "failed to get tree entry for commit '%s' at path '%s'",
giteaCommit.ID.String(), treePath)
}
nodeType, mode, err := mapGiteaNodeToTreeNodeModeAndType(giteaTreeEntry.Mode())
if err != nil {
return nil, err
}
return &types.TreeNode{
Mode: mode,
NodeType: nodeType,
Sha: giteaTreeEntry.ID.String(),
Name: giteaTreeEntry.Name(),
Path: treePath,
}, nil
}
// ListTreeNodes lists the child nodes of a tree reachable from ref via the specified path
// and includes the latest commit for all nodes if requested.
// IMPORTANT: recursive and includeLatestCommit can't be used together.
// Note: ref can be Branch / Tag / CommitSHA.
//
//nolint:gocognit // refactor if needed
func (g Adapter) ListTreeNodes(ctx context.Context, repoPath string,
ref string, treePath string, recursive bool, includeLatestCommit bool) ([]types.TreeNodeWithCommit, error) {
if recursive && includeLatestCommit {
// To avoid potential performance catastrophies, block recursive with includeLatestCommit
// TODO: this should return bad error to caller if needed?
// TODO: should this be refactored in two methods?
return nil, fmt.Errorf("latest commit with recursive query is not supported")
}
treePath = cleanTreePath(treePath)
giteaRepo, err := gitea.OpenRepository(ctx, repoPath)
if err != nil {
return nil, err
}
defer giteaRepo.Close()
// Get the giteaCommit object for the ref
giteaCommit, err := giteaRepo.GetCommit(ref)
if err != nil {
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
}
// Get the giteaTree object for the ref
giteaTree, err := giteaCommit.SubTree(treePath)
if err != nil {
return nil, processGiteaErrorf(err, "error getting tree for '%s'", treePath)
}
var giteaEntries gitea.Entries
if recursive {
giteaEntries, err = giteaTree.ListEntriesRecursive()
} else {
giteaEntries, err = giteaTree.ListEntries()
}
if err != nil {
return nil, processGiteaErrorf(err, "failed to list entries for tree '%s'", treePath)
}
var latestCommits []gitea.CommitInfo
if includeLatestCommit {
// TODO: can be speed up with latestCommitCache (currently nil)
latestCommits, _, err = giteaEntries.GetCommitsInfo(ctx, giteaCommit, treePath, nil)
if err != nil {
return nil, processGiteaErrorf(err, "failed to get latest commits for entries")
}
if len(latestCommits) != len(giteaEntries) {
return nil, fmt.Errorf("latest commit info doesn't match tree node info - count differs")
}
}
nodes := make([]types.TreeNodeWithCommit, len(giteaEntries))
for i := range giteaEntries {
giteaEntry := giteaEntries[i]
var nodeType types.TreeNodeType
var mode types.TreeNodeMode
nodeType, mode, err = mapGiteaNodeToTreeNodeModeAndType(giteaEntry.Mode())
if err != nil {
return nil, err
}
// giteaNode.Name() returns the path of the node relative to the tree.
relPath := giteaEntry.Name()
name := filepath.Base(relPath)
var commit *types.Commit
if includeLatestCommit {
commit, err = mapGiteaCommit(latestCommits[i].Commit)
if err != nil {
return nil, err
}
}
nodes[i] = types.TreeNodeWithCommit{
TreeNode: types.TreeNode{
NodeType: nodeType,
Mode: mode,
Sha: giteaEntry.ID.String(),
Name: name,
Path: filepath.Join(treePath, relPath),
},
Commit: commit,
}
}
return nodes, nil
}